dc
Version:
A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js
551 lines (536 loc) • 20.8 kB
JavaScript
module.exports = function (grunt) {
'use strict';
require('load-grunt-tasks')(grunt, {
pattern: ['grunt-*']
});
require('time-grunt')(grunt);
grunt.loadNpmTasks('grunt-karma');
var formatFileList = require('./grunt/format-file-list')(grunt);
var config = {
src: 'src',
spec: 'spec',
web: 'web',
pkg: require('./package.json'),
banner: grunt.file.read('./LICENSE_BANNER'),
jsFiles: module.exports.jsFiles
};
// in d3v4 and d3v5 pre-built d3.js are in different sub folders
var d3pkgSubDir = config.pkg.dependencies.d3.split('.')[0].replace(/[^\d]/g, '') === '4' ? 'build' : 'dist';
var sass = require('node-sass');
grunt.initConfig({
conf: config,
concat: {
js: {
src: '<%= conf.jsFiles %>',
dest: '<%= conf.pkg.name %>.js',
options: {
process: true,
sourceMap: true,
banner: '<%= conf.banner %>'
}
}
},
sass: {
options: {
implementation: sass
},
dist: {
files: {
'<%= conf.pkg.name %>.css': 'style/<%= conf.pkg.name %>.scss'
}
}
},
uglify: {
jsmin: {
options: {
mangle: true,
compress: true,
sourceMap: true,
banner: '<%= conf.banner %>'
},
src: '<%= conf.pkg.name %>.js',
dest: '<%= conf.pkg.name %>.min.js'
}
},
cssmin: {
options: {
shorthandCompacting: false,
roundingPrecision: -1
},
main: {
files: {
'<%= conf.pkg.name %>.min.css': ['<%= conf.pkg.name %>.css']
}
}
},
eslint: {
source: {
src: [
'<%= conf.src %>/**/*.js',
'!<%= conf.src %>/{banner,footer,d3v3-compat}.js',
'<%= conf.spec %>/**/*.js',
'Gruntfile.js',
'grunt/*.js',
'<%= conf.web %>/stock.js'],
},
options: {
configFile: '.eslintrc'
}
},
watch: {
jsdoc2md: {
files: ['docs/welcome.base.md', '<%= conf.src %>/**/*.js'],
tasks: ['build', 'jsdoc', 'jsdoc2md']
},
scripts: {
files: ['<%= conf.src %>/**/*.js', '<%= conf.web %>/stock.js'],
tasks: ['docs']
},
sass: {
files: ['style/<%= conf.pkg.name %>.scss'],
tasks: ['sass', 'cssmin:main', 'copy:dc-to-gh']
},
tests: {
files: ['<%= conf.src %>/**/*.js', '<%= conf.spec %>/**/*.js'],
tasks: ['test']
},
reload: {
files: ['<%= conf.pkg.name %>.js',
'<%= conf.pkg.name %>.css',
'<%= conf.web %>/js/<%= conf.pkg.name %>.js',
'<%= conf.web %>/css/<%= conf.pkg.name %>.css',
'<%= conf.pkg.name %>.min.js'],
options: {
livereload: true
}
}
},
connect: {
server: {
options: {
port: process.env.PORT || 8888,
base: '.'
}
}
},
jasmine: {
specs: {
options: {
display: 'short',
summary: true,
specs: '<%= conf.spec %>/*-spec.js',
helpers: [
'<%= conf.web %>/js/jasmine-jsreporter.js',
'<%= conf.web %>/js/compare-versions.js',
'<%= conf.spec %>/helpers/*.js'
],
styles: [
'<%= conf.web %>/css/dc.css'
],
outfile: '<%= conf.spec %>/index.html',
keepRunner: true
},
src: [
'<%= conf.web %>/js/d3.js',
'<%= conf.web %>/js/crossfilter.js',
'<%= conf.pkg.name %>.js'
]
}
},
karma: {
options: {
basePath: '',
frameworks: ['jasmine'],
files: [
// CSS files
'<%= conf.web %>/css/dc.css',
// Helpers
'<%= conf.web %>/js/jasmine-jsreporter.js',
'<%= conf.web %>/js/compare-versions.js',
'<%= conf.spec %>/helpers/*.js',
// JS code dependencies
'<%= conf.web %>/js/d3.js',
'<%= conf.web %>/js/crossfilter.js',
// Code to be tested
'<%= conf.pkg.name %>.js',
// Jasmine spec files
'<%= conf.spec %>/*spec.js'
],
exclude: [],
preprocessors: {},
// possible values: 'dots', 'progress'
reporters: ['progress', 'summary'],
summaryReporter: {
// 'failed', 'skipped' or 'all'
show: 'failed',
// Limit the spec label to this length
specLength: 100,
// Show an 'all' column as a summary
overviewColumn: true
},
port: 9876,
colors: true,
logLevel: 'INFO',
autoWatch: false,
browsers: ['Firefox'],
browserConsoleLogOptions: {level: 'error'},
singleRun: true,
concurrency: Infinity
},
unit: {},
coverage: {
reporters: ['progress', 'coverage'],
preprocessors: {
// source files, that you wanna generate coverage for
// do not include tests or libraries
// (these files will be instrumented by Istanbul)
'<%= conf.pkg.name %>.js': ['coverage']
},
// optionally, configure the reporter
coverageReporter: {
type: 'html',
dir: 'coverage/'
}
},
ci: {
browsers: ['ChromeNoSandboxHeadless', 'FirefoxHeadless'],
customLaunchers: {
// See https://github.com/karma-runner/karma/issues/2603
ChromeNoSandboxHeadless: {
base: 'Chrome',
flags: [
'--no-sandbox',
// See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
'--headless',
'--disable-gpu',
// Without a remote debugging port, Google Chrome exits immediately.
' --remote-debugging-port=9222'
]
},
FirefoxHeadless: {
base: 'Firefox',
flags: ['-headless']
}
},
concurrency: 1,
reporters: ['dots', 'summary']
},
sauceLabs: {
testName: 'dc.js unit tests',
customLaunchers: {
slFirefoxLinux: {
base: 'SauceLabs',
browserName: 'firefox',
version: '45.0',
platform: 'Linux'
},
slSafari: {
base: 'SauceLabs',
browserName: 'safari',
version: '9.0',
platform: 'OS X 10.11'
},
slInternetExplorer11: {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 10',
version: '11.0'
},
slInternetExplorer11win8: {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '11.0',
platform: 'Windows 8.1'
},
slMicrosoftEdge: {
base: 'SauceLabs',
browserName: 'MicrosoftEdge',
version: '14',
platform: 'Windows 10'
}
},
browsers: [
'slInternetExplorer11',
'slInternetExplorer11win8',
'slMicrosoftEdge',
'slFirefoxLinux',
'slSafari'
],
concurrency: 2,
browserNoActivityTimeout: 120000,
reporters: ['saucelabs', 'summary'],
singleRun: true
}
},
jsdoc: {
dist: {
src: ['docs/welcome.base.md', '<%= conf.src %>/**/*.js', '!<%= conf.src %>/{banner,footer}.js'],
options: {
destination: 'web/docs/html',
template: 'node_modules/ink-docstrap/template',
configure: 'jsdoc.conf.json'
}
}
},
jsdoc2md: {
dist: {
src: 'dc.js',
dest: 'web/docs/api-latest.md'
}
},
docco: {
options: {
dst: '<%= conf.web %>/docs'
},
howto: {
files: [
{
expand: true,
cwd: '<%= conf.web %>',
src: ['stock.js']
}
]
}
},
copy: {
'dc-to-gh': {
files: [
{
expand: true,
flatten: true,
nonull: true,
src: ['<%= conf.pkg.name %>.css', '<%= conf.pkg.name %>.min.css'],
dest: '<%= conf.web %>/css/'
},
{
expand: true,
flatten: true,
nonull: true,
src: [
'<%= conf.pkg.name %>.js',
'<%= conf.pkg.name %>.js.map',
'<%= conf.pkg.name %>.min.js',
'<%= conf.pkg.name %>.min.js.map',
'node_modules/d3/' + d3pkgSubDir + '/d3.js',
'node_modules/crossfilter2/crossfilter.js',
'node_modules/file-saver/FileSaver.js',
'node_modules/reductio/reductio.js',
'node_modules/whatwg-fetch/dist/fetch.umd.js'
],
dest: '<%= conf.web %>/js/'
},
{
nonull: true,
src: 'node_modules/promise-polyfill/dist/polyfill.js',
dest: '<%= conf.web %>/js/promise-polyfill.js'
},
{
expand: true,
flatten: true,
nonull: true,
src: [
'node_modules/compare-versions/index.js'
],
dest: '<%= conf.web %>/js/',
rename: function (dest, src) {
return dest + 'compare-versions.js';
}
}
]
}
},
fileindex: {
'examples-listing': {
options: {
format: formatFileList,
absolute: true,
title: 'Index of dc.js examples',
heading: 'Examples of using dc.js',
description: 'An attempt to present a simple example of each chart type.',
sourceLink: 'https://github.com/dc-js/dc.js/tree/master/<%= conf.web %>/examples'
},
files: [
{dest: '<%= conf.web %>/examples/index.html', src: ['<%= conf.web %>/examples/*.html']}
]
},
'transitions-listing': {
options: {
format: formatFileList,
absolute: true,
title: 'Index of dc.js transition tests',
heading: 'Eyeball tests for dc.js transitions',
description: 'Transitions can only be tested by eye. ' +
'These pages automate the transitions so they can be visually verified.',
sourceLink: 'https://github.com/dc-js/dc.js/tree/master/<%= conf.web %>/transitions'
},
files: [
{dest: '<%= conf.web %>/transitions/index.html', src: ['<%= conf.web %>/transitions/*.html']}
]
},
'resizing-listing': {
options: {
format: formatFileList,
absolute: true,
title: 'Index of dc.js resizing tests',
heading: 'Eyeball tests for resizing dc.js charts',
description: 'It\'s a lot easier to test resizing behavior by eye. ' +
'These pages fit the charts to the browser dynamically so it\'s easier to test. ' +
'For the examples with a single chart taking up the entire window, you can add <code>?resize=viewbox</code> ' +
'to the URL to test resizing the chart using the ' +
'<a href="http://dc-js.github.io/dc.js/docs/html/dc.baseMixin.html#useViewBoxResizing__anchor">useViewBoxResizing</a> strategy.',
sourceLink: 'https://github.com/dc-js/dc.js/tree/master/<%= conf.web %>/resizing'
},
files: [
{dest: '<%= conf.web %>/resizing/index.html', src: ['<%= conf.web %>/resizing/*.html']}
]
},
'zoom-listing': {
options: {
format: formatFileList,
absolute: true,
title: 'Index of dc.js zoom tests',
heading: 'Interactive test for dc.js chart zoom',
description: 'It\'s hard to conceive of a way to test zoom except by trying it. ' +
'So this is a substitute for automated tests in this area',
sourceLink: 'https://github.com/dc-js/dc.js/tree/master/<%= conf.web %>/zoom'
},
files: [
{dest: '<%= conf.web %>/zoom/index.html', src: ['<%= conf.web %>/zoom/*.html']}
]
}
},
'gh-pages': {
options: {
base: '<%= conf.web %>',
message: 'Synced from from master branch.'
},
src: ['**']
},
shell: {
merge: {
command: function (pr) {
return [
'git fetch origin',
'git checkout master',
'git reset --hard origin/master',
'git fetch origin',
'git merge --no-ff origin/pr/' + pr + ' -m \'Merge pull request #' + pr + '\''
].join('&&');
},
options: {
stdout: true,
failOnError: true
}
},
amend: {
command: 'git commit -a --amend --no-edit',
options: {
stdout: true,
failOnError: true
}
},
hooks: {
command: 'cp -n scripts/pre-commit.sh .git/hooks/pre-commit' +
' || echo \'Cowardly refusing to overwrite your existing git pre-commit hook.\''
},
hierarchy: {
command: 'dot -Tsvg -o web/img/class-hierarchy.svg class-hierarchy.dot'
}
},
browserify: {
dev: {
src: '<%= conf.pkg.name %>.js',
dest: 'bundle.js',
options: {
browserifyOptions: {
standalone: 'dc'
}
}
}
}
});
grunt.registerTask('merge', 'Merge a github pull request.', function (pr) {
grunt.log.writeln('Merge Github Pull Request #' + pr);
grunt.task.run(['shell:merge:' + pr, 'test' , 'shell:amend']);
});
grunt.registerTask(
'test-stock-example',
'Test a new rendering of the stock example web page against a baseline rendering',
function (option) {
require('./regression/stock-regression-test.js').testStockExample(this.async(), option === 'diff');
});
grunt.registerTask('update-stock-example', 'Update the baseline stock example web page.', function () {
require('./regression/stock-regression-test.js').updateStockExample(this.async());
});
grunt.registerTask('watch:scripts-sass-docs', function () {
grunt.config('watch', {
options: {
interrupt: true
},
scripts: grunt.config('watch').scripts,
sass: grunt.config('watch').sass
});
grunt.task.run('watch');
});
grunt.registerTask('safe-sauce-labs', function () {
if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) {
grunt.log.writeln('Skipping Sauce Lab tests - SAUCE_USERNAME/SAUCE_ACCESS_KEY not set');
return;
}
grunt.task.run('karma:sauceLabs');
});
// task aliases
grunt.registerTask('build', ['concat', 'sass', 'uglify', 'cssmin']);
grunt.registerTask('docs', ['build', 'copy', 'jsdoc', 'jsdoc2md', 'docco', 'fileindex']);
grunt.registerTask('web', ['docs', 'gh-pages']);
grunt.registerTask('server', ['docs', 'fileindex', 'jasmine:specs:build', 'connect:server', 'watch:scripts-sass-docs']);
grunt.registerTask('test', ['build', 'copy', 'karma:unit']);
// grunt.registerTask('test-browserify', ['build', 'copy', 'browserify', 'jasmine:browserify']);
grunt.registerTask('coverage', ['build', 'copy', 'karma:coverage']);
grunt.registerTask('ci', ['ci-pull', 'safe-sauce-labs']);
grunt.registerTask('ci-pull', ['build', 'copy', 'karma:ci']);
grunt.registerTask('lint', ['eslint']);
grunt.registerTask('default', ['build', 'shell:hooks']);
grunt.registerTask('doc-debug', ['build', 'jsdoc', 'jsdoc2md', 'watch:jsdoc2md']);
};
module.exports.jsFiles = [
'src/banner.js', // NOTE: keep this first
'src/core.js',
'src/errors.js',
'src/utils.js',
'src/logger.js',
'src/config.js',
'src/events.js',
'src/filters.js',
'src/base-mixin.js',
'src/margin-mixin.js',
'src/color-mixin.js',
'src/coordinate-grid-mixin.js',
'src/stack-mixin.js',
'src/cap-mixin.js',
'src/bubble-mixin.js',
'src/pie-chart.js',
'src/sunburst-chart.js',
'src/bar-chart.js',
'src/line-chart.js',
'src/data-count.js',
'src/data-table.js',
'src/data-grid.js',
'src/bubble-chart.js',
'src/composite-chart.js',
'src/series-chart.js',
'src/geo-choropleth-chart.js',
'src/bubble-overlay.js',
'src/row-chart.js',
'src/legend.js',
'src/html-legend.js',
'src/scatter-plot.js',
'src/number-display.js',
'src/heatmap.js',
'src/d3.box.js',
'src/box-plot.js',
'src/select-menu.js',
'src/text-filter-widget.js',
'src/cbox-menu.js',
'src/footer.js' // NOTE: keep this last
];