Skip to content

Commit 33af1d0

Browse files
committed
chore(build): execute pub get only if a pubspec.yaml changed and run dart analyzer on all dart files
`pub get` is now only executed when the `pubspec.yaml` in the `modules` folder is different than the `pubspec.yaml` in the `build/dart` folder. Generates the file `build/dart/_analyzer.dart` that imports all modules to run `dart analyzer` against all of them. The build will fail whenever there are errors, warnings or hints in `dart analyzer`. Changes the sources so that `dart analyzer` does not report any error, warning or hint. Closes angular#40
1 parent 64fe73e commit 33af1d0

22 files changed

Lines changed: 186 additions & 88 deletions

File tree

gulpfile.js

Lines changed: 115 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ var ejs = require('gulp-ejs');
1010
var path = require('path');
1111
var through2 = require('through2');
1212
var file2moduleName = require('./file2modulename');
13-
var exec = require('child_process').exec;
13+
var spawn = require('child_process').spawn;
14+
var fs = require('fs');
15+
var path = require('path');
16+
var readline = require('readline');
1417
var Q = require('q');
1518

1619
var js2es5Options = {
@@ -31,14 +34,6 @@ var js2dartOptions = {
3134

3235
var gulpTraceur = require('./tools/transpiler/gulp-traceur');
3336

34-
function execWithLog(command, options, done) {
35-
exec(command, options, function (err, stdout, stderr) {
36-
stdout && console.log(stdout);
37-
stderr && console.log(stderr);
38-
done(err);
39-
});
40-
}
41-
4237
// ---------
4338
// traceur runtime
4439

@@ -55,29 +50,18 @@ var sourceTypeConfigs = {
5550
compilerOptions: js2dartOptions,
5651
transpileSrc: ['modules/**/*.js'],
5752
htmlSrc: ['modules/*/src/**/*.html'],
58-
copySrc: ['modules/**/*.dart', 'modules/**/*.yaml'],
53+
copySrc: ['modules/**/*.dart'],
5954
outputDir: 'build/dart',
6055
outputExt: 'dart',
61-
mimeType: 'application/dart',
62-
postProcess: function(file, done) {
63-
if (file.path.match(/pubspec\.yaml/)) {
64-
console.log('pub get ' + file.path);
65-
execWithLog('pub get', {
66-
cwd: path.dirname(file.path)
67-
}, done);
68-
} else {
69-
done();
70-
}
71-
}
56+
mimeType: 'application/dart'
7257
},
7358
js: {
7459
compilerOptions: js2es5Options,
7560
transpileSrc: ['modules/**/*.js', 'modules/**/*.es6'],
7661
htmlSrc: ['modules/*/src/**/*.html'],
7762
copySrc: ['modules/**/*.es5'],
7863
outputDir: 'build/js',
79-
outputExt: 'js',
80-
postProcess: null
64+
outputExt: 'js'
8165
}
8266
};
8367

@@ -91,24 +75,35 @@ gulp.task('modules/build.dart/src', function() {
9175
return createModuleTask(sourceTypeConfigs.dart);
9276
});
9377

94-
gulp.task('modules/build.dart/analyzer', function() {
95-
var baseDir = sourceTypeConfigs.dart.outputDir;
96-
var files = [].slice.call(glob.sync('*/lib/*.dart', {
97-
cwd: baseDir
98-
}));
99-
files = files.filter(function(fileName) {
100-
return fileName.match(/(\w+)\/lib\/\1/);
101-
});
102-
return Q.all(files.map(function(fileName) {
103-
var deferred = Q.defer();
104-
execWithLog('dartanalyzer '+baseDir+'/'+fileName, {}, deferred.makeNodeResolver());
105-
return deferred.promise;
106-
}));
78+
gulp.task('modules/build.dart/pubspec', function(done) {
79+
var outputDir = sourceTypeConfigs.dart.outputDir;
80+
return gulp.src('modules/*/pubspec.yaml')
81+
.pipe(through2.obj(function(file, enc, done) {
82+
var targetFile = path.join(outputDir, file.relative);
83+
if (fs.existsSync(targetFile)) {
84+
file.previousContents = fs.readFileSync(targetFile);
85+
} else {
86+
file.previousContents = '';
87+
}
88+
this.push(file);
89+
done();
90+
}))
91+
.pipe(gulp.dest(outputDir))
92+
.pipe(through2.obj(function(file, enc, done) {
93+
if (file.previousContents.toString() !== file.contents.toString()) {
94+
console.log(file.path + ' changed, calling pub get');
95+
var stream = spawn('pub', ['get'], {
96+
stdio: [process.stdin, process.stdout, process.stderr],
97+
cwd: path.dirname(file.path)
98+
});
99+
stream.on('close', done);
100+
} else {
101+
done();
102+
}
103+
}));
107104
});
108105

109-
gulp.task('modules/build.dart', function(done) {
110-
runSequence('modules/build.dart/src', 'modules/build.dart/analyzer', done);
111-
});
106+
gulp.task('modules/build.dart', ['modules/build.dart/src', 'modules/build.dart/pubspec']);
112107

113108
gulp.task('modules/build.js', function() {
114109
return createModuleTask(sourceTypeConfigs.js);
@@ -136,15 +131,77 @@ function createModuleTask(sourceTypeConfig) {
136131
}))
137132
.pipe(gulp.dest(sourceTypeConfig.outputDir));
138133

139-
var s = mergeStreams(transpile, copy, html);
140-
if (!sourceTypeConfig.postProcess) {
141-
return s;
142-
}
143-
return s.pipe(through2.obj(function(file, enc, done) {
144-
sourceTypeConfig.postProcess(file, done);
145-
}));
134+
return mergeStreams(transpile, copy, html);
146135
}
147136

137+
// ------------------
138+
// ANALYZE
139+
140+
gulp.task('analyze/dartanalyzer', function(done) {
141+
var pubSpecs = [].slice.call(glob.sync('build/dart/*/pubspec.yaml', {
142+
cwd: __dirname
143+
}));
144+
var tempFile = '_analyzer.dart';
145+
// analyze in parallel!
146+
return Q.all(pubSpecs.map(function(pubSpecFile) {
147+
var dir = path.dirname(pubSpecFile);
148+
var srcFiles = [].slice.call(glob.sync('lib/**/*.dart', {
149+
cwd: dir
150+
}));
151+
var testFiles = [].slice.call(glob.sync('test/**/*_spec.dart', {
152+
cwd: dir
153+
}));
154+
var analyzeFile = ['library _analyzer;'];
155+
srcFiles.concat(testFiles).forEach(function(fileName, index) {
156+
if (fileName !== tempFile) {
157+
analyzeFile.push('import "./'+fileName+'" as mod'+index+';');
158+
}
159+
});
160+
fs.writeFileSync(path.join(dir, tempFile), analyzeFile.join('\n'));
161+
var defer = Q.defer();
162+
analyze(dir, defer.makeNodeResolver());
163+
return defer.promise;
164+
}));
165+
166+
function analyze(dirName, done) {
167+
var stream = spawn('dartanalyzer', ['--fatal-warnings', tempFile], {
168+
// inherit stdin and stderr, but filter stdout
169+
stdio: [process.stdin, 'pipe', process.stderr],
170+
cwd: dirName
171+
});
172+
// Filter out unused imports from our generated file.
173+
// We don't reexports from the generated file
174+
// as this could lead to name clashes when two files
175+
// export the same thing.
176+
var rl = require('readline').createInterface({
177+
input: stream.stdout,
178+
output: process.stdout,
179+
terminal: false
180+
});
181+
var hintCount = 0;
182+
rl.on('line', function(line) {
183+
if (line.match(/Unused import .*_analyzer\.dart/)) {
184+
return;
185+
}
186+
if (line.match(/\[hint\]/)) {
187+
hintCount++;
188+
}
189+
console.log(dirName + ':' + line);
190+
});
191+
stream.on('close', function(code) {
192+
var error;
193+
if (code !== 0) {
194+
error = new Error('Dartanalyzer failed with exit code ' + code);
195+
}
196+
if (hintCount > 0) {
197+
error = new Error('Dartanalyzer showed hints');
198+
}
199+
done(error);
200+
});
201+
}
202+
});
203+
204+
148205
// ------------------
149206
// WEB SERVER
150207
gulp.task('serve', connect.server({
@@ -167,4 +224,15 @@ gulp.task('serve', connect.server({
167224

168225
gulp.task('clean', ['modules/clean']);
169226

170-
gulp.task('build', ['jsRuntime/build', 'modules/build.dart', 'modules/build.js']);
227+
gulp.task('build', function(done) {
228+
runSequence(
229+
// parallel
230+
['jsRuntime/build', 'modules/build.dart', 'modules/build.js'],
231+
// sequential
232+
'analyze/dartanalyzer'
233+
);
234+
});
235+
236+
gulp.task('analyze', function(done) {
237+
runSequence('analyze/dartanalyzer');
238+
});

modules/change_detection/pubspec.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ name: change_detection
22
environment:
33
sdk: '>=1.4.0'
44
dependencies:
5+
facade:
6+
path: ../facade
57
dev_dependencies:
68
test_lib:
79
path: ../test_lib
8-
facade:
9-
path: ../facade
10+
guinness: ">=0.1.5 <0.2.0"

modules/change_detection/src/parser/scanner.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ export class Scanner {
368368
}
369369

370370
error(message:string) {
371-
var position:int = this.index + this.offset;
371+
var position:int = this.index;
372372
throw `Lexer Error: ${message} at column ${position} in expression [${input}]`;
373373
}
374374
}

modules/change_detection/src/record.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ export class ProtoRecord {
2525
this.prev = null;
2626
this.changeNotifier = null;
2727
this._clone = null;
28-
}
28+
this.changeContext = null;
29+
this.dispatcherContext = null;
30+
}
2931

3032
instantiate(watchGroup/*:wg.WatchGroup*/):Record {
3133
var record = this._clone = new Record(watchGroup, this);
3234
record.prev = this.prev._clone;
33-
record._checkPrev = this._prev._clone;
35+
record._checkPrev = this.prev._clone;
3436
return _clone;
3537
}
3638

modules/change_detection/test/change_detection_spec.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import {describe, it, expect} from 'test_lib/test_lib';
2-
import {ProtoWatchGroup, WatchGroup, WatchGroupDispatcher} from 'change_detection/change_detection';
3-
import {DOM} from 'facade/dom';
1+
import {describe, it, xit, expect} from 'test_lib/test_lib';
2+
import {ProtoWatchGroup, WatchGroup, WatchGroupDispatcher, ChangeDetection} from 'change_detection/change_detection';
43

54

65
export function main() {
76
describe('change_detection', function() {
87
describe('ChangeDetection', function() {
9-
it('should do simple watching', function() {
10-
return; // remove me once xit or CD works.
8+
xit('should do simple watching', function() {
119
var person = new Person('misko', 38);
1210
var pwg = new ProtoWatchGroup();
1311
pwg.watch('name', 'nameToken');
@@ -32,11 +30,16 @@ export function main() {
3230

3331
class Person {
3432
constructor(name:string, age:number) {
35-
this.name = null;
36-
this.a
33+
this.name = name;
34+
this.age = age;
3735
}
3836
}
3937

40-
class Dispatcher extends WatchGroupDispatcher {
38+
class LoggingDispatcher extends WatchGroupDispatcher {
39+
constructor() {
40+
this.log = null;
41+
}
42+
clear() {
4143

44+
}
4245
}

modules/change_detection/test/parser/lexer_spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {describe, it, expect} from 'test_lib/test_lib';
22
import {Scanner, Token} from 'change_detection/parser/scanner';
3-
import {DOM} from 'facade/dom';
43
import {List, ListWrapper} from "facade/collection";
54
import {StringWrapper} from "facade/lang";
65

modules/core/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ dependencies:
1111
dev_dependencies:
1212
test_lib:
1313
path: ../test_lib
14+
guinness: ">=0.1.5 <0.2.0"

modules/core/src/annotations/directive.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import {Type} from 'facade/lang';
2-
import {ElementServicesFunction} from './facade';
1+
// import {Type} from 'facade/lang';
2+
// import {ElementServicesFunction} from './facade';
33
import {ABSTRACT} from 'facade/lang';
44

55

6-
@ABSTRACT
6+
@ABSTRACT()
77
export class Directive {
88
constructor({
99
selector,
1010
lightDomServices,
1111
implementsTypes
12-
}:{
12+
}/*:{
1313
selector:String,
1414
lightDomServices:ElementServicesFunction,
1515
implementsTypes:Array<Type>
16-
})
16+
}*/)
1717
{
1818
this.lightDomServices = lightDomServices;
1919
this.selector = selector;

modules/core/src/annotations/facade.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
library core.annotations.facade;
2+
13
import 'package:di/di.dart' show Module;
24
import '../compiler/element_module.dart' show ElementModule;
35

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import {Type, List} from 'facade/lang';
1+
// import {Type, List} from 'facade/lang';
22

33
export class TemplateConfig {
44
constructor({
55
url,
66
directives,
77
formatters,
88
source
9-
}: {
9+
}/*: {
1010
url: String,
1111
directives: List<Type>,
1212
formatters: List<Type>,
1313
source: List<TemplateConfig>
14-
})
14+
}*/)
1515
{}
1616
}

0 commit comments

Comments
 (0)