Skip to content

Commit db97d73

Browse files
committed
feat(build): Move HTML copying into the broccoli task.
This includes all tasks to construct a Dart tree, except for formatting, and reverse engineers/refactors the various copy tools for added more sanity.
1 parent 0e3d0fb commit db97d73

File tree

4 files changed

+167
-21
lines changed

4 files changed

+167
-21
lines changed

gulpfile.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ gulp.task('build/clean.docs', clean(gulp, gulpPlugins, {
294294
// ------------
295295
// transpile
296296

297-
gulp.task('build/transpile.dart', ['build.broccoli.tools'], function() {
297+
gulp.task('build/tree.dart', ['build.broccoli.tools'], function() {
298298
return getBroccoli().forDartTree().buildOnce();
299299
});
300300

@@ -617,12 +617,7 @@ gulp.task('test.transpiler.unittest', function() {
617617

618618
// Builds all Dart packages, but does not compile them
619619
gulp.task('build/packages.dart', function(done) {
620-
runSequence(
621-
'build/transpile.dart', // Creates the folder structure needed by subsequent tasks.
622-
['build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
623-
'build/format.dart',
624-
done
625-
);
620+
runSequence('build/tree.dart', 'build/format.dart', done);
626621
});
627622

628623
// Builds and compiles all Dart packages

tools/broccoli/broccoli-filter.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/// <reference path="../typings/es6-promise/es6-promise.d.ts" />
22

3+
interface FilterOptions {
4+
extensions?: string[]
5+
}
36

47
declare class Filter {
5-
constructor(inputTree: any);
8+
constructor(inputTree: any, options?: FilterOptions);
69
processString(contents: string, relativePath: string): string;
710
// NB: This function is probably not intended as part of the public API
811
processFile(srcDir: string, destDir: string, relativePath: string): Promise<any>;

tools/broccoli/trees/dart_tree.ts

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,65 @@
1+
/// <reference path="../../typings/node/node.d.ts" />
12
'use strict';
23

4+
import {MultiCopy} from './multi_copy';
35
var Funnel = require('broccoli-funnel');
6+
var glob = require('glob');
7+
var mergeTrees = require('broccoli-merge-trees');
48
var path = require('path');
9+
var renderLodashTemplate = require('broccoli-lodash');
510
var replace = require('broccoli-replace');
611
var stew = require('broccoli-stew');
712
var ts2dart = require('../broccoli-ts2dart');
813

9-
module.exports = function makeDartTree() {
10-
// Transpile everything in 'modules'...
11-
var modulesTree = new Funnel('modules', {
12-
include: ['**/*.js', '**/*.ts', '**/*.dart'], // .dart file available means don't translate.
13-
exclude: ['rtts_assert/**/*'], // ... except for the rtts_asserts (don't apply to Dart).
14-
destDir: '/' // Remove the 'modules' prefix.
15-
});
14+
/**
15+
* A funnel starting at modules, including the given filters, and moving into the root.
16+
* @param include Include glob filters.
17+
*/
18+
function modulesFunnel(include: string[], exclude?: string[]) {
19+
return new Funnel('modules', {include, destDir: '/', exclude});
20+
}
21+
22+
/**
23+
* Replaces $SCRIPT$ in .html files with actual <script> tags.
24+
*/
25+
function replaceScriptTagInHtml(content: string, relativePath: string): string {
26+
var scriptTags = '';
27+
if (relativePath.match(/^benchmarks/)) {
28+
scriptTags += '<script src="url_params_to_form.js" type="text/javascript"></script>\n';
29+
}
30+
var scriptName = relativePath.replace(/.*\/([^/]+)\.html$/, '$1.dart');
31+
scriptTags += '<script src="' + scriptName + '" type="application/dart"></script>\n' +
32+
'<script src="packages/browser/dart.js" type="text/javascript"></script>';
33+
return content.replace('$SCRIPTS$', scriptTags);
34+
}
35+
36+
function stripModulePrefix(relativePath: string): string {
37+
if (!relativePath.match(/^modules\//)) {
38+
throw new Error('Expected path to root at modules/: ' + relativePath);
39+
}
40+
return relativePath.replace(/^modules\//, '');
41+
}
1642

17-
// Transpile to dart.
18-
var dartTree = ts2dart.transpile(modulesTree);
43+
function getSourceTree() {
44+
// Transpile everything in 'modules' except for rtts_assertions.
45+
var tsInputTree = modulesFunnel(['**/*.js', '**/*.ts', '**/*.dart'], ['rtts_assert/**/*']);
46+
var transpiled = ts2dart.transpile(tsInputTree);
47+
// Native sources, dart only examples, etc.
48+
var dartSrcs = modulesFunnel(['**/*.dart']);
49+
return mergeTrees([transpiled, dartSrcs]);
50+
}
1951

52+
function fixDartFolderLayout(sourceTree) {
2053
// Move around files to match Dart's layout expectations.
21-
dartTree = stew.rename(dartTree, function(relativePath) {
54+
return stew.rename(sourceTree, function(relativePath) {
2255
// If a file matches the `pattern`, insert the given `insertion` as the second path part.
2356
var replacements = [
2457
{pattern: /^benchmarks\/test\//, insertion: ''},
2558
{pattern: /^benchmarks\//, insertion: 'web'},
2659
{pattern: /^benchmarks_external\/test\//, insertion: ''},
2760
{pattern: /^benchmarks_external\//, insertion: 'web'},
28-
{pattern: /^example.?\//, insertion: 'web/'},
29-
{pattern: /^example.?\/test\//, insertion: ''},
61+
{pattern: /^examples\/test\//, insertion: ''},
62+
{pattern: /^examples\//, insertion: 'web/'},
3063
{pattern: /^[^\/]*\/test\//, insertion: ''},
3164
{pattern: /^./, insertion: 'lib'}, // catch all.
3265
];
@@ -41,7 +74,75 @@ module.exports = function makeDartTree() {
4174
}
4275
throw new Error('Failed to match any path: ' + relativePath);
4376
});
77+
}
78+
79+
function getHtmlSourcesTree() {
80+
// Replace $SCRIPT$ markers in HTML files.
81+
var htmlSrcsTree = stew.map(modulesFunnel(['*/src/**/*.html']), replaceScriptTagInHtml);
82+
// Copy a url_params_to_form.js for each benchmark html file.
83+
var urlParamsToFormTree = new MultiCopy('', {
84+
srcPath: 'tools/build/snippets/url_params_to_form.js',
85+
targetPatterns: ['modules/benchmarks*/src/*', 'modules/benchmarks*/src/*/*'],
86+
});
87+
urlParamsToFormTree = stew.rename(urlParamsToFormTree, stripModulePrefix);
88+
return mergeTrees([htmlSrcsTree, urlParamsToFormTree]);
89+
}
90+
91+
92+
function getTemplatedPubspecsTree() {
93+
// The JSON structure for templating pubspec.yaml files.
94+
var BASE_PACKAGE_JSON = require('../../../package.json');
95+
var COMMON_PACKAGE_JSON = {
96+
version: BASE_PACKAGE_JSON.version,
97+
homepage: BASE_PACKAGE_JSON.homepage,
98+
bugs: BASE_PACKAGE_JSON.bugs,
99+
license: BASE_PACKAGE_JSON.license,
100+
contributors: BASE_PACKAGE_JSON.contributors,
101+
dependencies: BASE_PACKAGE_JSON.dependencies,
102+
devDependencies: {
103+
"yargs": BASE_PACKAGE_JSON.devDependencies['yargs'],
104+
"gulp-sourcemaps": BASE_PACKAGE_JSON.devDependencies['gulp-sourcemaps'],
105+
"gulp-traceur": BASE_PACKAGE_JSON.devDependencies['gulp-traceur'],
106+
"gulp": BASE_PACKAGE_JSON.devDependencies['gulp'],
107+
"gulp-rename": BASE_PACKAGE_JSON.devDependencies['gulp-rename'],
108+
"through2": BASE_PACKAGE_JSON.devDependencies['through2']
109+
}
110+
};
111+
// Generate pubspec.yaml from templates.
112+
// Lodash insists on dropping one level of extension, so first artificially rename the yaml
113+
// files to .yaml.template.
114+
var pubspecs = stew.rename(modulesFunnel(['**/pubspec.yaml']), '.yaml', '.yaml.template');
115+
// Then render the templates.
116+
return renderLodashTemplate(
117+
pubspecs,
118+
{files: ['**/pubspec.yaml.template'], context: {'packageJson': COMMON_PACKAGE_JSON}});
119+
}
120+
121+
function getDocsTree() {
122+
// LICENSE files
123+
var licenses = new MultiCopy('', {
124+
srcPath: 'LICENSE',
125+
targetPatterns: ['modules/*'],
126+
exclude: ['*/rtts_assert'], // Not in dart.
127+
});
128+
licenses = stew.rename(licenses, stripModulePrefix);
129+
130+
// Documentation.
131+
// Rename *.dart.md -> *.dart.
132+
var mdTree = stew.rename(modulesFunnel(['**/*.dart.md']),
133+
relativePath => relativePath.replace(/\.dart\.md$/, '.md'));
134+
// Copy all assets, ignore .js. and .dart. (handled above).
135+
var docs = modulesFunnel(['**/*.md', '**/*.png', '**/*.html', '**/*.css'],
136+
['**/*.js.md', '**/*.dart.md']);
137+
return mergeTrees([licenses, mdTree, docs]);
138+
}
139+
140+
module.exports = function makeDartTree() {
141+
var sourceTree = mergeTrees([getSourceTree(), getHtmlSourcesTree()]);
142+
sourceTree = fixDartFolderLayout(sourceTree);
143+
144+
var mergedResult = mergeTrees([sourceTree, getTemplatedPubspecsTree(), getDocsTree()]);
44145

45146
// Move the tree under the 'dart' folder.
46-
return stew.mv(dartTree, 'dart');
147+
return stew.mv(mergedResult, 'dart');
47148
};

tools/broccoli/trees/multi_copy.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/// <reference path="../../typings/node/node.d.ts" />
2+
/// <reference path="../../typings/fs-extra/fs-extra.d.ts" />
3+
import Writer = require('broccoli-writer');
4+
import fs = require('fs');
5+
import fsx = require('fs-extra');
6+
var minimatch = require('minimatch');
7+
var path = require('path');
8+
var glob = require('glob');
9+
10+
export interface MultiCopyOptions {
11+
/** The path of the file to copy. */
12+
srcPath: string,
13+
/** A list of glob patterns of folders to copy to, matched against the input tree. */
14+
targetPatterns: string[],
15+
/** List of glob patterns to *not* copy to, matched against the matches from `targetPatterns`. */
16+
exclude?: string[],
17+
}
18+
19+
/**
20+
* A writer that copies an input file from an input path into (potentially many) output locations
21+
* given by glob patterns, .
22+
*/
23+
export class MultiCopy extends Writer {
24+
constructor(private inputTree, private options: MultiCopyOptions) { super(); }
25+
26+
write(readTree: (tree) => Promise<string>, destDir: string): Promise<any> {
27+
return readTree(this.inputTree)
28+
.then((inputPath: string) => {
29+
var fileName = path.basename(this.options.srcPath);
30+
var data = fs.readFileSync(path.join(inputPath, this.options.srcPath), 'utf-8');
31+
32+
this.options.targetPatterns.forEach(pattern => {
33+
var paths: string[] = glob.sync(pattern);
34+
paths = paths.filter(p => fs.statSync(p).isDirectory());
35+
if (this.options.exclude) {
36+
paths = paths.filter(p => !this.options.exclude.some((excl) => minimatch(p, excl)));
37+
}
38+
paths.forEach(p => {
39+
var folder = path.join(destDir, p);
40+
fsx.mkdirsSync(folder);
41+
var outputPath = path.join(folder, fileName);
42+
fs.writeFileSync(outputPath, data);
43+
});
44+
});
45+
});
46+
}
47+
}

0 commit comments

Comments
 (0)