├── .babelrc ├── src ├── utils │ └── unique.js ├── index.js ├── gulp-tasks.js ├── gulp-tasks-sync.js ├── webpack.dist.config.js ├── gulp-tasks-dist.js ├── webpack.dev.config.js ├── gulp-tasks-linters.js ├── gulp-tasks-pack.js ├── gulp-tasks-test.js ├── gulp-options-builder.js ├── gulp-tasks-dev.js └── gulp-tasks-core.js ├── .npmignore ├── .gitignore ├── .eslintrc ├── package.json ├── .sass-lint.yml ├── README.md └── LICENSE /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015", "react" ], 3 | "plugins": [ 4 | "transform-object-rest-spread", "react-hot-loader/babel" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/unique.js: -------------------------------------------------------------------------------- 1 | export default (arr1, arr2) => { 2 | const result = (arr1 || []).concat( 3 | arr2 4 | ); 5 | 6 | return result.filter((elem, index) => 7 | result.indexOf(elem) === index 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import gulpTasks from './gulp-tasks'; 2 | 3 | export * from './gulp-tasks-pack'; 4 | export * from './gulp-tasks-dev'; 5 | export * from './gulp-tasks-dist'; 6 | export * from './gulp-tasks-linters'; 7 | export * from './gulp-tasks-sync'; 8 | export * from './gulp-tasks-test'; 9 | export * from './gulp-tasks-core'; 10 | export * from './gulp-options-builder'; 11 | 12 | export default gulpTasks; 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | lib 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | -------------------------------------------------------------------------------- /src/gulp-tasks.js: -------------------------------------------------------------------------------- 1 | import gulpTasksLinters from './gulp-tasks-linters'; 2 | import gulpTasksPack from './gulp-tasks-pack'; 3 | import gulpTasksTest from './gulp-tasks-test'; 4 | import gulpTasksDist from './gulp-tasks-dist'; 5 | import gulpTasksDev from './gulp-tasks-dev'; 6 | import gulpTasksSync from './gulp-tasks-sync'; 7 | import gulpTasksCore from './gulp-tasks-core'; 8 | 9 | export default function (gulp, options) { 10 | 11 | gulpTasksCore(gulp, options); 12 | gulpTasksPack(gulp, options); 13 | gulpTasksLinters(gulp, options); 14 | gulpTasksTest(gulp, options); 15 | gulpTasksDist(gulp, options); 16 | gulpTasksDev(gulp, options); 17 | gulpTasksSync(gulp, options); 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /src/gulp-tasks-sync.js: -------------------------------------------------------------------------------- 1 | import rsync from 'gulp-rsync'; 2 | 3 | import gulpOptionsBuilder from './gulp-options-builder'; 4 | import gulpTasksCore from './gulp-tasks-core'; 5 | 6 | export function syncTasks (gulp, opts) { 7 | 8 | const runSequence = require('run-sequence').use(gulp); 9 | 10 | gulpTasksCore(gulp); 11 | 12 | const options = gulpOptionsBuilder(opts); 13 | 14 | gulp.task('syncPre', (callback) => runSequence('dist', callback)); 15 | 16 | gulp.task('sync', ['syncPre'], () => { 17 | if (options.sync) { 18 | gulp.src(options.dist) 19 | .pipe(rsync({ 20 | root: options.dist, 21 | hostname: options.sync.hostname, 22 | username: options.sync.username, 23 | destination: options.sync.remoteDestination, 24 | recursive: true, 25 | relative: true, 26 | incremental: true, 27 | silent: true, 28 | clean: true, 29 | emptyDirectories: true, 30 | exclude: ['.DS_Store'] 31 | })); 32 | } 33 | 34 | }); 35 | }; 36 | 37 | export default syncTasks; 38 | -------------------------------------------------------------------------------- /src/webpack.dist.config.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | 3 | import webpack from 'webpack'; 4 | import deepAssign from 'deep-assign'; 5 | import unique from './utils/unique'; 6 | 7 | import gulpOptionsBuilder from './gulp-options-builder'; 8 | const options = gulpOptionsBuilder(); 9 | 10 | const env = deepAssign({ 11 | __DEV_MODE__: false, 12 | NODE_ENV: '"production"', 13 | 'process.env.NODE_ENV': '"production"' 14 | }, options.env); 15 | 16 | const config = {...options.webpack}; 17 | 18 | config.plugins = [ 19 | new webpack.DefinePlugin(env), 20 | new webpack.optimize.OccurenceOrderPlugin() 21 | ]; 22 | 23 | if (options.argv.minify) { 24 | config.plugins.push(new webpack.optimize.UglifyJsPlugin({ 25 | compress: { 26 | warnings: false 27 | } 28 | })); 29 | } 30 | 31 | if (options.webpack.plugins) { 32 | options.webpack.plugins.forEach((plugin) => 33 | config.plugins.push(plugin) 34 | ); 35 | } 36 | 37 | config.resolve.extensions = unique( 38 | config.resolve.extensions, 39 | ['', '.react', '.jsx', '.js', '.json', '.htm', '.html', '.scss', '.md', '.svg'] 40 | ); 41 | 42 | config.resolve.modulesDirectories = unique( 43 | config.resolve.modulesDirectories, 44 | ['node_modules/grommet/node_modules', 'node_modules'] 45 | ); 46 | 47 | config.resolveLoader.modulesDirectories = unique( 48 | config.resolveLoader.modulesDirectories, 49 | ['node_modules/grommet/node_modules', 'node_modules'] 50 | ); 51 | 52 | export default config; 53 | module.exports = config; 54 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | parser: babel-eslint 3 | 4 | parserOptions: 5 | ecmaFeatures: 6 | experimentalObjectRestSpread: true 7 | jsx: true 8 | sourceType: "module" 9 | 10 | plugins: 11 | - react 12 | 13 | env: 14 | browser: true 15 | node: true 16 | es6: true 17 | 18 | globals: 19 | __DEV__: true 20 | __THEME__: true 21 | __DEV_MODE__: true 22 | __SOCKET_HOST__: true 23 | IntlPolyfill: true 24 | Modernizr: true 25 | describe: true 26 | it: true 27 | beforeEach: true 28 | afterEach: true 29 | before: true 30 | after: true 31 | 32 | rules: 33 | # ERRORS 34 | space-before-blocks: 2 35 | indent: [2, 2, { SwitchCase: 1 }] 36 | brace-style: 2 37 | comma-dangle: 2 38 | no-unused-expressions: 2 39 | eol-last: 2 40 | dot-notation: 2 41 | no-unused-vars: [2, args: none] 42 | semi: [2, "always"] 43 | 44 | # DISABLED 45 | max-len: 0 46 | #change soon back to max-len: [1, 80] 47 | no-underscore-dangle: 0 48 | new-cap: 0 49 | no-use-before-define: 0 50 | key-spacing: 0 51 | eqeqeq: 0 52 | strict: 0 53 | space-unary-ops: 0 54 | yoda: 0 55 | no-loop-func: 0 56 | no-trailing-spaces: 0 57 | no-multi-spaces: 0 58 | no-shadow: 0 59 | no-alert: 0 60 | no-process-exit: 0 61 | no-extend-native: 0 62 | block-scoped-var: 0 63 | quotes: 0 64 | jsx-quotes: 0 65 | consistent-return: 0 66 | 67 | # REACT DISABLED 68 | react/display-name: 0 69 | react/jsx-sort-prop-types: 0 70 | react/prop-types: 0 71 | react/no-did-mount-set-state: 0 72 | react/no-did-update-set-state: 0 73 | react/jsx-max-props-per-line: 0 74 | react/jsx-sort-props: 0 75 | react/no-multi-comp: 0 76 | react/jsx-boolean-value: 0 77 | react/no-danger: 0 78 | 79 | react/jsx-curly-spacing: 1 80 | react/jsx-no-duplicate-props: 1 81 | react/jsx-no-undef: 1 82 | react/jsx-uses-react: 1 83 | react/jsx-uses-vars: 1 84 | react/no-unknown-property: 1 85 | react/react-in-jsx-scope: 1 86 | react/self-closing-comp: 1 87 | react/sort-comp: 1 88 | react/jsx-wrap-multilines: 1 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grommet-toolbox", 3 | "version": "2.0.3", 4 | "main": "lib/index.js", 5 | "description": "Developer Environment for Grommet applications", 6 | "authors": [ 7 | "Alan Souza", 8 | "Bryan Jacquot", 9 | "Chris Carlozzi", 10 | "Eric Soderberg" 11 | ], 12 | "homepage": "http://grommet.io", 13 | "bugs": "https://github.com/grommet/grommet-toolbox/issues", 14 | "license": "Apache-2.0", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/grommet/grommet-toolbox.git" 18 | }, 19 | "dependencies": { 20 | "babel-cli": "^6.6.5", 21 | "babel-eslint": "^7.0.0", 22 | "babel-jest": "^15.0.0", 23 | "babel-loader": "^6.2.4", 24 | "babel-plugin-transform-object-rest-spread": "^6.6.5", 25 | "babel-preset-es2015": "^6.6.0", 26 | "babel-preset-react": "^6.5.0", 27 | "babel-register": "^6.7.2", 28 | "css-loader": "^0.25.0", 29 | "deep-assign": "^2.0.0", 30 | "del": "^2.2.0", 31 | "eslint-plugin-react": "^6.2.1", 32 | "file-loader": "^0.9.0", 33 | "grommet-icon-loader": "^0.4.0", 34 | "gulp": "^3.9.1", 35 | "gulp-babel": "^6.1.2", 36 | "gulp-cache": "^0.4.5", 37 | "gulp-env": "^0.4.0", 38 | "gulp-eslint": "^3.0.1", 39 | "gulp-file": "^0.3.0", 40 | "gulp-if": "^2.0.0", 41 | "gulp-jest": "^2.0.0", 42 | "gulp-open": "^2.0.0", 43 | "gulp-rsync": "0.0.6", 44 | "gulp-sass-lint": "^1.2.0", 45 | "gulp-util": "^3.0.7", 46 | "imports-loader": "^0.6.5", 47 | "jest-cli": "^20.0.4", 48 | "json-loader": "^0.5.4", 49 | "mkdirp": "^0.5.1", 50 | "node-sass": "^3.4.2", 51 | "path-is-absolute": "^1.0.0", 52 | "react-hot-loader": "^3.0.0-beta.6", 53 | "rsync": "^0.5.0", 54 | "run-sequence": "^1.1.5", 55 | "sass-lint": "^1.8.1", 56 | "sass-loader": "^4.0.2", 57 | "style-loader": "^0.13.1", 58 | "tarball-extract": "^0.0.6", 59 | "webpack": "^1.12.14", 60 | "webpack-dev-server": "^1.14.1", 61 | "webpack-stream": "^3.1.0", 62 | "yargs": "^5.0.0" 63 | }, 64 | "scripts": { 65 | "build": "node_modules/.bin/babel src --out-dir lib --copy-files --loose-mode" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/gulp-tasks-dist.js: -------------------------------------------------------------------------------- 1 | import gulpWebpack from 'webpack-stream'; 2 | import path from 'path'; 3 | 4 | import gulpOptionsBuilder from './gulp-options-builder'; 5 | import gulpTasksCore from './gulp-tasks-core'; 6 | import gulpTasksTest from './gulp-tasks-test'; 7 | import gulpTasksLinters from './gulp-tasks-linters'; 8 | 9 | export function distTasks (gulp, opts) { 10 | 11 | const runSequence = require('run-sequence').use(gulp); 12 | 13 | gulpTasksCore(gulp, opts); 14 | gulpTasksTest(gulp, opts); 15 | gulpTasksLinters(gulp, opts); 16 | 17 | const options = gulpOptionsBuilder(opts); 18 | 19 | gulp.task('dist-preprocess', (callback) => { 20 | if (!options.argv.preprocess) { 21 | callback(); 22 | return; 23 | } 24 | 25 | if (options.distPreprocess) { 26 | if (process.env.CI) { 27 | runSequence('preprocess', options.distPreprocess, 'copy', callback); 28 | } else { 29 | runSequence('preprocess', options.distPreprocess, 'copy', 'test:coverage', callback); 30 | } 31 | } else { 32 | if (process.env.CI) { 33 | runSequence('preprocess', 'copy', callback); 34 | } else { 35 | runSequence('preprocess', 'copy', 'test:coverage', callback); 36 | } 37 | } 38 | }); 39 | 40 | gulp.task('dist', ['dist-preprocess'], (done) => { 41 | 42 | let webpackConfigPath = path.resolve( 43 | __dirname, 'webpack.dist.config.js' 44 | ); 45 | 46 | if (options.argv.config) { 47 | webpackConfigPath = path.resolve(options.argv.config); 48 | } 49 | 50 | const config = require(webpackConfigPath); 51 | 52 | if (Array.isArray(config)) { 53 | var doneCount = 0; 54 | config.forEach((c, index) => { 55 | gulp.src(options.mainJs) 56 | .pipe(gulpWebpack(c)) 57 | .pipe(gulp.dest(options.dist)).on('end', () => { 58 | doneCount++; 59 | if (doneCount === config.length) { 60 | done(); 61 | } 62 | }); 63 | }); 64 | } else { 65 | gulp.src(options.mainJs) 66 | .pipe(gulpWebpack(config)) 67 | .pipe(gulp.dest(options.dist)).on('end', done); 68 | } 69 | }); 70 | }; 71 | 72 | export default distTasks; 73 | -------------------------------------------------------------------------------- /src/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | 3 | import webpack from 'webpack'; 4 | import deepAssign from 'deep-assign'; 5 | import unique from './utils/unique'; 6 | 7 | import gulpOptionsBuilder from './gulp-options-builder'; 8 | const options = gulpOptionsBuilder(); 9 | 10 | const env = deepAssign({}, options.env, { 11 | __DEV_MODE__: true, 12 | NODE_ENV: '"development"' 13 | }); 14 | 15 | delete options.webpack.entry; 16 | const protocol = (options.devServer && options.devServer.https) ? 'https' : 'http'; 17 | const config = deepAssign({ 18 | entry: [ 19 | 'react-hot-loader/patch', 20 | 'webpack-dev-server/client/index.js?' + protocol + '://' + (options.devServerHost || 'localhost') + ':' + (options.devServerPort || '8080'), 21 | 'webpack/hot/dev-server', 22 | './' + options.mainJs 23 | ], 24 | 25 | output: { 26 | filename: 'index.js', 27 | path: options.dist, 28 | publicPath: '/' 29 | }, 30 | 31 | devtool: 'eval-source-map' 32 | 33 | }, options.webpack); 34 | 35 | // Ensure dev loaders are used. 36 | config.module.loaders = config.module.loaders.map(entry => { 37 | let {loader} = entry; 38 | if (/sass/.test(loader)) { 39 | // returns style!css?sourceMap!sass?sourceMap&outputStyle... 40 | loader = loader.replace(/css!/, 'css?sourceMap!'); 41 | loader = loader.replace(/css$/, 'css?sourceMap'); 42 | loader = loader.replace(/(outputStyle)/, 'sourceMap&$1'); 43 | } 44 | 45 | entry.loader = loader; 46 | return entry; 47 | }); 48 | 49 | config.plugins = [ 50 | new webpack.HotModuleReplacementPlugin(), 51 | new webpack.DefinePlugin(env) 52 | ]; 53 | 54 | if (options.webpack.plugins) { 55 | options.webpack.plugins.forEach((plugin) => 56 | config.plugins.push(plugin) 57 | ); 58 | } 59 | 60 | config.resolve.extensions = unique( 61 | config.resolve.extensions, 62 | ['', '.react', '.jsx', '.js', '.json', '.htm', '.html', '.scss', '.md', '.svg'] 63 | ); 64 | 65 | config.resolve.modulesDirectories = unique( 66 | config.resolve.modulesDirectories, 67 | ['node_modules/grommet/node_modules', 'node_modules'] 68 | ); 69 | 70 | config.resolveLoader.modulesDirectories = unique( 71 | config.resolveLoader.modulesDirectories, 72 | ['node_modules/grommet/node_modules', 'node_modules'] 73 | ); 74 | 75 | export default config; 76 | module.exports = config; 77 | -------------------------------------------------------------------------------- /src/gulp-tasks-linters.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | import eslint from 'gulp-eslint'; 4 | import cache from 'gulp-cache'; 5 | import deepAssign from 'deep-assign'; 6 | import sassLint from 'gulp-sass-lint'; 7 | 8 | import gulpOptionsBuilder from './gulp-options-builder'; 9 | 10 | export function linterTasks (gulp, opts) { 11 | 12 | const options = gulpOptionsBuilder(opts); 13 | 14 | let scssLintPath = path.resolve(process.cwd(), '.sass-lint.yml'); 15 | try { 16 | fs.accessSync(scssLintPath, fs.F_OK); 17 | } catch (e) { 18 | scssLintPath = path.resolve(__dirname, '../.sass-lint.yml'); 19 | } 20 | 21 | let esLintPath = options.eslintConfigPath || path.resolve(process.cwd(), '.eslintrc'); 22 | try { 23 | fs.accessSync(esLintPath, fs.F_OK); 24 | } catch (e) { 25 | esLintPath = path.resolve(__dirname, '../.eslintrc'); 26 | } 27 | 28 | let eslintOverride = options.eslintOverride ? 29 | require(options.eslintOverride) : {}; 30 | 31 | if (options.customEslintPath) { 32 | eslintOverride = require(options.customEslintPath); 33 | console.warn('customEslintPath has been deprecated. You should use eslintOverride instead'); 34 | } 35 | 36 | gulp.task('scsslint', () => { 37 | if (options.scsslint) { 38 | return gulp.src(options.scssAssets || []) 39 | .pipe(sassLint({ 40 | configFile: scssLintPath 41 | })) 42 | .pipe(sassLint.format()) 43 | .pipe(sassLint.failOnError()); 44 | } 45 | return false; 46 | }); 47 | 48 | gulp.task('jslint', () => { 49 | const eslintRules = deepAssign({ 50 | configFile: esLintPath 51 | }, eslintOverride); 52 | 53 | let jslintPipe = eslint(eslintRules); 54 | 55 | if (options.lintCache) { 56 | const eslintRuleSet = fs.readFileSync(eslintRules.configFile, 'utf8'); 57 | 58 | jslintPipe = cache(jslintPipe, { 59 | key: (file) => eslintRuleSet + file.contents.toString('utf8'), 60 | success: (linted) => linted.eslint && !linted.eslint.messages.length, 61 | value: (linted) => ({eslint: linted.eslint}) 62 | }); 63 | } else { 64 | console.warn('Lint cache is currently disabled'); 65 | } 66 | 67 | return gulp.src([].concat(options.jsAssets || []).concat(options.testPaths || [])) 68 | .pipe(jslintPipe) 69 | .pipe(eslint.formatEach()) 70 | .pipe(eslint.failAfterError()) 71 | .on('error', () => process.exit(1)); 72 | }); 73 | }; 74 | 75 | export default linterTasks; 76 | -------------------------------------------------------------------------------- /.sass-lint.yml: -------------------------------------------------------------------------------- 1 | options: 2 | formatter: stylish 3 | merge-default-rules: false 4 | rules: 5 | border-zero: 6 | - 1 7 | - convention: none 8 | brace-style: 9 | - 1 10 | - allow-single-line: true 11 | class-name-format: 12 | - 1 13 | - convention: hyphenatedbem 14 | clean-import-paths: 15 | - 1 16 | - filename-extension: false 17 | leading-underscore: false 18 | empty-line-between-blocks: 19 | - 1 20 | - ignore-single-line-rulesets: true 21 | extends-before-declarations: 0 22 | extends-before-mixins: 0 23 | final-newline: 24 | - 1 25 | - include: true 26 | force-attribute-nesting: 0 27 | force-element-nesting: 0 28 | force-pseudo-nesting: 0 29 | function-name-format: 30 | - 1 31 | - allow-leading-underscore: true 32 | convention: hyphenatedlowercase 33 | hex-length: 34 | - 0 35 | - style: short 36 | hex-notation: 37 | - 0 38 | - style: lowercase 39 | id-name-format: 40 | - 1 41 | - convention: hyphenatedbem 42 | indentation: 43 | - 0 44 | - size: 2 45 | leading-zero: 46 | - 1 47 | - include: true 48 | mixin-name-format: 49 | - 1 50 | - allow-leading-underscore: true 51 | convention: hyphenatedlowercase 52 | mixins-before-declarations: 0 53 | nesting-depth: 54 | - 0 55 | - max-depth: 6 56 | no-color-literals: 0 57 | no-css-comments: 1 58 | no-debug: 1 59 | no-duplicate-properties: 1 60 | no-empty-rulesets: 1 61 | no-ids: 0 62 | no-important: 1 63 | no-invalid-hex: 1 64 | no-mergeable-selectors: 1 65 | no-misspelled-properties: 66 | - 1 67 | - extra-properties: 68 | - animate 69 | - overflow-scrolling 70 | no-qualifying-elements: 71 | - 1 72 | - allow-element-with-attribute: true 73 | allow-element-with-class: true 74 | allow-element-with-id: false 75 | no-trailing-zero: 1 76 | no-url-protocols: 0 77 | no-vendor-prefixes: 0 78 | placeholder-in-extend: 1 79 | placeholder-name-format: 80 | - 1 81 | - convention: hyphenatedbem 82 | property-sort-order: 0 83 | quotes: 84 | - 0 85 | - style: single 86 | shorthand-values: 1 87 | single-line-per-selector: 1 88 | space-after-bang: 89 | - 1 90 | - include: false 91 | space-after-colon: 1 92 | space-after-comma: 1 93 | space-before-bang: 94 | - 1 95 | - include: true 96 | space-before-brace: 97 | - 1 98 | - include: true 99 | space-before-colon: 1 100 | space-between-parens: 101 | - 1 102 | - include: false 103 | trailing-semicolon: 1 104 | url-quotes: 0 105 | variable-for-property: 106 | - 0 107 | - properties: [] 108 | variable-name-format: 109 | - 1 110 | - allow-leading-underscore: true 111 | convention: hyphenatedlowercase 112 | zero-unit: 0 113 | -------------------------------------------------------------------------------- /src/gulp-tasks-pack.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import del from 'del'; 3 | import fs from 'fs'; 4 | import cp from 'child_process'; 5 | import tarball from 'tarball-extract'; 6 | 7 | export function packTasks (gulp) { 8 | 9 | gulp.task('pack', (done) => { 10 | const packageJSON = path.join(process.cwd(), 'package.json'); 11 | const contents = fs.readFileSync(packageJSON); 12 | const json = JSON.parse(contents); 13 | if (json.dependencies) { 14 | json.bundledDependencies = Object.keys(json.dependencies); 15 | fs.writeFileSync(packageJSON, JSON.stringify(json, null, 2)); 16 | } 17 | 18 | try { 19 | cp.exec('npm pack', (err, stdout, stderr) => { 20 | console.log(stdout); 21 | console.error(stderr); 22 | 23 | if (err) { 24 | throw err; 25 | } 26 | const licenseMap = { 27 | name: json.name, 28 | version: json.version, 29 | dependencies: { 30 | licenseNotFound: [] 31 | } 32 | }; 33 | 34 | const tarballName = `${json.name}-${json.version}.tgz`; 35 | tarball.extractTarball(tarballName, './tmp', (err) => { 36 | if (err) { 37 | throw err; 38 | } 39 | const dependencies = fs.readdirSync('./tmp/package/node_modules'); 40 | 41 | dependencies.forEach((dependency) => { 42 | const dependencyPackageJSON = path.join( 43 | process.cwd(), `node_modules/${dependency}/package.json` 44 | ); 45 | const contents = fs.readFileSync(dependencyPackageJSON); 46 | const json = JSON.parse(contents); 47 | let license = json.license; 48 | if (!license && json.licenses) { 49 | license = json.licenses[0]; 50 | } 51 | 52 | if (!license) { 53 | licenseMap.dependencies.licenseNotFound.push(dependency); 54 | } else if (license.type) { 55 | licenseMap.dependencies[dependency] = license.type; 56 | } else { 57 | licenseMap.dependencies[dependency] = license; 58 | } 59 | }); 60 | 61 | const dependencyLicense = path.join( 62 | process.cwd(), `${json.name}-${json.version}-licenses.json` 63 | ); 64 | 65 | //write dependency license map 66 | fs.writeFileSync(dependencyLicense, JSON.stringify( 67 | licenseMap, null, 2) 68 | ); 69 | 70 | //revert original package.json 71 | fs.writeFileSync(packageJSON, JSON.stringify( 72 | JSON.parse(contents), null, 2) 73 | ); 74 | 75 | del.sync(['./tmp']); 76 | 77 | done(); 78 | }); 79 | }); 80 | } catch (e) { 81 | console.log(e); 82 | 83 | //revert original package.json 84 | fs.writeFileSync(packageJSON, JSON.stringify( 85 | JSON.parse(contents), null, 2) 86 | ); 87 | 88 | done(); 89 | } 90 | }); 91 | }; 92 | 93 | export default packTasks; 94 | -------------------------------------------------------------------------------- /src/gulp-tasks-test.js: -------------------------------------------------------------------------------- 1 | import env from 'gulp-env'; 2 | import jest from 'gulp-jest'; 3 | import gulpif from 'gulp-if'; 4 | import gutil from 'gulp-util'; 5 | 6 | import gulpOptionsBuilder from './gulp-options-builder'; 7 | 8 | export function testTasks (gulp, opts) { 9 | 10 | const envs = env.set({ 11 | NODE_ENV: 'test' 12 | }); 13 | 14 | const runSequence = require('run-sequence').use(gulp); 15 | 16 | const options = gulpOptionsBuilder(opts); 17 | 18 | let watch; 19 | 20 | gulp.task('test', () => { 21 | if (watch) { 22 | process.env.NODE_ENV = 'test'; 23 | } 24 | if (options.testPaths) { 25 | return gulp.src(options.testPaths) 26 | .pipe(gulpif(!watch, envs)) 27 | .pipe(jest({ 28 | modulePathIgnorePatterns: [ 29 | "/dist/", 30 | "/templates/" 31 | ], 32 | testPathIgnorePatterns: options.testPaths.filter( 33 | (path) => path.startsWith('!') 34 | ).map((path) => path.substring(1)), 35 | rootDir: options.base || process.cwd(), 36 | verbose: true, 37 | ...options.argv 38 | })) 39 | .on('error', (error) => { 40 | gutil.log(error.message); 41 | if (!watch) { 42 | process.exit(1); 43 | } 44 | }) 45 | .pipe(gulpif(!watch, envs.reset)); 46 | } 47 | }); 48 | 49 | gulp.task('test:update', () => { 50 | if (options.testPaths) { 51 | return gulp.src(options.testPaths) 52 | .pipe(gulpif(!watch, envs)) 53 | .pipe(jest({ 54 | modulePathIgnorePatterns: [ 55 | "/dist/", 56 | "/templates/" 57 | ], 58 | testPathIgnorePatterns: options.testPaths.filter( 59 | (path) => path.startsWith('!') 60 | ).map((path) => path.substring(1)), 61 | rootDir: options.base || process.cwd(), 62 | updateSnapshot: true, 63 | ...options.argv 64 | })) 65 | .on('error', (error) => { 66 | gutil.log(error.message); 67 | process.exit(1); 68 | }) 69 | .pipe(gulpif(!watch, envs.reset)); 70 | } 71 | }); 72 | 73 | gulp.task('test:watcher', () => 74 | gulp.watch([...options.testPaths, ...options.jsAssets], ['test']) 75 | ); 76 | 77 | gulp.task('test:watch', () => { 78 | watch = true; 79 | runSequence('test', 'test:watcher'); 80 | }); 81 | 82 | gulp.task('test:coverage', () => { 83 | if (options.testPaths) { 84 | return gulp.src(options.testPaths) 85 | .pipe(envs) 86 | .pipe(jest({ 87 | collectCoverageFrom: options.jsAssets, 88 | collectCoverage: true, 89 | coverageReporters: ['lcov'], 90 | modulePathIgnorePatterns: [ 91 | "/dist/", 92 | "/templates/" 93 | ], 94 | testPathIgnorePatterns: options.testPaths.filter( 95 | (path) => path.startsWith('!') 96 | ).map((path) => path.substring(1)), 97 | rootDir: options.base || process.cwd(), 98 | ...options.argv 99 | })) 100 | .on('error', (error) => { 101 | gutil.log(error.message); 102 | process.exit(1); 103 | }) 104 | .pipe(envs.reset) 105 | .on('finish', () => { 106 | console.log( 107 | 'Test coverage report available at coverage/lcov-report/index.html' 108 | ); 109 | }); 110 | } 111 | }); 112 | }; 113 | 114 | export default testTasks; 115 | -------------------------------------------------------------------------------- /src/gulp-options-builder.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | import deepAssign from 'deep-assign'; 4 | import yargs from 'yargs'; 5 | 6 | const argv = yargs 7 | .option('minify', { 8 | type: 'boolean', 9 | default: true 10 | }) 11 | .option('open', { 12 | type: 'boolean', 13 | default: true 14 | }) 15 | .option('preprocess', { 16 | type: 'boolean', 17 | default: true 18 | }) 19 | .argv; 20 | 21 | delete argv._; 22 | delete argv.$0; 23 | 24 | const deprecated = (name, warning) => { 25 | console.warn(`[grommet-toolbox] DEPRECATED: ${name}. ${warning}`); 26 | }; 27 | 28 | let options; 29 | export function getOptions (opts) { 30 | if (!options) { 31 | if (!opts) { 32 | const configPath = path.resolve(process.cwd(), 'grommet-toolbox.config.js'); 33 | try { 34 | fs.accessSync(configPath, fs.F_OK); 35 | } catch (e) { 36 | opts = {}; 37 | } 38 | 39 | if (!opts) { 40 | const config = require(configPath); 41 | opts = config.default || config; 42 | } 43 | } 44 | 45 | options = opts || {}; 46 | 47 | options.lintCache = opts.lintCache !== undefined ? opts.lintCache : true; 48 | 49 | if (options.scsslint || options.scsslint === false) { 50 | console.warn('[grommet-toolbox] scsslint option has been deprecated and will be removed in the next major release. SCSS linting is always enabled now.'); 51 | } 52 | 53 | options.scsslint = options.scsslint === undefined ? true : options.scsslint; 54 | 55 | options.dist = options.dist || path.resolve(process.cwd(), 'dist'); 56 | 57 | const jsLoader = options.jsLoader || { 58 | test: /\.jsx?|.react$/, 59 | loader: 'babel', 60 | exclude: /(node_modules|bower_components|src\/lib)/ 61 | }; 62 | 63 | const scssLoader = options.scssLoader || { 64 | test: /\.scss$/, 65 | loader: 'style!css!sass?outputStyle=compressed&' + 66 | 'includePaths[]=' + 67 | (encodeURIComponent( 68 | path.resolve(options.base || process.cwd(), './node_modules') 69 | )) + 70 | '&includePaths[]=' + 71 | (encodeURIComponent( 72 | path.resolve(options.base || process.cwd(), 73 | './node_modules/grommet/node_modules')) 74 | ) 75 | }; 76 | 77 | options.webpack = deepAssign({ 78 | entry: options.webpack && options.webpack.entry ? 79 | options.webpack.entry : path.resolve(options.mainJs), 80 | output: { 81 | filename: 'index.js' 82 | }, 83 | resolve: { 84 | root: [ 85 | path.resolve(process.cwd(), 'node_modules') 86 | ] 87 | }, 88 | module: { 89 | loaders: [] 90 | }, 91 | resolveLoader: {} 92 | }, options.webpack); 93 | 94 | options.webpack.module.loaders = options.webpack.module.loaders.concat( 95 | jsLoader, 96 | scssLoader, 97 | { 98 | test: /\.json$/, 99 | loader: 'json-loader' 100 | }, 101 | { 102 | test: /\.png$/, 103 | loader: 'file-loader?mimetype=image/png' 104 | }, 105 | { 106 | test: /\.jpg$/, 107 | loader: 'file-loader?mimetype=image/jpg' 108 | }, 109 | { 110 | test: /\.woff$/, 111 | loader: 'file-loader?mimetype=application/font-woff' 112 | }, 113 | { 114 | test: /\.otf$/, 115 | loader: 'file-loader?mimetype=application/font/opentype' 116 | }, 117 | { 118 | test: /\.css$/, 119 | loader: 'style-loader!css-loader' 120 | } 121 | ); 122 | 123 | // Argv Deprecation warnings 124 | if (argv.skipPreprocess) { 125 | deprecated('skipPreprocess', 'Use --no-preprocess instead.'); 126 | argv.preprocess = false; 127 | } 128 | 129 | if (argv.skipOpen) { 130 | deprecated('skipOpen', 'Use --no-open instead.'); 131 | argv.open = false; 132 | } 133 | 134 | if (argv.skipMinify) { 135 | deprecated('skipMinify', 'Use --no-minify instead.'); 136 | argv.minify = false; 137 | } 138 | 139 | options.argv = deepAssign({}, options.argv, argv); 140 | } 141 | 142 | return options; 143 | }; 144 | 145 | export default getOptions; 146 | -------------------------------------------------------------------------------- /src/gulp-tasks-dev.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import WebpackDevServer from 'webpack-dev-server'; 3 | import gulpOpen from 'gulp-open'; 4 | import path from 'path'; 5 | import {writeFile} from 'fs'; 6 | import deepAssign from 'deep-assign'; 7 | 8 | import gulpOptionsBuilder from './gulp-options-builder'; 9 | import gulpTasksCore from './gulp-tasks-core'; 10 | 11 | export function devTasks (gulp, opts) { 12 | 13 | const runSequence = require('run-sequence').use(gulp); 14 | 15 | gulpTasksCore(gulp, opts); 16 | 17 | const options = gulpOptionsBuilder(opts); 18 | 19 | gulp.task('dev-preprocess', (callback) => { 20 | if (!options.argv.preprocess) { 21 | callback(); 22 | return; 23 | } 24 | 25 | if (options.devPreprocess) { 26 | runSequence( 27 | 'clean', 'generate-icons', options.devPreprocess, 'copy', callback 28 | ); 29 | } else { 30 | runSequence('clean', 'generate-icons', 'copy', callback); 31 | } 32 | }); 33 | 34 | gulp.task('dev', ['dev-preprocess'], () => { 35 | 36 | let webpackConfigPath = path.resolve( 37 | __dirname, 'webpack.dev.config.js' 38 | ); 39 | 40 | if (options.argv.config) { 41 | webpackConfigPath = path.resolve(options.argv.config); 42 | } 43 | 44 | const config = require(webpackConfigPath); 45 | 46 | const devServerConfig = { 47 | contentBase: options.dist, 48 | hot: !options.devServerDisableHot, 49 | inline: true, 50 | stats: { 51 | colors: true 52 | }, 53 | publicPath: config.output.publicPath, 54 | historyApiFallback: true 55 | }; 56 | 57 | if (options.watchOptions) { 58 | devServerConfig.watchOptions = options.watchOptions; 59 | } 60 | 61 | if (options.devServerProxy) { 62 | devServerConfig.proxy = options.devServerProxy; 63 | } 64 | 65 | if (options.devServer) { 66 | deepAssign(devServerConfig, options.devServer); 67 | } 68 | 69 | if (options.webpackProfile) config.profile = true; 70 | 71 | const compiler = webpack(config); 72 | 73 | if (options.webpackProfile) { 74 | compiler.plugin('done', stats => { 75 | const profileFile = path.resolve(options.webpackProfile); 76 | const statsString = JSON.stringify(stats.toJson()); 77 | writeFile(profileFile, statsString, (err) => { 78 | if (err) return console.error('Failed to write webpackProfile:', err); 79 | console.log('[webpack] Wrote webpack stats to:', profileFile); 80 | console.log('[webpack] Analyze stats at https://webpack.github.io/analyse/'); 81 | }); 82 | }); 83 | } 84 | 85 | const server = new WebpackDevServer(compiler, devServerConfig); 86 | 87 | server.use('/', (req, res, next) => { 88 | 89 | const acceptLanguageHeader = req.headers['accept-language']; 90 | 91 | if (acceptLanguageHeader) { 92 | const acceptedLanguages = acceptLanguageHeader.match( 93 | /[a-zA-z\-]{2,10}/g 94 | ); 95 | if (acceptedLanguages) { 96 | res.cookie('languages', JSON.stringify(acceptedLanguages)); 97 | } 98 | } 99 | 100 | if (req.url.match(/.+\/img\//)) { // img 101 | res.redirect(301, req.url.replace(/.*\/(img\/.*)$/, '/$1')); 102 | } else if (req.url.match(/\/img\//)) { // img 103 | next(); 104 | } else if (req.url.match(/.+\/video\//)) { // video 105 | res.redirect(301, req.url.replace(/.*\/(video\/.*)$/, '/$1')); 106 | } else if (req.url.match(/\/video\//)) { // video 107 | next(); 108 | } else if (req.url.match(/.+\/font\//)) { // font 109 | res.redirect(301, req.url.replace(/.*\/(font\/.*)$/, '/$1')); 110 | } else if (req.url.match(/\/font\//)) { // font 111 | next(); 112 | } else if (req.url.match(/.+\/.*\.[^\/]*$/)) { // file 113 | res.redirect(301, req.url.replace(/.*\/([^\/]*)$/, '/$1')); 114 | } else { 115 | next(); 116 | } 117 | }); 118 | 119 | // Always open on all ports unless overridden 120 | const host = options.devServerHost || '0.0.0.0'; 121 | 122 | server.listen(options.devServerPort || 8080, host, (err) => { 123 | if (err) { 124 | console.error('[webpack-dev-server] failed to start:', err); 125 | } else { 126 | const protocol = (options.devServer && options.devServer.https) ? 'https' : 'http'; 127 | const openHost = (host === '0.0.0.0') ? 'localhost' : host; 128 | const suffix = options.publicPath ? options.publicPath + '/' : ''; 129 | const openURL = protocol + '://' + openHost + ':' + options.devServerPort + suffix; 130 | 131 | let openMsg = '[webpack-dev-server] started: '; 132 | if (!options.argv.open) { 133 | openMsg += `app available at location: \u001b[33m${openURL}\u001b[39m`; 134 | } else { 135 | openMsg += 'opening the app in your default browser...'; 136 | } 137 | 138 | console.log(openMsg); 139 | if (!options.argv.open) return; 140 | 141 | gulp.src(__filename) 142 | .pipe(gulpOpen({ 143 | uri: openURL 144 | })); 145 | } 146 | }); 147 | 148 | server.app.get('/reload', (req, res) => { 149 | // Tell connected browsers to reload. 150 | server.sockWrite(server.sockets, 'ok'); 151 | res.sendStatus(200); 152 | }); 153 | 154 | server.app.get('/invalid', (req, res) => { 155 | // Tell connected browsers some change is about to happen. 156 | server.sockWrite(server.sockets, 'invalid'); 157 | res.sendStatus(200); 158 | }); 159 | 160 | }); 161 | }; 162 | 163 | export default devTasks; 164 | -------------------------------------------------------------------------------- /src/gulp-tasks-core.js: -------------------------------------------------------------------------------- 1 | import del from 'del'; 2 | import file from 'gulp-file'; 3 | import gulpif from 'gulp-if'; 4 | import babel from 'gulp-babel'; 5 | import cache from 'gulp-cache'; 6 | import path from 'path'; 7 | import fs from 'fs'; 8 | import loader from 'grommet-icon-loader'; 9 | import mkdirp from 'mkdirp'; 10 | import pathIsAbsolute from 'path-is-absolute'; 11 | 12 | import gulpOptionsBuilder from './gulp-options-builder'; 13 | 14 | let loaded; 15 | 16 | export function coreTasks (gulp, opts) { 17 | const options = gulpOptionsBuilder(opts); 18 | 19 | if (!loaded) { 20 | const runSequence = require('run-sequence').use(gulp); 21 | 22 | gulp.task('copy', (done) => { 23 | let count = 0; 24 | (options.copyAssets || []).forEach((copyAsset) => { 25 | if (copyAsset.filename) { 26 | count++; 27 | gulp.src('./') 28 | .pipe(file(copyAsset.filename, copyAsset.asset)) 29 | .pipe(gulp.dest(copyAsset.dist ? copyAsset.dist : options.dist)); 30 | } else { 31 | const asset = copyAsset.asset ? copyAsset.asset : copyAsset; 32 | const assets = [asset]; 33 | if (copyAsset.ignores) { 34 | copyAsset.ignores.forEach((ignore) => { 35 | assets.push('!' + asset.replace('**', '') + ignore + '/**'); 36 | assets.push('!' + asset + ignore); 37 | assets.push('!' + asset + '**/' + ignore); 38 | assets.push('!' + asset + '**/' + ignore + '/**'); 39 | }); 40 | } 41 | 42 | let babelrcPath = path.resolve(process.cwd(), '.babelrc'); 43 | try { 44 | fs.accessSync(babelrcPath, fs.F_OK); 45 | } catch (e) { 46 | babelrcPath = path.resolve(__dirname, '../.babelrc'); 47 | } 48 | 49 | const babelConfig = JSON.parse(fs.readFileSync(babelrcPath)); 50 | 51 | gulp.src(assets, { 52 | dot: true 53 | }).pipe(gulpif(copyAsset.babel, babel(babelConfig))) 54 | .pipe(gulp.dest(copyAsset.dist ? copyAsset.dist : options.dist)) 55 | .on('end', () => { 56 | count++; 57 | if (count === options.copyAssets.length) { 58 | done(); 59 | } 60 | }); 61 | } 62 | 63 | }); 64 | }); 65 | 66 | gulp.task('generate-icons', (done) => { 67 | const basePath = options.base || process.cwd(); 68 | const iconsConfig = options.icons || {}; 69 | let iconInputFolder = iconsConfig.source; 70 | if (iconInputFolder) { 71 | if (!pathIsAbsolute(iconsConfig.source)) { 72 | iconInputFolder = path.resolve( 73 | basePath, iconsConfig.source || 'src/img/icons' 74 | ); 75 | } 76 | 77 | fs.readdir(iconInputFolder, (err, icons) => { 78 | if (icons) { 79 | if (iconsConfig.destination) { 80 | icons.forEach((icon, index) => { 81 | if (/\.svg$/.test(icon)) { 82 | var iconPath = path.join(iconInputFolder, icon); 83 | var content = fs.readFileSync(iconPath, 'utf8'); 84 | var query = options.icons.context ? 85 | `?context=${options.icons.context}` : '?context=grommet/'; 86 | query += ( 87 | '©right=(C) Copyright 2014-2015 Hewlett Packard Enterprise Development LP' 88 | ); 89 | var loaderContext = { 90 | query: query, 91 | resourcePath: iconPath, 92 | addDependency: () => {}, 93 | async: () => { 94 | return (err, result) => { 95 | var iconDestFolder = iconsConfig.destination; 96 | if (!pathIsAbsolute(iconsConfig.destination)) { 97 | iconDestFolder = path.resolve( 98 | basePath, iconsConfig.destination 99 | ); 100 | } 101 | 102 | del.sync([iconDestFolder]); 103 | 104 | mkdirp(iconDestFolder, (err) => { 105 | 106 | if (err) { 107 | throw err; 108 | } 109 | 110 | var componentName = icon.replace('.svg', '.js'); 111 | componentName = componentName.replace(/^(.)|-([a-z])/g, 112 | (g) => { 113 | return g.length > 1 ? 114 | g[1].toUpperCase() : g.toUpperCase(); 115 | } 116 | ); 117 | 118 | var destinationFile = path.resolve( 119 | iconDestFolder, componentName 120 | ); 121 | 122 | fs.writeFile(destinationFile, result, (err) => { 123 | if (err) { 124 | throw err; 125 | } 126 | 127 | if (index === icons.length - 1) { 128 | done(); 129 | } 130 | }); 131 | }); 132 | }; 133 | } 134 | }; 135 | loader.apply(loaderContext, [content]); 136 | } 137 | }); 138 | } else { 139 | console.log( 140 | 'Please specify the options.icons.destination property in your gulpfile.' 141 | ); 142 | } 143 | } else { 144 | done(); 145 | } 146 | }); 147 | } else { 148 | done(); 149 | } 150 | }); 151 | 152 | gulp.task('preprocess', (callback) => 153 | runSequence( 154 | 'clean', 'generate-icons', 'jslint', 'scsslint', callback 155 | ) 156 | ); 157 | 158 | gulp.task('clean', () => del.sync([options.dist])); 159 | 160 | gulp.task('clear-cache', (done) => cache.clearAll(done)); 161 | 162 | gulp.task('pre-commit', options.preCommitTasks || 163 | ['jslint','scsslint','test']); 164 | 165 | gulp.task('node-clean', (done) => { 166 | require('rimraf')(path.resolve(process.cwd(), 'node_modules'), (err) => { 167 | if (err) { 168 | throw err; 169 | } 170 | done(); 171 | }); 172 | }); 173 | 174 | loaded = true; 175 | } 176 | 177 | if (options.base) { 178 | process.chdir(options.base); 179 | } 180 | } 181 | 182 | export default coreTasks; 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grommet-toolbox 2 | 3 | [![Dependency Status](https://david-dm.org/grommet/grommet-toolbox.svg)](https://david-dm.org/grommet/grommet-toolbox) [![devDependency Status](https://david-dm.org/grommet/grommet-toolbox/dev-status.svg)](https://david-dm.org/grommet/grommet-toolbox#info=devDependencies) 4 | 5 | [DEPRECATED]: Please use `grommet pack` and `grommet check` available in the new [grommet-cli](https://github.com/grommet/grommet-cli) 6 | 7 | Developer Environment for Grommet applications with the following built-in features: 8 | 9 | * Ability to create a production ready distribution with minified JavaScript and CSS 10 | * Ability to sync your distribution to a remote location 11 | * JavaScript and Sass linters 12 | * Development server with hot-reloading 13 | * Test infrastructure based on tape and gulp watch 14 | * Code coverage using Istanbul 15 | * Convert raw svgs to Grommet icons with accessibility support 16 | * Bundle your project and its dependencies in a compressed file 17 | 18 | grommet-toolbox runs on top of Gulp. 19 | 20 | ### Install 21 | 22 | ```bash 23 | npm install grommet-toolbox --save-dev 24 | ``` 25 | 26 | ### Basic usage 27 | 28 | **gulpfile.babel.js** 29 | ```javascript 30 | import gulp from 'gulp'; 31 | import grommetToolbox from 'grommet-toolbox'; 32 | 33 | var opts = { 34 | copyAssets: [ 35 | 'src/index.html', 36 | { 37 | asset: 'src/img/**', 38 | dist: 'dist/img/' 39 | } 40 | ], 41 | scssAssets: ['src/scss/**/*.scss'], 42 | jsAssets: ['src/js/**/*.js'], 43 | mainJs: 'src/js/index.js', 44 | mainScss: 'src/scss/index.scss', 45 | devServerPort: 9000 46 | }; 47 | 48 | grommetToolbox(gulp, opts); 49 | ``` 50 | 51 | Grommet-toolbox augments gulp object with these additional tasks: 52 | 53 | * **gulp copy**: uses `copyAssets` option to move files to distribution folder. 54 | * **gulp generate-icons**: uses `icons` option to convert raw svg icons to Grommet icons. 55 | * **gulp scsslint**: uses `scssAssets` option to lint your Sass code. 56 | * **gulp jslint**: uses `jsAssets` and `testPaths` options to lint your JavaScript code. 57 | * **gulp dev**: starts a webpack dev server with hot reloading. See options for example configuration. 58 | * `--config`: Set the path of the config file to use. 59 | * `--no-preprocess`: Skips preprocess tasks. 60 | * `--no-open`: Skips opening dev server url in a browser. 61 | * **gulp dist**: prepares your application/library for production. 62 | * `--config`: Set the path of the config file to use. 63 | * `--no-preprocess`: Skips preprocess tasks. 64 | * `--no-minify`: Skips minifying JS code. 65 | * **gulp sync**: uses `sync` option to sync distribution content to a remote server. 66 | * **gulp test**: uses `testPaths` option to execute tests based on Tape. 67 | * **gulp test:watch**: runs tests and watch for changes to execute the tests again. 68 | * **gulp test:coverage**: runs tests and generates a code coverage report. 69 | * **gulp test:update**: runs all the tests and updates the Jest snapshots for the project. 70 | * **gulp pack**: uses the package.json dependencies object to create a compressed file with all the dependencies included. 71 | 72 | ### Recommended Usage 73 | 74 | As your configuration grows it gets really difficult to manipulate everything inside a single gulp file. Grommet-toolbox offers a config file where you can store your application specific settings. This is the **recommended** way of using this tool. Now you will have two files, **grommet-toolbox.config.js** and **gulpfile.babel.js**: 75 | 76 | **grommet-toolbox.config.js** 77 | ```javascript 78 | export default { 79 | copyAssets: [ 80 | 'src/index.html', 81 | { 82 | asset: 'src/img/**', 83 | dist: 'dist/img/' 84 | } 85 | ], 86 | scssAssets: ['src/scss/**/*.scss'], 87 | jsAssets: ['src/js/**/*.js'], 88 | mainJs: 'src/js/index.js', 89 | mainScss: 'src/scss/index.scss', 90 | devServerPort: 9000 91 | }; 92 | ``` 93 | 94 | **gulpfile.babel.js** 95 | ```javascript 96 | import gulp from 'gulp'; 97 | import grommetToolbox from 'grommet-toolbox'; 98 | 99 | grommetToolbox(gulp); 100 | ``` 101 | 102 | grommet-toolbox will look into your application's root folder and extract the configuration for your project. 103 | 104 | ### Options 105 | 106 | | property | type | description | default | example | 107 | | ------------- |---------------|-----------------|------------- |------------| 108 | | argv | object | Optional. Default cli args set on gulp tasks. *See above.* | `{}` | `{open: false}` | 109 | | base | string | Optional. Base working directory | process.cwd() | `base: '.'` | 110 | | copyAssets | array | Optional. Assets to be copied to the distribution folder | undefined | [See copyAssets WIKI](https://github.com/grommet/grommet-toolbox/wiki/copyAssets-WIKI) | 111 | | eslintConfigPath | string | Optional. Path to your custom eslint config file | undefined | `eslintConfigPath: path.resolve(__dirname, '../.eslintrc')` | 112 | | eslintOverride | string | Optional. Path to your custom eslint overrides | undefined | `eslintOverride: path.resolve(__dirname, 'customEslintrc')` | 113 | | devPreprocess | array | Optional. A set of tasks to run before `gulp dev` | undefined | `['set-webpack-alias']` | 114 | | devServerDisableHot | boolean | Optional. If true, will disable webpack hot reloading | false | `devServerDisableHot: true` | 115 | | devServerHost | string | Optional. Host address for the webpack dev server | 'localhost' | `devServerHost: '127.0.0.1'` | 116 | | devServerPort | int | Optional. Sets a listener port for the webpack dev server | 8080 | `devServerPort: 9000` | 117 | | devServerProxy | object | Optional. Proxy requests from the webpack dev server | undefined | `devServerProxy: { '/rest/*': 'http://localhost:8114' }`| 118 | | devServer | object | Optional. Any additional options for the webpack dev server | undefined | `devServer: { https: true }`| 119 | | dist | string | Optional. Location of the distribution folder | 'dist' | `dist: 'distribution'` | 120 | | distPreprocess | array | Optional. A set of tasks to run before `gulp dist` | undefined | `['dist-css']` | 121 | | env | object | Optional. Adds environment variables for Node | undefined | `{ DEV_MODE: 'true'}` | 122 | | icons | object | Optional. Converts raw icons to a Grommet icon | undefined | [See icon WIKI](https://github.com/grommet/grommet-toolbox/wiki/icon-WIKI) | 123 | | jsAssets | array | Required. Location of your JavaScript Assets | [] | `jsAssets: ['src/js/**/*.js']` | 124 | | jsLoader | object | Optional. If you want to use another webpack loader for your JavaScript Assets | react-loader | `{ test: /\.jsx?$/, loader: 'babel-loader', exclude: /(node_modules|bower_components|src\/lib)/ }` | 125 | | lintCache | boolean | Optional. If true, it will skip caching for linters (build time increases). | true | `lintCache: false` | 126 | | mainJs | string | Required. Location of your main JavaScript file | undefined | `mainJs: 'src/js/index.js'` | 127 | | preCommitTasks | array | Optional. The gulp tasks to run as git pre-commit hooks | jslint, scsslint, test | `['jslint','scsslint','test']` | 128 | | publicPath | string | Optional. Your main app context | '/' | `publichPath: '/docs'` | 129 | | scssAssets | array | Optional. Location of your Sass Assets | [] | `scssAssets: ['src/scss/**/*.scss']` | 130 | | scssLoader | object | Optional. If you want to use another webpack loader for your SCSS Assets | react-loader | `{ test: /\.scss?$/, loader: 'file?name=assets/css/[name].css!sass'}` | 131 | | scsslint | boolean | (deprecated) Optional. If false, it will skip Sass linting | true | `scsslint: false` | 132 | | sync | object | Optional. Syncs your content to a remote server | undefined | `sync: { hostname: 'grommet.io', username: 'grommet', remoteDestination: '/var/www/html/'}` | 133 | | testPaths | array | Optional. Location of your test assets | undefined | `testPaths: ['test/**/*.js']` | 134 | | webpack | object | Optional. Additional webpack options to be used in gulp dist | undefined | [See Webpack Configuration](https://webpack.github.io/docs/configuration.html) | 135 | | webpackProfile | string | Optional. Location to save webpack profile stats in json format. | undefined | `webpackProfile: './stats.json'` | 136 | 137 | ### Example 138 | 139 | [See grommet-todo](https://github.com/grommet/grommet-todo) 140 | 141 | ### Advanced 142 | 143 | [See Advanced Usage wiki](https://github.com/grommet/grommet-toolbox/wiki/Advanced-Usage) 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------