├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github └── issue_template.md ├── .gitignore ├── .travis.yml ├── bin ├── archive.js ├── ask.js ├── build.js ├── cli-actions │ ├── build.js │ ├── export.js │ ├── help.js │ ├── init.js │ ├── install.js │ ├── serve.js │ └── version.js ├── copy-source-files.js ├── default-config.js ├── inquiries │ ├── confirm.js │ ├── edition.js │ └── starterkit.js ├── install-edition.js ├── install-plugin.js ├── install-starterkit.js ├── patternlab.js ├── replace-config.js ├── resolve-config.js ├── scaffold.js ├── serve.js ├── utils.js └── validate-config.js ├── license ├── package-lock.json ├── package.json ├── readme.md └── test ├── build.test.js ├── cli-build.test.js ├── cli-export.test.js ├── cli-init.test.js ├── export.test.js ├── fixtures └── patternlab-config.json ├── install-plugin.test.js ├── install-starterkit.test.js ├── mocks ├── liverserver.mock.js └── patternlab.mock.js ├── replace_config_paths.test.js ├── resolve_config.test.js ├── scaffold.test.js ├── serve.test.js └── utils ├── getUniqueProjectPath.js └── spawnCmd.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-var": "error", 4 | "prefer-const": "error", 5 | "accessor-pairs": 2, 6 | "arrow-spacing": [ 7 | 2, 8 | { 9 | "before": true, 10 | "after": true 11 | } 12 | ], 13 | "block-spacing": [ 14 | 2, 15 | "always" 16 | ], 17 | "brace-style": [ 18 | 2, 19 | "1tbs", 20 | { 21 | "allowSingleLine": true 22 | } 23 | ], 24 | "camelcase": [ 25 | 2, 26 | { 27 | "properties": "never" 28 | } 29 | ], 30 | "comma-dangle": [ 31 | 2, 32 | "never" 33 | ], 34 | "comma-spacing": [ 35 | 2, 36 | { 37 | "before": false, 38 | "after": true 39 | } 40 | ], 41 | "comma-style": [ 42 | 2, 43 | "last" 44 | ], 45 | "constructor-super": 2, 46 | "curly": [ 47 | 2, 48 | "multi-line" 49 | ], 50 | "dot-location": [ 51 | 2, 52 | "property" 53 | ], 54 | "eol-last": 2, 55 | "eqeqeq": [ 56 | 2, 57 | "allow-null" 58 | ], 59 | "func-call-spacing": [ 60 | 2, 61 | "never" 62 | ], 63 | "handle-callback-err": [ 64 | 2, 65 | "^(err|error)$" 66 | ], 67 | "key-spacing": [ 68 | 2, 69 | { 70 | "beforeColon": false, 71 | "afterColon": true 72 | } 73 | ], 74 | "keyword-spacing": [ 75 | 2, 76 | { 77 | "before": true, 78 | "after": true 79 | } 80 | ], 81 | "new-cap": [ 82 | 2, 83 | { 84 | "newIsCap": true, 85 | "capIsNew": false 86 | } 87 | ], 88 | "new-parens": 2, 89 | "no-array-constructor": 2, 90 | "no-caller": 2, 91 | "no-class-assign": 2, 92 | "no-cond-assign": 2, 93 | "no-const-assign": 2, 94 | "no-constant-condition": [ 95 | 2, 96 | { 97 | "checkLoops": false 98 | } 99 | ], 100 | "no-control-regex": 2, 101 | "no-debugger": 2, 102 | "no-delete-var": 2, 103 | "no-dupe-args": 2, 104 | "no-dupe-class-members": 2, 105 | "no-dupe-keys": 2, 106 | "no-duplicate-case": 2, 107 | "no-duplicate-imports": 2, 108 | "no-empty-character-class": 2, 109 | "no-empty-pattern": 2, 110 | "no-eval": 2, 111 | "no-ex-assign": 2, 112 | "no-extend-native": 2, 113 | "no-extra-bind": 2, 114 | "no-extra-boolean-cast": 2, 115 | "no-extra-parens": [ 116 | 2, 117 | "functions" 118 | ], 119 | "no-fallthrough": 2, 120 | "no-floating-decimal": 2, 121 | "no-func-assign": 2, 122 | "no-global-assign": 2, 123 | "no-implied-eval": 2, 124 | "no-inner-declarations": [ 125 | 2, 126 | "functions" 127 | ], 128 | "no-invalid-regexp": 2, 129 | "no-irregular-whitespace": 2, 130 | "no-iterator": 2, 131 | "no-label-var": 2, 132 | "no-labels": [ 133 | 2, 134 | { 135 | "allowLoop": false, 136 | "allowSwitch": false 137 | } 138 | ], 139 | "no-lone-blocks": 2, 140 | "no-mixed-spaces-and-tabs": 2, 141 | "no-multi-spaces": 2, 142 | "no-multi-str": 2, 143 | "no-multiple-empty-lines": [ 144 | 2, 145 | { 146 | "max": 1 147 | } 148 | ], 149 | "no-native-reassign": 2, 150 | "no-negated-in-lhs": 2, 151 | "no-new": 2, 152 | "no-new-func": 2, 153 | "no-new-object": 2, 154 | "no-new-require": 2, 155 | "no-new-symbol": 2, 156 | "no-new-wrappers": 2, 157 | "no-obj-calls": 2, 158 | "no-octal": 2, 159 | "no-octal-escape": 2, 160 | "no-path-concat": 2, 161 | "no-proto": 2, 162 | "no-redeclare": 2, 163 | "no-regex-spaces": 2, 164 | "no-return-assign": [ 165 | 2, 166 | "except-parens" 167 | ], 168 | "no-self-assign": 2, 169 | "no-self-compare": 2, 170 | "no-sequences": 2, 171 | "no-shadow-restricted-names": 2, 172 | "no-sparse-arrays": 2, 173 | "no-template-curly-in-string": 2, 174 | "no-this-before-super": 2, 175 | "no-throw-literal": 2, 176 | "no-undef": 2, 177 | "no-undef-init": 2, 178 | "no-unexpected-multiline": 2, 179 | "no-unmodified-loop-condition": 2, 180 | "no-unneeded-ternary": [ 181 | 2, 182 | { 183 | "defaultAssignment": false 184 | } 185 | ], 186 | "no-unreachable": 2, 187 | "no-unsafe-finally": 2, 188 | "no-unsafe-negation": 2, 189 | "no-unused-vars": [ 190 | 2, 191 | { 192 | "vars": "all", 193 | "args": "none" 194 | } 195 | ], 196 | "no-useless-call": 2, 197 | "no-useless-computed-key": 2, 198 | "no-useless-constructor": 2, 199 | "no-useless-escape": 2, 200 | "no-useless-rename": 2, 201 | "no-whitespace-before-property": 2, 202 | "no-with": 2, 203 | "object-property-newline": [ 204 | 2, 205 | { 206 | "allowMultiplePropertiesPerLine": true 207 | } 208 | ], 209 | "one-var": [ 210 | 2, 211 | { 212 | "initialized": "never" 213 | } 214 | ], 215 | "operator-linebreak": [ 216 | 2, 217 | "after", 218 | { 219 | "overrides": { 220 | "?": "before", 221 | ":": "before" 222 | } 223 | } 224 | ], 225 | "padded-blocks": [ 226 | 2, 227 | "never" 228 | ], 229 | "quotes": [ 230 | 2, 231 | "single", 232 | { 233 | "avoidEscape": true, 234 | "allowTemplateLiterals": true 235 | } 236 | ], 237 | "rest-spread-spacing": [ 238 | 2, 239 | "never" 240 | ], 241 | "semi-spacing": [ 242 | 2, 243 | { 244 | "before": false, 245 | "after": true 246 | } 247 | ], 248 | "space-before-blocks": [ 249 | 2, 250 | "always" 251 | ], 252 | "space-in-parens": [ 253 | 2, 254 | "never" 255 | ], 256 | "space-infix-ops": 2, 257 | "space-unary-ops": [ 258 | 2, 259 | { 260 | "words": true, 261 | "nonwords": false 262 | } 263 | ], 264 | "spaced-comment": [ 265 | 2, 266 | "always", 267 | { 268 | "line": { 269 | "markers": [ 270 | "*package", 271 | "!", 272 | "," 273 | ] 274 | }, 275 | "block": { 276 | "balanced": true, 277 | "markers": [ 278 | "*package", 279 | "!", 280 | "," 281 | ], 282 | "exceptions": [ 283 | "*" 284 | ] 285 | } 286 | } 287 | ], 288 | "template-curly-spacing": [ 289 | 2, 290 | "never" 291 | ], 292 | "unicode-bom": [ 293 | 2, 294 | "never" 295 | ], 296 | "use-isnan": 2, 297 | "valid-typeof": 2, 298 | "wrap-iife": [ 299 | 2, 300 | "any" 301 | ], 302 | "yield-star-spacing": [ 303 | 2, 304 | "both" 305 | ], 306 | "yoda": [ 307 | 2, 308 | "never" 309 | ] 310 | }, 311 | "env": { 312 | "es6": true, 313 | "node": true 314 | }, 315 | "parserOptions": { 316 | "ecmaVersion": 7 317 | }, 318 | "extends": "eslint:recommended" 319 | } 320 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | readme.md merge=union 3 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### General information 2 | 3 | - NodeJS version: x.x.x 4 | - NPM version: x.x.x 5 | 6 | ### Problem description 7 | 8 | ### Reproduction steps 9 | -------------------------------------------------------------------------------- /.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 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional eslint cache 37 | .eslintcache 38 | 39 | # Optional REPL history 40 | .node_repl_history 41 | 42 | # macOS specific 43 | *.DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Icon must end with two \r 48 | Icon 49 | 50 | 51 | # Thumbnails 52 | ._* 53 | 54 | # Files that might appear in the root of a volume 55 | .DocumentRevisions-V100 56 | .fseventsd 57 | .Spotlight-V100 58 | .TemporaryItems 59 | .Trashes 60 | .VolumeIcon.icns 61 | .com.apple.timemachine.donotpresent 62 | 63 | # Directories potentially created on remote AFP share 64 | .AppleDB 65 | .AppleDesktop 66 | Network Trash Folder 67 | Temporary Items 68 | .apdisk 69 | 70 | # PatternLab CLI specific 71 | 72 | .DS_Store 73 | latest-change.txt 74 | patternlab.json 75 | .sass-cache/* 76 | /sass-cache 77 | Thumbs.db 78 | source/css/style.css.map 79 | .idea/ 80 | public 81 | tmp 82 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - 6 5 | before_script: 6 | - npm install edition-node 7 | - npm install starterkit-mustache-base 8 | - npm install eslint 9 | branches: 10 | only: 11 | - master 12 | -------------------------------------------------------------------------------- /bin/archive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs-promise'); 3 | const path = require('path'); 4 | const Archiver = require('archiver'); 5 | const isValidConfig = require('./validate-config'); 6 | const debug = require('./utils').debug; 7 | 8 | /** 9 | * @func exportPatterns 10 | * @desc Exports the patterns into the patternExportDirectory. 11 | * @param {object} config - The passed PatternLab config. 12 | */ 13 | function exportPatterns(config) { 14 | if (!isValidConfig) throw new TypeError('export: Expects config not to be empty OR of type object if not empty.'); 15 | 16 | const archive = new Archiver('zip', {}); 17 | const exportsPath = path.resolve('./', config.patternExportDirectory, 'patterns.zip'); 18 | const output = fs.createWriteStream(exportsPath); 19 | 20 | output.on('close', () => { 21 | debug(`export: Exported patterns in ${exportsPath} - ${archive.pointer()} total bytes.`); 22 | }); 23 | 24 | archive.on('error', function (err) { 25 | throw new TypeError(`export: An error occured during zipping the patterns: ${err}`); 26 | }); 27 | 28 | archive.pipe(output); 29 | 30 | archive.glob('?(_patterns|_data|_meta|_annotations)/**', { 31 | cwd: config.paths.source.root 32 | }, {}) 33 | .finalize(); 34 | } 35 | 36 | module.exports = exportPatterns; 37 | -------------------------------------------------------------------------------- /bin/ask.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const inquirer = require('inquirer'); 3 | const wrapsAsync = require('./utils').wrapAsync; 4 | const confirmSetup = require('./inquiries/confirm'); 5 | const editionSetup = require('./inquiries/edition'); 6 | const starterkitSetup = require('./inquiries/starterkit'); 7 | const ask = inquirer.prompt; 8 | 9 | /** 10 | * @func init 11 | * @desc Initiates a PatternLab project by getting user input through inquiry. Scaffolds the project and download mandatory files 12 | * @param {object} options - Options passed in from CLI 13 | * @param {boolean} options.force - Flag whether to force install in existing project directory. May overwrite stuff. 14 | */ 15 | const init = options => wrapsAsync(function*() { 16 | /** 17 | * @property {string} project_root="./" - Path to the project root directory 18 | * @property {string|Symbol} edition - The name of the edition npm package or a Symbol for no install 19 | */ 20 | const editionAnswers = yield ask(editionSetup); 21 | 22 | /** 23 | * @property {object|Symbol} starterkit - The name of a starterkit npm package or a Symbol for no install 24 | */ 25 | const starterkitAnswers = yield ask(starterkitSetup); 26 | 27 | /** 28 | * @property {boolean} confirm - A bool hold the confirmation status 29 | */ 30 | const confirmation = yield ask(confirmSetup); 31 | 32 | // IF we have no confirmation we start all over again. 33 | if (!confirmation.confirm) return init(options); 34 | 35 | return { 36 | // Destructure the answers 37 | projectDir: editionAnswers.project_root, 38 | edition: editionAnswers.edition !== false ? editionAnswers.edition : '', 39 | starterkit: starterkitAnswers.starterkit !== false ? starterkitAnswers.starterkit : '' 40 | }; 41 | }); 42 | 43 | module.exports = init; 44 | -------------------------------------------------------------------------------- /bin/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const pl = require('patternlab-node'); 3 | const debug = require('./utils').debug; 4 | const isValidConfig = require('./validate-config'); 5 | 6 | /** 7 | * @func build 8 | * @desc Init patternLab core and build the PatternLab files. 9 | * @param {object} config - The passed PatternLab config. 10 | * @param {object} options - Additional opts to specify build mode. 11 | */ 12 | function build(config, options) { 13 | if (!isValidConfig) throw new TypeError('build: Expects config not to be empty and of type object.'); 14 | 15 | // Initiate PatternLab core with the config 16 | const patternLab = pl(config); 17 | 18 | /** 19 | * Check whether a flag was passed for build 20 | * 1. Build only patterns 21 | * 2. Normal build 22 | */ 23 | if (options && options.patternsOnly) { // 1 24 | debug(`build: Building only patterns now into ${config.paths.public.root}`); 25 | return patternLab.patternsonly(function () { 26 | debug(`build: Yay, your patterns were successfully built ☺`); 27 | }, config.cleanPublic); 28 | } else { // 2 29 | debug(`build: Building your project now into ${config.paths.public.root}`); 30 | return patternLab.build(function () { 31 | debug(`build: Yay, your PatternLab project was successfully built ☺`); 32 | }, config.cleanPublic); 33 | } 34 | } 35 | 36 | module.exports = build; 37 | -------------------------------------------------------------------------------- /bin/cli-actions/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const buildPatterns = require('../build'); 3 | const copyFiles = require('../copy-source-files'); 4 | const resolveConfig = require('../resolve-config'); 5 | const wrapAsync = require('../utils').wrapAsync; 6 | 7 | const build = options => wrapAsync(function*() { 8 | const config = yield resolveConfig(options.parent.config); 9 | yield copyFiles(config.paths); 10 | buildPatterns(config, options); 11 | }); 12 | 13 | module.exports = build; 14 | -------------------------------------------------------------------------------- /bin/cli-actions/export.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const archive = require('../archive'); 3 | const resolveConfig = require('../resolve-config'); 4 | const wrapAsync = require('../utils').wrapAsync; 5 | 6 | const _export = options => wrapAsync(function*() { 7 | const config = yield resolveConfig(options.parent.config); 8 | archive(config); 9 | }); 10 | 11 | module.exports = _export; 12 | -------------------------------------------------------------------------------- /bin/cli-actions/help.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = () => { 3 | /* eslint-disable */ 4 | console.log(` 5 | Examples: 6 | $ patternlab init # Initialize a PatternLab project.'); 7 | $ patternlab # Builds the PatternLab from the current dir'); 8 | $ patternlab --config # PatternLab from a config in a specified directory');` 9 | ); 10 | /* eslint-enable */ 11 | }; 12 | -------------------------------------------------------------------------------- /bin/cli-actions/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const ask = require('../ask'); 3 | const scaffold = require('../scaffold'); 4 | const installEdition = require('../install-edition'); 5 | const installStarterkit = require('../install-starterkit'); 6 | const defaultPatternlabConfig = require('../default-config'); 7 | const replaceConfigPaths = require('../replace-config'); 8 | const ora = require('ora'); 9 | const path = require('path'); 10 | const wrapAsync = require('../utils').wrapAsync; 11 | const writeJsonAsync = require('../utils').writeJsonAsync; 12 | 13 | const init = options => wrapAsync(function*() { 14 | const sourceDir = 'source'; 15 | const publicDir = 'public'; 16 | const exportDir = 'pattern_exports'; 17 | const answers = options.projectDir ? options : yield ask(options); 18 | const projectDir = answers.projectDir || './'; 19 | const edition = answers.edition; 20 | const starterkit = answers.starterkit; 21 | 22 | /** 23 | * Process the init routines 24 | * 1 Replace config paths 25 | * 2. Scaffold the folder structure 26 | * 3. If `edition` is present: 27 | * 3.1 Install edition 28 | * 3.2 Reassign adjustedconfig 29 | * 4. If `starterkit` is present install it and copy over the mandatory starterkit files to sourceDir 30 | * 5. Save patternlab-config.json in projectDir 31 | */ 32 | const spinner = ora(`Setting up PatternLab project in ${projectDir}`).start(); 33 | let patternlabConfig = replaceConfigPaths(defaultPatternlabConfig, projectDir, sourceDir, publicDir, exportDir); // 1 34 | 35 | yield scaffold(projectDir, sourceDir, publicDir, exportDir); // 2 36 | 37 | if (edition) { 38 | spinner.text = `⊙ patternlab → Installing edition: ${edition}`; 39 | const newConf = yield installEdition(edition, patternlabConfig); // 3.1 40 | patternlabConfig = Object.assign(patternlabConfig, newConf); // 3.2 41 | spinner.succeed(`⊙ patternlab → Installed edition: ${edition}`); 42 | } 43 | if (starterkit) { 44 | spinner.text = `⊙ patternlab → Installing starterkit ${starterkit}`; 45 | spinner.start(); 46 | yield installStarterkit(starterkit, patternlabConfig); 47 | spinner.succeed(`⊙ patternlab → Installed starterkit: ${starterkit}`); 48 | } // 4 49 | yield writeJsonAsync(path.resolve(projectDir, 'patternlab-config.json'), patternlabConfig); // 5 50 | 51 | spinner.succeed(`⊙ patternlab → Yay ☺. PatternLab Node was successfully initialised in ${projectDir}`); 52 | return true; 53 | }); 54 | 55 | module.exports = init; 56 | -------------------------------------------------------------------------------- /bin/cli-actions/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const ora = require('ora'); 3 | const installPlugin = require('../install-plugin'); 4 | const installStarterkit = require('../install-starterkit'); 5 | const resolveConfig = require('../resolve-config'); 6 | const wrapAsync = require('../utils').wrapAsync; 7 | const writeJsonAsync = require('../utils').writeJsonAsync; 8 | 9 | /** 10 | * install 11 | * @desc Handles async install and activation of starterkits/plugins 12 | * @param {object} options 13 | */ 14 | const install = options => wrapAsync(function*() { 15 | const config = yield resolveConfig(options.parent.config); 16 | 17 | const spinner = ora(`⊙ patternlab → Installing additional resources …`).start(); 18 | 19 | if (options.starterkits && Array.isArray(options.starterkits)) { 20 | const starterkits = yield Promise.all(options.starterkits.map(starterkit => 21 | wrapAsync(function*() { 22 | spinner.text = `⊙ patternlab → Installing starterkit: ${starterkit}`; 23 | return yield installStarterkit({ 24 | name: starterkit, 25 | value: starterkit 26 | }, config) 27 | }) 28 | )); 29 | spinner.succeed(`⊙ patternlab → Installed following starterkits: ${starterkits.join(', ')}`); 30 | } 31 | if (options.plugins && Array.isArray(options.plugins)) { 32 | const plugins = yield Promise.all(options.plugins.map(plugin => 33 | wrapAsync(function*() { 34 | return yield installPlugin({ 35 | name: plugin, 36 | value: plugin 37 | }, config) 38 | }) 39 | )); 40 | spinner.succeed(`⊙ patternlab → Installed following plugins: ${plugins.join(', ')}`); 41 | } 42 | yield writeJsonAsync(options.parent.config, config); 43 | spinner.succeed(`⊙ patternlab → Updated config`); 44 | }); 45 | 46 | module.exports = install; 47 | -------------------------------------------------------------------------------- /bin/cli-actions/serve.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const resolveConfig = require('../resolve-config'); 3 | const build = require('./build'); 4 | const servePatterns = require('../serve'); 5 | const wrapAsync = require('../utils').wrapAsync; 6 | 7 | const serve = options => wrapAsync(function*() { 8 | const config = yield resolveConfig(options.parent.config); 9 | yield build(options); 10 | servePatterns(config, options.watch); 11 | }); 12 | 13 | module.exports = serve; 14 | -------------------------------------------------------------------------------- /bin/cli-actions/version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = ({ 3 | version, 4 | dependencies: { 5 | 'patternlab-node': coreVersion 6 | } 7 | }) => `${version} (PatternLab Node Core version: ${coreVersion})`; 8 | -------------------------------------------------------------------------------- /bin/copy-source-files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const copy = require('./utils').copyWithPattern; 3 | const debug = require('./utils').debug; 4 | const wrapAsync = require('./utils').wrapAsync; 5 | 6 | /** 7 | * @func copyFilesFromSourceToPublic 8 | * @desc Copies files from the source path to the public path. 9 | * @param {object} paths - The passed PatternLab config paths member. 10 | * @return {Array} 11 | */ 12 | const copyFilesFromSourceToPublic = paths => wrapAsync(function*() { 13 | // Copy files over 14 | const copiedFiles = [ 15 | copy(paths.source.styleguide, '*', paths.public.root), 16 | copy(paths.source.js, '**/*.js', paths.public.js), 17 | copy(paths.source.css, '*.css', paths.public.css), 18 | copy(paths.source.images, '*', paths.public.images), 19 | copy(paths.source.fonts, '*', paths.public.fonts), 20 | copy(paths.source.root, 'favicon.ico', paths.public.root) 21 | ]; 22 | debug(`build: Your files were copied over to ${paths.public.root}`); 23 | return yield Promise.all(copiedFiles); 24 | }); 25 | 26 | module.exports = copyFilesFromSourceToPublic; 27 | -------------------------------------------------------------------------------- /bin/default-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * patternLabConf 3 | * @desc PatternLab config boilerplate 4 | * @type {config} 5 | * @property {object} paths 6 | * @property {Array} styleGuideExcludes 7 | * @property {string} defaultPattern 8 | * @property {boolean} cleanPublic 9 | * @property {string} patternExtension 10 | * @property {Array} ignored-extensions 11 | * @property {Array} ignored-directories 12 | * @property {boolean} debug 13 | * @property {object} ishControlsHide 14 | * @property {string} ishMinimum 15 | * @property {string} ishMaximum 16 | * @property {Array} patternStateCascade 17 | * @property {Array} patternExportPatternPartials 18 | * @property {string} patternExportDirectory 19 | * @property {string} baseurl 20 | * @property {boolean} cacheBust 21 | * @property {string} starterkitSubDir 22 | * @property {object} outputFileSuffixes 23 | */ 24 | const patternLabConf = { 25 | paths: { 26 | source: { 27 | root: './source/', 28 | patterns: './source/_patterns/', 29 | data: './source/_data/', 30 | meta: './source/_meta/', 31 | annotations: './source/_annotations/', 32 | styleguide: 'node_modules/styleguidekit-assets-default/dist/', 33 | patternlabFiles: 'node_modules/styleguidekit-mustache-default/views/', 34 | js: './source/js', 35 | images: './source/images', 36 | fonts: './source/fonts', 37 | css: './source/css/' 38 | }, 39 | public: { 40 | root: './public/', 41 | patterns: './public/patterns/', 42 | data: './public/styleguide/data/', 43 | annotations: './public/annotations/', 44 | styleguide: './public/styleguide/', 45 | js: './public/js', 46 | images: './public/images', 47 | fonts: './public/fonts', 48 | css: './public/css' 49 | } 50 | }, 51 | styleGuideExcludes: ['templates', 'pages'], 52 | defaultPattern: 'all', 53 | cleanPublic: false, 54 | patternExtension: 'mustache', 55 | 'ignored-extensions': ['scss', 'DS_Store', 'less'], 56 | 'ignored-directories': ['scss'], 57 | debug: false, 58 | ishControlsHide: { 59 | s: false, 60 | m: false, 61 | l: false, 62 | full: false, 63 | random: false, 64 | disco: false, 65 | hay: true, 66 | mqs: false, 67 | find: false, 68 | 'views-all': false, 69 | 'views-annotations': false, 70 | 'views-code': false, 71 | 'views-new': false, 72 | 'tools-all': false, 73 | 'tools-docs': false 74 | }, 75 | ishMinimum: '240', 76 | ishMaximum: '2600', 77 | patternStateCascade: ['inprogress', 'inreview', 'complete'], 78 | patternExportPatternPartials: [], 79 | patternExportDirectory: './pattern_exports/', 80 | baseurl: '', 81 | cacheBust: true, 82 | starterkitSubDir: 'dist', 83 | outputFileSuffixes: { 84 | rendered: '', 85 | rawTemplate: '', 86 | markupOnly: '.markup-only' 87 | } 88 | }; 89 | module.exports = patternLabConf; 90 | -------------------------------------------------------------------------------- /bin/inquiries/confirm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** confirmSetup {Array} - Inquirer question to confirm selection */ 3 | const confirmSetup = [{ 4 | type: 'confirm', 5 | name: 'confirm', 6 | message: 'Are you happy with your choices? (Hit enter for YES)?', 7 | default: true 8 | }]; 9 | 10 | module.exports = confirmSetup; 11 | -------------------------------------------------------------------------------- /bin/inquiries/edition.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const inquirer = require('inquirer'); 3 | 4 | /** editionSetup {Array} - Inquirer question logic for first question regarding editions */ 5 | const editionSetup = [ 6 | { 7 | type: 'input', 8 | name: 'project_root', 9 | message: 'Please specify a directory for your PatternLab project.', 10 | default: () => './' 11 | }, 12 | { 13 | type: 'list', 14 | name: 'edition', 15 | message: 'Which edition do you want to use (defaults to edition-node)?', 16 | choices: [{ 17 | name: 'edition-node', 18 | value: 'edition-node' 19 | }, { 20 | name: 'edition-node-grunt', 21 | value: 'edition-node-grunt' 22 | }, { 23 | name: 'edition-node-gulp', 24 | value: 'edition-node-gulp' 25 | }, 26 | new inquirer.Separator(), 27 | { 28 | name: 'None', 29 | value: false 30 | } 31 | ], 32 | default: function () { 33 | return { 34 | name: 'edition-node', 35 | value: 'edition-node' 36 | } 37 | } 38 | }]; 39 | 40 | module.exports = editionSetup; 41 | -------------------------------------------------------------------------------- /bin/inquiries/starterkit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const inquirer = require('inquirer'); 3 | /** starterkitSetup {Array} - Inquirer question logic for regarding starterkits */ 4 | const starterkitSetup = [{ 5 | type: 'list', 6 | name: 'starterkit', 7 | message: 'Which starterkit do you want to use?', 8 | choices: [ 9 | { 10 | name: 'starterkit-mustache-demo', 11 | value: '@pattern-lab/starterkit-mustache-demo' 12 | }, 13 | { 14 | name: 'starterkit-mustache-bootstrap', 15 | value: 'starterkit-mustache-bootstrap' 16 | }, 17 | { 18 | name: 'starterkit-mustache-foundation', 19 | value: 'starterkit-mustache-foundation' 20 | }, 21 | { 22 | name: 'starterkit-twig-base', 23 | value: 'starterkit-twig-base' 24 | }, 25 | { 26 | name: 'starterkit-twig-demo', 27 | value: 'starterkit-twig-demo' 28 | }, 29 | { 30 | name: 'starterkit-mustache-materialdesign', 31 | value: 'starterkit-mustache-materialdesign' 32 | }, 33 | { 34 | name: 'starterkit-twig-drupal-demo', 35 | value: 'starterkit-twig-drupal-demo' 36 | }, 37 | { 38 | name: 'starterkit-twig-drupal-minimal', 39 | value: 'starterkit-twig-drupal-minimal' 40 | }, 41 | { 42 | name: 'starterkit-mustache-webdesignday', 43 | value: 'starterkit-mustache-webdesignday' 44 | }, 45 | { 46 | name: 'starterkit-mustache-base', 47 | value: '@pattern-lab/starterkit-mustache-base' 48 | }, 49 | new inquirer.Separator(), 50 | { 51 | name: 'None', 52 | value: false 53 | }], 54 | default: { 55 | name: 'starterkit-mustache-base', 56 | value: 'starterkit-mustache-base'} 57 | }]; 58 | module.exports = starterkitSetup; 59 | -------------------------------------------------------------------------------- /bin/install-edition.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const hasYarn = require('has-yarn'); 4 | const checkAndInstallPackage = require('./utils').checkAndInstallPackage; 5 | const copyAsync = require('./utils').copyAsync; 6 | const wrapAsync = require('./utils').wrapAsync; 7 | 8 | const installEdition = (edition, config) => wrapAsync(function*() { 9 | /** 10 | * 1. Trigger edition install 11 | * 2. Copy over the mandatory edition files to sourceDir 12 | * 3. Check whether we need to deal with peerDeps for npm < v3 Can be deprecated once we drop Node v4 13 | * 4. Adjust config paths 14 | */ 15 | const sourceDir = config.paths.source.root; 16 | yield checkAndInstallPackage(edition); // 1 17 | yield copyAsync(path.resolve('./node_modules', edition, 'source', '_meta'), path.resolve(sourceDir, '_meta')); // 2 18 | const editionPath = (+process.version.match(/^v(\d*)\.\d*\.\d*$/)[1] <= 4 && !hasYarn()) ? path.join('node_modules', edition, 'node_modules') : 'node_modules'; // 3 19 | config.paths.source.styleguide = config.paths.source.styleguide.replace(/node_modules/, editionPath); // 4 20 | config.paths.source.patternlabFiles = config.paths.source.patternlabFiles.replace(/node_modules/, editionPath); // 5 21 | return config; 22 | }); 23 | 24 | module.exports = installEdition; 25 | -------------------------------------------------------------------------------- /bin/install-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const checkAndInstallPackage = require('./utils').checkAndInstallPackage; 3 | const wrapAsync = require('./utils').wrapAsync; 4 | 5 | const installPlugin = (plugin, config) => wrapAsync(function*() { 6 | const name = plugin.name || plugin; 7 | const url = `pattern-lab/${name}`; 8 | yield checkAndInstallPackage(name, url); 9 | // Put the installed plugin in the patternlab-config.json 10 | config[name] = false; 11 | return name; 12 | }); 13 | 14 | module.exports = installPlugin; 15 | 16 | -------------------------------------------------------------------------------- /bin/install-starterkit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const checkAndInstallPackage = require('./utils').checkAndInstallPackage; 4 | const copyAsync = require('./utils').copyAsync; 5 | const wrapAsync = require('./utils').wrapAsync; 6 | 7 | const installStarterkit = (starterkit, config) => wrapAsync(function * () { 8 | const sourceDir = config.paths.source.root; 9 | const name = starterkit.value || starterkit; 10 | const url = name.startsWith('@pattern-lab/') 11 | ? name 12 | : `pattern-lab/${name}`; 13 | yield checkAndInstallPackage(name, url); 14 | yield copyAsync(path.resolve('./node_modules', name, 'dist'), path.resolve(sourceDir)); 15 | return name; 16 | }); 17 | 18 | module.exports = installStarterkit; 19 | -------------------------------------------------------------------------------- /bin/patternlab.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | const cli = require('commander'); 4 | const build = require('./cli-actions/build'); 5 | const help = require('./cli-actions/help'); 6 | const version = require('./cli-actions/version'); 7 | const init = require('./cli-actions/init'); 8 | const install = require('./cli-actions/install'); 9 | const exportPatterns = require('./cli-actions/export'); 10 | const serve = require('./cli-actions/serve'); 11 | const error = require('./utils').error; 12 | const log = require('./utils').log; 13 | const pkg = require('../package.json'); 14 | 15 | // Register error logging 16 | log.on('patternlab.error', err => console.log(err)); // eslint-disable-line 17 | 18 | // Conditionally register verbose logging 19 | const verboseLogs = verbose => log.on('patternlab.debug', msg => console.log(msg)); // eslint-disable-line 20 | 21 | // Conditionally unregister all logging 22 | const silenceLogs = () => { 23 | log.removeAllListeners('patternlab.debug'); 24 | log.removeAllListeners('patternlab.error'); 25 | }; 26 | 27 | // Split strings into an array 28 | const list = val => val.split(','); 29 | 30 | /** 31 | * Hook up cli version, usage and options 32 | */ 33 | cli 34 | .version(version(pkg), '-V, --version') 35 | .usage(' [options]') 36 | .arguments(' [options]') 37 | .option('-c, --config ', 'Specify config file. Default looks up the project dir', val => val.trim(), './patternlab-config.json') 38 | .option('-v, --verbose', 'Show verbose console logs', verboseLogs) 39 | .option('--silent', 'Turn off console logs', silenceLogs); 40 | 41 | /** 42 | * build 43 | * @desc Setup patternlab's `build` cmd 44 | */ 45 | cli 46 | .command('build') 47 | .alias('compile') 48 | .description('Build the PatternLab. Optionally (re-)build only the patterns') 49 | .option('-p, --patterns-only', 'Whether to only build patterns') 50 | .action(build); 51 | 52 | /** 53 | * export 54 | * @desc Export a PatternLab patterns into a compressed format 55 | */ 56 | cli 57 | .command('export') 58 | .description('Export a PatternLab patterns into a compressed format') 59 | .action(exportPatterns); 60 | 61 | /** 62 | * init 63 | * @desc Initialize a PatternLab project from scratch or import an edition and/or starterkit 64 | */ 65 | cli 66 | .command('init') 67 | .description('Initialize a PatternLab project from scratch or import an edition and/or starterkit') 68 | .option('-p, --project-dir ', 'Specify a project directory') 69 | .option('-e, --edition ', 'Specify an edition to install') 70 | .option('-k, --starterkit ', 'Specify a starterkit to install') 71 | .action(init); 72 | 73 | /** 74 | * install 75 | * @desc Installs Pattern Lab related modules like starterkits or plugins 76 | */ 77 | cli 78 | .command('install') 79 | .alias('add') 80 | .description('Installs Pattern Lab related modules like starterkits or plugins') 81 | .option('--starterkits ', 'Specify one or more starterkit to install', list) 82 | .option('--plugins ', 'Specify one or more plugins to install', list) 83 | .action(install); 84 | 85 | /** 86 | * serve 87 | * @desc Starts a server to inspect files in browser 88 | */ 89 | cli 90 | .command('serve') 91 | .alias('browse') 92 | .description('Starts a server to inspect files in browser') 93 | .option('-w, --watch', 'Start watching for changes') 94 | .action(serve); 95 | 96 | // Show additional help 97 | cli.on('--help', help); 98 | 99 | /** 100 | * Catch all unsupported commands and delegate to the cli's help 101 | * Parse at the end because Node emit is immediate 102 | */ 103 | cli 104 | .on('*', () => { 105 | error('Invalid command provided. See the help for available commands/options.'); 106 | cli.help(); 107 | }) 108 | .parse(process.argv); 109 | 110 | -------------------------------------------------------------------------------- /bin/replace-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const _ = require('lodash'); 4 | 5 | /** 6 | * @func replaceConfigPaths 7 | * @desc Immutable replace source and public paths in the passed config. 8 | * @param {config} config - The passed PatternLab config. 9 | * @param {string} projectDir - The project directory path, defaults to ./ 10 | * @param {string} sourceDir - The source root directory path. 11 | * @param {string} publicDir - The public root directory path. 12 | * @param {string} exportDir - The export root directory path. 13 | * @return {config} - Returns a modified config. Original stays unaltered. 14 | */ 15 | function replaceConfigPaths(config, projectDir, sourceDir, publicDir, exportDir) { 16 | const conf = Object.assign({}, config); 17 | _.map(conf.paths.source, (value, key) => { conf.paths.source[key] = value.replace(/^\.\/source/g, path.join(projectDir, sourceDir)) }); 18 | _.map(conf.paths.public, (value, key) => { conf.paths.public[key] = value.replace(/^\.\/public/g, path.join(projectDir, publicDir)) }); 19 | conf.patternExportDirectory = path.join(projectDir, exportDir); 20 | return conf; 21 | } 22 | 23 | module.exports = replaceConfigPaths; 24 | -------------------------------------------------------------------------------- /bin/resolve-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const exists = require('path-exists'); 3 | const path = require('path'); 4 | const error = require('./utils').error; 5 | const readJsonAsync = require('./utils').readJsonAsync; 6 | const wrapAsync = require('./utils').wrapAsync; 7 | 8 | /** 9 | * @func resolveConfig 10 | * @desc Resolves the given PatternLab config file. 11 | * @param {string} [configPath=./patternlab-config.json] - Path to the patternlab-config.json. Defaults to project dir. 12 | * @return {object|boolean} Returns the config object or false otherwise. 13 | */ 14 | function resolveConfig(configPath) { 15 | return wrapAsync(function*() { 16 | if (typeof configPath !== 'string') { 17 | error('resolveConfig: If configPath is set, it is expected to be of type string.'); 18 | return false; 19 | } 20 | if (!exists.sync(configPath)) { 21 | error(`resolveConfig: configPath ${configPath} does not exists`); 22 | return false; 23 | } 24 | 25 | /** 26 | * Setup the config. 27 | * 1. Check if user specified custom PatternLab config location 28 | * 2. Read the config file 29 | */ 30 | try { 31 | const absoluteConfigPath = path.resolve(configPath); // 1 32 | return yield readJsonAsync(absoluteConfigPath); // 2 33 | } catch (err) { 34 | error('resolveConfig: Got an error during parsing your PatternLab config. Please make sure your config file exists.'); 35 | error(err); 36 | return false; 37 | } 38 | }); 39 | } 40 | 41 | module.exports = resolveConfig; 42 | -------------------------------------------------------------------------------- /bin/scaffold.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const wrapAsync = require('./utils').wrapAsync; 4 | const mkdirsAsync = require('./utils').mkdirsAsync; 5 | 6 | /** 7 | * @func scaffold 8 | * @desc Generate file and folder structure for a PatternLab project 9 | * @param {string} projectDir - The project root directory path. 10 | * @param {string} sourceDir - The source root directory path. 11 | * @param {string} publicDir - The public root directory path. 12 | * @param {string} exportDir - The export root directory path. 13 | * @return {void} 14 | */ 15 | const scaffold = (projectDir, sourceDir, publicDir, exportDir) => wrapAsync(function*() { 16 | /** 17 | * Create mandatory files structure 18 | * 1. Create project source directory 19 | * 2. Create project public directory 20 | * 3. Create project export directory 21 | */ 22 | yield Promise.all([ 23 | mkdirsAsync(path.resolve(projectDir, path.normalize(sourceDir))), // 1 24 | mkdirsAsync(path.resolve(projectDir, path.normalize(publicDir))), // 2 25 | mkdirsAsync(path.resolve(projectDir, path.normalize(exportDir))) // 3 26 | ]); 27 | }); 28 | 29 | module.exports = scaffold; 30 | -------------------------------------------------------------------------------- /bin/serve.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const chokidar = require('chokidar'); 3 | const liveServer = require('@pattern-lab/live-server'); 4 | const patternlab = require('patternlab-node'); 5 | const path = require('path'); 6 | const _ = require('lodash'); 7 | 8 | const buildPatterns = require('./build'); 9 | const isValidConfig = require('./validate-config'); 10 | const copyWithPattern = require('./utils').copyWithPattern; 11 | const wrapAsync = require('./utils').wrapAsync; 12 | const error = require('./utils').error; 13 | 14 | /** 15 | * @func serve 16 | * @desc Start a browser-sync server in the PatternLab public dir 17 | * @param {object} config - The passed PatternLab config 18 | * @param {boolean} watch - Whether to set up watches 19 | */ 20 | function serve(config, watch) { 21 | if (!isValidConfig) throw new TypeError('serve: Expects config not to be empty and of type object.'); 22 | 23 | if (!_.has(config, 'paths.public.root') || _.isEmpty(config.paths.public.root)) { 24 | throw new TypeError('serve: config.paths.public.root is empty or does not exist. Please check your PatternLab config.'); 25 | } 26 | if (!_.has(config, 'paths.source.root') || _.isEmpty(config.paths.source.root)) { 27 | throw new TypeError('serve: config.paths.source.root is empty or does not exist. Please check your PatternLab config.'); 28 | } 29 | 30 | try { 31 | const pl = patternlab(); 32 | const src = config.paths.source; 33 | const publicDir = path.resolve(config.paths.public.root); 34 | const sourceCSS = path.join(path.resolve(src.css), '/**/*.css'); 35 | const sourceStyleguide = path.join(path.resolve(src.styleguide), '/**/*.*'); 36 | const patterns = pl.getSupportedTemplateExtensions().map(dotExtension => path.join(path.resolve(src.patterns), `/**/*${dotExtension}`)); 37 | 38 | // The liveserver config 39 | const liveServerConf = { 40 | root: publicDir, 41 | open: true, 42 | ignore: path.join(publicDir), 43 | file: 'index.html' 44 | }; 45 | 46 | /** 47 | * @func copyAndReloadCSS 48 | */ 49 | const copyAndReloadCSS = () => wrapAsync(function *() { 50 | yield copyWithPattern(path.resolve(src.css), '**/*.css', path.resolve(config.paths.public.css)); 51 | liveServer.refreshCSS(); 52 | }); 53 | 54 | /** 55 | * @func copyAndReloadStyleguide 56 | */ 57 | const copyAndReloadStyleguide = () => wrapAsync(function *() { 58 | yield copyWithPattern(path.resolve(src.styleguide), '**/!(*.css)', path.resolve(config.paths.public.styleguide)); 59 | yield copyWithPattern(path.resolve(src.styleguide), '**/*.css', path.resolve(config.paths.public.styleguide)); 60 | liveServer.refreshCSS(); 61 | }); 62 | 63 | /** 64 | * @func reload 65 | * @desc Calls browser-sync's reload method to tell browsers to refresh their page 66 | */ 67 | const buildAndReload = function () { 68 | buildPatterns(config); 69 | liveServer.reload(); 70 | }; 71 | 72 | if (watch) { 73 | /** 74 | * 1. Watch source css, then copy css and callreloadCSS 75 | * 2. Watch source styleguide, then copy styleguide and css and call reloadCSS 76 | * 3. Watch pattern-specific and engine-specific extensions, run build and reload 77 | */ 78 | chokidar.watch(sourceCSS).on('change', copyAndReloadCSS); // 1 79 | chokidar.watch(sourceStyleguide).on('change', copyAndReloadStyleguide); // 2 80 | const patternWatches = [ 81 | path.join(path.resolve(src.patterns), '**/*.json'), 82 | path.join(path.resolve(src.patterns), '**/*.md'), 83 | path.join(path.resolve(src.data), '*.json'), 84 | path.join(path.resolve(src.fonts), '*'), 85 | path.join(path.resolve(src.images), '*'), 86 | path.join(path.resolve(src.meta), '*'), 87 | path.join(path.resolve(src.annotations), '*') 88 | ].concat(patterns); // 3 89 | 90 | chokidar.watch(patternWatches).on('change', buildAndReload); 91 | } 92 | // Init browser-sync 93 | liveServer.start(liveServerConf); 94 | } catch (err) { 95 | error(err); 96 | } 97 | } 98 | 99 | module.exports = serve; 100 | -------------------------------------------------------------------------------- /bin/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs-promise'); 3 | const spawn = require('execa'); 4 | const glob = require('glob'); 5 | const path = require('path'); 6 | const chalk = require('chalk'); 7 | const EventEmitter = require('events').EventEmitter; 8 | const hasYarn = require('has-yarn'); 9 | 10 | /** 11 | * @name log 12 | * @desc tiny event-based logger 13 | * @type {*} 14 | */ 15 | const log = Object.assign({ 16 | debug(msg) { 17 | this.emit('patternlab.debug', `${chalk.cyan('⊙ patternlab →')} ${chalk.dim(msg)}`); 18 | }, 19 | error(msg) { 20 | this.emit('patternlab.error', `${chalk.red('⊙ patternlab →')} ${chalk.dim(msg)}`); 21 | } 22 | }, EventEmitter.prototype); 23 | 24 | /** 25 | * @func debug 26 | * @desc Coloured debug log 27 | * @param {*} msg - The variadic messages to log out. 28 | * @return {void} 29 | */ 30 | const debug = log.debug.bind(log); 31 | 32 | /** 33 | * @func error 34 | * @desc Coloured error log 35 | * @param {*} e - The variadic messages to log out. 36 | * @return {void} 37 | */ 38 | const error = log.error.bind(log); 39 | 40 | /** 41 | * @func wrapAsync 42 | * @desc Wraps an generator function to yield out promisified stuff 43 | * @param {function} fn - Takes a generator function 44 | */ 45 | const wrapAsync = fn => new Promise((resolve, reject) => { 46 | const generator = fn(); 47 | (function spwn(val) { 48 | let res; 49 | try { 50 | res = {}.toString.call(val) !== '[object Error]' ? generator.next(val) : generator.throw(val); 51 | } catch (err) { 52 | return reject(err); 53 | } 54 | const v = res.value; 55 | if (res.done) return resolve(v); 56 | Promise.resolve(v).then(spwn).catch(spwn); 57 | })(); 58 | }); 59 | 60 | /** 61 | * @func glob 62 | * @desc Promisified glob function 63 | * @param {string} pattern - A glob pattern to match against 64 | * @param {object} opts - A configuration object. See glob package for details 65 | * @return {Promise} 66 | */ 67 | const asyncGlob = (pattern, opts) => new Promise( 68 | (resolve, reject) => glob(pattern, opts, 69 | (err, matches) => (err !== null) ? reject(err) : resolve(matches) 70 | ) 71 | ); 72 | 73 | /** 74 | * @func copyWithPattern 75 | * @desc Copies multiple files asynchronously from one dir to another according to a glob pattern specified 76 | * @param {string} cwd - The path to search for file(s) at 77 | * @param {string} pattern - A glob pattern to match the file(s) 78 | * @param {string} dest - The destination dir path 79 | * @return {Promise} 80 | */ 81 | const copyWithPattern = (cwd, pattern, dest) => wrapAsync(function*() { 82 | const files = yield asyncGlob(pattern, {cwd: cwd}); 83 | if (files.length === 0) debug('copy: Nothing to copy'); 84 | // Copy concurrently 85 | const promises = files.map(file => fs.copy( 86 | path.join(cwd, file), 87 | path.join(dest, file)) 88 | ); 89 | return yield Promise.all(promises); 90 | }); 91 | 92 | /** 93 | * @func fetchPackage 94 | * @desc Fetches and saves packages from npm into node_modules and adds a reference in the package.json under dependencies 95 | * @param {string} packageName - The package name 96 | * @param {string} [url] - A URL which will be used to fetch the package from 97 | */ 98 | const fetchPackage = (packageName, url) => wrapAsync(function*() { 99 | const useYarn = hasYarn(); 100 | const pm = useYarn ? 'yarn' : 'npm'; 101 | const installCmd = useYarn ? 'add' : 'install'; 102 | try { 103 | if (packageName || url) { 104 | const cmd = yield spawn(pm, [installCmd, url || packageName]); 105 | error(cmd.stderr); 106 | } 107 | } catch (err) { 108 | error(`fetchPackage: Fetching required dependencies from ${pm} failed for ${packageName} with ${err}`); 109 | throw err; // Rethrow error 110 | } 111 | }); 112 | 113 | /** 114 | * @func checkAndInstallPackage 115 | * Checks whether a package for a given packageName is installed locally. If package cannot be found, fetch and install it 116 | * @param {string} packageName - The package name 117 | * @param {string} [url] - A URL which will be used to fetch the package from 118 | * @return {boolean} 119 | */ 120 | const checkAndInstallPackage = (packageName, url) => wrapAsync(function*() { 121 | try { 122 | require.resolve(packageName); 123 | return true; 124 | } catch (err) { 125 | debug(`checkAndInstallPackage: ${packageName} not installed. Fetching it now …`); 126 | yield fetchPackage(packageName, url); 127 | return false; 128 | } 129 | }); 130 | 131 | /** 132 | * @func noop 133 | * @desc Plain arrow expression for noop 134 | */ 135 | const noop = () => {}; 136 | 137 | module.exports = { 138 | copyWithPattern, 139 | copyAsync: fs.copy, 140 | mkdirsAsync: fs.mkdirs, 141 | moveAsync: fs.move, 142 | writeJsonAsync: fs.outputJson, 143 | readJsonAsync: fs.readJson, 144 | error, 145 | debug, 146 | log, 147 | wrapAsync, 148 | checkAndInstallPackage, 149 | noop 150 | }; 151 | -------------------------------------------------------------------------------- /bin/validate-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @func isValidConfig 4 | * @desc Checks validity of a patternlab config 5 | * @param {object} config - Name of the command to check against. 6 | * @return {object} - Returns true is all is good, false otherwise. 7 | */ 8 | function isValidConfig(config) { 9 | return (!config || typeof config !== 'object'); 10 | } 11 | 12 | module.exports = isValidConfig; 13 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Raphael Okon 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "patternlab-node-cli", 3 | "description": "Command-line interface (CLI) for the patternlab-node core.", 4 | "version": "0.0.1-alpha.12", 5 | "bin": { 6 | "patternlab": "bin/patternlab.js" 7 | }, 8 | "author": { 9 | "name": "Raphael Okon" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@pattern-lab/live-server": "1.3.2", 14 | "archiver": "2.1.1", 15 | "chalk": "2.3.1", 16 | "chokidar": "2.0.2", 17 | "commander": "2.14.1", 18 | "execa": "0.9.0", 19 | "fs-promise": "2.0.3", 20 | "glob": "7.1.2", 21 | "has-yarn": "1.0.0", 22 | "inquirer": "5.1.0", 23 | "lodash": "4.17.5", 24 | "ora": "2.0.0", 25 | "path-exists": "3.0.0", 26 | "patternlab-node": "2.12.0", 27 | "sanitize-filename": "1.6.1" 28 | }, 29 | "devDependencies": { 30 | "eslint": "4.18.1", 31 | "proxyquire": "1.8.0", 32 | "tap": "11.1.1" 33 | }, 34 | "files": [ 35 | "bin" 36 | ], 37 | "keywords": [ 38 | "Pattern Lab", 39 | "Atomic Web Design", 40 | "Node", 41 | "JavaScript" 42 | ], 43 | "scripts": { 44 | "lint": "eslint ./{bin,test}", 45 | "test": "tap './test/*.test.js' --reporter spec --timeout=120", 46 | "pretest": "npm run lint && npm install" 47 | }, 48 | "repository": { 49 | "type": "git", 50 | "url": "git://github.com/pattern-lab/patternlab-node-cli.git" 51 | }, 52 | "bugs": "https://github.com/pattern-lab/patternlab-node-cli/issues", 53 | "license": "MIT", 54 | "engines": { 55 | "node": ">=6.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > THIS REPOSITORY MOVED TO https://github.com/pattern-lab/patternlab-node/tree/dev/packages/cli 2 | 3 | # PatternLab Node CLI 4 | 5 | > Command-line interface (CLI) for the patternlab-node core. 6 | 7 | [![Build Status](https://travis-ci.org/pattern-lab/patternlab-node-cli.svg?branch=master)](https://travis-ci.org/pattern-lab/patternlab-node-cli) 8 | 9 | 10 | ## Installation 11 | *Note: Global installs are currently not supported and will be fixed when the Pattern Lab core hits v3.0.0* 12 | 13 | #### Via NPM 14 | `npm install pattern-lab/patternlab-node-cli --save-dev` 15 | 16 | #### Via Yarn 17 | `yarn add pattern-lab/patternlab-node-cli --dev` 18 | 19 | ## Getting Started 20 | 1. In order to use PatternLab you need to initialize a PatternLab project with `patternlab init`. The CLI will ask you some setup question and scaffold your project based on it. 21 | 2. Build your patterns use `patternlab build`. The PatternLab CLI will assume that the `patternlab-config.json` is in the project root. Othewise specify a custom path to config with `patternlab build --config path/to/config` 22 | 3. To view your patterns in the browser preview `patternlab serve` or again specify a custom config location `patternlab serve --config path/to/config` 23 | 4. To export your patterns in the browser preview `patternlab export` or again specify a custom config location `patternlab export --config path/to/config` 24 | 25 | ## API & Usage 26 | ### General usage 27 | ``` 28 | Usage: patternlab [options] 29 | Commands: 30 | build|compile [options] Build the PatternLab. Optionally (re-)build only the patterns 31 | export Export a PatternLab patterns into a compressed format 32 | init [options] Initialize a PatternLab project from scratch or import an edition and/or starterkit 33 | install|add [options] Installs Pattern Lab related modules like starterkits or plugins 34 | serve|browse [options] Starts a server to inspect files in browser 35 | 36 | Options: 37 | -h, --help output usage information 38 | -V, --version output the version number 39 | -c, --config Specify config file. Default looks up the project dir 40 | -v, --verbose Show verbose logging 41 | --silent Turn off console logs 42 | ``` 43 | 44 | ### Build/Compile PatternLab 45 | ``` 46 | Usage: build|compile [options] 47 | 48 | Build the PatternLab. Optionally (re-)build only the patterns 49 | 50 | Options: 51 | -h, --help output usage information 52 | -p, --patterns-only Whether to only build patterns 53 | ``` 54 | 55 | ### Initialize PatternLab 56 | ``` 57 | Usage: init [options] 58 | 59 | Initialize a PatternLab project from scratch or import an edition and/or starterkit 60 | Passing no options starts the init in interactive mode 61 | 62 | Options: 63 | -h, --help output usage information 64 | -p, --project-dir Specify a project directory. Default: ./ 65 | -e, --edition Specify an edition to install. Default: edition-node 66 | -k, --starterkit Specify a starterkit to install. Default: starterkit-mustache-base 67 | ``` 68 | 69 | ### Serve PatternLab 70 | ``` 71 | Usage: serve|browse [options] 72 | 73 | Starts a server to inspect files in browser 74 | 75 | 76 | Options: 77 | -h, --help output usage information 78 | -w, --watch Start watching for changes 79 | ``` 80 | 81 | ### Export PatternLab 82 | ``` 83 | Usage: export [options] 84 | 85 | Export a PatternLab patterns into a compressed format 86 | 87 | Options: 88 | -h, --help output usage information 89 | ``` 90 | 91 | ### Install Pattern Lab starterkits or plugins 92 | ``` 93 | Usage: install|add [options] 94 | 95 | Installs Pattern Lab related modules like starterkits or plugins 96 | 97 | Options: 98 | -h, --help output usage information 99 | --starterkits Specify one or more starterkits to install 100 | --plugins Specify one or more plugins to install 101 | 102 | ``` 103 | 104 | ## Examples 105 | ``` 106 | $ patternlab init # Initialize a PatternLab project. 107 | $ patternlab build # Builds PatternLab from the current dir 108 | $ patternlab build --config # Builds PatternLab from different project directory 109 | ``` 110 | ## License 111 | MIT © [Raphael Okon](https://github.com/raphaelokon) 112 | -------------------------------------------------------------------------------- /test/build.test.js: -------------------------------------------------------------------------------- 1 | const proxyquire = require('proxyquire'); 2 | const tap = require('tap'); 3 | const config = require('../bin/default-config'); 4 | const patternLabMock = require('./mocks/patternlab.mock.js'); 5 | 6 | // Require build and mock patternlab.build() so that we only test the build module behavior 7 | const build = proxyquire('../bin/build', {'patternlab-node': patternLabMock}); 8 | const opts = {patternsOnly: true}; 9 | 10 | tap.test('Build ->', t => { 11 | t.throws(() => { build() }, {}, 'throws when config is empty'); 12 | t.throws(() => { build(123) }, {}, 'throws when config is not of type object'); 13 | t.throws(() => { build(undefined, opts) }, {}, '--patterns-only throws when config is empty'); 14 | t.throws(() => { build(undefined, opts) }, {}, '--patterns-only throws when config is not of type object'); 15 | t.type(build(config), 'boolean', 'returns a bool'); 16 | t.type(build(config, opts), 'boolean', '--patterns-only returns a bool'); 17 | t.end(); 18 | }); 19 | -------------------------------------------------------------------------------- /test/cli-build.test.js: -------------------------------------------------------------------------------- 1 | const exists = require('path-exists'); 2 | const getUniqueProjectPath = require('./utils/getUniqueProjectPath'); 3 | const path = require('path'); 4 | const spawnCmd = require('./utils/spawnCmd'); 5 | const tap = require('tap'); 6 | const wrapAsync = require('../bin/utils').wrapAsync; 7 | 8 | const projectRoot = getUniqueProjectPath(); 9 | 10 | tap.test('Init and build ->', t => wrapAsync(function*() { 11 | yield spawnCmd(['init', '--verbose', '--project-dir', projectRoot, '--edition', 'edition-node', '--starterkit', '@pattern-lab/starterkit-mustache-base']); 12 | yield spawnCmd(['build', '--config', `${projectRoot}/patternlab-config.json`]); 13 | t.ok(exists.sync(path.resolve(projectRoot, 'public')), 'should build all files into public dir'); 14 | t.ok(exists.sync(path.resolve(projectRoot, 'public', 'annotations')), 'with a annotations dir'); 15 | t.ok(exists.sync(path.resolve(projectRoot, 'public', 'css')), 'with a css dir'); 16 | t.ok(exists.sync(path.resolve(projectRoot, 'public', 'images')), 'with a images dir'); 17 | t.ok(exists.sync(path.resolve(projectRoot, 'public', 'styleguide')), 'with a styleguide dir'); 18 | t.end(); 19 | })); 20 | -------------------------------------------------------------------------------- /test/cli-export.test.js: -------------------------------------------------------------------------------- 1 | const exists = require('path-exists'); 2 | const getUniqueProjectPath = require('./utils/getUniqueProjectPath'); 3 | const path = require('path'); 4 | const spawnCmd = require('./utils/spawnCmd'); 5 | const tap = require('tap'); 6 | const wrapAsync = require('../bin/utils').wrapAsync; 7 | 8 | const projectRoot = getUniqueProjectPath(); 9 | 10 | tap.test('Init and export ->', t => wrapAsync(function*() { 11 | yield spawnCmd(['init', '--verbose', '--project-dir', projectRoot, '--edition', 'edition-node', '--starterkit', '@pattern-lab/starterkit-mustache-base']); 12 | yield spawnCmd(['export', '--config', `${projectRoot}/patternlab-config.json`]); 13 | t.ok(exists.sync(path.resolve(projectRoot, 'pattern_exports', 'patterns.zip')), ' should create patterns.zip'); 14 | t.end(); 15 | })); 16 | -------------------------------------------------------------------------------- /test/cli-init.test.js: -------------------------------------------------------------------------------- 1 | const exists = require('path-exists'); 2 | const getUniqueProjectPath = require('./utils/getUniqueProjectPath'); 3 | const path = require('path'); 4 | const spawnCmd = require('./utils/spawnCmd'); 5 | const tap = require('tap'); 6 | const wrapAsync = require('../bin/utils').wrapAsync; 7 | 8 | const projectRoot = getUniqueProjectPath(); 9 | 10 | tap.test('Init ->', t => wrapAsync(function*() { 11 | yield spawnCmd(['init', '--verbose', '--project-dir', projectRoot, '--edition', 'edition-node', '--starterkit', '@pattern-lab/starterkit-mustache-base']); 12 | t.ok(exists.sync(path.resolve(projectRoot)), 'should initialize a PatternLab project'); 13 | t.ok(exists.sync(path.resolve(projectRoot, 'source')), 'with a source dir'); 14 | t.ok(exists.sync(path.resolve(projectRoot, 'public')), 'with a public dir'); 15 | t.ok(exists.sync(path.resolve(projectRoot, 'pattern_exports')), 'with a pattern_exports dir'); 16 | t.ok(exists.sync(path.resolve(projectRoot, 'patternlab-config.json')), 'with a pattern_exports dir'); 17 | t.end(); 18 | })); 19 | -------------------------------------------------------------------------------- /test/export.test.js: -------------------------------------------------------------------------------- 1 | const exportPatterns = require('../bin/cli-actions/export'); 2 | const tap = require('tap'); 3 | const wrapAsync = require('../bin/utils').wrapAsync; 4 | 5 | tap.test('Export ->', t => { 6 | t.plan(2) 7 | t.test('with options empty', t => wrapAsync(function*() { 8 | try { 9 | yield exportPatterns() 10 | } catch (err) { 11 | t.type(err, TypeError, 'throws when options are empty'); 12 | t.end(); 13 | } 14 | })); 15 | t.test('with options not an object', t => wrapAsync(function*() { 16 | try { 17 | yield exportPatterns(123) 18 | } catch (err) { 19 | t.type(err, TypeError, 'throws when passed options are not of type object'); 20 | t.end(); 21 | } 22 | })); 23 | }); 24 | -------------------------------------------------------------------------------- /test/fixtures/patternlab-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "paths": { 3 | "source": { 4 | "root": "test/fixtures/source/", 5 | "patterns": "test/fixtures/source/_patterns/", 6 | "data": "test/fixtures/source/_data/", 7 | "meta": "test/fixtures/source/_meta/", 8 | "annotations": "test/fixtures/source/_annotations/", 9 | "styleguide": "node_modules/styleguidekit-assets-default/dist/", 10 | "patternlabFiles": "node_modules/styleguidekit-mustache-default/views/", 11 | "js": "test/fixtures/source/js", 12 | "images": "test/fixtures/source/images", 13 | "fonts": "test/fixtures/source/fonts", 14 | "css": "test/fixtures/source/css/" 15 | }, 16 | "public": { 17 | "root": "test/fixtures/public/", 18 | "patterns": "test/fixtures/public/patterns/", 19 | "data": "test/fixtures/public/styleguide/data/", 20 | "annotations": "test/fixtures/public/annotations/", 21 | "styleguide": "test/fixtures/public/styleguide/", 22 | "js": "test/fixtures/public/js", 23 | "images": "test/fixtures/public/images", 24 | "fonts": "test/fixtures/public/fonts", 25 | "css": "test/fixtures/public/css" 26 | } 27 | }, 28 | "styleGuideExcludes": [ 29 | "templates", 30 | "pages" 31 | ], 32 | "defaultPattern": "all", 33 | "cleanPublic": false, 34 | "patternExtension": "mustache", 35 | "ignored-extensions": [ 36 | "scss", 37 | "DS_Store", 38 | "less" 39 | ], 40 | "ignored-directories": [ 41 | "scss" 42 | ], 43 | "debug": false, 44 | "ishControlsHide": { 45 | "s": false, 46 | "m": false, 47 | "l": false, 48 | "full": false, 49 | "random": false, 50 | "disco": false, 51 | "hay": true, 52 | "mqs": false, 53 | "find": false, 54 | "views-all": false, 55 | "views-annotations": false, 56 | "views-code": false, 57 | "views-new": false, 58 | "tools-all": false, 59 | "tools-docs": false 60 | }, 61 | "ishMinimum": "240", 62 | "ishMaximum": "2600", 63 | "patternStateCascade": [ 64 | "inprogress", 65 | "inreview", 66 | "complete" 67 | ], 68 | "patternExportPatternPartials": [], 69 | "patternExportDirectory": "test/fixtures/pattern_exports", 70 | "baseurl": "", 71 | "cacheBust": true, 72 | "starterkitSubDir": "dist", 73 | "outputFileSuffixes": { 74 | "rendered": "", 75 | "rawTemplate": "", 76 | "markupOnly": ".markup-only" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/install-plugin.test.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const installPlugin = require('../bin/install-plugin'); 3 | const wrapAsync = require('../bin/utils').wrapAsync; 4 | const getUniqueProjectPath = require('./utils/getUniqueProjectPath'); 5 | const moduleExist = require.resolve; 6 | 7 | const projectRoot = getUniqueProjectPath(); 8 | 9 | const minimalConfig = { 10 | paths: { 11 | source: { 12 | root: projectRoot 13 | } 14 | } 15 | }; 16 | 17 | tap.test('Install plugin-node-tab ->', t => wrapAsync(function*() { 18 | yield installPlugin('plugin-node-tab', minimalConfig); 19 | const pkg = yield moduleExist('plugin-node-tab'); 20 | t.ok(pkg, 'module should exist after install'); 21 | t.equal(minimalConfig['plugin-node-tab'], false, 'and persist it on the patternlab-config.json'); 22 | t.end(); 23 | })); 24 | -------------------------------------------------------------------------------- /test/install-starterkit.test.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const installStarterkit = require('../bin/install-starterkit'); 3 | const wrapAsync = require('../bin/utils').wrapAsync; 4 | const getUniqueProjectPath = require('./utils/getUniqueProjectPath'); 5 | const moduleExist = require.resolve; 6 | 7 | const projectRoot = getUniqueProjectPath(); 8 | 9 | const minimalConfig = { 10 | paths: { 11 | source: { 12 | root: projectRoot 13 | } 14 | } 15 | }; 16 | 17 | tap.test('Install starterkit-mustache-demo ->', t => wrapAsync(function*() { 18 | yield installStarterkit('@pattern-lab/starterkit-mustache-demo', minimalConfig); 19 | const pkg = yield moduleExist('@pattern-lab/starterkit-mustache-demo'); 20 | t.ok(pkg, 'module should exist after install'); 21 | t.end(); 22 | })); 23 | 24 | tap.test('Install starterkit-mustache-base ->', t => wrapAsync(function*() { 25 | yield installStarterkit('@pattern-lab/starterkit-mustache-base', minimalConfig); 26 | const pkg = yield moduleExist('@pattern-lab/starterkit-mustache-base'); 27 | t.ok(pkg, 'module should exist after install'); 28 | t.end(); 29 | })); 30 | 31 | tap.test('Install starterkit-mustache-bootstrap ->', t => wrapAsync(function*() { 32 | yield installStarterkit('starterkit-mustache-bootstrap', minimalConfig); 33 | const pkg = yield moduleExist('starterkit-mustache-bootstrap'); 34 | t.ok(pkg, 'module should exist after install'); 35 | t.end(); 36 | })); 37 | 38 | tap.test('Install starterkit-mustache-foundation ->', t => wrapAsync(function*() { 39 | yield installStarterkit('starterkit-mustache-foundation', minimalConfig); 40 | const pkg = yield moduleExist('starterkit-mustache-foundation'); 41 | t.ok(pkg, 'module should exist after install'); 42 | t.end(); 43 | })); 44 | 45 | tap.test('Install starterkit-mustache-acidtest ->', t => wrapAsync(function*() { 46 | yield installStarterkit('starterkit-mustache-acidtest', minimalConfig); 47 | const pkg = yield moduleExist('starterkit-mustache-acidtest'); 48 | t.ok(pkg, 'module should exist after install'); 49 | t.end(); 50 | })); 51 | 52 | tap.test('Install starterkit-mustache-materialdesign ->', t => wrapAsync(function*() { 53 | yield installStarterkit('starterkit-mustache-materialdesign', minimalConfig); 54 | const pkg = yield moduleExist('starterkit-mustache-materialdesign'); 55 | t.ok(pkg, 'module should exist after install'); 56 | t.end(); 57 | })); 58 | -------------------------------------------------------------------------------- /test/mocks/liverserver.mock.js: -------------------------------------------------------------------------------- 1 | function liveServerMock() { 2 | return { 3 | reload: function () { 4 | return true; 5 | }, 6 | refreshCSS: function () { 7 | return true; 8 | }, 9 | start: function () { 10 | return true; 11 | } 12 | } 13 | } 14 | 15 | module.exports = liveServerMock; 16 | -------------------------------------------------------------------------------- /test/mocks/patternlab.mock.js: -------------------------------------------------------------------------------- 1 | function patternLabMock() { 2 | return { 3 | build: function () { 4 | return true; 5 | }, 6 | help: function () { 7 | return true; 8 | }, 9 | patternsonly: function () { 10 | return true; 11 | }, 12 | liststarterkits: function () { 13 | return true; 14 | }, 15 | loadstarterkit: function () { 16 | return true; 17 | } 18 | } 19 | } 20 | 21 | module.exports = patternLabMock; 22 | -------------------------------------------------------------------------------- /test/replace_config_paths.test.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const replaceConfigPaths = require('../bin/replace-config'); 3 | const config = require('../bin/default-config'); 4 | 5 | tap.test('replaceConfigPaths ->', t => { 6 | const newConfig = replaceConfigPaths(config, 'projectDir', 'sourceDir', 'publicDir', 'exportDir'); 7 | for (const k of Object.keys(newConfig.paths.source)) { 8 | t.ok(/^projectDir\/sourceDir\/|^node_modules/.test(newConfig.paths.source[k]), `should be ok for newConfig.paths.source.${k}`); 9 | } 10 | for (const l of Object.keys(newConfig.paths.public)) { 11 | t.ok(/^projectDir\/publicDir\//.test(newConfig.paths.public[l]), `should be ok for newConfig.paths.public.${l}`); 12 | } 13 | t.ok(/^projectDir\/exportDir/.test(newConfig.patternExportDirectory), `should be ok for newConfig.patternExportDirectory`); 14 | t.end(); 15 | }); 16 | -------------------------------------------------------------------------------- /test/resolve_config.test.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const wrapAsync = require('../bin/utils').wrapAsync; 3 | const resolveConfig = require('../bin/resolve-config'); 4 | 5 | tap.test('resolveConfig ->', t => wrapAsync(function*() { 6 | const config = yield resolveConfig('./test/fixtures/patternlab-config.json'); 7 | const badConfig = yield resolveConfig(123); 8 | const configNotFound = yield resolveConfig('./test/fixtures/some-config'); 9 | t.type(config, 'object', 'should return a config of type object'); 10 | t.ok(config.paths, 'config should have a paths property'); 11 | t.notOk(badConfig, 'returns false when configPath is not of type string'); 12 | t.notOk(configNotFound, 'returns false when configPath is not found'); 13 | t.end(); 14 | })); 15 | -------------------------------------------------------------------------------- /test/scaffold.test.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const path = require('path'); 3 | const exists = require('path-exists'); 4 | const scaffold = require('../bin/scaffold'); 5 | const getUniqueProjectPath = require('./utils/getUniqueProjectPath'); 6 | const wrapAsync = require('../bin/utils').wrapAsync; 7 | 8 | const projectDir = getUniqueProjectPath(); 9 | const sourceDir = 'source'; 10 | const publicDir = 'public'; 11 | const exportDir = 'patterns_export'; 12 | 13 | tap.test('Scaffold ->', t => wrapAsync(function*() { 14 | yield scaffold(projectDir, sourceDir, publicDir, exportDir); 15 | t.ok(exists.sync(path.resolve(projectDir)), 'should create project dir'); 16 | t.ok(exists.sync(path.resolve(projectDir, sourceDir)), 'should create source dir'); 17 | t.ok(exists.sync(path.resolve(projectDir, publicDir)), 'should create public dir'); 18 | t.ok(exists.sync(path.resolve(projectDir, exportDir)), 'should create export dir'); 19 | t.end(); 20 | })); 21 | -------------------------------------------------------------------------------- /test/serve.test.js: -------------------------------------------------------------------------------- 1 | const proxyquire = require('proxyquire'); 2 | const tap = require('tap'); 3 | const _ = require('lodash'); 4 | const resolveConfig = require('../bin/resolve-config'); 5 | const liveServerMock = require('./mocks/liverserver.mock.js'); 6 | const wrapAsync = require('../bin/utils').wrapAsync; 7 | 8 | // Require preview but mock patternlab so that we only test the module behavior 9 | const preview = proxyquire('../bin/serve', {'@pattern-lab/live-server': liveServerMock}); 10 | 11 | tap.test('Serve ->', t => wrapAsync(function*() { 12 | const config = yield resolveConfig('./test/fixtures/patternlab-config.json'); 13 | config.paths.source.root = undefined; 14 | t.throws(() => { preview(); }, {}, 'throws when config is empty'); 15 | t.throws(() => { preview(123); }, {}, 'throws when config is not of type object'); 16 | t.throws(() => { 17 | _.unset(config, 'paths.source.root'); 18 | preview(config); 19 | }, {}, 'throws when no source root dir is set on config'); 20 | t.throws(() => { 21 | _.unset(config, 'paths.public.root'); 22 | preview(config); 23 | }, {}, 'throws when no public root dir is set on config'); 24 | t.end(); 25 | })); 26 | -------------------------------------------------------------------------------- /test/utils/getUniqueProjectPath.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | module.exports = () => { 4 | const UUID = crypto.randomBytes(16).toString('hex'); 5 | return `./tmp/${UUID}`; 6 | }; 7 | -------------------------------------------------------------------------------- /test/utils/spawnCmd.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const spawn = require('execa'); 3 | const wrapAsync = require('../../bin/utils').wrapAsync; 4 | const cli = path.resolve(__dirname, '../../bin/patternlab.js'); 5 | 6 | const spawnCmd = (args, endFn) => wrapAsync(function*() { 7 | const fn = endFn || function () {}; 8 | yield spawn('node', [cli].concat(args)); 9 | fn(); 10 | }); 11 | 12 | module.exports = spawnCmd; 13 | --------------------------------------------------------------------------------