├── .npmignore
├── .npmrc
├── docs
├── troubleshooting.md
├── banner.png
├── features
│ ├── browser-sync.md
│ ├── webpack.md
│ ├── js.md
│ ├── copy.md
│ ├── drupal-drush.md
│ ├── icons.md
│ ├── css.md
│ ├── sprite.md
│ └── pattern-lab.md
├── extra.css
├── index.md
└── usage.md
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .eslintignore
├── .travis.yml
├── banner.png
├── .gitignore
├── .editorconfig
├── AUTHORS
├── mkdocs.yml
├── CONTRIBUTORS
├── templates
├── .stylelintrc.json
├── .eslintrc.json
├── gulpfile.js
├── webpack.config.js
└── _icons.scss
├── lib
├── drupal.js
├── copy.js
├── core.js
├── browser-sync.js
├── sprite.js
├── webpack.js
├── js.js
├── icons.js
├── css.js
└── pattern-lab--php-twig.js
├── .eslintrc.js
├── LICENSE
├── README.md
├── package.json
├── index.js
├── CONTRIBUTING.md
└── gulpfile.default.yml
/.npmignore:
--------------------------------------------------------------------------------
1 | tests
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | ---
2 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @EvanLovely @blaryjp
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | /templates
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 8
4 | - 7
5 | - 6
6 |
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/gulp-drupal-stack/HEAD/banner.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | tests/css/dest/
4 |
5 | .idea
6 | npm-debug.log
7 |
--------------------------------------------------------------------------------
/docs/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/gulp-drupal-stack/HEAD/docs/banner.png
--------------------------------------------------------------------------------
/docs/features/browser-sync.md:
--------------------------------------------------------------------------------
1 | Create a connection between your desk and your website, using Browsersync.
2 |
3 | ## Commands
4 |
5 | - `gulp serve` - Launch Browsersync server
6 |
7 | ---
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Drupal editor configuration normalization
2 | # @see http://editorconfig.org/
3 |
4 | # This is the top-most .editorconfig file; do not search in parent directories.
5 | root = true
6 |
7 | # All files.
8 | [*]
9 | end_of_line = LF
10 | indent_style = space
11 | indent_size = 2
12 | charset = utf-8
13 | trim_trailing_whitespace = true
14 | insert_final_newline = true
15 |
16 | [composer.{json,lock}]
17 | indent_size = 4
18 |
--------------------------------------------------------------------------------
/docs/features/webpack.md:
--------------------------------------------------------------------------------
1 | Manage your JS files using webpack.
2 |
3 | ## Usage
4 |
5 | Copy the `webpack.config.js` example file in your project.
6 | You can then configure the JS bundles that you want:
7 | ```js
8 | entry: {
9 | main: path.resolve(__dirname, './js/main.js')
10 | [...]
11 | },
12 | ```
13 |
14 | ## Commands
15 |
16 | - `gulp webpack` - Launch webpack compilation
17 | - `gulp validate:webpack` - Test JS with ESLINT
18 | - `gulp watch:webpack` - Watch and compile
19 |
20 | ---
21 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the official list of gulp-drupal-stack authors for copyright purposes.
2 | # This file is distinct from the CONTRIBUTORS files
3 | # and it lists the copyright holders only.
4 |
5 | # Names should be added to this file as one of
6 | # Organization's name
7 | # Individual's name
8 | # Individual's name
9 | # See CONTRIBUTORS for the meaning of multiple email addresses.
10 |
11 | # Please keep the list sorted.
12 |
13 | OVH SAS
14 |
--------------------------------------------------------------------------------
/docs/features/js.md:
--------------------------------------------------------------------------------
1 | Compiles JS files using Babel. You can optionaly concat, uglify, and add sourcemaps.
2 |
3 | !!! note ""
4 | We recommand to use [webpack](features/webpack.md) for big SPA.
5 |
6 | ## Commands
7 |
8 | - `gulp js` - Compile all JS files using Babel
9 | - `gulp watch:js` - Watch and compile
10 | - `gulp validate:js` - Test JS with ESLINT
11 | - `gulp js:bundleBower` - (optional) compile, uglify, concat bower dependencies (result files will be `bower--*deps*.js`)
12 | - `gulp watch:bower` - (optional) Watch and compile bower deps
13 |
14 | ---
15 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Gulp Drupal Stack
2 | docs_dir: docs
3 |
4 | pages:
5 | - Home: index.md
6 | - Usage: usage.md
7 | - Features:
8 | - CSS: features/css.md
9 | - JS: features/js.md
10 | - Webpack: features/webpack.md
11 | - Icons: features/icons.md
12 | - Sprite: features/sprite.md
13 | - Drupal-Drush: features/drupal-drush.md
14 | - Browsersync: features/browser-sync.md
15 | - Copy: features/copy.md
16 | - Troubleshooting: troubleshooting.md
17 |
18 | markdown_extensions:
19 | - toc:
20 | permalink: True
21 | - admonition:
22 |
23 | extra_css:
24 | - extra.css
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Description
2 |
3 |
4 |
5 |
6 | ### Steps to Reproduce
7 |
8 |
9 | 1. [First Step]
10 | 2. [Second Step]
11 | 3. [and so on...]
12 |
13 | **Expected behavior:**
14 |
15 |
16 |
17 | **Actual behavior:**
18 |
19 |
20 |
21 | **Reproduces how often:**
22 |
23 |
24 | ### Additional Information
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | # This is the official list of people who can contribute
2 | # (and typically have contributed) code to the gulp-drupal-stack repository.
3 | #
4 | # Names should be added to this file only after verifying that
5 | # the individual or the individual's organization has agreed to
6 | # the appropriate CONTRIBUTING.md file.
7 | #
8 | # Names should be added to this file like so:
9 | # Individual's name
10 | # Individual's name
11 | #
12 | # Please keep the list sorted.
13 | #
14 |
15 | Antoine Leblanc
16 | Xavier Ternisien
17 |
--------------------------------------------------------------------------------
/templates/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "stylelint-scss"
4 | ],
5 | "ignoreFiles": [],
6 | "rules": {
7 | "declaration-colon-space-after": "always",
8 | "declaration-no-important": true,
9 | "indentation": 2,
10 | "max-nesting-depth": 5,
11 | "selector-max-specificity": "0,5,5",
12 | "scss/at-extend-no-missing-placeholder": null,
13 | "scss/selector-no-redundant-nesting-selector": true,
14 | "at-rule-no-vendor-prefix": true,
15 | "media-feature-name-no-vendor-prefix": true,
16 | "property-no-vendor-prefix": true,
17 | "selector-no-vendor-prefix": true,
18 | "value-no-vendor-prefix": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/features/copy.md:
--------------------------------------------------------------------------------
1 | Copy any files to a destination folder (useful for node_modules vendoring).
2 |
3 | ## Usage
4 |
5 | You simply put the files (input) that you want to copy to output (dist).
6 | ```yml
7 | copy:
8 | enabled: true
9 | files:
10 | - src: "node_modules/lodash/dist/lodash.js"
11 | dest: "dist/vendors/"
12 | - src: "node_modules/underscore/dist/*.js"
13 | dest: "dist/vendors/"
14 | concat: true # (optional) enable concat
15 | destName: "underscore.js" # (optional) concatened file name
16 | [...]
17 | ```
18 |
19 | ## Commands
20 |
21 | - `gulp copy` - Launch copy task
22 | - `gulp watch:copy` - Watch and copy
23 |
24 | ---
25 |
--------------------------------------------------------------------------------
/docs/features/drupal-drush.md:
--------------------------------------------------------------------------------
1 | Launch a Drupal command.
2 |
3 | ## Commands
4 |
5 | - `gulp cr` - Launch Drupal command (by default `drush cr`)
6 | - `gulp watch:drupal` - Watch files and launch `gulp cr`
7 |
8 | ## Config
9 |
10 | - `config.drupal.themeFile` - The filename of your YOUR_THEME.info.yml (required for PatternLab tasks)
11 | - `config.drupal.dir` - The root directory for Drush
12 | - `config.drupal.command` - The drush command to execute
13 |
14 | ## Notes
15 |
16 | If you want to use it with [Drucker](https://github.com/ovh/drucker), you need to:
17 |
18 | - set the `config.drupal.dir` to `./path/to/drucker`
19 | - set the `config.drupal.command` to `. load-env && drush cr`
20 |
21 | ---
22 |
--------------------------------------------------------------------------------
/lib/drupal.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const core = require('./core');
4 | const browserSync = require('browser-sync');
5 |
6 | module.exports = (gulp, config, tasks) => {
7 | function clearDrupalCache(done) {
8 | core.sh(`cd ${config.drupal.dir} && ${config.drupal.command}`, true, () => {
9 | if (config.browserSync.enabled) {
10 | browserSync.get('server').reload();
11 | }
12 | done();
13 | });
14 | }
15 |
16 | clearDrupalCache.description = 'Clear Drupal Cache';
17 |
18 | gulp.task('cr', clearDrupalCache);
19 |
20 | gulp.task('watch:drupal', () => {
21 | gulp.watch(config.drupal.watch, clearDrupalCache);
22 | });
23 | tasks.watch.push('watch:drupal');
24 | };
25 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // rule reference: http://eslint.org/docs/rules
2 | // individual rule reference: http://eslint.org/docs/rules/NAME-OF-RULE
3 | module.exports = {
4 | extends: 'airbnb-base',
5 | env: {
6 | es6: true,
7 | node: true,
8 | },
9 | rules: {
10 | strict: [0],
11 | 'prefer-spread': [0],
12 | 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
13 | 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }],
14 | 'no-console': [0],
15 | 'global-require': [0],
16 | 'import/no-dynamic-require': [0],
17 | 'comma-dangle': ['error', {
18 | arrays: 'never',
19 | objects: 'never',
20 | imports: 'never',
21 | exports: 'never',
22 | functions: 'never'
23 | }],
24 | 'prefer-destructuring': [0]
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Requirements
2 |
3 | * Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
4 | * All new code requires tests to ensure against regressions
5 |
6 | ## Title of the Pull Requests
7 |
8 |
9 | ### Description of the Change
10 |
11 |
12 |
13 |
14 | ### Benefits
15 |
16 |
17 |
18 |
19 | ### Possible Drawbacks
20 |
21 |
22 |
23 |
24 | ### Applicable Issues
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/extra.css:
--------------------------------------------------------------------------------
1 | p + ul,
2 | p + ol {
3 | margin-top: -20px !important;
4 | margin-left: 15px;
5 | }
6 | .admonition > *:last-child {
7 | margin-bottom: 0 !important;
8 | }
9 |
10 | .admonition.quote,
11 | .admonition.success {
12 | padding: 12px;
13 | margin-bottom: 24px;
14 | line-height: 24px;
15 | }
16 | .admonition.quote {
17 | background: hsla(0,0%,93%,.5);
18 | }
19 | .admonition.success {
20 | background: rgba(0,200,83,.1);
21 | }
22 |
23 | .admonition.note {
24 | border-left: .2rem solid #00b0ff !important;
25 | }
26 | .admonition.success {
27 | border-left: .2rem solid #00c853 !important;
28 | }
29 | .admonition.danger {
30 | border-left: .2rem solid #ff1744 !important;
31 | }
32 | .admonition.warning {
33 | border-left: .2rem solid #ff9100 !important;
34 | }
35 | .admonition.cite {
36 | border-left: .2rem solid #9e9e9e;
37 | }
38 |
--------------------------------------------------------------------------------
/lib/copy.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const concat = require('gulp-concat');
4 | const gulpif = require('gulp-if');
5 |
6 | module.exports = (gulp, config, tasks) => {
7 | function copyCurrentFiles(files) {
8 | return gulp.src(files.src)
9 | .pipe(gulpif(files.concat, concat(files.destName || 'undefined.js')))
10 | .pipe(gulp.dest(files.dest));
11 | }
12 |
13 | copyCurrentFiles.description = 'Copies multiple files into a given destination (and optionally concat them).';
14 |
15 | gulp.task('copy', gulp.parallel(config.copy.files.map(files => function copyFiles() {
16 | return copyCurrentFiles(files);
17 | })));
18 |
19 | gulp.task('watch:copy', gulp.parallel(config.copy.files.map(files => function watchCopyFiles() {
20 | return gulp.watch(files.src, gulp.series(function copyFiles() {
21 | return copyCurrentFiles(files);
22 | }));
23 | })));
24 |
25 | tasks.compile.push('copy');
26 | tasks.watch.push('watch:copy');
27 | };
28 |
29 |
--------------------------------------------------------------------------------
/docs/features/icons.md:
--------------------------------------------------------------------------------
1 | Uses [gulp-iconfont](https://github.com/nfroidure/gulp-iconfont). Grabs a folder of SVG icons and turns them into font icons, creates a Sass mixin and class for each based on filename, adds all to a demo page.
2 |
3 | ## Usage
4 |
5 | Given a file named `facebook.svg`, you can use this Sass mixin:
6 |
7 | ```scss
8 | @include icon('facebook');
9 | ```
10 |
11 | Or this HTML class:
12 |
13 | ```html
14 |
15 | ```
16 |
17 | ## Commands
18 |
19 | - `gulp icons` - Compile Icons
20 | - `gulp watch:icons` - Watch for icons and compile
21 |
22 | ## Config
23 |
24 | - `config.icons.src` - Array or String of globbed SVG files
25 | - `config.icons.dest` - Destination directory for the fonts
26 | - `config.icons.fontPathPrefix` - Font path prefix
27 | - `config.icons.iconName` - Name of the icon (will be the name of the font)
28 | - `config.icons.classNamePrefix` - Icon class name prefix (default: "icon")
29 | - `config.icons.templates.css.dest` - The generated SCSS file
30 |
31 | ---
32 |
--------------------------------------------------------------------------------
/templates/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-airbnb",
3 | "env": {
4 | "browser": true,
5 | "es6": true,
6 | "node": true
7 | },
8 | "globals": {
9 | "Drupal": true,
10 | "drupalSettings": true,
11 | "drupalTranslations": true,
12 | "domready": true,
13 | "jQuery": true,
14 | "_": true,
15 | "matchMedia": true,
16 | "Backbone": true,
17 | "Modernizr": true,
18 | "CKEDITOR": true
19 | },
20 | "rules": {
21 | "consistent-return": [0],
22 | "no-underscore-dangle": [0],
23 | "max-nested-callbacks": [1, 3],
24 | "import/no-mutable-exports": [1],
25 | "no-plusplus": [1, {
26 | "allowForLoopAfterthoughts": true
27 | }],
28 | "no-param-reassign": [0],
29 | "no-prototype-builtins": [0],
30 | "valid-jsdoc": [1, {
31 | "prefer": {
32 | "returns": "return",
33 | "property": "prop"
34 | },
35 | "requireReturn": false
36 | }],
37 | "brace-style": ["error", "stroustrup"],
38 | "no-unused-vars": [1]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2013-present OVH SAS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/templates/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const gulp = require('gulp');
4 | const yaml = require('js-yaml');
5 | const fs = require('fs');
6 |
7 | // `rc` allows all config options to be overridden with CLI flags like `--js.enabled=""` or in `~/.gulp-drupal-corerc` files, among many others: https://www.npmjs.com/package/rc
8 | const config = require('rc')('gulp-drupal-stack', yaml.safeLoad(fs.readFileSync(`${__dirname}/gulpfile.yml`, 'utf8'), { json: true }));
9 | const drupalStack = require('gulp-drupal-stack');
10 |
11 | const tasks = {
12 | compile: [],
13 | watch: [],
14 | validate: [],
15 | test: [],
16 | clean: [],
17 | 'default': []
18 | };
19 |
20 | drupalStack(gulp, config, tasks);
21 |
22 | gulp.task('clean', gulp.parallel(tasks.clean));
23 | gulp.task('compile', gulp.series(
24 | 'clean',
25 | gulp.series(tasks.compile)
26 | ));
27 | gulp.task('build', gulp.series(['compile'])); // alias
28 | gulp.task('validate', gulp.parallel(tasks.validate));
29 | gulp.task('test', gulp.parallel(tasks.test));
30 | gulp.task('watch', gulp.parallel(tasks.watch));
31 | tasks.default.push('watch');
32 | gulp.task('default', gulp.series(
33 | 'compile',
34 | gulp.parallel(tasks.default)
35 | ));
36 |
--------------------------------------------------------------------------------
/docs/features/css.md:
--------------------------------------------------------------------------------
1 | Compiles SCSS to CSS using [gulp-sass](https://github.com/dlmanning/gulp-sass), which in turn uses `node-sass`, which in turn uses `libsass`.
2 |
3 | ## Usage
4 |
5 | You can create individual CSS files bundle, using the config:
6 | ```yml
7 | src:
8 | - scss/main.scss # -> dist/main.css
9 | - scss/my/app1.scss # -> dist/app1.css
10 | - scss/my/app2.scss # -> dist/app2.css
11 | [...]
12 | ```
13 |
14 | ## Commands
15 |
16 | - `gulp css` - Compile SCSS to CSS
17 | - `gulp watch:css` - Watch and compile
18 | - `gulp validate:css` - Test SCSS with [gulp-sass-lint](https://github.com/sasstools/gulp-sass-lint), which uses [sass-lint](https://github.com/sasstools/sass-lint) (Pure node.js - no Ruby)
19 | - `gulp format:css` - Format SCSS with [gulp-csscombx](https://github.com/drugan/gulp-csscombx), which uses [csscombx](https://github.com/drugan/csscombx)
20 |
21 | ## Config
22 |
23 | - `config.css.src` - Array of SCSS files
24 | - `config.css.dest` - String of Destination directory for CSS
25 | - `config.css.lint.enabled` - Boolean for if Linting should occur
26 | - `config.css.csscombx.enabled` - Boolean for if Formating should occur
27 | - `config.css.sourceComments` - Boolean - Enable comments written in CSS that shows SCSS source. **Don't turn on permanently**, it's useful if SourceMaps aren't working.
28 | - `config.css.autoPrefixerBrowsers` - Array of [Browsers to Support](https://github.com/ai/browserslist#queries) for Autoprefixer (used by PostCSS).
29 |
30 | ---
31 |
--------------------------------------------------------------------------------
/templates/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const merge = require('webpack-merge');
4 | const path = require('path');
5 | const webpack = require('webpack');
6 |
7 | // Shared configuration.
8 | const commonConfig = {
9 | context: path.resolve(__dirname, './'),
10 | entry: {
11 | main: path.resolve(__dirname, './js/main.js'),
12 | },
13 | output: {
14 | path: path.resolve(__dirname, 'dist'),
15 | filename: '[name].js',
16 | },
17 | externals: {
18 | jquery: 'jQuery',
19 | },
20 | module: {
21 | rules: [{
22 | test: /\.js$/,
23 | exclude: /node_modules/,
24 | use: 'babel-loader',
25 | }],
26 | },
27 | plugins: [],
28 | };
29 |
30 | // Development configuration.
31 | const developmentConfig = {
32 | devtool: 'cheap-eval-source-map',
33 | plugins: [
34 | new webpack.DefinePlugin({
35 | 'process.env.NODE_ENV': JSON.stringify('development'),
36 | }),
37 | ],
38 | };
39 |
40 | // Production configuration.
41 | const productionConfig = {
42 | devtool: 'source-map',
43 | plugins: [
44 | new webpack.DefinePlugin({
45 | 'process.env.NODE_ENV': JSON.stringify('production'),
46 | }),
47 | new webpack.LoaderOptionsPlugin({
48 | minimize: true,
49 | debug: false,
50 | }),
51 | new webpack.optimize.UglifyJsPlugin({
52 | sourceMap: 'source-map',
53 | }),
54 | ],
55 | };
56 |
57 | // Export config based on the current environment.
58 | if (process.env.NODE_ENV === 'production') {
59 | module.exports = merge(commonConfig, productionConfig);
60 | } else {
61 | module.exports = merge(commonConfig, developmentConfig);
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Gulp Drupal Stack
2 | =================
3 |
4 | 
5 |
6 | [](https://travis-ci.org/ovh/gulp-drupal-stack)
7 | [](http://gulp-drupal-stack.readthedocs.io/en/latest/?badge=latest)
8 |
9 |
10 | This stack core is to be included in your main project and sets up many Gulp tasks that can work in many flexible ways by passing in different `config` objects, which can be based off of `gulpfile.default.yml` (and is merged with).
11 |
12 |
13 | ## Features
14 |
15 | - SCSS => CSS compiling with LibSass, PostCSS, linting, CSScomb(x), and SourceMaps
16 | - JS compiling via Babel, linting and aggregation
17 | - webpack module bundling
18 | - SVG => Font Icons compiling with support for adding mixins and classes to SCSS along with a demo page
19 | - Drupal file watching to trigger Drush cache clears
20 | - Copy any files to an other location
21 | - Sprite generator (with Retina Display support)
22 |
23 | All is easily configurable by changing values in your `gulpfile.yml` file in your project. These values are merged into the `gulpfile.default.yml` file - look there for the available options and defaults.
24 |
25 |
26 | ## Documentation
27 |
28 | Full documentation is available [here](https://gulp-drupal-stack.readthedocs.io/en/latest/).
29 |
30 |
31 | #### TODO
32 |
33 | - Browsersync live reload and style injection (should be OK, not tested)
34 | - Images => Images optimization (to validate)
35 | - JS specs => JS tests using Karma
36 |
37 |
38 | ## Contributing
39 |
40 | Have a look at the [Contributing section](.github/CONTRIBUTING.md).
41 |
42 |
43 | ## Credits
44 |
45 | Original project from [`p2-theme-core`](https://github.com/phase2/p2-theme-core).
46 |
47 |
48 | ## License
49 |
50 | MIT (original license)
51 |
--------------------------------------------------------------------------------
/docs/features/sprite.md:
--------------------------------------------------------------------------------
1 | Uses [gulp.spritesmith](https://github.com/twolfson/gulp.spritesmith). Grabs a folder of images and turns them into a sprite, creates a Sass mixin and class for each based on filename.
2 |
3 | A documentation for SCSS features is available [here](https://www.bignerdranch.com/blog/css-sprite-management-with-gulp-part2/).
4 |
5 | ## Usage
6 |
7 | Given a file named `facebook.png`, you can use this Sass mixin:
8 |
9 | ```scss
10 | @include sprite('sprite-facebook');
11 | ```
12 |
13 | Or this HTML class:
14 |
15 | ```html
16 |
17 | ```
18 |
19 | ### Retina Display support
20 |
21 | You can generate a second sprite for Retina Display.
22 | First, you need to duplicates all your images and append "@2x" in the filename. For example: "facebook.png" and "facebook@2x.png".
23 |
24 | After enabled it, you can now use the mixin:
25 |
26 | ```scss
27 | @include retina-sprite('sprite-facebook');
28 | ```
29 |
30 | It will automatically take sprite@2x for Retina Display, and normal sprite for others.
31 |
32 |
33 | ## Commands
34 |
35 | - `gulp sprite` - Generates the sprite
36 | - `gulp watch:sprite` - Watch for images modifed and regenerate the sprite
37 |
38 | ## Config
39 |
40 | - `config.sprite.src` - Array or String of globbed PNG files
41 | - `config.sprite.imgDest` - Destination directory for the sprite image file
42 | - `config.sprite.cssDest` - Destination directory for the sprite SCSS file
43 | - `config.sprite.imgName` - Name of the sprite image file
44 | - `config.sprite.cssName` - Name of the sprite SCSS file
45 | - `config.sprite.imgPathPrefix` - Sprite image path prefix
46 | - `config.sprite.spritesheetName` - Name of the sprite
47 | - `config.sprite.imagemin` - Enable imagemin compression for the sprite image file
48 | - `config.sprite.retina.enabled` - Enable retina sprite generation
49 | - `config.sprite.retina.imgName` - Name of the retina sprite image file
50 | - `config.sprite.retina.filter` - Images filter that match the retina files
51 |
52 | ---
53 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | Gulp Drupal Stack
2 | =================
3 |
4 | 
5 |
6 | This stack core is to be included in your main project and sets up many Gulp tasks that can work in many flexible ways by passing in different `config` objects, which can be based off of `gulpfile.default.yml` (and is merged with).
7 |
8 |
9 | ## Features
10 |
11 | - SCSS => CSS compiling with LibSass, PostCSS, linting, CSScomb(x), and SourceMaps
12 | - JS compiling via Babel, linting and aggregation
13 | - Pattern Lab Twig compiling & BrowserSync live reload and style injection
14 | - webpack module bundling
15 | - SVG => Font Icons compiling with support for adding mixins and classes to SCSS along with a demo page
16 | - Drupal file watching to trigger Drush cache clears
17 | - Copy any files to an other location
18 | - Sprite generator (with Retina Display support)
19 |
20 | All is easily configurable by changing values in your `gulpfile.yml` file in your project. These values are merged into the `gulpfile.default.yml` file - look there for the available options and defaults.
21 |
22 |
23 | ## Prerequisites
24 |
25 | - [Node](https://nodejs.org)
26 | - [Gulp-cli](http://gulpjs.com/): `npm install -g gulp-cli`
27 |
28 |
29 | ## Installation
30 |
31 | Follow theses steps:
32 |
33 | ```bash
34 | $ cd
35 | # (optional) init a new npm module
36 | $ npm init
37 | # Install it
38 | $ npm install gulp-drupal-stack --save-dev
39 | # Create a gulpfile.js
40 | $ cp node_modules/gulp-drupal-stack/templates/gulpfile.js ./
41 | # Create a gulpfile.yml (config file)
42 | $ vi gulpfile.yml
43 | #
44 | ```
45 |
46 | ### IDE/Text Editor Setup
47 |
48 | - Install an EditorConfig plugin
49 | - Ignore the indexing of these directories:
50 | - `node_modules/`
51 | - `bower_components/`
52 | - `dest/`
53 | - `pattern-lab/public/`
54 | - `pattern-lab/vendor/`
55 |
56 |
57 | ## Usage
58 |
59 | See [Usage](usage.md) section.
60 |
61 |
62 | ## Credits
63 |
64 | Original project from [p2-theme-core](https://github.com/phase2/p2-theme-core).
65 |
66 |
67 | ## License
68 |
69 | MIT (original license)
70 |
71 | ---
72 |
--------------------------------------------------------------------------------
/lib/core.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const exec = require('child_process').exec;
4 | const notifier = require('node-notifier');
5 |
6 | function sh(cmd, exitOnError, cb) {
7 | const child = exec(cmd, {
8 | encoding: 'utf8',
9 | timeout: 1000 * 60 * 3 // 3 min; just want to make sure nothing gets detached forever.
10 | });
11 | let stdout = '';
12 | child.stdout.on('data', (data) => {
13 | stdout += data;
14 | process.stdout.write(data);
15 | });
16 | child.stderr.on('data', (data) => {
17 | process.stdout.write(data);
18 | });
19 | child.on('close', (code) => {
20 | if (code > 0) {
21 | if (exitOnError) {
22 | if (typeof cb === 'function') {
23 | cb(new Error(`Error with code ${code} after running: ${cmd}`));
24 | } else {
25 | process.stdout.write(`Error with code ${code} after running: ${cmd} \n`);
26 | process.exit(code);
27 | }
28 | } else {
29 | notifier.notify({
30 | title: cmd,
31 | message: stdout,
32 | sound: true
33 | });
34 | }
35 | }
36 | if (typeof cb === 'function') cb();
37 | });
38 | }
39 |
40 | /**
41 | * Flatten Array
42 | * @param arrayOfArrays {Array[]}
43 | * @returns {Array}
44 | */
45 | function flattenArray(arrayOfArrays) {
46 | return [].concat.apply([], arrayOfArrays);
47 | }
48 |
49 | /**
50 | * Make an array unique by removing duplicate entries.
51 | * @param item {Array}
52 | * @returns {Array}
53 | */
54 | function uniqueArray(item) {
55 | const u = {};
56 | const newArray = [];
57 | for (let i = 0, l = item.length; i < l; ++i) {
58 | if (!{}.hasOwnProperty.call(u, item[i])) {
59 | newArray.push(item[i]);
60 | u[item[i]] = 1;
61 | }
62 | }
63 | return newArray;
64 | }
65 |
66 | /**
67 | * Prepare Error message for `done()` Gulp Task callbacks that do not contain Stack Traces.
68 | * @param {string} message
69 | * @returns {Error}
70 | * @see http://stackoverflow.com/a/39093327/1033782
71 | */
72 | function error(message) {
73 | const err = new Error(message);
74 | err.showStack = false;
75 | return err;
76 | }
77 |
78 | module.exports = {
79 | sh,
80 | flattenArray,
81 | uniqueArray,
82 | error
83 | };
84 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-drupal-stack",
3 | "version": "3.3.0",
4 | "description": "OVH Gulp tasks for Drupal themes and modules",
5 | "main": "index.js",
6 | "author": "OVH SAS",
7 | "keywords": [
8 | "prototyping"
9 | ],
10 | "scripts": {
11 | "test": "eslint index.js lib/**",
12 | "start": "nodemon --watch index.js --watch ./lib --exec 'npm test' ",
13 | "preversion": "npm run test",
14 | "postversion": "git push && git push --tags",
15 | "gulp": "gulp"
16 | },
17 | "license": "MIT",
18 | "dependencies": {
19 | "autoprefixer": "^8.0.0",
20 | "browser-sync": "^2.23.6",
21 | "clone": "^2.1.1",
22 | "del": "^3.0.0",
23 | "eslint": "^4.17.0",
24 | "eslint-config-airbnb": "^16.1.0",
25 | "eslint-plugin-import": "^2.8.0",
26 | "eslint-plugin-jsx-a11y": "^6.0.3",
27 | "eslint-plugin-react": "^7.6.1",
28 | "glob": "^7.1.2",
29 | "gulp": "^4.0.0",
30 | "gulp-babel": "^7.0.1",
31 | "gulp-cached": "^1.1.1",
32 | "gulp-concat": "^2.6.1",
33 | "gulp-copy": "^1.1.0",
34 | "gulp-csscombx": "^4.2.1",
35 | "gulp-eslint": "^4.0.2",
36 | "gulp-flatten": "^0.4.0",
37 | "gulp-iconfont": "^9.1.0",
38 | "gulp-if": "^2.0.2",
39 | "gulp-imagemin": "^4.1.0",
40 | "gulp-inject": "^4.3.0",
41 | "gulp-notify": "^3.2.0",
42 | "gulp-plumber": "^1.2.0",
43 | "gulp-postcss": "^7.0.1",
44 | "gulp-sass": "^3.1.0",
45 | "gulp-sass-glob": "github:tomgrooffer/gulp-sass-glob#99d06af",
46 | "gulp-sourcemaps": "^2.6.4",
47 | "gulp-stylelint": "^6.0.0",
48 | "gulp-uglify": "^3.0.0",
49 | "gulp.spritesmith": "^6.9.0",
50 | "js-yaml": "^3.10.0",
51 | "lodash": "^4.17.5",
52 | "main-bower-files": "^2.13.1",
53 | "merge-stream": "^1.0.1",
54 | "node-notifier": "^5.2.1",
55 | "sassdoc": "^2.5.0",
56 | "stylelint": "^8.4.0",
57 | "stylelint-scss": "^2.3.0",
58 | "through2": "^2.0.3",
59 | "vinyl-buffer": "^1.0.1",
60 | "webpack": "^3.11.0"
61 | },
62 | "devDependencies": {
63 | "eslint": "^4.17.0",
64 | "eslint-config-airbnb": "^16.1.0",
65 | "eslint-plugin-import": "^2.8.0",
66 | "gulp-debug": "^3.2.0",
67 | "gulp-util": "^3.0.8",
68 | "nodemon": "^1.14.12"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/browser-sync.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const browserSync = require('browser-sync').create('server');
4 | const path = require('path');
5 | const _ = require('lodash');
6 |
7 | module.exports = (gulp, config, tasks) => {
8 | const watchFiles = [];
9 | if (config.css.enabled) {
10 | watchFiles.push(path.join(config.css.dest, '*.css'));
11 | }
12 | if (config.browserSync.watchFiles) {
13 | config.browserSync.watchFiles.forEach((file) => {
14 | watchFiles.push(file);
15 | });
16 | }
17 | const options = {
18 | browser: config.browserSync.browser,
19 | files: watchFiles,
20 | port: config.browserSync.port,
21 | tunnel: config.browserSync.tunnel,
22 | open: config.browserSync.openBrowserAtStart,
23 | reloadOnRestart: true,
24 | reloadDelay: config.browserSync.reloadDelay,
25 | reloadDebounce: config.browserSync.reloadDebounce,
26 | // https://www.browsersync.io/docs/options#option-middleware
27 | middleware: config.browserSync.middleware || [],
28 | // https://www.browsersync.io/docs/options#option-rewriteRules
29 | rewriteRules: config.browserSync.rewriteRules || [],
30 | // placing at `
`
31 | snippetOptions: {
32 | rule: {
33 | match: /<\/body>/i,
34 | fn: (snippet, match) => snippet + match
35 | }
36 | },
37 | notify: {
38 | styles: [
39 | 'display: none',
40 | 'padding: 15px',
41 | 'font-family: sans-serif',
42 | 'position: fixed',
43 | 'font-size: 0.9em',
44 | 'z-index: 9999',
45 | 'bottom: 0px',
46 | 'right: 0px',
47 | 'border-bottom-left-radius: 5px',
48 | 'background-color: #1B2032',
49 | 'margin: 0',
50 | 'color: white',
51 | 'text-align: center'
52 | ]
53 | }
54 | };
55 | if (config.browserSync.domain) {
56 | _.merge(options, {
57 | proxy: config.browserSync.domain,
58 | startPath: config.browserSync.startPath,
59 | serveStatic: config.browserSync.serveStatic || []
60 | });
61 | } else {
62 | _.merge(options, {
63 | server: {
64 | baseDir: config.browserSync.baseDir
65 | },
66 | startPath: config.browserSync.startPath
67 | });
68 | }
69 |
70 | function serve() {
71 | return browserSync.init(options);
72 | }
73 | serve.description = 'Create a local server using Browsersync';
74 | gulp.task('serve', serve);
75 | tasks.default.push('serve');
76 | };
77 |
--------------------------------------------------------------------------------
/lib/sprite.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const gulpif = require('gulp-if');
4 | const join = require('path').join;
5 | const del = require('del');
6 | const spritesmith = require('gulp.spritesmith');
7 | const buffer = require('vinyl-buffer');
8 | const imagemin = require('gulp-imagemin');
9 | const merge = require('merge-stream');
10 |
11 | module.exports = (gulp, config, tasks) => {
12 | function sprite() {
13 | const spriteData = gulp.src(config.sprite.src)
14 | .pipe(spritesmith({
15 | imgName: config.sprite.imgName,
16 | imgPath: `${config.sprite.imgPathPrefix}${config.sprite.imgName}`,
17 | cssName: config.sprite.cssName,
18 | cssSpritesheetName: config.sprite.spritesheetName,
19 | retinaSrcFilter: config.sprite.retina.enabled ? config.sprite.retina.filter : undefined,
20 | retinaImgName: config.sprite.retina.enabled ? config.sprite.retina.imgName : undefined,
21 | retinaImgPath: config.sprite.retina.enabled ? `${config.sprite.imgPathPrefix}${config.sprite.retina.imgName}` : undefined,
22 | cssRetinaSpritesheetName: config.sprite.retina.enabled ? `${config.sprite.spritesheetName}-2x` : undefined,
23 | cssVarMap(datas) {
24 | // eslint-disable-next-line no-param-reassign
25 | datas.name = `${config.sprite.spritesheetName}-${datas.name}`;
26 | }
27 | }));
28 |
29 | // Pipe image stream through image optimizer and onto disk
30 | const imgStream = spriteData.img
31 | // DEV: We must buffer our stream into a Buffer for `imagemin`
32 | .pipe(buffer())
33 | .pipe(gulpif(config.sprite.imagemin, imagemin()))
34 | .pipe(gulp.dest(config.sprite.imgDest));
35 |
36 | // Pipe CSS stream onto disk
37 | const cssStream = spriteData.css
38 | .pipe(gulp.dest(config.sprite.cssDest));
39 |
40 | // Return a merged stream to handle both `end` events
41 | return merge(imgStream, cssStream);
42 | }
43 |
44 | sprite.description = 'Generates a sprite (img and scss files).';
45 |
46 | gulp.task('sprite', sprite);
47 |
48 | gulp.task('clean:sprite', (done) => {
49 | del([
50 | join(config.sprite.imgDest, config.sprite.imgName),
51 | join(config.sprite.cssDest, config.sprite.cssName)
52 | ], { force: true }).then(() => {
53 | done();
54 | });
55 | });
56 |
57 | gulp.task('watch:sprite', () => {
58 | gulp.watch(config.sprite.src, sprite);
59 | });
60 |
61 | tasks.watch.push('watch:sprite');
62 |
63 | tasks.compile.push('sprite');
64 |
65 | tasks.clean.push('clean:sprite');
66 | };
67 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const _ = require('lodash');
4 | const yaml = require('js-yaml');
5 | const fs = require('fs');
6 |
7 | // read default config settings
8 | const defaultConfig = yaml.safeLoad(fs.readFileSync(`${__dirname}/gulpfile.default.yml`, 'utf8'), { json: true });
9 |
10 | module.exports = (gulp, userConfig, tasks) => {
11 | const config = _.merge(defaultConfig, userConfig);
12 |
13 | /* eslint-disable global-require */
14 | if (config.browserSync.enabled) {
15 | require('./lib/browser-sync')(gulp, config, tasks);
16 | }
17 |
18 | if (config.icons.enabled) {
19 | require('./lib/icons')(gulp, config, tasks);
20 | }
21 |
22 | if (config.sprite.enabled) {
23 | require('./lib/sprite')(gulp, config, tasks);
24 | }
25 |
26 | if (config.js.enabled) {
27 | require('./lib/js')(gulp, config, tasks);
28 | }
29 |
30 | if (config.css.enabled) {
31 | require('./lib/css')(gulp, config, tasks);
32 | }
33 |
34 | if (config.patternLab.enabled) {
35 | require('./lib/pattern-lab--php-twig')(gulp, config, tasks);
36 | }
37 |
38 | if (config.drupal.enabled) {
39 | require('./lib/drupal')(gulp, config, tasks);
40 | }
41 |
42 | if (config.webpack.enabled) {
43 | require('./lib/webpack')(gulp, config, tasks);
44 | }
45 |
46 | if (config.copy.enabled) {
47 | require('./lib/copy')(gulp, config, tasks);
48 | }
49 |
50 | /* eslint-enable global-require */
51 |
52 | // This is a fix fo Gulp, because series and paparallel needs at least one task
53 | gulp.task('nothing-to-do', done => done());
54 |
55 | if (!tasks.clean.length) {
56 | tasks.clean.push('nothing-to-do');
57 | }
58 | if (!tasks.compile.length) {
59 | tasks.compile.push('nothing-to-do');
60 | }
61 | if (!tasks.validate.length) {
62 | tasks.validate.push('nothing-to-do');
63 | }
64 | if (!tasks.test.length) {
65 | tasks.test.push('nothing-to-do');
66 | }
67 | if (!tasks.watch.length) {
68 | tasks.watch.push('nothing-to-do');
69 | }
70 | if (!tasks.default.length) {
71 | tasks.default.push('nothing-to-do');
72 | }
73 |
74 | // Instead of `gulp.parallel`, which is what is set in Pattern Lab Starter's `gulpfile.js`, this
75 | // uses `gulp.series`. Needed to help with the Gulp task dependencies lost going from v3 to v4.
76 | // We basically need icons compiled before CSS & CSS/JS compiled before inject:pl before pl
77 | // compile. The order of the `require`s above is the order that compiles run in; not perfect, but
78 | // it works.
79 | // eslint-disable-next-line no-param-reassign
80 | tasks.compile = gulp.series(tasks.compile);
81 | };
82 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to gulp-drupal-stack
2 |
3 | This project accepts contributions. In order to contribute, you should
4 | pay attention to a few things:
5 |
6 | 1. your code must follow the coding style rules
7 | 2. your code must be unit-tested
8 | 3. your code must be documented
9 | 4. your work must be signed (see below)
10 | 5. you may contribute through GitHub Pull Requests
11 |
12 | # Submitting Modifications
13 |
14 | The contributions should be submitted through Github Pull Requests
15 | and follow the DCO which is defined below.
16 |
17 | # Licensing for new files
18 |
19 | gulp-drupal-stack is licensed under a MIT license. Anything
20 | contributed to gulp-drupal-stack must be released under this license.
21 |
22 | # Developer Certificate of Origin (DCO)
23 |
24 | To improve tracking of contributions to this project we will use a
25 | process modeled on the modified DCO 1.1 and use a "sign-off" procedure
26 | on patches that are being emailed around or contributed in any other
27 | way.
28 |
29 | The sign-off is a simple line at the end of the explanation for the
30 | patch, which certifies that you wrote it or otherwise have the right
31 | to pass it on as an open-source patch. The rules are pretty simple:
32 | if you can certify the below:
33 |
34 | By making a contribution to this project, I certify that:
35 |
36 | (a) The contribution was created in whole or in part by me and I have
37 | the right to submit it under the open source license indicated in
38 | the file; or
39 |
40 | (b) The contribution is based upon previous work that, to the best of
41 | my knowledge, is covered under an appropriate open source License
42 | and I have the right under that license to submit that work with
43 | modifications, whether created in whole or in part by me, under
44 | the same open source license (unless I am permitted to submit
45 | under a different license), as indicated in the file; or
46 |
47 | (c) The contribution was provided directly to me by some other person
48 | who certified (a), (b) or (c) and I have not modified it.
49 |
50 | (d) The contribution is made free of any other party's intellectual
51 | property claims or rights.
52 |
53 | (e) I understand and agree that this project and the contribution are
54 | public and that a record of the contribution (including all
55 | personal information I submit with it, including my sign-off) is
56 | maintained indefinitely and may be redistributed consistent with
57 | this project or the open source license(s) involved.
58 |
59 |
60 | then you just add a line saying
61 |
62 | Signed-off-by: Random J Developer
63 |
64 | using your real name (sorry, no pseudonyms or anonymous contributions.)
65 |
--------------------------------------------------------------------------------
/templates/_icons.scss:
--------------------------------------------------------------------------------
1 | ///
2 | /// THIS IS A GENERATED FILE!!!
3 | ///
4 |
5 | $icon-font-base-name: "{{ fontName }}";
6 | $icon-font-path: "{{ fontPath }}";
7 | $icon-font-class-prefix: "{{ classNamePrefix }}";
8 |
9 | $font-icons: ({% _.each(glyphs, function(glyph) { %}
10 | {{ glyph.name }}: "\{{ glyph.content }}",{% }); %}
11 | );
12 |
13 | @font-face {
14 | font-family: $icon-font-base-name;
15 | src: url("#{$icon-font-path}#{$icon-font-base-name}.eot?cachebust=#{random(99999)}");
16 | src: /* stylelint-disable-line declaration-colon-space-after */
17 | url("#{$icon-font-path}#{$icon-font-base-name}.eot?cachebust=#{random(99999)}#iefix") format("eot"),
18 | url("#{$icon-font-path}#{$icon-font-base-name}.woff?cachebust=#{random(99999)}") format("woff"),
19 | url("#{$icon-font-path}#{$icon-font-base-name}.ttf?cachebust=#{random(99999)}") format("truetype"),
20 | url("#{$icon-font-path}#{$icon-font-base-name}.svg?cachebust=#{random(99999)}#icons") format("svg");
21 | font-weight: normal;
22 | font-style: normal;
23 | }
24 |
25 | @mixin font-icon-base() {
26 | font-family: $icon-font-base-name;
27 | display: inline-block;
28 | vertical-align: middle;
29 | line-height: 1;
30 | font-weight: normal;
31 | font-style: normal;
32 | speak: none;
33 | text-decoration: inherit;
34 | text-transform: none;
35 | text-rendering: optimizeLegibility;
36 | }
37 |
38 | @mixin font-icon-replace($pseudo) {
39 | position: relative;
40 | right: 9999px;
41 | &:#{$pseudo} { /* stylelint-disable-line */
42 | position: absolute;
43 | height: 100%;
44 | text-align: center;
45 | top: 0;
46 | right: -9999px;
47 | }
48 | }
49 |
50 | /// Main Icon mixin.
51 | /// @param {String} $icon - Machine name of icon (filename).
52 | /// @param {String} $pseudo [before] - `before` | `after` The pseudo element to place the icon in.
53 | /// @todo Allow `$pseudo: false` to be declared so we don't have to use a pseudo element if we don't want to.
54 | /// @param {Bool} $text-replace [false]
55 | /// @param {String} $size [inherit]
56 | /// @param {String} $color [inherit]
57 | /// @param {Bool} $block [false] - If `display: block` should be applied.
58 | /// @example SCSS
59 | /// .class {
60 | /// @include icon('close');
61 | /// }
62 | @mixin icon(
63 | $icon: "search", // just a default
64 | $pseudo: before,
65 | $text-replace: false,
66 | $size: inherit,
67 | $color: inherit,
68 | $block: false
69 | ) {
70 | &:#{$pseudo} { /* stylelint-disable-line */
71 |
72 | @include font-icon-base();
73 | content: map-get($font-icons, $icon);
74 | font-size: $size;
75 | color: $color;
76 |
77 | @content;
78 | }
79 | // Replace text with icon, like classic sprites
80 | @if $text-replace {
81 | @include font-icon-replace($pseudo);
82 | }
83 | // Get around fighting with line-heights
84 | @if $block {
85 | display: block;
86 | }
87 | }
88 |
89 | /**
90 | * Font application to generic DOM
91 | */
92 |
93 | //@font-face {
94 | // font-family: $icon-font-base-name;
95 | // src: $icon-font-source-1;
96 | // src: $icon-font-source-2;
97 | // font-weight: normal;
98 | // font-style: normal;
99 | //}
100 |
101 | // Everything with .icon--something has a base set of styles in order to view
102 | [class*="#{$icon-font-class-prefix}--"] {
103 | @include font-icon-base;
104 | }
105 |
106 | // Print .icon--thingy classes using default :before for easy elements
107 | @each $icon-name, $icon-symbol in $font-icons {
108 | .#{$icon-font-class-prefix}--#{$icon-name}::before {
109 | content: map-get($font-icons, $icon-name);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/lib/webpack.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const eslint = require('gulp-eslint');
4 | const cached = require('gulp-cached');
5 | const webpack = require('webpack');
6 | const browserSync = require('browser-sync');
7 | const core = require('./core');
8 | const through2 = require('through2');
9 | const clone = require('clone');
10 |
11 | module.exports = (gulp, config, tasks) => {
12 | if (!config.webpack.config) {
13 | process.stdout.write(`Config passed in requires webpack config. gulpfile.yml file should contain this:
14 | webpack:
15 | enabled: true
16 | config: './webpack.config.js'
17 | Note: you can copy the template from templates/webpack.config.js.
18 | `);
19 | process.exit(1);
20 | }
21 |
22 | function validateJs() {
23 | return gulp.src(config.webpack.eslint.src)
24 | .pipe(cached('validate:webpack'))
25 | .pipe(eslint())
26 | .pipe(eslint.format());
27 | }
28 |
29 | validateJs.description = 'Lint webpack.';
30 |
31 | if (config.webpack.eslint.enabled) {
32 | gulp.task('validate:webpack', () => validateJs().pipe(eslint.failAfterError()));
33 | tasks.validate.push('validate:webpack');
34 | gulp.task('watch:validate:webpack', () => gulp.watch(config.webpack.eslint.src, validateJs));
35 | tasks.watch.push('watch:validate:webpack');
36 | }
37 |
38 | function compileWebpack() {
39 | return gulp.src(config.webpack.config)
40 | .pipe(function runCompileWebpack() {
41 | return through2.obj((file, enc, done) => {
42 | // Config options - https://webpack.js.org/configuration/
43 | let webpackConfig;
44 | try { webpackConfig = clone(require(file.path)); } catch (error) { return done(error); }
45 | if (!webpackConfig.plugins) webpackConfig.plugins = [];
46 | if (typeof webpackConfig.devtool === 'undefined') webpackConfig.devtool = 'cheap-module-source-map';
47 |
48 | return webpack(webpackConfig).run((err, stats) => {
49 | if (err) {
50 | console.error(err.stack || err);
51 | if (err.details) {
52 | console.error(err.details);
53 | }
54 | done(err);
55 | }
56 |
57 | // Stats config options: https://webpack.js.org/configuration/stats/
58 | console.log(stats.toString({
59 | chunks: false, // Makes the build much quieter
60 | colors: true // Shows colors in the console
61 | }));
62 |
63 | done(stats.hasErrors() ? core.error('webpack Compile Failed.') : null);
64 | });
65 | });
66 | }());
67 | }
68 |
69 | gulp.task('webpack', compileWebpack);
70 |
71 | function watchWebpack() {
72 | return gulp.src(config.webpack.config)
73 | .pipe(function runWatchWebpack() {
74 | return through2.obj((file, enc, done) => {
75 | // Config options - https://webpack.js.org/configuration/
76 | let webpackConfig;
77 | try { webpackConfig = clone(require(file.path)); } catch (error) { return done(error); }
78 | if (!webpackConfig.plugins) webpackConfig.plugins = [];
79 | if (typeof webpackConfig.devtool === 'undefined') webpackConfig.devtool = 'cheap-module-source-map';
80 |
81 | webpackConfig.plugins.push(new webpack.LoaderOptionsPlugin({
82 | debug: true
83 | }));
84 |
85 | return webpack(webpackConfig).watch({
86 | // https://webpack.js.org/configuration/watch/#watchoptions
87 | }, (err, stats) => {
88 | if (err) {
89 | console.error(err.stack || err);
90 | if (err.details) {
91 | console.error(err.details);
92 | }
93 | done(err);
94 | }
95 |
96 | // Stats config options: https://webpack.js.org/configuration/stats/
97 | console.log(stats.toString({
98 | chunks: false, // Makes the build much quieter
99 | colors: true // Shows colors in the console
100 | }));
101 |
102 | if (config.browserSync.enabled) browserSync.get('server').reload();
103 | });
104 | });
105 | }());
106 | }
107 |
108 | gulp.task('watch:webpack', watchWebpack);
109 |
110 | tasks.watch.push('watch:webpack');
111 | tasks.compile.push('webpack');
112 | };
113 |
--------------------------------------------------------------------------------
/lib/js.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const eslint = require('gulp-eslint');
4 | const sourcemaps = require('gulp-sourcemaps');
5 | const uglify = require('gulp-uglify');
6 | const concat = require('gulp-concat');
7 | const babel = require('gulp-babel');
8 | const cached = require('gulp-cached');
9 | const gulpif = require('gulp-if');
10 | const del = require('del');
11 | const browserSync = require('browser-sync');
12 | const bowerFiles = require('main-bower-files');
13 | const path = require('path');
14 |
15 | module.exports = (gulp, config, tasks) => {
16 | function validateJs() {
17 | return gulp.src(config.js.eslint.src)
18 | .pipe(cached('validate:js'))
19 | .pipe(eslint())
20 | .pipe(eslint.format());
21 | }
22 |
23 | validateJs.description = 'Lint JS.';
24 |
25 | if (config.js.eslint.enabled) {
26 | gulp.task('validate:js', () => validateJs().pipe(eslint.failAfterError()));
27 | tasks.validate.push('validate:js');
28 | gulp.task('watch:validate:js', () => gulp.watch(config.js.eslint.src, validateJs));
29 | tasks.watch.push('watch:validate:js');
30 | }
31 |
32 | function compileJs(done) {
33 | gulp.src(config.js.src)
34 | .pipe(gulpif(config.js.sourceMap.enabled, sourcemaps.init()))
35 | .pipe(gulpif(config.js.babel, babel())) // all babel options handled in `.babelrc`
36 | .pipe(gulpif(config.js.concat, concat(config.js.destName)))
37 | .pipe(gulpif(config.js.uglify, uglify()))
38 | .pipe(gulpif(config.js.sourceMap.enabled, sourcemaps.write((config.js.sourceMapEmbed) ? null : './')))
39 | .pipe(gulp.dest(config.js.dest))
40 | .on('end', () => {
41 | if (config.browserSync.enabled) {
42 | browserSync.get('server').reload();
43 | }
44 | done();
45 | });
46 | }
47 |
48 | compileJs.description = 'Transpile JS using Babel, concat and uglify.';
49 |
50 | gulp.task('js', compileJs);
51 |
52 | gulp.task('watch:js', () => gulp.watch(config.js.src, compileJs));
53 |
54 | gulp.task('clean:js', (done) => {
55 | del([
56 | `${config.js.dest}*.{js,js.map}`
57 | ], { force: true }).then(() => {
58 | done();
59 | });
60 | });
61 |
62 | /**
63 | * Bundle up Bower JS Dependencies.
64 | * Creates `bower--{devDeps,deps}.min.js` in `config.js.dest`.
65 | * @param devDeps {boolean} If true, just devDeps, else just deps.
66 | * @param done {function}
67 | */
68 | function bundleBower(done, devDeps) {
69 | const exclusions = config.js.bundleBowerExclusions;
70 | const files = bowerFiles({
71 | paths: './',
72 | filter: (filePath) => {
73 | let isExcluded = false;
74 | if (exclusions && exclusions.length) {
75 | const directories = filePath.split('/');
76 | // see if any directory name matches anything in the excluded list
77 | isExcluded = directories.some(dir => exclusions.some(item => item === dir));
78 | }
79 | return path.extname(filePath) === '.js' && !(isExcluded);
80 | },
81 | includeDev: devDeps ? 'exclusive' : false // `'exclusive'` does just devDeps w/o deps
82 | });
83 | if (files.length) {
84 | gulp.src(files)
85 | .pipe(gulpif(config.js.sourceMap.enabled, sourcemaps.init()))
86 | .pipe(concat(`bower--${devDeps ? 'devDeps' : 'deps'}.min.js`))
87 | .pipe(gulpif(config.js.uglify, uglify()))
88 | .pipe(gulpif(config.js.sourceMap.enabled, sourcemaps.write((config.js.sourceMapEmbed) ? null : './')))
89 | .pipe(gulp.dest(config.js.dest))
90 | .on('end', () => {
91 | process.stdout.write(`Bower ${devDeps ? 'devDeps' : 'deps'} bundled: ${files.map(file => path.basename(file)).join(', ')}.\n`);
92 | done();
93 | });
94 | } else {
95 | done();
96 | }
97 | }
98 |
99 | if (config.js.bundleBower) {
100 | gulp.task('js:bundleBower', gulp.parallel(
101 | function bundleBowerDeps(done) { bundleBower(done); },
102 | function bundleBowerDevDeps(done) { bundleBower(done, true); }
103 | ));
104 |
105 | const bowerBasePath = config.js.bowerBasePath || './';
106 | gulp.task('watch:bower', () => {
107 | gulp.watch(path.join(bowerBasePath, 'bower.json'), gulp.series('js:bundleBower'));
108 | });
109 | tasks.compile.push('js:bundleBower');
110 | tasks.watch.push('watch:bower');
111 | }
112 |
113 | tasks.clean.push('clean:js');
114 | tasks.compile.push('js');
115 | tasks.watch.push('watch:js');
116 | };
117 |
--------------------------------------------------------------------------------
/docs/usage.md:
--------------------------------------------------------------------------------
1 | ## Config
2 |
3 | All is easily configurable by changing values in your `gulpfile.yml` file in your project. These values are merged into the `gulpfile.default.yml` file.
4 |
5 | For example, you can enable SCSS and JS like this:
6 | ```yml
7 | css:
8 | enabled: true
9 | js:
10 | enabled: true
11 | ```
12 |
13 | You can find all the available options and defaults settings inside the `gulpfile.default.yml` file.
14 |
15 |
16 | ## Folders structure
17 |
18 | - source/ (only for Pattern Lab)
19 | - _annotations/ ([annotations](http://patternlab.io/docs/pattern-adding-annotations.html) for Patterns)
20 | - _data/ (Global JSON data files available to all Patterns, can add multiple)
21 | - _patterns/ (Twig, Scss, and JS all in here)
22 | - 00-base/ (Twig Namespace: `@base`)
23 | - Contains what all that follows needs: variables, mixins, and grid layouts for examples
24 | - 01-atoms/ (Twig Namespace: `@atoms`)
25 | - 02-molecules (Twig Namespace: `@molecules`)
26 | - 03-organisms (Twig Namespace: `@organisms`)
27 | - 04-templates (Twig Namespace: `@templates`)
28 | - 05-pages (Twig Namespace: `@pages`)
29 | - _meta/ (contains the header and footer Twig templates for PL; add any `` or `