|
| 1 | +'use strict'; |
| 2 | +const bs = require('browser-sync').create('PatternLab'); |
| 3 | +const buildPatterns = require('./build'); |
| 4 | +const patternlab = require('patternlab-node'); |
| 5 | +const htmlInjector = require('bs-html-injector'); |
| 6 | +const path = require('path'); |
| 7 | +const _ = require('lodash'); |
| 8 | +const isValidConfig = require('./validate-config'); |
| 9 | +const copyWithPattern = require('./utils').copyWithPattern; |
| 10 | +const wrapAsync = require('./utils').wrapAsync; |
| 11 | +const error = require('./utils').error; |
| 12 | + |
| 13 | +/** |
| 14 | + * @func serve |
| 15 | + * @desc Start a browser-sync server in the PatternLab public dir |
| 16 | + * @param {object} config - The passed PatternLab config |
| 17 | + * @param {boolean} watch - Whether to set up watches |
| 18 | + */ |
| 19 | +function serve(config, watch) { |
| 20 | + if (!isValidConfig) throw new TypeError('serve: Expects config not to be empty and of type object.'); |
| 21 | + |
| 22 | + if (!_.has(config, 'paths.public.root') || _.isEmpty(config.paths.public.root)) { |
| 23 | + throw new TypeError('serve: config.paths.public.root is empty or does not exist. Please check your PatternLab config.'); |
| 24 | + } |
| 25 | + if (!_.has(config, 'paths.source.root') || _.isEmpty(config.paths.source.root)) { |
| 26 | + throw new TypeError('serve: config.paths.source.root is empty or does not exist. Please check your PatternLab config.'); |
| 27 | + } |
| 28 | + |
| 29 | + try { |
| 30 | + const pl = patternlab(); |
| 31 | + const src = config.paths.source; |
| 32 | + const publicDir = path.resolve(config.paths.public.root); |
| 33 | + const sourceCSS = path.join(path.resolve(src.css), '/**/*.css'); |
| 34 | + const sourceStyleguide = path.join(path.resolve(src.styleguide), '/**/*.*'); |
| 35 | + const patterns = pl.getSupportedTemplateExtensions().map(dotExtension => path.join(path.resolve(src.patterns), `/**/*${dotExtension}`)); |
| 36 | + |
| 37 | + // The browser-sync config |
| 38 | + const bsConfig = { |
| 39 | + server: publicDir, |
| 40 | + snippetOptions: { |
| 41 | + blacklist: ['/index.html', '/', '/?*'] // Ignore all HTML files within the templates folder |
| 42 | + }, |
| 43 | + notify: { |
| 44 | + styles: [ |
| 45 | + 'display: none', |
| 46 | + 'padding: 15px', |
| 47 | + 'font-family: sans-serif', |
| 48 | + 'position: fixed', |
| 49 | + 'font-size: 1em', |
| 50 | + 'z-index: 9999', |
| 51 | + 'bottom: 0px', |
| 52 | + 'right: 0px', |
| 53 | + 'border-top-left-radius: 5px', |
| 54 | + 'background-color: #1B2032', |
| 55 | + 'opacity: 0.4', |
| 56 | + 'margin: 0', |
| 57 | + 'color: white', |
| 58 | + 'text-align: center' |
| 59 | + ] |
| 60 | + } |
| 61 | + }; |
| 62 | + |
| 63 | + /** |
| 64 | + * @func copyAndReloadCSS |
| 65 | + */ |
| 66 | + const copyAndReloadCSS = () => wrapAsync(function *() { |
| 67 | + yield copyWithPattern(path.resolve(src.css), '**/*.css', path.resolve(config.paths.public.css)); |
| 68 | + bs.reload('*.css'); |
| 69 | + }); |
| 70 | + |
| 71 | + /** |
| 72 | + * @func copyAndReloadStyleguide |
| 73 | + */ |
| 74 | + const copyAndReloadStyleguide = () => wrapAsync(function *() { |
| 75 | + yield copyWithPattern(path.resolve(src.styleguide), '**/!(*.css)', path.resolve(config.paths.public.styleguide)); |
| 76 | + yield copyWithPattern(path.resolve(src.styleguide), '**/*.css', path.resolve(config.paths.public.styleguide)); |
| 77 | + bs.reload('*.css'); |
| 78 | + }); |
| 79 | + |
| 80 | + /** |
| 81 | + * @func reload |
| 82 | + * @desc Calls browser-sync's reload method to tell browsers to refresh their page |
| 83 | + */ |
| 84 | + const buildAndReload = function () { |
| 85 | + buildPatterns(config); |
| 86 | + bs.reload(); |
| 87 | + }; |
| 88 | + |
| 89 | + // Register plugins |
| 90 | + bs.use(htmlInjector, { |
| 91 | + files: [publicDir + '/index.html', publicDir + '../styleguide/styleguide.html'] |
| 92 | + }); |
| 93 | + |
| 94 | + if (watch) { |
| 95 | + /** |
| 96 | + * 1. Watch source css, then copy css and callreloadCSS |
| 97 | + * 2. Watch source styleguide, then copy styleguide and css and call reloadCSS |
| 98 | + * 3. Watch pattern-specific and engine-specific extensions, run build and reload |
| 99 | + */ |
| 100 | + bs.watch(sourceCSS).on('change', copyAndReloadCSS); // 1 |
| 101 | + bs.watch(sourceStyleguide).on('change', copyAndReloadStyleguide); // 2 |
| 102 | + const patternWatches = [ |
| 103 | + path.join(path.resolve(src.patterns), '**/*.json'), |
| 104 | + path.join(path.resolve(src.patterns), '**/*.md'), |
| 105 | + path.join(path.resolve(src.data), '*.json'), |
| 106 | + path.join(path.resolve(src.fonts), '*'), |
| 107 | + path.join(path.resolve(src.images), '*'), |
| 108 | + path.join(path.resolve(src.meta), '*'), |
| 109 | + path.join(path.resolve(src.annotations), '*') |
| 110 | + ].concat(patterns); // 3 |
| 111 | + |
| 112 | + bs.watch(patternWatches).on('change', buildAndReload); |
| 113 | + } |
| 114 | + |
| 115 | + // Init browser-sync |
| 116 | + bs.init(bsConfig); |
| 117 | + } catch (err) { |
| 118 | + error(err); |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +module.exports = serve; |
0 commit comments