From 9142332d92d1b497e957d91606aaf790cb1150e6 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 21 Sep 2015 22:50:00 -0400 Subject: [PATCH 001/282] first pass --- .editorconfig | 6 +- .gitattributes | 9 +- .gitignore | 28 +- .jshintrc | 10 - .verb.md | 191 +-- LICENSE | 2 +- README.md | 289 ++--- docs/_templates/about.md | 4 - docs/_templates/api-config.md | 0 docs/_templates/api-data.md | 89 -- docs/_templates/api-middleware.md | 66 - docs/_templates/api-task.md | 4 - docs/_templates/api-template.md | 36 - docs/_templates/api.md | 151 --- docs/_templates/built-in-helpers.md | 94 -- docs/_templates/cli.md | 96 -- docs/_templates/collections.md | 15 - docs/_templates/config.md | 45 - docs/_templates/data.md | 37 - docs/_templates/engines.md | 91 -- docs/_templates/faq.md | 3 - docs/_templates/features.md | 7 - docs/_templates/getting-started.md | 32 - docs/_templates/helper-conflict-resolution.md | 133 -- docs/_templates/helpers.md | 26 - docs/_templates/helpers/apidocs.md | 29 - docs/_templates/includes.md | 53 - docs/_templates/middleware.md | 33 - docs/_templates/options.md | 31 - docs/_templates/overview.md | 52 - docs/_templates/plugins/conflicts.md | 12 - docs/_templates/protected-properties.md | 12 - docs/_templates/template-data.md | 35 - docs/_templates/using-layouts.md | 21 - docs/_templates/variables.md | 45 - docs/_templates/verbfile.md | 15 - docs/_templates/verbmd.md | 94 -- docs/sections/_temp.md | 287 ---- docs/sections/cli-notice.md | 2 - docs/sections/features.md | 5 - docs/sections/install-verb-cli.md | 5 - docs/sections/overview.md | 192 --- docs/sections/verb-md.md | 33 - docs/sections/verbfile.md | 21 - docs/sections/why.md | 1 - example.js | 27 + examples/config.js | 5 + gulpfile.js | 34 + index.js | 539 ++++++-- lib/config.js | 91 ++ lib/helpers/apidocs.js | 20 - lib/helpers/async.js | 19 - lib/helpers/collections.js | 35 - lib/helpers/custom/example.js | 28 - lib/helpers/custom/github.js | 3 - lib/helpers/custom/index.js | 3 - lib/helpers/custom/linkify.js | 3 - lib/helpers/custom/resolve.js | 25 - lib/helpers/custom/shield.js | 24 - lib/helpers/custom/twitter.js | 3 - lib/helpers/sync.js | 18 - lib/{middleware => }/index.js | 0 lib/init.js | 67 - lib/local.js | 22 + lib/middleware/append.js | 16 - lib/middleware/assets.js | 38 - lib/middleware/conflict.js | 158 --- lib/middleware/copyright.js | 44 - lib/middleware/debug.js | 62 - lib/middleware/dest.js | 10 - lib/middleware/diff.js | 30 - lib/middleware/engine.js | 10 - lib/middleware/examples.js | 29 - lib/middleware/ext.js | 21 - lib/middleware/layout.js | 13 - lib/middleware/lint-after.js | 32 - lib/middleware/lint.js | 15 - lib/middleware/matter.js | 18 - lib/middleware/multi-toc.js | 84 -- lib/middleware/props.js | 13 - lib/middleware/readme.js | 22 - lib/middleware/src.js | 35 - lib/pkg.json | 51 - lib/plugins/comments.js | 58 - lib/plugins/conflicts.js | 168 --- lib/plugins/dest.js | 48 - lib/plugins/format.js | 85 -- lib/plugins/index.js | 1 - lib/plugins/init.js | 37 - lib/plugins/lint.js | 19 - lib/plugins/reflinks.js | 72 -- lib/plugins/render.js | 44 - lib/stack.js | 96 -- lib/transforms/config/del.js | 26 - lib/transforms/config/get.js | 35 - lib/transforms/config/index.js | 21 - lib/transforms/config/option.js | 38 - lib/transforms/config/set.js | 28 - lib/transforms/config/union.js | 30 - lib/transforms/env/author.js | 20 - lib/transforms/env/cwd.js | 29 - lib/transforms/env/env.js | 31 - lib/transforms/env/files.js | 28 - lib/transforms/env/fork.js | 15 - lib/transforms/env/git.js | 15 - lib/transforms/env/github.js | 22 - lib/transforms/env/ignore.js | 33 - lib/transforms/env/index.js | 22 - lib/transforms/env/keys.js | 12 - lib/transforms/env/missing.js | 20 - lib/transforms/env/paths.js | 9 - lib/transforms/env/pkg.js | 19 - lib/transforms/env/templates.js | 25 - lib/transforms/env/travis.js | 17 - lib/transforms/env/user.js | 10 - lib/transforms/env/username.js | 21 - lib/transforms/init/argv.js | 10 - lib/transforms/init/data.js | 15 - lib/transforms/init/defaults.js | 30 - lib/transforms/init/engines.js | 11 - lib/transforms/init/helpers.js | 11 - lib/transforms/init/index.js | 3 - lib/transforms/init/load.js | 40 - lib/transforms/init/loaders.js | 13 - lib/transforms/init/metadata.js | 21 - lib/transforms/init/middleware.js | 43 - lib/transforms/init/plugins.js | 23 - lib/transforms/init/runner.js | 14 - lib/transforms/init/templates.js | 12 - lib/transforms/modifiers/github_url.js | 13 - lib/transforms/modifiers/index.js | 3 - lib/transforms/modifiers/repository.js | 14 - lib/transforms/modifiers/twitter_url.js | 13 - lib/utils.js | 112 ++ lib/utils/defaultIgnores.js | 17 - lib/utils/index.js | 153 --- package.json | 152 +-- recipes/api-docs.md | 13 - recipes/api-documentation.md | 17 - recipes/generate-a-table-of-contents.md | 25 - recipes/template-data.md | 38 - recipes/using-layouts.md | 21 - recipes/verbfile.md | 15 - recipes/verbmd.md | 94 -- test/app.applyLayout.js | 106 ++ test/app.collection.js | 131 ++ test/app.compile.js | 43 + test/app.copy.js | 12 + test/app.create.js | 173 +++ test/app.data.js | 93 ++ test/app.dest.js | 222 ++++ test/app.doc.js | 20 + test/app.docs.js | 22 + test/app.engines.js | 204 +++ test/app.events.js | 50 + test/app.get-set.js | 71 + test/app.handle.js | 22 + test/app.handlers.js | 136 ++ test/app.include.js | 21 + test/app.includes.js | 23 + test/app.js | 94 ++ test/app.layout.js | 20 + test/app.layouts.js | 22 + test/app.lookups.js | 108 ++ test/app.middleware.js | 59 + test/app.option.js | 103 ++ test/app.render.js | 87 ++ test/app.route.js | 92 ++ test/app.src.js | 296 +++++ test/app.task.js | 149 +++ test/app.use.js | 280 ++++ test/app.view.compile.js | 23 + test/app.view.render.js | 72 ++ test/app.watch.js | 23 + test/collection.events.js | 26 + test/collection.js | 123 ++ test/collection.options.js | 24 + test/collection.use.js | 155 +++ test/dest.js | 908 +++++++++++++ test/fixtures/apidocs-comments.js | 15 - test/fixtures/assets/a.hbs | 8 + test/fixtures/assets/b.hbs | 8 + test/fixtures/assets/c.hbs | 8 + test/fixtures/assets/d.hbs | 7 + test/fixtures/auto-loading/a.js | 4 - test/fixtures/auto-loading/a.md | 1 - test/fixtures/auto-loading/b.js | 4 - test/fixtures/auto-loading/c.js | 4 - test/fixtures/auto-loading/package.json | 94 -- test/fixtures/copy/example.txt | 1 + test/fixtures/data/a.json | 3 + test/fixtures/data/a.yml | 1 - test/fixtures/data/alert.json | 7 + test/fixtures/data/b.json | 3 + test/fixtures/data/c.json | 3 + test/fixtures/data/data.json | 3 + test/fixtures/data/test.json | 4 + test/fixtures/dest-path/a.hbs | 8 + test/fixtures/dest-path/b.hbs | 8 + test/fixtures/dest-path/c.hbs | 8 + test/fixtures/dest-path/d.hbs | 7 + test/fixtures/docs/a.md | 17 - test/fixtures/docs/b.md | 17 - test/fixtures/drafts/a.hbs | 6 + test/fixtures/drafts/b.hbs | 6 + test/fixtures/drafts/c.hbs | 6 + test/fixtures/drafts/d.hbs | 5 + .../front-matter/autodetect-no-lang.md | 5 + test/fixtures/front-matter/autodetect-yaml.md | 5 + test/fixtures/front-matter/lang-yaml.md | 5 + test/fixtures/generic/run.dmc | 1 + test/fixtures/generic/test.dmc | 1 + test/fixtures/helpers/a.js | 3 + test/fixtures/helpers/b.js | 3 + test/fixtures/helpers/c.js | 3 + test/fixtures/helpers/obj.js | 9 + test/fixtures/includes/a.md | 1 - test/fixtures/includes/footer.hbs | 4 + test/fixtures/includes/header.hbs | 4 + test/fixtures/includes/info.hbs | 1 + test/fixtures/includes/post.hbs | 11 + test/fixtures/layouts/base.hbs | 10 + test/fixtures/layouts/default.hbs | 6 + test/fixtures/layouts/post.hbs | 13 + test/fixtures/middleware/copyright.js | 12 - test/fixtures/middleware/multi-toc-options.md | 21 - test/fixtures/middleware/multi-toc.md | 21 - test/fixtures/middleware/toc.md | 21 - test/fixtures/middleware/todo.js | 15 - test/fixtures/package.json | 60 - test/fixtures/pages/a.hbs | 1 + test/fixtures/pages/b.hbs | 1 + test/fixtures/pages/c.hbs | 1 + test/fixtures/parsers/a.a | 1 + test/fixtures/parsers/a.js | 3 + test/fixtures/parsers/b.js | 3 + test/fixtures/parsers/c.js | 3 + test/fixtures/parsers/x.x | 1 + test/fixtures/posts/a.txt | 4 + test/fixtures/posts/b.txt | 4 + test/fixtures/posts/c.txt | 4 + test/fixtures/routes/example.hbs | 1 + test/fixtures/routes/example.txt | 1 + test/fixtures/scaffolds/test.md | 1 + test/fixtures/scaffolds/test.txt | 1 + .../fixtures/templates/README-with-include.md | 3 - test/fixtures/templates/README.md | 1 - test/fixtures/templates/a.tmpl | 1 + test/fixtures/templates/b.tmpl | 1 + test/fixtures/templates/c.tmpl | 1 + test/fixtures/templates/helpers.md | 42 - test/fixtures/test.coffee | 1 + test/fixtures/test/run.jade | 1 + test/fixtures/vinyl/bom-utf16be.txt | Bin 0 -> 244 bytes test/fixtures/vinyl/bom-utf16le.txt | Bin 0 -> 244 bytes test/fixtures/vinyl/bom-utf8.txt | 1 + test/fixtures/vinyl/test-symlink | 1 + test/fixtures/vinyl/test-symlink-dir | 1 + test/fixtures/vinyl/test.coffee | 1 + test/fixtures/vinyl/wow/suchempty | 1 + test/fixtures/watch/test.txt | 1 + test/fixtures/yfm/complex.hbs | 12 + test/fixtures/yfm/yfm.hbs | 5 + test/group.js | 130 ++ test/helpers.builtin.js | 155 --- test/helpers.collections.js | 29 - test/helpers.js | 594 +++++++++ test/helpers.user-defined.js | 32 - test/includes.js | 122 -- test/layouts.js | 112 ++ test/list.js | 499 +++++++ test/mergePartials.js | 118 ++ test/middleware.js | 143 -- test/out-fixtures/fixtures/vinyl/test.coffee | 1 + test/plugin.todos.js | 77 -- test/renameKey.js | 349 +++++ test/render.js | 69 + test/routes.js | 97 ++ test/store.js | 230 ++++ test/support/index.js | 17 + test/support/spy.js | 27 + test/verb.create.js | 147 --- test/verb.cwd.js | 18 - test/verb.data.js | 147 --- test/verb.defaults.js | 39 - test/verb.js | 19 - test/verb.pkg.js | 17 - test/verb.render.js | 182 --- test/verb.variables.js | 37 - test/view.content.js | 28 + test/view.events.js | 27 + test/view.js | 1149 +++++++++++++++++ test/view.methods.js | 39 + test/view.option.js | 28 + test/view.render.js | 26 + test/view.set.js | 33 + test/view.use.js | 59 + test/viewTypes.js | 33 + test/views.js | 340 +++++ verbfile.js | 50 - 300 files changed, 9229 insertions(+), 6817 deletions(-) delete mode 100644 docs/_templates/about.md delete mode 100644 docs/_templates/api-config.md delete mode 100644 docs/_templates/api-data.md delete mode 100644 docs/_templates/api-middleware.md delete mode 100644 docs/_templates/api-task.md delete mode 100644 docs/_templates/api-template.md delete mode 100644 docs/_templates/api.md delete mode 100644 docs/_templates/built-in-helpers.md delete mode 100644 docs/_templates/cli.md delete mode 100644 docs/_templates/collections.md delete mode 100644 docs/_templates/config.md delete mode 100644 docs/_templates/data.md delete mode 100644 docs/_templates/engines.md delete mode 100644 docs/_templates/faq.md delete mode 100644 docs/_templates/features.md delete mode 100644 docs/_templates/getting-started.md delete mode 100644 docs/_templates/helper-conflict-resolution.md delete mode 100644 docs/_templates/helpers.md delete mode 100644 docs/_templates/helpers/apidocs.md delete mode 100644 docs/_templates/includes.md delete mode 100644 docs/_templates/middleware.md delete mode 100644 docs/_templates/options.md delete mode 100644 docs/_templates/overview.md delete mode 100644 docs/_templates/plugins/conflicts.md delete mode 100644 docs/_templates/protected-properties.md delete mode 100644 docs/_templates/template-data.md delete mode 100644 docs/_templates/using-layouts.md delete mode 100644 docs/_templates/variables.md delete mode 100644 docs/_templates/verbfile.md delete mode 100644 docs/_templates/verbmd.md delete mode 100644 docs/sections/_temp.md delete mode 100644 docs/sections/cli-notice.md delete mode 100644 docs/sections/features.md delete mode 100644 docs/sections/install-verb-cli.md delete mode 100644 docs/sections/overview.md delete mode 100644 docs/sections/verb-md.md delete mode 100644 docs/sections/verbfile.md delete mode 100644 docs/sections/why.md create mode 100644 example.js create mode 100644 examples/config.js create mode 100644 gulpfile.js create mode 100644 lib/config.js delete mode 100644 lib/helpers/apidocs.js delete mode 100644 lib/helpers/async.js delete mode 100644 lib/helpers/collections.js delete mode 100644 lib/helpers/custom/example.js delete mode 100644 lib/helpers/custom/github.js delete mode 100644 lib/helpers/custom/index.js delete mode 100644 lib/helpers/custom/linkify.js delete mode 100644 lib/helpers/custom/resolve.js delete mode 100644 lib/helpers/custom/shield.js delete mode 100644 lib/helpers/custom/twitter.js delete mode 100644 lib/helpers/sync.js rename lib/{middleware => }/index.js (100%) delete mode 100644 lib/init.js create mode 100644 lib/local.js delete mode 100644 lib/middleware/append.js delete mode 100644 lib/middleware/assets.js delete mode 100644 lib/middleware/conflict.js delete mode 100644 lib/middleware/copyright.js delete mode 100644 lib/middleware/debug.js delete mode 100644 lib/middleware/dest.js delete mode 100644 lib/middleware/diff.js delete mode 100644 lib/middleware/engine.js delete mode 100644 lib/middleware/examples.js delete mode 100644 lib/middleware/ext.js delete mode 100644 lib/middleware/layout.js delete mode 100644 lib/middleware/lint-after.js delete mode 100644 lib/middleware/lint.js delete mode 100644 lib/middleware/matter.js delete mode 100644 lib/middleware/multi-toc.js delete mode 100644 lib/middleware/props.js delete mode 100644 lib/middleware/readme.js delete mode 100644 lib/middleware/src.js delete mode 100644 lib/pkg.json delete mode 100644 lib/plugins/comments.js delete mode 100644 lib/plugins/conflicts.js delete mode 100644 lib/plugins/dest.js delete mode 100644 lib/plugins/format.js delete mode 100644 lib/plugins/index.js delete mode 100644 lib/plugins/init.js delete mode 100644 lib/plugins/lint.js delete mode 100644 lib/plugins/reflinks.js delete mode 100644 lib/plugins/render.js delete mode 100644 lib/stack.js delete mode 100644 lib/transforms/config/del.js delete mode 100644 lib/transforms/config/get.js delete mode 100644 lib/transforms/config/index.js delete mode 100644 lib/transforms/config/option.js delete mode 100644 lib/transforms/config/set.js delete mode 100644 lib/transforms/config/union.js delete mode 100644 lib/transforms/env/author.js delete mode 100644 lib/transforms/env/cwd.js delete mode 100644 lib/transforms/env/env.js delete mode 100644 lib/transforms/env/files.js delete mode 100644 lib/transforms/env/fork.js delete mode 100644 lib/transforms/env/git.js delete mode 100644 lib/transforms/env/github.js delete mode 100644 lib/transforms/env/ignore.js delete mode 100644 lib/transforms/env/index.js delete mode 100644 lib/transforms/env/keys.js delete mode 100644 lib/transforms/env/missing.js delete mode 100644 lib/transforms/env/paths.js delete mode 100644 lib/transforms/env/pkg.js delete mode 100644 lib/transforms/env/templates.js delete mode 100644 lib/transforms/env/travis.js delete mode 100644 lib/transforms/env/user.js delete mode 100644 lib/transforms/env/username.js delete mode 100644 lib/transforms/init/argv.js delete mode 100644 lib/transforms/init/data.js delete mode 100644 lib/transforms/init/defaults.js delete mode 100644 lib/transforms/init/engines.js delete mode 100644 lib/transforms/init/helpers.js delete mode 100644 lib/transforms/init/index.js delete mode 100644 lib/transforms/init/load.js delete mode 100644 lib/transforms/init/loaders.js delete mode 100644 lib/transforms/init/metadata.js delete mode 100644 lib/transforms/init/middleware.js delete mode 100644 lib/transforms/init/plugins.js delete mode 100644 lib/transforms/init/runner.js delete mode 100644 lib/transforms/init/templates.js delete mode 100644 lib/transforms/modifiers/github_url.js delete mode 100644 lib/transforms/modifiers/index.js delete mode 100644 lib/transforms/modifiers/repository.js delete mode 100644 lib/transforms/modifiers/twitter_url.js create mode 100644 lib/utils.js delete mode 100644 lib/utils/defaultIgnores.js delete mode 100644 lib/utils/index.js delete mode 100644 recipes/api-docs.md delete mode 100644 recipes/api-documentation.md delete mode 100644 recipes/generate-a-table-of-contents.md delete mode 100644 recipes/template-data.md delete mode 100644 recipes/using-layouts.md delete mode 100644 recipes/verbfile.md delete mode 100644 recipes/verbmd.md create mode 100644 test/app.applyLayout.js create mode 100644 test/app.collection.js create mode 100644 test/app.compile.js create mode 100644 test/app.copy.js create mode 100644 test/app.create.js create mode 100644 test/app.data.js create mode 100644 test/app.dest.js create mode 100644 test/app.doc.js create mode 100644 test/app.docs.js create mode 100644 test/app.engines.js create mode 100644 test/app.events.js create mode 100644 test/app.get-set.js create mode 100644 test/app.handle.js create mode 100644 test/app.handlers.js create mode 100644 test/app.include.js create mode 100644 test/app.includes.js create mode 100644 test/app.js create mode 100644 test/app.layout.js create mode 100644 test/app.layouts.js create mode 100644 test/app.lookups.js create mode 100644 test/app.middleware.js create mode 100644 test/app.option.js create mode 100644 test/app.render.js create mode 100644 test/app.route.js create mode 100644 test/app.src.js create mode 100644 test/app.task.js create mode 100644 test/app.use.js create mode 100644 test/app.view.compile.js create mode 100644 test/app.view.render.js create mode 100644 test/app.watch.js create mode 100644 test/collection.events.js create mode 100644 test/collection.js create mode 100644 test/collection.options.js create mode 100644 test/collection.use.js create mode 100644 test/dest.js delete mode 100644 test/fixtures/apidocs-comments.js create mode 100644 test/fixtures/assets/a.hbs create mode 100644 test/fixtures/assets/b.hbs create mode 100644 test/fixtures/assets/c.hbs create mode 100644 test/fixtures/assets/d.hbs delete mode 100644 test/fixtures/auto-loading/a.js delete mode 100644 test/fixtures/auto-loading/a.md delete mode 100644 test/fixtures/auto-loading/b.js delete mode 100644 test/fixtures/auto-loading/c.js delete mode 100644 test/fixtures/auto-loading/package.json create mode 100644 test/fixtures/copy/example.txt create mode 100644 test/fixtures/data/a.json delete mode 100644 test/fixtures/data/a.yml create mode 100644 test/fixtures/data/alert.json create mode 100644 test/fixtures/data/b.json create mode 100644 test/fixtures/data/c.json create mode 100644 test/fixtures/data/data.json create mode 100644 test/fixtures/data/test.json create mode 100644 test/fixtures/dest-path/a.hbs create mode 100644 test/fixtures/dest-path/b.hbs create mode 100644 test/fixtures/dest-path/c.hbs create mode 100644 test/fixtures/dest-path/d.hbs delete mode 100644 test/fixtures/docs/a.md delete mode 100644 test/fixtures/docs/b.md create mode 100644 test/fixtures/drafts/a.hbs create mode 100644 test/fixtures/drafts/b.hbs create mode 100644 test/fixtures/drafts/c.hbs create mode 100644 test/fixtures/drafts/d.hbs create mode 100644 test/fixtures/front-matter/autodetect-no-lang.md create mode 100644 test/fixtures/front-matter/autodetect-yaml.md create mode 100644 test/fixtures/front-matter/lang-yaml.md create mode 100644 test/fixtures/generic/run.dmc create mode 100644 test/fixtures/generic/test.dmc create mode 100644 test/fixtures/helpers/a.js create mode 100644 test/fixtures/helpers/b.js create mode 100644 test/fixtures/helpers/c.js create mode 100644 test/fixtures/helpers/obj.js delete mode 100644 test/fixtures/includes/a.md create mode 100644 test/fixtures/includes/footer.hbs create mode 100644 test/fixtures/includes/header.hbs create mode 100644 test/fixtures/includes/info.hbs create mode 100644 test/fixtures/includes/post.hbs create mode 100644 test/fixtures/layouts/base.hbs create mode 100644 test/fixtures/layouts/default.hbs create mode 100644 test/fixtures/layouts/post.hbs delete mode 100644 test/fixtures/middleware/copyright.js delete mode 100644 test/fixtures/middleware/multi-toc-options.md delete mode 100644 test/fixtures/middleware/multi-toc.md delete mode 100644 test/fixtures/middleware/toc.md delete mode 100644 test/fixtures/middleware/todo.js delete mode 100644 test/fixtures/package.json create mode 100644 test/fixtures/pages/a.hbs create mode 100644 test/fixtures/pages/b.hbs create mode 100644 test/fixtures/pages/c.hbs create mode 100644 test/fixtures/parsers/a.a create mode 100644 test/fixtures/parsers/a.js create mode 100644 test/fixtures/parsers/b.js create mode 100644 test/fixtures/parsers/c.js create mode 100644 test/fixtures/parsers/x.x create mode 100644 test/fixtures/posts/a.txt create mode 100644 test/fixtures/posts/b.txt create mode 100644 test/fixtures/posts/c.txt create mode 100644 test/fixtures/routes/example.hbs create mode 100644 test/fixtures/routes/example.txt create mode 100644 test/fixtures/scaffolds/test.md create mode 100644 test/fixtures/scaffolds/test.txt delete mode 100644 test/fixtures/templates/README-with-include.md delete mode 100644 test/fixtures/templates/README.md create mode 100644 test/fixtures/templates/a.tmpl create mode 100644 test/fixtures/templates/b.tmpl create mode 100644 test/fixtures/templates/c.tmpl delete mode 100644 test/fixtures/templates/helpers.md create mode 100644 test/fixtures/test.coffee create mode 100644 test/fixtures/test/run.jade create mode 100644 test/fixtures/vinyl/bom-utf16be.txt create mode 100644 test/fixtures/vinyl/bom-utf16le.txt create mode 100644 test/fixtures/vinyl/bom-utf8.txt create mode 120000 test/fixtures/vinyl/test-symlink create mode 120000 test/fixtures/vinyl/test-symlink-dir create mode 100644 test/fixtures/vinyl/test.coffee create mode 100644 test/fixtures/vinyl/wow/suchempty create mode 100644 test/fixtures/watch/test.txt create mode 100644 test/fixtures/yfm/complex.hbs create mode 100644 test/fixtures/yfm/yfm.hbs create mode 100644 test/group.js delete mode 100644 test/helpers.builtin.js delete mode 100644 test/helpers.collections.js create mode 100644 test/helpers.js delete mode 100644 test/helpers.user-defined.js delete mode 100644 test/includes.js create mode 100644 test/layouts.js create mode 100644 test/list.js create mode 100644 test/mergePartials.js delete mode 100644 test/middleware.js create mode 100644 test/out-fixtures/fixtures/vinyl/test.coffee delete mode 100644 test/plugin.todos.js create mode 100644 test/renameKey.js create mode 100644 test/render.js create mode 100644 test/routes.js create mode 100644 test/store.js create mode 100644 test/support/index.js create mode 100644 test/support/spy.js delete mode 100644 test/verb.create.js delete mode 100644 test/verb.cwd.js delete mode 100644 test/verb.data.js delete mode 100644 test/verb.defaults.js delete mode 100644 test/verb.js delete mode 100644 test/verb.pkg.js delete mode 100644 test/verb.render.js delete mode 100644 test/verb.variables.js create mode 100644 test/view.content.js create mode 100644 test/view.events.js create mode 100644 test/view.js create mode 100644 test/view.methods.js create mode 100644 test/view.option.js create mode 100644 test/view.render.js create mode 100644 test/view.set.js create mode 100644 test/view.use.js create mode 100644 test/viewTypes.js create mode 100644 test/views.js delete mode 100644 verbfile.js diff --git a/.editorconfig b/.editorconfig index c2c63f78..991900b7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,10 @@ insert_final_newline = true trim_trailing_whitespace = false insert_final_newline = false -[test/**/*] +[test/**] +trim_trailing_whitespace = false +insert_final_newline = false + +[templates/**] trim_trailing_whitespace = false insert_final_newline = false diff --git a/.gitattributes b/.gitattributes index f9f012bc..660957e7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,10 @@ -* text=lf +# Enforce Unix newlines +* text eol=lf + +# binaries +*.ai binary +*.psd binary *.jpg binary *.gif binary *.png binary -*.jpeg binary \ No newline at end of file +*.jpeg binary diff --git a/.gitignore b/.gitignore index 2ae54b69..80a228ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,15 @@ -lib-cov -*.seed -*.log -*.csv -*.dat -*.out -*.pid -*.gz -*.rar +*.DS_Store *.sublime-* +_gh_pages +bower_components +node_modules npm-debug.log -.DS* +actual +test/actual +temp +tmp TODO.md - -# Always-ignore dirs -node_modules/ -tmp/ -temp/ -vendor/ +vendor +.idea +benchmark coverage -support diff --git a/.jshintrc b/.jshintrc index ec81ac56..1d9d5929 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,28 +1,18 @@ { "asi": false, "boss": true, - "camelcase": true, "curly": false, "eqeqeq": true, "eqnull": true, "esnext": true, - "freeze": true, - "funcscope": true, "immed": true, - "indent": 2, "latedef": false, "laxbreak": true, "laxcomma": false, - "maxcomplexity": 7, - "maxdepth": 3, "mocha": true, "newcap": true, "noarg": true, "node": true, - "noempty": true, - "nonbsp": true, - "quotmark": "single", - "strict": true, "sub": true, "undef": true, "unused": true diff --git a/.verb.md b/.verb.md index a43b07ca..4442d3e8 100644 --- a/.verb.md +++ b/.verb.md @@ -1,172 +1,52 @@ -# {%= name %} {%= badge("fury") %} {%= badge("travis") %} +# {%= name %} {%= badge("fury") %} > {%= description %} -**Built by verb** - -The follow projects use verb to build the reamde and other docs: - -- [micromatch](https://github.com/jonschlinkert/micromatch/) (1.7m downloads/mo) - this readme is pretty extensive, with a TOC and other advanced features -- [is-glob](https://www.npmjs.com/package/is-glob) (1.6m downloads/mo) - example of simple readme -- [repeat-string](https://www.npmjs.com/package/is-glob) (2.2m downloads/mo) - example of another basic readme. - -**Quickstart** - -Install `verb` and `verb-cli` globally: - -```sh -$ npm i verb verb-cli -g -``` - -Next, just add a `.verb.md` markdown template to your project and run `verb` in the commandline _(**NOTE** that verb will overwrite the existing `README`, so make sure your work is committed!)_. - -I'm working on a site for verb, but in the meantime a good place to see `.verb.md` examples is to surf [my projects](https://github.com/jonschlinkert). + ## Install -{%= include("install-npm") %} +{%= include("install-npm", {save: true}) %} ## Usage ```js -var verb = require('{%= name %}'); +var assemble = require('{%= name %}'); +var app = assemble(); ``` -## Table of contents - - -## Features -{%= docs("sections/features.md") %} - -## CLI - -_(WIP)_ - -## API - -> Verb's API is organized into the following categories: - -* [Template API](#template-api) -* [Config API](#config-api) -* [Data API](#data-api) -* [Middleware API](#middleware-api) -* [Task API](#task-api) +## assemblefile.js - -### Template API - -_(WIP)_ - -Methods: - -- `.create` -- `.loader` -- `.load` -- `.engine` -- `.helper` -- `.helpers` -- `.asyncHelper` -- `.asyncHelpers` -- `.render` - -Verb exposes entire API from [template]. See the [template docs] the full API. - -### Config API - -**Transforms** - -Run immediately during init. Used to extend or modify the `this` object. +The following example `assemblefile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. ```js -verb.transform('engine', function() { - this.engine('md', require('engine-lodash')); +var assemble = require('assemble'); +var extname = require('gulp-extname'); +var less = require('gulp-less'); +var app = assemble(); + +app.task('html', function() { + app.src('templates/*.hbs') + .pipe(extname('.html')) + .pipe(app.dest('dist/')); }); -``` - -**Application Settings** - -> Set arbitrary values on `verb.cache`: - -- `.set` -- `.get` -- `.del` - -See the [config-cache docs] the full API. - -**Options** - -> Set and get values from `verb.options`: - -- `.option` -- `.enable` -- `.enabled` -- `.disable` -- `.disabled` -- `.disabled` -See the [option-cache docs] the full API. - -_(WIP)_ - -### Data API - -> Set and get values from `verb.cache.data` - -- `.data` - -Verb exposes entire API from [plasma]. See the [plasma docs] the full API. - -_(WIP)_ - -### Middleware API - -Verb exposes the entire [en-route] API. See the [en-route docs] the full API. - -_(WIP)_ - -### Task API - -### [.task](index.js#L114) - -Define a Verb task. - -**Params** - -* `name` **{String}**: Task name -* `fn` **{Function}** - -**Example** - -```js -verb.task('docs', function() { - verb.src(['.verb.md', 'docs/*.md']) - .pipe(verb.dest('./')); +app.task('css', function () { + app.src('styles/*.less') + .pipe(less()) + .pipe(app.dest('dist/assets/css')); }); -``` - -### [.watch](index.js#L180) - -Re-run the specified task(s) when a file changes. -**Params** - -* `glob` **{String|Array}**: Filepaths or glob patterns. -* `fn` **{Function}**: Task(s) to watch. - -**Example** - -```js -verb.task('watch', function() { - verb.watch('docs/*.md', ['docs']); -}); +app.task('default', ['html', 'css']); ``` +## API {%= apidocs("index.js") %} ## Related projects -{%= related(verb.related.list, {remove: name}) %} -## Why use Verb? -{%= docs("sections/why.md") %} +Assemble is built on top of these great projects: + +{%= related(verb.related.list) %} ## Running tests {%= include("tests") %} @@ -174,29 +54,16 @@ verb.task('watch', function() { ## Contributing {%= include("contributing") %} -## Troubleshooting - -1. First things first, please make sure to run `npm cache clear`, then do `npm i verb verb-cli -g`. If that doesn't clear things up, try #2. -2. Create [an issue]({%= bugs.url %}). We'd love to help, so please be sure to provide as much detail as possible, including: - - version of verb and verb-cli - - platform - - any error messages or other information that might be useful. - - -## Major changes -- `v0.4.0`: Verb now requires [verb-cli] to run. See the [getting started](#getting-started) section for details. +If Assemble doesn't do what you need, [please let us know][issue]. ## Author {%= include("author") %} ## License -{%= copyright({start: 2014, linkify: true}) %} -{%= license({linkify: true}) %} +{%= copyright() %} +{%= license() %} *** {%= include("footer") %} - -[verb-cli]: https://github.com/verbose/verb-cli - -{%= reflinks(verb.reflinks.list) %} \ No newline at end of file +{%= reflinks(verb.reflinks) %} diff --git a/LICENSE b/LICENSE index fa30c4cb..65f90aca 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2015, Jon Schlinkert. +Copyright (c) 2015, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 93d4c882..d4ac75d5 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,16 @@ -# verb [![NPM version](https://badge.fury.io/js/verb.svg)](http://badge.fury.io/js/verb) [![Build Status](https://travis-ci.org/verbose/verb.svg)](https://travis-ci.org/verbose/verb) +# verb2 [![NPM version](https://badge.fury.io/js/verb2.svg)](http://badge.fury.io/js/verb2) > Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes. -**Built by verb** - -The follow projects use verb to build the reamde and other docs: - -* [micromatch](https://github.com/jonschlinkert/micromatch/) (1.7m downloads/mo) - this readme is pretty extensive, with a TOC and other advanced features -* [is-glob](https://www.npmjs.com/package/is-glob) (1.6m downloads/mo) - example of simple readme -* [repeat-string](https://www.npmjs.com/package/is-glob) (2.2m downloads/mo) - example of another basic readme. - -**Quickstart** - -Install `verb` and `verb-cli` globally: - -```sh -$ npm i verb verb-cli -g -``` - -Next, just add a `.verb.md` markdown template to your project and run `verb` in the commandline _(**NOTE** that verb will overwrite the existing `README`, so make sure your work is committed!)_. - -I'm working on a site for verb, but in the meantime a good place to see `.verb.md` examples is to surf [my projects](https://github.com/jonschlinkert). - -## Install - -Install with [npm](https://www.npmjs.com/) - -```sh -$ npm i verb --save-dev -``` - -## Usage - -```js -var verb = require('verb'); -``` - -## Table of contents - -* [Features](#features) -* [CLI](#cli) +* [Install](#install) +* [Usage](#usage) +* [assemblefile.js](#assemblefilejs) * [API](#api) - - [Template API](#template-api) - - [Config API](#config-api) - - [Data API](#data-api) - - [Middleware API](#middleware-api) - - [Task API](#task-api) - - [](#-task--indexjs-l114-)[.task](index.js#L114) - - [](#-watch--indexjs-l180-)[.watch](index.js#L180) - * [Related projects](#related-projects) -* [Why use Verb?](#why-use-verb-) * [Running tests](#running-tests) * [Contributing](#contributing) -* [Troubleshooting](#troubleshooting) -* [Major changes](#major-changes) * [Author](#author) * [License](#license) @@ -64,214 +18,216 @@ _(Table of contents generated by [verb])_ -## Features +## Install -* Generate markdown docs, or HTML -* Generate a Table of Contents simply by adding `` to any document. -* Include templates from locally installed npm packages with the `{%= include() %}` helper -* Include templates from your project's `docs/` directory with the `{%= docs() %}` helper -* Change the templates directory for either helper by passing a `cwd` to the helper: example: `{%= docs("foo", {cwd: ''}) %}` +Install with [npm](https://www.npmjs.com/) -## CLI +```sh +$ npm i verb2 --save +``` -_(WIP)_ +## Usage -## API +```js +var assemble = require('verb2'); +var app = assemble(); +``` -> Verb's API is organized into the following categories: +## assemblefile.js -* [Template API](#template-api) -* [Config API](#config-api) -* [Data API](#data-api) -* [Middleware API](#middleware-api) -* [Task API](#task-api) +The following example `assemblefile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. -### Template API +```js +var assemble = require('assemble'); +var extname = require('gulp-extname'); +var less = require('gulp-less'); +var app = assemble(); + +app.task('html', function() { + app.src('templates/*.hbs') + .pipe(extname('.html')) + .pipe(app.dest('dist/')); +}); -_(WIP)_ +app.task('css', function () { + app.src('styles/*.less') + .pipe(less()) + .pipe(app.dest('dist/assets/css')); +}); -Methods: +app.task('default', ['html', 'css']); +``` -* `.create` -* `.loader` -* `.load` -* `.engine` -* `.helper` -* `.helpers` -* `.asyncHelper` -* `.asyncHelpers` -* `.render` +## API -Verb exposes entire API from [template](https://github.com/jonschlinkert/template). See the [template docs] the full API. +### [Verb](index.js#L27) -### Config API +Create an `verb` application. This is the main function exported by the verb module. -**Transforms** +**Params** -Run immediately during init. Used to extend or modify the `this` object. +* `options` **{Object}**: Optionally pass default options to use. + +**Example** ```js -verb.transform('engine', function() { - this.engine('md', require('engine-lodash')); -}); +var verb = require('verb'); +var app = verb(); ``` -**Application Settings** +### [.src](index.js#L158) -> Set arbitrary values on `verb.cache`: +Glob patterns or filepaths to source files. -* `.set` -* `.get` -* `.del` +**Params** -See the [config-cache docs] the full API. +* `glob` **{String|Array}**: Glob patterns or file paths to source files. +* `options` **{Object}**: Options or locals to merge into the context and/or pass to `src` plugins -**Options** +**Example** -> Set and get values from `verb.options`: +```js +app.src('src/*.hbs', {layout: 'default'}); +``` -* `.option` -* `.enable` -* `.enabled` -* `.disable` -* `.disabled` -* `.disabled` +### [.symlink](index.js#L173) -See the [option-cache docs] the full API. +Glob patterns or paths for symlinks. -_(WIP)_ +**Params** -### Data API +* `glob` **{String|Array}** -> Set and get values from `verb.cache.data` +**Example** -* `.data` +```js +app.symlink('src/**'); +``` -Verb exposes entire API from [plasma](https://github.com/jonschlinkert/plasma). See the [plasma docs] the full API. +### [.dest](index.js#L189) -_(WIP)_ +Specify a destination for processed files. -### Middleware API +**Params** -Verb exposes the entire [en-route] API. See the [en-route docs] the full API. +* `dest` **{String|Function}**: File path or rename function. +* `options` **{Object}**: Options and locals to pass to `dest` plugins -_(WIP)_ +**Example** -### Task API +```js +app.dest('dist/'); +``` -### [.task](index.js#L114) +### [.copy](index.js#L210) -Define a Verb task. +Copy files with the given glob `patterns` to the specified `dest`. **Params** -* `name` **{String}**: Task name -* `fn` **{Function}** +* `patterns` **{String|Array}**: Glob patterns of files to copy. +* `dest` **{String|Function}**: Desination directory. +* `returns` **{Stream}**: Stream, to continue processing if necessary. **Example** ```js -verb.task('docs', function() { - verb.src(['.verb.md', 'docs/*.md']) - .pipe(verb.dest('./')); +app.task('assets', function() { + app.copy('assets/**', 'dist/'); }); ``` -### [.watch](index.js#L180) +### [.toStream](index.js#L230) -Re-run the specified task(s) when a file changes. +Push a view collection into a vinyl stream. **Params** -* `glob` **{String|Array}**: Filepaths or glob patterns. -* `fn` **{Function}**: Task(s) to watch. +* `collection` **{String}**: The name of the view collection to push into the stream. +* **{Function}**: Optionally pass a filter function to use for filtering views. +* `returns` **{Stream}** **Example** ```js -verb.task('watch', function() { - verb.watch('docs/*.md', ['docs']); -}); +app.toStream('posts', function(file) { + return file.path !== 'index.hbs'; +}) ``` -### [.src](index.js#L60) +### [.renderFile](index.js#L258) -Glob patterns or filepaths to source files. +Render a vinyl file. **Params** -* `glob` **{String|Array}**: Glob patterns or file paths to source files. -* `options` **{Object}**: Options or locals to merge into the context and/or pass to `src` plugins +* `locals` **{Object}**: Optionally locals to pass to the template engine for rendering. +* `returns` **{Object}** **Example** ```js -verb.src('src/*.hbs', {layout: 'default'}) +app.src('*.hbs') + .pipe(app.renderFile()); ``` -### [.dest](index.js#L76) +### [.task](index.js#L294) -Specify a destination for processed files. +Define a task to be run when the task is called. **Params** -* `dest` **{String|Function}**: File path or rename function. -* `options` **{Object}**: Options and locals to pass to `dest` plugins +* `name` **{String}**: Task name +* `fn` **{Function}**: function that is called when the task is run. **Example** ```js -verb.dest('dist') +app.task('default', function() { + app.src('templates/*.hbs') + .pipe(app.dest('dist/')); +}); ``` -### [.copy](index.js#L95) +### [.run](index.js#L313) -Copy a `glob` of files to the specified `dest`. +Run one or more tasks. **Params** -* `glob` **{String|Array}** -* `dest` **{String|Function}** -* `returns` **{Stream}**: Stream, to continue processing if necessary. +* `tasks` **{Array|String}**: Task name or array of task names. +* `cb` **{Function}**: callback function that exposes `err` **Example** ```js -verb.task('assets', function() { - verb.copy('assets/**', 'dist'); +app.run(['foo', 'bar'], function(err) { + if (err) console.error('ERROR:', err); }); ``` -### [.diff](index.js#L116) - -Display a visual representation of the difference between two objects or strings. - **Params** -* `a` **{Object|String}** -* `b` **{Object|String}** -* `methodName` **{String}**: Optionally pass a [jsdiff](https://github.com/nathan7/jsdiff)method name to use. The default is `diffJson` +* `glob` **{String|Array}**: Filepaths or glob patterns. +* `tasks` **{Array}**: Task(s) to watch. **Example** ```js -var doc = verb.views.docs['foo.md']; -verb.render(doc, function(err, content) { - verb.diff(doc.orig, content); +app.task('watch', function() { + app.watch('docs/*.md', ['docs']); }); ``` ## Related projects +Assemble is built on top of these great projects: + * [assemble](https://www.npmjs.com/package/assemble): Static site generator for Grunt.js, Yeoman and Node.js. Used by Zurb Foundation, Zurb Ink, H5BP/Effeckt,… [more](https://www.npmjs.com/package/assemble) | [homepage](http://assemble.io) -* [composer](https://www.npmjs.com/package/composer): The build system used to create verb, assemble and generate. | [homepage](https://github.com/jonschlinkert/composer) +* [composer](https://www.npmjs.com/package/composer): API-first task runner with three methods: task, run and watch. | [homepage](https://github.com/jonschlinkert/composer) * [engine](https://www.npmjs.com/package/engine): Template engine based on Lo-Dash template, but adds features like the ability to register helpers… [more](https://www.npmjs.com/package/engine) | [homepage](https://github.com/jonschlinkert/engine) * [template](https://www.npmjs.com/package/template): Render templates using any engine. Supports, layouts, pages, partials and custom template types. Use template… [more](https://www.npmjs.com/package/template) | [homepage](https://github.com/jonschlinkert/template) -## Why use Verb? - -It's magical and smells like chocolate. If that's not enough for you, it's also the most powerful and easy-to-use documentation generator for node.js. And it's magical. - ## Running tests Install dev dependencies: @@ -284,18 +240,7 @@ $ npm i -d && npm test Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/verbose/verb/issues/new). -## Troubleshooting - -1. First things first, please make sure to run `npm cache clear`, then do `npm i verb verb-cli -g`. If that doesn't clear things up, try #2. -2. Create [an issue](https://github.com/verbose/verb/issues). We'd love to help, so please be sure to provide as much detail as possible, including: - -* version of verb and verb-cli -* platform -* any error messages or other information that might be useful. - -## Major changes - -* `v0.4.0`: Verb now requires [verb-cli](https://github.com/verbose/verb-cli)to run. See the [getting started](#getting-started) section for details. +If Assemble doesn't do what you need, [please let us know][issue]. ## Author @@ -306,9 +251,15 @@ Pull requests and stars are always welcome. For bugs and feature requests, [plea ## License -Copyright © 2014-2015 [Jon Schlinkert](https://github.com/jonschlinkert) +Copyright © 2015 Jon Schlinkert Released under the MIT license. *** -_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on August 21, 2015._ +_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 20, 2015._ + +[config-cache]: https://github.com/jonschlinkert/config-cache +[jsdiff]: https://github.com/nathan7/jsdiff +[option-cache]: https://github.com/jonschlinkert/option-cache +[plasma]: https://github.com/jonschlinkert/plasma +[template]: https://github.com/jonschlinkert/template \ No newline at end of file diff --git a/docs/_templates/about.md b/docs/_templates/about.md deleted file mode 100644 index ab4fa91f..00000000 --- a/docs/_templates/about.md +++ /dev/null @@ -1,4 +0,0 @@ -# About Verb - -Many documentation generators require strict conventions to work. Verb is the opposite. - diff --git a/docs/_templates/api-config.md b/docs/_templates/api-config.md deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/_templates/api-data.md b/docs/_templates/api-data.md deleted file mode 100644 index f77f9df6..00000000 --- a/docs/_templates/api-data.md +++ /dev/null @@ -1,89 +0,0 @@ -# Data - -> Define load, transform and process data to be passed to templates as context at render time - -This document describes how Verb works with data, and the methods to used for setting and getting data. - -## Table of contents - - - -## Overview - -- Data that will be passed to templates is stored on `verb.cache.data`. - -**verb.data and package.json** - -- At runtime, Verb loads your project's package.json, so the data can be used in templates. -- Data from package.json is stored on `verb.cache.data`. -- Get the `data` object directly using `verb.cache.data`, or with `verb.get('data')`. -- Get values from this object using `verb.cache.data[foo]` or `verb.get('data.foo')`. - -**verb.env** - -- A read-only clone of the data from package.json is stored on `verb._env`. -- You may get values from this object using `verb.env('foo')` - -## API - -### .data - -```js -// pass an object -verb.data({foo: 'bar'}); - -// pass a glob -verb.data('foo/*.json'); -verb.data('foo/*.yml'); -verb.data('foo/*.{json,yml}'); -``` - -### .set / .get - -Set and get arbitrary values on `verb.cache`. - -### .set - -Store a value: - -```js -verb.set('a', 'b'); -verb.get('a'); -//=> 'b' -``` - -Use object paths (dot notation) to set nested values: - -```js -verb.set('a.b.c', 'd'); -verb.get('a'); -//=> {b: {c: 'd'}} -``` - -**Good to know** - -Although the `.set()` and `.get()` methods store values on `verb.cache`, Verb only uses child objects on `verb.cache`, like `verb.cache.data`. You may use this object however you want. - -### .get - -Sugar for `verb.cache[foo]` - - - -Dot notation may also be used for getting values. - -**Dot notation** - -For example, instead of doing `verb.cache.data.repository.url` - -```js -// get the project name -verb.get('data.name'); - -// or to get the unmodified name from `_env` -verb.env('name'); -``` - -## package.json - -Along with other sources of data, Verb uses data from package.json to render templates. diff --git a/docs/_templates/api-middleware.md b/docs/_templates/api-middleware.md deleted file mode 100644 index fdc1600e..00000000 --- a/docs/_templates/api-middleware.md +++ /dev/null @@ -1,66 +0,0 @@ -# Middleware - -> Methods for defining and using middleware and routes - -**What is "middleware"?** - -A middleware is a function that is invoked by a middleware handler. - -**What is a "handler"?** - -Middleware handlers are invoked at pre-determined points during runtime, each of which is typically associated with a specific middleware method known as a "verb" that will only be invoked by that handler. - -For example, the `.onLoad()` method is invoked by the `onLoad` handler when templates are loaded, and the `.preRender()` method is invoked by the `preRender` handler before templates are passed to the engine for rendering. - -**What does the handler do?** - -When a handler is invoked - -**Middleware stack** - -A middleware stack is an array of middlewares. - -- Each file that passes through the [pipeline](./api-task.md#pipeline.md) has its own middleware stack, which may have zero or more middleware functions -- Middleware functions are always invoked in the order in which they are defined. - - -**Routes** - -Given that "verbs" determine **when** middleware - - -**Verbs** - -> verbs determine **when to run** - -- Special middleware methods known as "verbs" determine **when** middleware functions are run -- Each verb is invoked by a middleware _handler_ that is configured to run that specific verb, at a specic point during runtime. For example, the `onLoad` handler invokes all `.onLoad()` middleware when a template is loaded. - -**Routes** - -> routes determine **which files to operate on** - -- Routes are used to selectively match **which** files to operate on. - - certain triggers - -Middleware is any number of functions that are invoked by the Express.js routing layer before your final request handler is, and thus sits in the middle between a raw request and the final intended route. We often refer to these functions as the middleware stack since they are always invoked in the order they are added. - - -## API - -### .use - -### .route - -### .all - -### .onLoad - -### .preRender` - -### .postRender` - -*** - -## Related topics \ No newline at end of file diff --git a/docs/_templates/api-task.md b/docs/_templates/api-task.md deleted file mode 100644 index fbb588fb..00000000 --- a/docs/_templates/api-task.md +++ /dev/null @@ -1,4 +0,0 @@ - - - -## Pipeline \ No newline at end of file diff --git a/docs/_templates/api-template.md b/docs/_templates/api-template.md deleted file mode 100644 index d981fa77..00000000 --- a/docs/_templates/api-template.md +++ /dev/null @@ -1,36 +0,0 @@ -# Templates - -> Define, load, and render templates - -## Table of contents - - - -## API - -### .create - -**Examples** - -- `.page` -- `.partial` -- `.layout` - - -### .loader - - -### .load - - -### .engine - - -### .helper - - -### .render - - -## Related topics - diff --git a/docs/_templates/api.md b/docs/_templates/api.md deleted file mode 100644 index 807a0e7a..00000000 --- a/docs/_templates/api.md +++ /dev/null @@ -1,151 +0,0 @@ -# Verb API - -> Learn about the various components of Verb's API and how everything fits together. - -## Concepts - -Verb's API is a superset of the following concepts: - -- [Template API](#template-api) -- [Config API](#options-api) -- [Data API](#data-api) -- [Middleware API](#middleware-api) -- [Workflow (Task) API](#workflow-api) - - -## Developer summary - -- **Template API**: methods for loading, caching and rendering, like `.loader()`, `.load()`, `.page()`, `.partial()`, `.layout()`, `.render()`, `.engine()`, `.helper()` etc. -- **Config API**: methods for setting options, `.option()`, `.enable()`, `.disable()` -- **Data API**: methods for loading, and caching data to be passed to templates, like `.data()`, `.transform()` -- **Middleware API**: methods related to routes and middleware, like `.use()`, `.preRender()` etc -- **Workflow (Task) API**: methods for reading and writing to disk, and watching files: `.src()`, `.dest()`, `.task()`, `.watch()`. Without doing math, I'm guessing this is less than 10% of the API - -## Config API - -> Get and set globally available options and config values - -Verb's Config API exposes methods for setting and getting configuration values that can be used anywhere in your verb application. - -**Summary** - -- Options are stored on the `verb.options` object. - - -**Options Methods** - -Options are stored on the `verb.options` object. - -- `.option` -- `.enable` and `.enabled` -- `.disable` and `.disabled` - -**Cache Methods** - -Arbitrary values may be stored on the `verb.cache` object, as long as they do not overwrite Verb's [protected properties][]. - -- `.get` -- `.set` - -The `.get()` and `.set()` methods are used for getting and setting values on the `verb.cache` object. This object is "reserved" for you to store whatever values you need. - -**Usage** - -```js -verb.set('drink', 'Tea, Earl Grey, hot.'); - -var drink = verb.get('drink'); -//=> 'Tea, Earl Grey, hot.' -``` - -## Template API - -> Define, load and render templates - -- `.create` -- `.page` -- `.partial` -- `.layout` -- `.engine` -- `.helper` -- `.render` - - -## Data API - -> Define load, transform and process data to be passed as context to templates at runtime - -- `.data` -- `.transform` - - -## Middleware API - -- `.use` -- `.route` -- `.all` -- `.onLoad` -- `.preRender` -- `.postRender` - - -## Libraries used - -> TODO - -- **Workflow API** - - + vinyl, vinyl-fs: file format - + orchestrator: `.task`, `.src`, `.dest` methods - -- **Config API** - - + options-cache - -- **Data API** - - + plasma - -- **Template API** - - + template - + helper-cache - + engine-cache - + loader-cache - -- **Middleware API** - - + en-route (route) - - -## Workflow API - -> Read, copy, process and write files - -The Workflow API handles "the moving parts" of the build process and consists the following methods: - -- `.task` -- `.src` -- `.dest` -- `.watch` - -**Example** - -```js -verb.task('default', function() { - verb.src('templates/*.hbs') - .pipe(verb.dest('templates/*.hbs')) -}); -``` - -**Learn more** - -- [defining-tasks](./defining-tasks.md) -- [sessions](./task-sessions.md) (TODO: @doowb) - - -**Related** - -- [plugins](./authoring-plugins.md) -- [protected properties](./protected-properties.md) - diff --git a/docs/_templates/built-in-helpers.md b/docs/_templates/built-in-helpers.md deleted file mode 100644 index 39e079c9..00000000 --- a/docs/_templates/built-in-helpers.md +++ /dev/null @@ -1,94 +0,0 @@ -# Built-in helpers - -> Verb ships with more than 100 of built-in template helpers, geared towards making it as easy as possible to build your documentation. - -## Helper collections - -- [console](#console) -- [template-helpers](#template-helpers) -- [markdown-utils](#markdown-utils) -- [readme helpers](#readme-helpers) - -### [console] - -Node.js's [console methods][console] are exposed as helpers. - -**Helper example** - -```js -{%%= console.log("foo") %} -``` - -### [template-helpers] - -All helpers from the [template-helpers] library are available to be used in your templates. - -**Helper example** - -```js -{$%= embed("example.js") %} -``` - -Note that the path helpers are exposed on the `path` object to avoid potential conflicts with other commonly-named path helpers, such as `filename`, etc. - -**Example path helper** - -```js -{%%= path.basename(dest.path) %} -``` - -### [markdown-utils] - -All methods from [markdown-utils] expose as helpers on the `mdu` object. - -**Example markdown helper** - -```js -{%%= mdu.link("verb", "https://github.com/verbose/verb", "awesome") %} -//=> [verb](https://github.com/verbose/verb "awesome") -``` - -### readme helpers - -Although these are referred to as readme helpers, they're useful in any kind of documentation. - -- `apidocs`: generate API documentation from opinionated JavaScript code comments. -- `changelog`: generate a markdown formatted changelog -- `codelinks`: _(TODO)_ -- `copyright`: generate a copyright statement from project metadata -- `coverage`: inject the code coverage summary or report generated by [istanbul] `.txt` -- `date`: add formatted dates -- `license`: generate a license statement from project metadata -- `multiToc`: generate a multi-document table of contents - - -**Example Table of Contents helper** - -_(Just use `` to generate a TOC for the current file only)_ - -```js -{%= multiToc("docs/*.md") %} -``` - -Renders something like: - -```markdown -* [one.md](./one.md) -* [two.md](./two.md) -* [three.md](./one.md) -``` - -### generic helpers - -- `read` -- `relative` -- `yaml` - - - -[console]: https://nodejs.org/api/console.html -[template-helpers]: https://github.com/jonschlinkert/template-helpers -[markdown-utils]: https://github.com/jonschlinkert/markdown-utils - - -{%= reflinks(['istanbul']) %} diff --git a/docs/_templates/cli.md b/docs/_templates/cli.md deleted file mode 100644 index 3f264de2..00000000 --- a/docs/_templates/cli.md +++ /dev/null @@ -1,96 +0,0 @@ -# CLI - -> Verb's commands and command line usage - -## Usage - -All commands are preceded by `--`: - -```bash -$ verb --foo -``` - -## Commands - -- `--nocheck`: don't lint or correct template helpers or variables. See [conflicts]. - -### Data store - -Verb offers a few methods for persisting and getting "default" config data from the command line. Any values set from the command line are saved in a JSON file in - -- when a property is set from the command line it will persist - -**Data store commands** - -- [.set](#-set): set a property. -- [.get](#-get): show a property in the command line -- [.del](#-del): delete a property - -`verb.store.data` `verb.cache.data`[1] from the command line. which is the object Verb uses for storing data to be passed as context to templates at render time. - -#### .set - -Set or extend a property on `verb.cache.data`. - -```bash -$ verb --set foo=bar -``` - -**Dot notation** - -Dot-notation may also be used to set values. - -Example: - -```bash -$ verb --set author.name="Jon Schlinkert" -``` - -#### .get - -Show a property from `verb.cache.data` in the command line. - -```bash -$ verb --get foo -``` - -Outputs: - -```bash -$ foo = bar -``` - -**Dot notation** - -Dot-notation may also be used to get values. - -Example: - -```bash -$ verb --get author.name -``` - -Outputs: - -```bash -$ author.name = "Jon Schlinkert" -``` - -#### .del - -Delete a property from `verb.cache.data` - -```bash -$ verb --del foo && verb --get foo -``` -Outputs: - -```bash -$ foo = undefined -``` - -### Other commands - -```bash -$ verb --foo -``` diff --git a/docs/_templates/collections.md b/docs/_templates/collections.md deleted file mode 100644 index 7d09da65..00000000 --- a/docs/_templates/collections.md +++ /dev/null @@ -1,15 +0,0 @@ - -## Notes - -### Collections - - middleware to generate collections from page front-matter - - helpers to use collection data in pages - -### Pagination - - this goes along with collections but should act independently - - plugin to generate new pages to add to the stream based on pagination options - - helpers to use pagination in pages - -### Relative Linking - - helpers to generate relative links between normal pages, collections pages (lists), and pagination pages (navigation). - diff --git a/docs/_templates/config.md b/docs/_templates/config.md deleted file mode 100644 index 6ad50ba0..00000000 --- a/docs/_templates/config.md +++ /dev/null @@ -1,45 +0,0 @@ -# Data - -> Setting and getting data to be used in templates - -## Table of contents - - - -Verb uses data from package.json to render templates, but there are several other ways to add data. This document describes how Verb works with data, and the methods to used for setting and getting data. - -## Overview - -- Options are stored on `verb.options`. - - -## package.json - -> Verb uses data from package.json to render templates, and more... - -Verb passes the entire package.json object to the template engine to be used as context for templates. But sometimes this isn't enough. For example, you might have a template that requires a Twitter or GitHub username. - -### verb object - -If your package.json file has a `verb` property, Verb will use it to extend the context. - -**Example** - -```json -{ - "name": "my-project", - "description": "It's awesome, seriously.", - - "verb": { - "ignore": [ - ".git" - ], - "deps": { - "ignore": [ - "support" - ] - }, - "data": {} - } -} -``` \ No newline at end of file diff --git a/docs/_templates/data.md b/docs/_templates/data.md deleted file mode 100644 index 8aea0a3f..00000000 --- a/docs/_templates/data.md +++ /dev/null @@ -1,37 +0,0 @@ -# Data - -> Setting and getting data to be used in templates - -## Table of contents - - - -Verb uses data from package.json to render templates, but there are several other ways to add data. This document describes how Verb works with data, and the methods to used for setting and getting data. - -## package.json - -> Verb uses data from package.json to render templates, and more... - -Verb passes the entire package.json object to the template engine to be used as context for templates. But sometimes this isn't enough. For example, you might have a template that requires a Twitter or GitHub username. - -### verb object - -If your package.json file has a `verb` object with a `data` property, Verb will use it to extend the context. - -**Example** - -This example shows how you would add your Twitter username to package.json so that it will be used in templates: - -```json -{ - "name": "my-project", - "description": "It's awesome, seriously.", - - "verb": { - "data": { - "twitter": {"username": "jonschlinkert"} - } - } -} -``` -Also see how to set [configuration values](./config.md) in package.json. \ No newline at end of file diff --git a/docs/_templates/engines.md b/docs/_templates/engines.md deleted file mode 100644 index fb3acd8d..00000000 --- a/docs/_templates/engines.md +++ /dev/null @@ -1,91 +0,0 @@ -# Engines - -> Registering and using template engines with Verb. - -Verb's default template engine is [engine-lodash][], but Verb can use any template engine to render templates. - -## Table of contents - - - -## Overview - -- engines are registered with `verb.engine()` -- when registered, engines are stored as objects on `verb.engines`. -- async engines must follow [consolidate.js][consolidate] conventions, and sync engines must follow [engines.js][engines] conventions. - - -## Register an engine - -Engines are registered using `verb.register()`. - -**Example** - -```js -verb.register('foo', {}, function() {}); -``` - -`.register` takes the following arguments: - -- `ext` **{String}**: once registered, the engine will process any files with this extension. -- `options` **{Object}**: optionally pass an object of options -- `engine` **{Function|Object}**: the engine to be registered (more on [engine format below](#author-an-engine)) - -### Usage examples - -Render handlebars templates in files with the `.hbs` extension: - -**Install** - -```bash -$ npm i consolidate handlebars --save-dev -``` - -**Usage** - -```js -// make sure you have handlebars installed in node_modules too! -var consolidate = require('consolidate'); -verb.engine('.hbs', consolidate.handlebars); -``` - -Render Lo-Dash templates in files with the `.tmpl` extension: - -**Install** - -```bash -$ npm i engine-lodash --save-dev -``` - -**Usage** - -```js -verb.engine('.tmpl', require('engine-lodash')); -``` - -## Author an engine - -> Engines are just javascript functions that transform a string - -Engines can be a function, or an object with `.render()` and/or `.renderSync()` methods. - -**Example** - -The most basic engine is a function that takes the following paramters: - -- `str`: the string to process -- `options` which will be passed to the engine as context/locals -- `cb`: callback function - -```js -var coffee = require('coffee-script'); - -verb.engine('coffee', {ext: '.js'}, function(str, opts, cb) { - try { - cb(null, coffee.compile(str, opts)); - } catch(err) { - return cb(err); - } -}); -``` - diff --git a/docs/_templates/faq.md b/docs/_templates/faq.md deleted file mode 100644 index 43ef856d..00000000 --- a/docs/_templates/faq.md +++ /dev/null @@ -1,3 +0,0 @@ -# FAQ - -> Frequently asked questions diff --git a/docs/_templates/features.md b/docs/_templates/features.md deleted file mode 100644 index 5141e215..00000000 --- a/docs/_templates/features.md +++ /dev/null @@ -1,7 +0,0 @@ - -- Generate API docs from code comments (this couldn't be easier!) -- Add a [.verb.md](#verbmd) markdown template to your project, and verb will build your readme using data from package.json. -- If you need more, create a [verbfile.js](#verbfile.js)! Verb can complex documentation too, including multi-page TOCs, cross-reference links, auto-generated links to dependencies and so on. -- Verb can run any [gulp](https://github.com/gulpjs/gulp) plugin -- Verb is built on top of [Template](https://github.com/jonschlinkert/template). All of Template's methods are exposed on the API. -- You can get by with a simple `.verb.md` markdown template, or do things like add layouts, pages, partials, helpers, register a template engine, load data or use a `.transform()` or two to modify that data at runtime. diff --git a/docs/_templates/getting-started.md b/docs/_templates/getting-started.md deleted file mode 100644 index 001a4f72..00000000 --- a/docs/_templates/getting-started.md +++ /dev/null @@ -1,32 +0,0 @@ -# Getting started - -> First steps to start using verb - -## First run - -The first time you run verb you will be asked to provide two or three pieces of data so that verb has the necessary data to render your project's templates. - -**Questions you'll be asked** - -- `What's your name?` -- `What's your GitHub username?` -- `What's your Twitter username?` - -You can skip any of these, but Verb may not be able to render certain templates without this information. - -## verb init - -Repeat the [first run](#first-run) questions: - -```sh -verb --init -``` - -## Recommended variables - -Although verb only asks for three values, it's recommended add a few other commonly used variables as well. - -_(TODO)_ - -See the documentation for [cli commands](./cli.md), including [verb `--set`]('./cli.md#set') if you aren't sure how to set variables. - diff --git a/docs/_templates/helper-conflict-resolution.md b/docs/_templates/helper-conflict-resolution.md deleted file mode 100644 index 810902c3..00000000 --- a/docs/_templates/helper-conflict-resolution.md +++ /dev/null @@ -1,133 +0,0 @@ -# Helper conflict resolution - -> This describes how helper conflict resolution works in Verb, as well as **why it works this way** - -_(If you have any suggestions for doing this differently, please let us know!)_ - -**What's a "helper conflict"?** - -Helper conflicts occurr when all of the following conditions are met: - -1. A helper function is registered with the same name as an arbitrary property on the context, and -2. The template engine being used does not have first class support for helper functions -3. The user opts to _not_ namespace the data on some arbitrary property - -_(Note that if namespacing is used consistently across all projects, like using `foo.name` instead of just `name`, then these issues go away. However, [namespacing presents its own set challenges][1].)_ - -**What is "first class" helper support?** - -First, some background. Verb must pass templates, partials, data and helper functions to engines in a way that the engine understands. Some engines, like Handlebars, support all of these concepts as separate, first-class entities. Which makes life easy and provides (non-hacky) options for keeping data, helpers and templates cleanly separated. It also dramatically reduces the logic required to handle each of these concepts. We'll call these engines Type 1 engines. Other engines, like Lo-Dash, only understand templates and data. We'll call these Type 2 engines. Neither is better or worse. - -An advantage of Type 2 engines is that they are usually faster and, given that they have no real conventions for helpers, helpers are just plain javascript, which makes them (marginally) easier to write. - -Verb's default engine is [engine-lodash], a Type 2 engine. Given that Type 2 engines have no first class support for helpers, helpers are treated the same as any other data and are passed as arbitrary properties on the context. If a property on the context happens to have a function value, the only special treatment it gets is that the engine simply calls the function (helper) in the given context. Still, it's just another property on the context. - -The same applies to partials. Partials are also passed as arbitrary properties on the context. - -**The challenge** - -What makes this a sticky situation is that we need to adhere to the rules of JavaScript: Object keys must be unique. - -**Example** - -By way of example, let's see what happens when the following occurs: - -```js -// `context` and `helpers` are passed to an engine -// on the engine's `options` object - -var options = {context: {foo: 'bar'}, helpers: {foo: [function]}} -``` - -At render-time, these objects are passed to the engine separately, then, since the engine needs all of these values on one object (as context), the (Type 2) engine merges them together just before rendering a template. (This is something an implementor would need to do when implementing an engine. Feel free to follow the pattern used by [engine-lodash]). - -**Who should win: data or helper?** - -Continuing with the above code example, what should we end up with when we merge the `helpers` and `context` objects? - -```js -var ctx = _.extend({}, options.helpers, options.context); -//=> ctx.foo === 'bar' ??? -``` -Or: - -```js -var ctx = _.extend({}, options.context, options.helpers); -//=> ctx.foo === [function] ??? -``` - -And which of the following should the template engine give priority to? - -```js -// non-function value -{%%= foo %} - -// helper function -{%%= foo("quux") %} -``` - -You might be wondering: - -**But, how often does this happen?** - -The short answer is: a lot. The long answer is: a looooooooot. - -**Isn't this just an edge case?** - -No, it happens a lot. In other words, it happens more often than not a lot. - -_(Just seeing if you're paying attention :)_ - -## Solution: namespacing on-the-fly - -Hack or not, of all the solutions we've tried we find this to be the easiest to maintain. - -**Conflict resolution middleware** - -Verb's [conflict-resolution middleware][] does the following: - -1. Gets keys of all properties on the `verb.cache.data` object -2. Scans templates for helpers that use those properties -3. When a helper is found with a name that matches a property on the context: - + the helper function is moved from `verb._.helpers.foo` to `verb._.helpers.__.foo` - + the helper is renamed in the string using the `__.` prefix (see below) - -**Resulting in the following** - -The final context object ends up like this: - -```js -var ctx = _.extend({}, options.context, options.helpers); -//=> {foo: 'bar', __: {foo: [function]}} -``` - -And the helper is renamed as follows - -```js -// from this -{%%= foo("quux") %} - -// to this -{%%= __.foo("quux") %} -``` - -Now, both of the following templates will resolve: - -```js -{%%= foo %} -{%%= __.foo("quux") %} -``` - -**Done!** - -To the user, this is transparent. You would never even know it was happening unless you inspected the string after the `.preRender()` middleware is called. Benchmarks showed a 2 millisecond difference when conflicting helpers were updated in ten templates. Clearly this would compound, but in our opinion it's a very acceptable compromise if your using Verb on a small project or just using verb to build a readme. - -## Notes - -#### 1 - -As it relates specifically to the **auto-generated data in Verb**, the challenge with namespacing is that it dramatically increases the amount of logic necessary to normalize data that would otherwise be on a single object. - -For example, there are several transforms and middleware that normalize package.json values. Namespacing with arbitrary objects necessitates far more logic to provide the same user experience. - -More so, I=if we were to, say, allow the user to define a custom property to use, then templates quickly become much less likely to be re-usable and transferrable across projects. diff --git a/docs/_templates/helpers.md b/docs/_templates/helpers.md deleted file mode 100644 index ec760ec1..00000000 --- a/docs/_templates/helpers.md +++ /dev/null @@ -1,26 +0,0 @@ -# Helpers - -> Registering and using helpers with Verb. - -Helpers are used in templates to transform data, include other templates, or do anything else that can be done with JavaScript _inside templates_. - -## Overview - -- generated helpers -- built-in helpers -- user-defined helpers - - -**Built-in helpers** - -By default, Verb uses [engine-lodash][], so the helpers we use by default are really just javascript functions that we pass to Lo-Dash on the context at render time. - - -## Verb's defaults - -Helpers are used when data needs to be updated dynamically, or when it needs to be calculated - - -## Related - -- [Helper conflict resolution](./helper-conflict-resolution.md) diff --git a/docs/_templates/helpers/apidocs.md b/docs/_templates/helpers/apidocs.md deleted file mode 100644 index 386e7c98..00000000 --- a/docs/_templates/helpers/apidocs.md +++ /dev/null @@ -1,29 +0,0 @@ -# apidocs - -> Generate API documentation from code comments using the apidocs helper. - -This is about using and customizing the `apidocs` helper. - - - -## Custom template - -> Define a custom template to use - -Pass the path to your template on the `template` option. - -In verb, you can set the path the following ways: - -**API** - -```js -verb.option('apidocs', {template: './path/to/template.js'}); -``` - -**CLI** - -Store the path or module name on the global config: - -```sh -$ verb --set apidocs.template="./path/to/template.js" -``` \ No newline at end of file diff --git a/docs/_templates/includes.md b/docs/_templates/includes.md deleted file mode 100644 index 74f77d92..00000000 --- a/docs/_templates/includes.md +++ /dev/null @@ -1,53 +0,0 @@ -# Includes - -## author - -> Add an `## Author` section - -**Example** - -```markdown -## Author -{%%= include("author") %} -``` - -**Result** - -```markdown -## Author - -**Jon Schlinkert** - -+ [github/jonschlinkert](https://github.com/jonschlinkert) -+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) -``` - -**Twitter** - -```js -{%%= include("author") %} -{%%= include("author", {username: 'jonschlinkert'}) %} -{%%= include("author", {twitter: 'jonschlinkert'}) %} -{%%= include("author", {twitter: {username: 'jonschlinkert'}}) %} -``` - -**GitHub** - -```js -{%%= include("author") %} -{%%= include("author", {username: 'jonschlinkert'}) %} -{%%= include("author", {github: 'jonschlinkert'}) %} -{%%= include("author", {github: {username: 'jonschlinkert'}}) %} -``` - -**Both** - -```js -{%%= include("author") %} -{%%= include("author", {username: 'jonschlinkert'}) %} -{%%= include("author", {github: 'jonschlinkert', twitter: 'jonschlinkert'}) %} -{%%= include("author", { - github: {username: 'jonschlinkert'}, - twitter: {username: 'jonschlinkert'} -}) %} -``` \ No newline at end of file diff --git a/docs/_templates/middleware.md b/docs/_templates/middleware.md deleted file mode 100644 index 8d657f9d..00000000 --- a/docs/_templates/middleware.md +++ /dev/null @@ -1,33 +0,0 @@ -# Middleware - -> Using routes and middleware with Verb - -## Overview - - -## VERBS - -### .onLoad - -**Handles** - -- partials -- layouts -- renderable templates - - -### .preRender - -**Handles** - -- renderable templates only - - -### .postRender - -**Handles** - -- renderable templates only - - -## Pro Tips diff --git a/docs/_templates/options.md b/docs/_templates/options.md deleted file mode 100644 index 115a0084..00000000 --- a/docs/_templates/options.md +++ /dev/null @@ -1,31 +0,0 @@ -# Options - -> Setting and getting options - -## Table of contents - - - -## Overview - -- Options are stored on `verb.options`. - - -### package.json - -> setting options in package.json - -If your package.json file has a `verb` property with an `options` object, Verb will use it to extend the `verb.options` object. - -**Example** - -```json -{ - "name": "my-project", - "description": "It's awesome, seriously.", - - "verb": { - "options": {} - } -} -``` \ No newline at end of file diff --git a/docs/_templates/overview.md b/docs/_templates/overview.md deleted file mode 100644 index 74e540dd..00000000 --- a/docs/_templates/overview.md +++ /dev/null @@ -1,52 +0,0 @@ -# Verb Overview - -> Stuff you might find useful to know about Verb. - -## Setting variables - -### inside a document - -If you need to override a variable on the context, you can set variables directly inside a document. In fact, there are multiple ways to do this: - -**Front matter** - -```html ---- -foo: "bar" ---- - -{%%= foo %} -//=> "bar" -``` - -**Lo-Dash template** - -```js -{%% var foo = "bar" %} -{%%= foo %} -//=> "bar" -``` - -**Helper locals** - -If you're using a helper, you can pass data as the second parameter: - -```js -{%%= include("author", {username: "jonschlinkert"}) %} -``` - -## Includes - -**Headings** - -When using the `docs` or `include` helpers, Verb adds one heading level to the heading levels set in the includes to ensure that they flow with rest of the document. - -So `##` becomes `###`. - -**cwd** - -Change the current working directory (the directory to use for getting templates): - -```js -{%%= include("foo", {cwd: "my-includes"}) %} -``` diff --git a/docs/_templates/plugins/conflicts.md b/docs/_templates/plugins/conflicts.md deleted file mode 100644 index 40c3d60a..00000000 --- a/docs/_templates/plugins/conflicts.md +++ /dev/null @@ -1,12 +0,0 @@ -# Linter plugin - -> Lint templates for missing or conflicting variables and helpers. - -**Disable** - -To disable the plugin: - -```sh -$ verb --disable linter -``` - diff --git a/docs/_templates/protected-properties.md b/docs/_templates/protected-properties.md deleted file mode 100644 index c238d832..00000000 --- a/docs/_templates/protected-properties.md +++ /dev/null @@ -1,12 +0,0 @@ -# Protected properties - -> - -Verb stores certain information on a few objects that should not be overwritten. These properties are: - -- `verb.cache` -- `verb.cache.data` -- `verb.options` - - - diff --git a/docs/_templates/template-data.md b/docs/_templates/template-data.md deleted file mode 100644 index aad2f613..00000000 --- a/docs/_templates/template-data.md +++ /dev/null @@ -1,35 +0,0 @@ -## Template data - -> Load data to pass to templates - - -```js -var verb = require('verb'); -``` - -```js -verb.data('data/*.yml'); -verb.layout('default.md', '\n<<% body %>>\n'); -verb.includes('includes/*.md'); -verb.docs('docs/*.md'); - -verb.helperAsync('docs', function (name, locals, cb) { - var doc = this.cache.docs[name]; - this.render(doc, locals, function (err, content) { - if (err) return cb(err); - cb(null, content); - }); -}); - -verb.task('readme', function() { - verb.src('.verb.md') - .pipe(verb.dest('./')); -}); - -verb.task('docs', function() { - verb.src('docs/_templates/*.md') - .pipe(verb.dest('./wiki')); -}); - -verb.task('default', ['readme', 'docs']); -``` diff --git a/docs/_templates/using-layouts.md b/docs/_templates/using-layouts.md deleted file mode 100644 index 836b932e..00000000 --- a/docs/_templates/using-layouts.md +++ /dev/null @@ -1,21 +0,0 @@ -## Using layouts - -> Layouts are used to wrap other templates with common or project-wide content. - -In your verbfile.js: - -```js -var verb = require('verb'); - -// register a layout with verb. -verb.layout('default.md', {content: '# {%%= name %}\n\n<<%% body %>>\n'}); - -// register a page, and use the layout by adding the `layout` property. -verb.page('api.md', {content: 'API docs...', layout: 'default.md'}); - -//=> '# {%%= name %}\n\nAPI docs...\n' -``` - -**What's with the wierd `body` syntax?** - -Verb is a documentation generator, the crazy `body` syntax is verb's way of ensure that we avoid collision with templates that might be in code examples. diff --git a/docs/_templates/variables.md b/docs/_templates/variables.md deleted file mode 100644 index b64a8cc4..00000000 --- a/docs/_templates/variables.md +++ /dev/null @@ -1,45 +0,0 @@ -# Variables - -> Describes how variables are used in Verb, namespacing, and protected variables. - -- `author` -- `bugs` -- `data` -- `dependencies` -- `deps` -- `description` -- `devDependencies` -- `engines` -- `files` -- `fork` -- `github.username` -- `homepage` -- `ignore` -- `include` -- `keywords` -- `license` -- `main` -- `name` -- `node` -- `repository.url` -- `repository` -- `scripts` -- `test` -- `twitter.username` -- `type` -- `url` -- `username` -- `verb` -- `version` - -## user vs username - -- `user`: The actual owner of the GitHub project -- `username`: Your username - -This distinguishment is important when you're rendering the readme for a project that belongs to an org that _isn't you_. - - -## Protected variables - - diff --git a/docs/_templates/verbfile.md b/docs/_templates/verbfile.md deleted file mode 100644 index fd241be6..00000000 --- a/docs/_templates/verbfile.md +++ /dev/null @@ -1,15 +0,0 @@ -# verbfile - -> Creating a basic verbfile.js - -This is what the default `verbfile.js` looks like: - -```js -var verb = require('verb'); - -verb.task('default', function() { - verb.src('.verb.md') - .pipe(verb.dest('.')); -}); -``` - diff --git a/docs/_templates/verbmd.md b/docs/_templates/verbmd.md deleted file mode 100644 index 6772d44b..00000000 --- a/docs/_templates/verbmd.md +++ /dev/null @@ -1,94 +0,0 @@ -# .verb.md - -> .verb.md is a markdown template that Verb uses to generate your project's readme. - -If your project has a `.verb.md` template, verb will automatically render it using data from your project's package.json file. - -## Example .verb.md - -You can use any variables, helpers, templates or data you want in Verb documentation. This example below just happens to be the template that Verb's maintainers like on their own projects. - -Below we'll describe each section. - -```markdown -# {%%= name %} {%%= badge("fury") %} - -> {%%= description %} - -## Install -{%%= include("install") %} - -## API -{%%= apidocs("index.js") %} - -## Author -{%%= include("author") %} - -## License -{%%= copyright() %} -{%%= license() %} - -*** - -{%%= include("footer") %} -``` - -_(before going further, please read [how verb works](./how-verb-works.md) if you need a primer on how these templates work.)_ - -There are only a couple of template variables used, the rest of example consists of [helpers](./helpers.md). Let's go over all of them. - - -## Variables - -Simple variables, like `name` are typically used when their values can be resolved using data from package.json without doing a lot of work to get the data. - -### name - -Uses the value of the `name` property from package.json. - -```js -{%%= name %} -``` - -Uses the `description` property from package.json. - -```js -> {%%= description %} -``` - -## Helpers - -Helpers are used when data needs to be updated dynamically, or when it needs to be calculated - -### badge - -This is a helper that gets badge templates from node_modules and renders them using data in package.json. There are other badges, like `travis`, but you can add your own if you want or do a pr to verb to add more. - -```js -{%%= badge("fury") %} -``` - -### description - -```js -{%%= include("install") %} -``` - -```js -{%%= apidocs("index.js") %} -``` - -```js -{%%= include("author") %} -``` - -```js -{%%= license() %} -``` -```js -{%%= copyright() %} -``` - -```js -{%%= include("footer") %} -``` \ No newline at end of file diff --git a/docs/sections/_temp.md b/docs/sections/_temp.md deleted file mode 100644 index b85109ba..00000000 --- a/docs/sections/_temp.md +++ /dev/null @@ -1,287 +0,0 @@ -# {%= name %} {%= badge("fury") %} {%= badge("travis") %} - -> {%= description %} - -**Heads up!** - -As of v0.4.0, Verb now requires [verb-cli][] to run. See the [getting started](#getting-started) section for details. - -**Features** - -- Build API docs from code comments (this couldn't be easier!) -- Add a [.verb.md](#verbmd) markdown template to your project, and verb will build your readme using data from package.json. -- If you need more, create a [verbfile.js](#verbfile.js)! Verb can complex documentation too, including multi-page TOCs, cross-reference links, auto-generated links to dependencies and so on. -- Verb can run any [gulp](https://github.com/gulpjs/gulp) plugin -- Verb is built on top of [Template](https://github.com/jonschlinkert/template). All of Template's methods are exposed on the API. -- You can get by with a simple `.verb.md` markdown template, or do things like add layouts, pages, partials, helpers, register a template engine, load data or use a `.transform()` or two to modify that data at runtime. - - -## Install verb-cli - -As of v0.4.0, Verb requires verb-cli to run. To install verb-cli, run: - -```bash -npm i -g verb-cli -``` - -## .verb.md - -Add a `.verb.md.` [template][verbmd] to your project and run `verb` from the command line to generate the project's readme using data from package.json. - -If you need more, use a [verbfile.js][verbfile]. - -**Example .verb.md** - -This is a basic readme template that Verb's own maintainers like to use. - -```markdown -# {%%= name %} {%%= badge("fury") %} - -> {%%= description %} - -## Install -{%%= include("install") %} - -## API -{%%= apidocs("index.js") %} - -## Author -{%%= include("author") %} - -## License -{%%= copyright() %} -{%%= license() %} - -*** - -{%%= include("footer") %} -``` - -`.verb.md` files are rendered using data from `package.json`, but Verb is not restricted to package.json. You can use any data you want, with any templates, helpers, etc. - - -## verbfile.js - -For projects that need more than readme documentation, you can add a `verbfile.js` to the project, and be sure to install verb locally with: - -```bash -npm i verb --save-dev -``` - -**Example basic verbfile.js** - -```js -var verb = require('verb'); - -// load data for templates if needed -verb.data('foo/*.json'); - -verb.task('default', function() { - verb.src(['.verb.md', 'docs/*.md']) - .pipe(verb.dest('./')); -}); -``` - -*** - -# Template API - -> See [Template](https://github.com/jonschlinkert/template) for all available methods. - -### .data - -> Load data to pass to templates. - -Any of these work: - -```js -verb.data({foo: 'bar'}); -verb.data('package.json'); -verb.data(['foo/*.{json,yml}']); -``` - -### .helper - -> Add helpers to be used in templates. - -```js -verb.helper('read', function(filepath) { - return fs.readFileSync(filepath, 'utf8'); -}); - -//=> {%%= read("foo.txt") %} -``` - -### .partial - -> Add partials to be used in other templates. - -```js -verb.partial('notice', { content: '...' }); -verb.partial('banner', { content: '/*! Copyright (c) 2014 Jon Schlinkert, Brian Woodward... */' }); -// or load a glob of partials -verb.partials('partials/*.md'); - -// optionally pass locals, all template types support this -verb.partials('partials/*.md', {site: {title: 'Code Project'}}); -``` - -**Usage** - -Use the `partial` helper to inject into other templates: - -```js -{%%= partial("banner") %} -``` - -Get a cached partial: - -```js -var banner = verb.cache.partials['banner']; -``` - -### .page - -> Add pages that might be rendered (really, any template is renderable, pages fit the part though) - -```js -verb.page('toc.md', { content: 'Table of Contents...'}); -// or load a glob of pages -verb.pages('pages/*.md', {site: {title: 'Code Project'}}); -``` - -Use the `page` helper to inject pages into other templates: - -```js -{%%= page("toc") %} -``` - -Get a cached page: - -```js -var toc = verb.cache.pages['toc']; -``` - -Pages are `renderable` templates, so they also have a `.render()` method: - -```js -var toc = verb.cache.pages['toc']; -// async -toc.render({}, function(err, content) { - console.log(content); -}); - -// or sync -var res = toc.render(); -``` - -**Params** - - - `locals` **{Object}**: Optionally pass locals as the first arg - - `callback` **{Function}**: If a callback is passed, the template will be rendered async, otherwise sync. - - -### .layout - -> Add layouts, which are used to "wrap" other templates: - -```js -verb.layout('default', {content: [ - '', - ' ', - ' ', - ' ', - ' {%%= title %}', - ' ', - ' ', - ' <<% body %>>', // `body` is the insertion point for another template - ' ', - '' -].join('\n')}); - -// or load a glob of layouts -verb.layouts('layouts/*.md', {site: {title: 'Code Project'}}); -``` - -Layouts may be use with any other template, including other layouts. Any level of nesting is also possible. - -**Body tags** - -Layouts use a `body` as the insertion point for other templates. The syntax verb uses for the `body` tag is: - -```js -<<% body %>> -``` - -Admittedly, it's a strange syntax, but that's why we're using it. Verb shouldn't collide with templates that you might be using in your documentation. - - -**Usage** - -Layouts can be defined in template locals: - -```js -// either of these work (one object or two) -verb.page('toc.md', { content: 'Table of Contents...'}, { layout: 'default' }); -verb.partial('foo.md', { content: 'partial stuff', layout: 'block' }); -``` - -Or in the front matter of a template. For example, here is how another layout would use our layout example from earlier: - -```js -// using this 'inline' template format to make it easy to see what's happening -// this could be loaded from a file too -verb.layout('sidebar', {content: [ - '---', - 'layout: default', - '---', - '
', - ' <<% body %>>', - '
' -].join('\n')}); -``` - -# Task API - -{%= apidocs("index.js") %} - - -## Why Verb instead of X? - -I created Verb to help me maintain my own projects. Any time that is spent maintaining a project that could be automated instead, is time that is being taken away from real productivity. - -**Verb does exactly what I needed** - -To that end, I wanted a documentation generator that would work in the following scenarios: - -- **simple, no config**: most projects don't need complicated docs. e.g. "just build the readme and don't ask me questions." -- **handle the boilerplate stuff**: APIs change from project to project, but my name doesn't, my github URL doesn't, and my choice of licence doesn't. Verb should know those things. -- **don't ask me questions**: I just want to run `verb`, and it should work. No setup or config. There is more than enough data in package.json to handle the boilerplate part of a readme. -- **generate API docs**: When I want [API docs](#api-docs), I should have to jump through hoops, or add `.json` files to directories. I should be able to add the docs wherever I want, to the README, separate docs, or use templates to generate a gh-pages site. -- **render markdown, not HTML**: this one was important to me. There are hundreds of [great libs](https://github.com/jonschlinkert/remarkable) that can render markdown to HTML. Once you have well-formatted markdown documentation, it's easy to convert to HTML. - - -## Running tests - -Install dev dependencies: - -```bash -npm test -``` - -## Contributing -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue]({%= bugs.url %}) - -## Author -{%= include("author") %} - -## License -{%= copyright({year: 2014}) %} -{%= license() %} - -*** - -{%= include("footer") %} - -[verb-cli]: https://github.com/verbose/verb-cli - diff --git a/docs/sections/cli-notice.md b/docs/sections/cli-notice.md deleted file mode 100644 index cfc7e9bd..00000000 --- a/docs/sections/cli-notice.md +++ /dev/null @@ -1,2 +0,0 @@ - -As of v0.4.0, Verb now requires [verb-cli] to run. See the [getting started](#getting-started) section for details. \ No newline at end of file diff --git a/docs/sections/features.md b/docs/sections/features.md deleted file mode 100644 index 547e0761..00000000 --- a/docs/sections/features.md +++ /dev/null @@ -1,5 +0,0 @@ -- Generate markdown docs, or HTML -- Generate a Table of Contents simply by adding `` to any document. -- Include templates from locally installed npm packages with the `{%%= include() %}` helper -- Include templates from your project's `docs/` directory with the `{%%= docs() %}` helper -- Change the templates directory for either helper by passing a `cwd` to the helper: example: `{%%= docs("foo", {cwd: ''}) %}` diff --git a/docs/sections/install-verb-cli.md b/docs/sections/install-verb-cli.md deleted file mode 100644 index 475147e5..00000000 --- a/docs/sections/install-verb-cli.md +++ /dev/null @@ -1,5 +0,0 @@ -As of v0.4.0, Verb requires verb-cli to run. To install verb-cli, run: - -```bash -npm i -g verb-cli -``` \ No newline at end of file diff --git a/docs/sections/overview.md b/docs/sections/overview.md deleted file mode 100644 index 2e893d30..00000000 --- a/docs/sections/overview.md +++ /dev/null @@ -1,192 +0,0 @@ -**Heads up!** -{%= docs("sections/cli-notice") %} - -**Features** -{%= docs("sections/features") %} - -## Install verb-cli -{%= docs("sections/install-verb-cli") %} - -## .verb.md -{%= docs("sections/verb-md") %} - -## verbfile.js -{%= docs("sections/verbfile") %} - -*** - -# Template API - -> See [Template](https://github.com/jonschlinkert/template) for all available methods. - -### .data - -> Load data to pass to templates. - -Any of these work: - -```js -verb.data({foo: 'bar'}); -verb.data('package.json'); -verb.data(['foo/*.{json,yml}']); -``` - -### .helper - -> Add helpers to be used in templates. - -```js -verb.helper('read', function(filepath) { - return fs.readFileSync(filepath, 'utf8'); -}); - -//=> {%%= read("foo.txt") %} -``` - -### .partial - -> Add partials to be used in other templates. - -```js -verb.partial('notice', { content: '...' }); -verb.partial('banner', { content: '/*! Copyright (c) 2014 Jon Schlinkert, Brian Woodward... */' }); -// or load a glob of partials -verb.partials('partials/*.md'); - -// optionally pass locals, all template types support this -verb.partials('partials/*.md', {site: {title: 'Code Project'}}); -``` - -**Usage** - -Use the `partial` helper to inject into other templates: - -```js -{%%= partial("banner") %} -``` - -Get a cached partial: - -```js -var banner = verb.cache.partials['banner']; -``` - -### .page - -> Add pages that might be rendered (really, any template is renderable, pages fit the part though) - -```js -verb.page('toc.md', { content: 'Table of Contents...'}); -// or load a glob of pages -verb.pages('pages/*.md', {site: {title: 'Code Project'}}); -``` - -Use the `page` helper to inject pages into other templates: - -```js -{%%= page("toc") %} -``` - -Get a cached page: - -```js -var toc = verb.cache.pages['toc']; -``` - -Pages are `renderable` templates, so they also have a `.render()` method: - -```js -var toc = verb.cache.pages['toc']; -// async -toc.render({}, function(err, content) { - console.log(content); -}); - -// or sync -var res = toc.render(); -``` - -**Params** - - - `locals` **{Object}**: Optionally pass locals as the first arg - - `callback` **{Function}**: If a callback is passed, the template will be rendered async, otherwise sync. - - -### .layout - -> Add layouts, which are used to "wrap" other templates: - -```js -verb.layout('default', {content: [ - '', - ' ', - ' ', - ' ', - ' {%%= title %}', - ' ', - ' ', - ' <<% body %>>', // `body` is the insertion point for another template - ' ', - '' -].join('\n')}); - -// or load a glob of layouts -verb.layouts('layouts/*.md', {site: {title: 'Code Project'}}); -``` - -Layouts may be use with any other template, including other layouts. Any level of nesting is also possible. - -**Body tags** - -Layouts use a `body` as the insertion point for other templates. The syntax verb uses for the `body` tag is: - -```js -<<% body %>> -``` - -Admittedly, it's a strange syntax, but that's why we're using it. Verb shouldn't collide with templates that you might be using in your documentation. - - -**Usage** - -Layouts can be defined in template locals: - -```js -// either of these work (one object or two) -verb.page('toc.md', { content: 'Table of Contents...'}, { layout: 'default' }); -verb.partial('foo.md', { content: 'partial stuff', layout: 'block' }); -``` - -Or in the front matter of a template. For example, here is how another layout would use our layout example from earlier: - -```js -// using this 'inline' template format to make it easy to see what's happening -// this could be loaded from a file too -verb.layout('sidebar', {content: [ - '---', - 'layout: default', - '---', - '
', - ' <<% body %>>', - '
' -].join('\n')}); -``` - -# Task API - -{%= apidocs("index.js") %} - - -## Why Verb instead of X? - -I created Verb to help me maintain my own projects. Any time that is spent maintaining a project that could be automated instead, is time that is being taken away from real productivity. - -**Verb does exactly what I needed** - -To that end, I wanted a documentation generator that would work in the following scenarios: - -- **simple, no config**: most projects don't need complicated docs. e.g. "just build the readme and don't ask me questions." -- **handle the boilerplate stuff**: APIs change from project to project, but my name doesn't, my github URL doesn't, and my choice of licence doesn't. Verb should know those things. -- **don't ask me questions**: I just want to run `verb`, and it should work. No setup or config. There is more than enough data in package.json to handle the boilerplate part of a readme. -- **generate API docs**: When I want [API docs](#api-docs), I should have to jump through hoops, or add `.json` files to directories. I should be able to add the docs wherever I want, to the README, separate docs, or use templates to generate a gh-pages site. -- **render markdown, not HTML**: this one was important to me. There are hundreds of [great libs](https://github.com/jonschlinkert/remarkable) that can render markdown to HTML. Once you have well-formatted markdown documentation, it's easy to convert to HTML. diff --git a/docs/sections/verb-md.md b/docs/sections/verb-md.md deleted file mode 100644 index a9b20fc9..00000000 --- a/docs/sections/verb-md.md +++ /dev/null @@ -1,33 +0,0 @@ - -Add a `.verb.md.` [template]() to your project and run `verb` from the command line to generate the project's readme using data from package.json. - -If you need more, use a [verbfile.js](). - -**Example .verb.md** - -This is a basic readme template that Verb's maintainers like to use. - -```markdown -# {%%= name %} {%%= badge("fury") %} - -> {%%= description %} - -## Install -{%%= include("install") %} - -## API -{%%= apidocs("index.js") %} - -## Author -{%%= include("author") %} - -## License -{%%= copyright() %} -{%%= license() %} - -*** - -{%%= include("footer") %} -``` - -`.verb.md` files are rendered using data from `package.json`, but Verb is not restricted to package.json. You can use any data you want, with any templates, helpers, etc. diff --git a/docs/sections/verbfile.md b/docs/sections/verbfile.md deleted file mode 100644 index 8a1d38f7..00000000 --- a/docs/sections/verbfile.md +++ /dev/null @@ -1,21 +0,0 @@ - -For projects that need more than readme documentation, you can add a `verbfile.js` to the project, and be sure to install verb locally with: - -```bash -npm i verb --save-dev -``` - -**Example basic verbfile.js** - -```js -var verb = require('verb'); - -// load data for templates if needed -verb.data('foo/*.json'); - -verb.task('default', function() { - verb.src(['.verb.md', 'docs/*.md']) - .pipe(verb.dest('./')); -}); -``` - diff --git a/docs/sections/why.md b/docs/sections/why.md deleted file mode 100644 index 1b1051be..00000000 --- a/docs/sections/why.md +++ /dev/null @@ -1 +0,0 @@ -It's magical and smells like chocolate. If that's not enough for you, it's also the most powerful and easy-to-use documentation generator for node.js. And it's magical. diff --git a/example.js b/example.js new file mode 100644 index 00000000..5bb23d87 --- /dev/null +++ b/example.js @@ -0,0 +1,27 @@ +var argv = require('minimist')(process.argv.slice(2)); +var lint = require('lint-templates'); +var verb = require('./'); +var app = verb() + .use(lint()) + + +// app.includes.load('*.md'); +app.badges.load('*.md'); + +app.on('error', function (err) { + if (err) { + console.log(err.stack); + process.exit(0); + } +}); + +app.badges.getView('bower') + // .set('data.name', 'NAME') + // .set('data.github.repopath', 'verbiagesss') + .lint(/\{%=?([^%]+)%}/g) + .lint(/\{{([^}]+)}}/g) + // .render(function (err, res) { + // if (err) return console.error('Error:', err); + // if (res) console.log('Done:', res); + // }); + diff --git a/examples/config.js b/examples/config.js new file mode 100644 index 00000000..0c3afd63 --- /dev/null +++ b/examples/config.js @@ -0,0 +1,5 @@ +var verb = require('../'); +var app = verb(); + +app.config('helpers', app.helper.bind(app)); +console.log(app._.helpers) diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..9ae53e91 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,34 @@ +var gulp = require('gulp'); +var mocha = require('gulp-mocha'); +var istanbul = require('gulp-istanbul'); +var jshint = require('gulp-jshint'); +require('jshint-stylish'); + +var lint = ['index.js', 'lib/utils.js', 'test/*.js']; + +gulp.task('coverage', function () { + return gulp.src(lint) + .pipe(istanbul()) + .pipe(istanbul.hookRequire()); +}); + +gulp.task('mocha', ['coverage'], function () { + return gulp.src('test/*.js') + .pipe(mocha({reporter: 'spec'})) + .pipe(istanbul.writeReports()); +}); + +gulp.task('jshint', function () { + return gulp.src(lint) + .pipe(jshint()) + .pipe(jshint.reporter('jshint-stylish')) +}); + +gulp.task('default', ['mocha', 'jshint'], function (cb) { + console.log('Finished "default"'); + + // force the process to end since `verb.watch` + // holds it open in the tests + process.exit(); + cb(); +}); diff --git a/index.js b/index.js index 248297b9..725d6c91 100644 --- a/index.js +++ b/index.js @@ -1,143 +1,454 @@ 'use strict'; -var diff = require('diff'); -var chalk = require('chalk'); -var extend = require('extend-shallow'); -var Template = require('template'); -var Composer = require('composer').Composer; -var vfs = require('vinyl-fs'); +/** + * module dependencies + */ -var stack = require('./lib/stack'); -var init = require('./lib/init'); +var path = require('path'); +var store = require('data-store'); +var Local = require('./lib/local'); +var loader = require('assemble-loader'); +var includes = require('readme-includes'); +var badges = require('readme-badges'); +var Templates = require('templates'); +var Composer = require('composer'); +var proto = Composer.prototype; +var lib = require('./lib'); +var utils = lib.utils; /** - * Initialize `Verb` + * Create an `verb` application. This is the main function exported + * by the verb module. * - * @param {Object} `context` - * @api private + * ```js + * var verb = require('verb'); + * var app = verb(); + * ``` + * @param {Object} `options` Optionally pass default options to use. + * @api public */ -function Verb() { - Template.apply(this, arguments); +function Verb(options) { + if (!(this instanceof Verb)) { + return new Verb(options); + } + this.isVerb = true; + Templates.apply(this, arguments); Composer.apply(this, arguments); - init(this); + this.options = options || {}; + this.initVerb(this, this.options); } -extend(Verb.prototype, Composer.prototype); -Template.extend(Verb.prototype); +Templates.inherit(Verb, Composer); /** - * Set application defaults that may be overridden by the user. - * This is a temporary method and should not be used. - * - * @param {String} `key` - * @param {*} `value` - * @api private + * `Verb` prototype methods */ -Verb.prototype.defaults = function(key/*, value*/) { - if (typeof key === 'object') { - arguments[0] = {defaults: arguments[0]}; - } else { - arguments[0] = 'defaults.' + arguments[0]; - } - this.option.apply(this, arguments); - return this; -}; +Templates.extend(Verb, { + constructor: Verb, -/** - * Glob patterns or filepaths to source files. - * - * ```js - * verb.src('src/*.hbs', {layout: 'default'}) - * ``` - * - * @param {String|Array} `glob` Glob patterns or file paths to source files. - * @param {Object} `options` Options or locals to merge into the context and/or pass to `src` plugins - * @api public - */ + /** + * Initialize Verb defaults + */ -Verb.prototype.src = function(glob, opts) { - return stack.src(this, glob, opts); -}; + initVerb: function(app, opts) { + this.store = store('verb', opts.store); + this.local = new Local(this.cache.data.verb); -/** - * Specify a destination for processed files. - * - * ```js - * verb.dest('dist') - * ``` - * - * @param {String|Function} `dest` File path or rename function. - * @param {Object} `options` Options and locals to pass to `dest` plugins - * @api public - */ + // console.log(this.local.locals) -Verb.prototype.dest = function(dest, opts) { - return stack.dest(this, dest, opts); -}; + this.initEngines(this); + this.initMiddleware(this); + this.initViewTypes(this); -/** - * Copy a `glob` of files to the specified `dest`. - * - * ```js - * verb.task('assets', function() { - * verb.copy('assets/**', 'dist'); - * }); - * ``` - * - * @param {String|Array} `glob` - * @param {String|Function} `dest` - * @return {Stream} Stream, to continue processing if necessary. - * @api public - */ + this.option('view', function (view) { + if (view.src) view.path = view.src; + }); -Verb.prototype.copy = function(glob, dest, opts) { - return vfs.src(glob, opts).pipe(vfs.dest(dest, opts)); -}; + this.on('option', function (key) { + utils.reloadViews(app, key); + }); -/** - * Display a visual representation of the difference between - * two objects or strings. - * - * ```js - * var doc = verb.views.docs['foo.md']; - * verb.render(doc, function(err, content) { - * verb.diff(doc.orig, content); - * }); - * ``` - * - * @param {Object|String} `a` - * @param {Object|String} `b` - * @param {String} `methodName` Optionally pass a [jsdiff] method name to use. The default is `diffJson` - * @api public - */ + this.on('use', function () { + utils.reloadViews(app); + }); + }, -Verb.prototype.diff = function(a, b, method) { - method = method || 'diffJson'; - a = a || this.env; - b = b || this.cache.data; - diff[method](a, b).forEach(function (res) { - var color = chalk.gray; - if (res.added) { - color = chalk.green; - } - if (res.removed) { - color = chalk.red; - } - process.stderr.write(color(res.value)); - }); - console.log('\n'); -}; + /** + * Default template engines + */ -/** - * Expose `verb.Verb` - */ + initEngines: function () { + this.option('rethrow', {regex: /\{%=?([^%]*)%}/}); + this.engine('hbs', require('engine-handlebars')); + this.engine('md', require('engine-base'), { + delims: ['{%', '%}'] + }); + }, + + /** + * Default middleware + * | Ensure user-defined layout is recognized + * | Parse front-matter + */ + + initMiddleware: function (app) { + this.onLoad(/\.md$/, function (view, next) { + utils.matter.parse(view, next); + }); + }, + + /** + * Default `viewTypes` + * | includes + * | layouts + * | pages + * | files + */ + + initViewTypes: function () { + // this.use(loader()); + this.defaultHelpers(); + + this + .use(loader()) + .use(function (app) { + return function (collection) { + if (!collection.isCollection) return collection; + var fn = this.getView; + + this.getView = function (name) { + return fn.apply(this, arguments) || this.loadView(name, { + ext: '.md' + }); + }; + return this; + }; + }); + + this.data('author', { + name: 'Jon Schlinkert' + }); + this.data('twitter', { + username: 'jonschlinkert' + }); + this.data('github', { + username: 'jonschlinkert' + }); -Verb.prototype.Verb = Verb; + this.data('runner', { + name: this.get('cache.data.name'), + url: this.get('cache.data.repository.url'), + }); + + this.create('includes', { + viewType: ['partial'], + renameKey: function (key) { + var cwd = this.options.cwd; + if (!cwd || key.indexOf(cwd) === -1) { + return key; + } + var len = cwd.length + 1; + return key.slice(len); + }, + cwd: includes, + engine: 'md', + }); + + this.create('badges', { + viewType: ['partial'], + renameKey: function (key) { + var cwd = this.options.cwd || ''; + var len = cwd.length + 1; + return key.slice(len); + }, + cwd: badges, + engine: 'md' + }); + + this.create('docs', { + viewType: ['partial'], + renameKey: utils.basename, + engine: 'md' + }); + + this.create('layouts', { + viewType: ['layout'], + renameKey: utils.basename, + engine: 'md' + }); + + this.include('npm', { + content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); + + this.create('files'); + }, + + defaultHelpers: function () { + this.helper('date', function () { + return new Date(); + }); + + var mdu = require('markdown-utils'); + this.helperGroup('mdu', mdu); + + this.helper('log', function (msg) { + console.log.apply(console, arguments); + }); + + this.helper('related', function (keys) { + var url = 'https://www.npmjs.com/package/'; + keys = utils.arrayify(keys); + keys.forEach(function (key, i) { + keys[i] = '+ ' + mdu.link(key, url + key); + }); + return '\n' + keys.join('\n'); + }); + + this.asyncHelper('trim', function (str) { + return str.trim(); + }); + + this.helper('license', function () { + + }); + + this.helper('copyright', function () { + + }); + + lib.config(this, this.local.locals); + }, + + local: function (key) { + return this.get('cache.data.verb.' + key); + }, + + // userConfig: function () { + // // this.helpers(this.store.get('helpers')); + // // this.helpers(this.local.get('helpers')); + + // // this.asyncHelpers(this.store.get('asyncHelpers')); + // // this.asyncHelpers(this.local.get('asyncHelpers')); + + // // console.log(this._.helpers) + + // // var len = paths.length; + // // while(len--) { + // // var fp = tryRead(path.join(paths[len], name)); + // // if (fp) return fp; + // // } + // }, + + config: function (name, fn) { + var self = this; + fn = fn || utils.identity; + var config = this.get('cache.data.verb.' + name); + return utils.resolveConfig(config, function (key, val) { + fn(key, val(self.options)); + }); + }, + + /** + * Glob patterns or filepaths to source files. + * + * ```js + * app.src('src/*.hbs', {layout: 'default'}); + * ``` + * @name .src + * @param {String|Array} `glob` Glob patterns or file paths to source files. + * @param {Object} `options` Options or locals to merge into the context and/or pass to `src` plugins + * @api public + */ + + src: function () { + return utils.vfs.src.apply(utils.vfs, arguments); + }, + + /** + * Glob patterns or paths for symlinks. + * + * ```js + * app.symlink('src/**'); + * ``` + * @name .symlink + * @param {String|Array} `glob` + * @api public + */ + + symlink: function () { + return utils.vfs.symlink.apply(utils.vfs, arguments); + }, + + /** + * Specify a destination for processed files. + * + * ```js + * app.dest('dist/'); + * ``` + * @name .dest + * @param {String|Function} `dest` File path or rename function. + * @param {Object} `options` Options and locals to pass to `dest` plugins + * @api public + */ + + dest: function (dest) { + if (!dest) throw new Error('expected dest to be a string.'); + return utils.vfs.dest.apply(utils.vfs, arguments) + .on('data', function () {}); // TODO: fix this + }, + + /** + * Copy files with the given glob `patterns` to the specified `dest`. + * + * ```js + * app.task('assets', function() { + * app.copy('assets/**', 'dist/'); + * }); + * ``` + * @name .copy + * @param {String|Array} `patterns` Glob patterns of files to copy. + * @param {String|Function} `dest` Desination directory. + * @return {Stream} Stream, to continue processing if necessary. + * @api public + */ + + copy: function(patterns, dest, options) { + return utils.vfs.src(patterns, options) + .pipe(utils.vfs.dest(dest, options)); + }, + + /** + * Push a view collection into a vinyl stream. + * + * ```js + * app.toStream('posts', function(file) { + * return file.path !== 'index.hbs'; + * }) + * ``` + * @name .toStream + * @param {String} `collection` The name of the view collection to push into the stream. + * @param {Function} Optionally pass a filter function to use for filtering views. + * @return {Stream} + * @api public + */ + + toStream: function (name) { + var views = this.getViews(name) || {}; + var stream = utils.through.obj(); + setImmediate(function () { + Object.keys(views).forEach(function (key) { + var view = views[key]; + var file = utils.toVinyl(view); + stream.write(file); + }); + stream.end(); + }); + return utils.srcStream(stream); + }, + + /** + * Render a vinyl file. + * + * ```js + * app.src('*.hbs') + * .pipe(app.renderFile()); + * ``` + * + * @name .renderFile + * @param {Object} `locals` Optionally locals to pass to the template engine for rendering. + * @return {Object} + * @api public + */ + + renderFile: function (locals) { + var app = this; + var collection = this.viewCollection(); + + return utils.through.obj(function (file, enc, cb) { + if (typeof locals === 'function') { + cb = locals; + locals = {}; + } + + try { + var view = collection.addView(file); + var ctx = utils.merge({}, app.cache.data, locals, view.data); + + app.render(view, ctx, function (err, res) { + if (err) return cb(err); + file = app.view(res); + cb(null, file); + }); + + } catch(err) { + err.method = 'renderFile'; + app.emit('error', err); + } + }); + }, + + /** + * Define a task to be run when the task is called. + * + * ```js + * app.task('default', function() { + * app.src('templates/*.hbs') + * .pipe(app.dest('dist/')); + * }); + * ``` + * @name .task + * @param {String} `name` Task name + * @param {Function} `fn` function that is called when the task is run. + * @api public + */ + + task: function (/*name*/) { + utils.runtimes(this); + return proto.task.apply(this, arguments); + }, + + /** + * Run one or more tasks. + * + * ```js + * app.run(['foo', 'bar'], function(err) { + * if (err) console.error('ERROR:', err); + * }); + * ``` + * @name .run + * @param {Array|String} `tasks` Task name or array of task names. + * @param {Function} `cb` callback function that exposes `err` + * @api public + */ + + run: function (/*tasks, cb*/) { + return proto.run.apply(this, arguments); + }, + + /** + * Re-run the specified task(s) when a file changes. + * + * ```js + * app.task('watch', function() { + * app.watch('docs/*.md', ['docs']); + * }); + * ``` + * + * @param {String|Array} `glob` Filepaths or glob patterns. + * @param {Array} `tasks` Task(s) to watch. + * @api public + */ + + watch: function (/*glob, tasks*/) { + return proto.watch.apply(this, arguments); + } +}); /** - * Expose `verb` + * Expose the `Verb` constructor */ -module.exports = new Verb(); +module.exports = Verb; diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 00000000..aafb2033 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,91 @@ + +var Config = require('map-config'); + +module.exports = function (app, config) { + var keys = Object.keys(app.views); + var views = {}; + + keys.forEach(function (key) { + var instance = app[key]; + + views[key] = function (config) { + var mapper = new Config({ + options: 'option', + set: 'set', + addViews: 'addViews' + }, instance); + return mapper.process(config); + }; + }); + + var collections = new Config(views); + var configMap = new Config({ + collections: function (config) { + return collections.process(config); + }, + helpers: 'helpers', + asyncHelpers: 'asyncHelpers' + }, app); + + configMap.process(config); + // console.log(app._.helpers) + + // configMap.process({ + // "ignore": [ + // ".git" + // ], + // "deps": { + // "include": [ + // "readme-includes", + // "readme-badges" + // ], + // "ignore": [ + // "support" + // ] + // }, + // "collections": { + // "docs": { + // "options": { + // "cwd": "fixtures/foo" + // }, + // "set": { + // "a.b.c": "d" + // } + // }, + // "includes": { + // "options": { + // "cwd": "fixtures/bar" + // } + // }, + // "badges": { + // "options": { + // "cwd": "fixtures/baz" + // }, + // "addViews": { + // "foo": "this is a badge" + // } + // } + // }, + // "helpers": { + // "whatever": "@/helper-related" + // }, + // "related": { + // "list": [ + // "composer", + // "assemble", + // "template", + // "engine" + // ] + // }, + // "reflinks": [ + // "jsdiff", + // "option-cache", + // "template", + // "plasma", + // "config-cache" + // ] + // }); + + // // console.log(verb.docs.get('a.b.c')); + // console.log(verb._.helpers); +}; diff --git a/lib/helpers/apidocs.js b/lib/helpers/apidocs.js deleted file mode 100644 index 7db075e2..00000000 --- a/lib/helpers/apidocs.js +++ /dev/null @@ -1,20 +0,0 @@ - -var comments = require('js-comments'); - -module.exports = function(app) { - app.loader('apidocs', function(templates) { - for (var key in templates) { - if (templates.hasOwnProperty(key)) { - var file = templates[key]; - var obj = comments.parse(file.content); - file.content = comments.render(obj); - } - } - return templates; - }); - - return function (patterns, opts) { - var view = app.lookup('comments', patterns); - return view.content; - } -}; diff --git a/lib/helpers/async.js b/lib/helpers/async.js deleted file mode 100644 index ebfc6212..00000000 --- a/lib/helpers/async.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var helper = require('async-helper-base'); -var example = require('./custom/example'); -var reflinks = require('helper-reflinks'); -var related = require('helper-related'); - -/** - * Transform for loading default async helpers - */ - -module.exports = function(app) { - app.asyncHelper('reflinks', reflinks); - app.asyncHelper('related', related()); - app.asyncHelper('example', helper('example', example)); - app.asyncHelper('include', helper('include')); - app.asyncHelper('badge', helper('badge')); - app.asyncHelper('docs', helper('doc')); -}; diff --git a/lib/helpers/collections.js b/lib/helpers/collections.js deleted file mode 100644 index 602bb76a..00000000 --- a/lib/helpers/collections.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Transform for loading helper collections - * - * - Loads template-helpers - * - Loads logging-helpers - * - Exposes markdown-utils as helpers - * - exposes path helpers on the `path.` property - * - * ```js - * {%= mdu.link(author.name, author.url) %} - * //=> [Jon Schlinkert](https://github.com/jonschlinkert) - * - * {%= path.extname("foo.md") %} - * //=> '.md' - * ``` - */ - -module.exports = function(app) { - app.helpers({console: console}); - app.helpers(require('logging-helpers')); - - // namespaced helpers - app.helpers({fn: require('./custom/')}); - app.helpers({mdu: require('markdown-utils')}); - app.helpers({mm: require('micromatch')}); - - // remove `path` helpers from root and add them to `path.` - var helpers = require('template-helpers'); - app.helpers(_.omit(helpers._, Object.keys(helpers.path))); - app.helpers({path: helpers.path}); -}; diff --git a/lib/helpers/custom/example.js b/lib/helpers/custom/example.js deleted file mode 100644 index 16018b6e..00000000 --- a/lib/helpers/custom/example.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -/** - * Create a code example from the contents of the specified - * JavaScript file. - * - * ```js - * {%%= example("foo", {name: "my-module"}) %} - * ``` - * - * @param {String} `fp` The path of the file to include. - * @param {String} `options` - * @option {String} `name` Replace `./` in `require('./')` with the given name. - * @return {String} - */ - -module.exports = function example(str, opts) { - if (typeof str !== 'string') { - throw new TypeError('example-helper expects a string.'); - } - - opts = opts || {}; - if (opts.name) { - str = str.split(/\(['"]\.\/['"]\)/).join('(\'' + opts.name + '\')'); - } - - return str; -}; diff --git a/lib/helpers/custom/github.js b/lib/helpers/custom/github.js deleted file mode 100644 index d0cd78a2..00000000 --- a/lib/helpers/custom/github.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('../../utils').linkify('github', true); diff --git a/lib/helpers/custom/index.js b/lib/helpers/custom/index.js deleted file mode 100644 index 874ea554..00000000 --- a/lib/helpers/custom/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('export-files')(__dirname); diff --git a/lib/helpers/custom/linkify.js b/lib/helpers/custom/linkify.js deleted file mode 100644 index 6b40b534..00000000 --- a/lib/helpers/custom/linkify.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('../../utils').linkify; diff --git a/lib/helpers/custom/resolve.js b/lib/helpers/custom/resolve.js deleted file mode 100644 index 5b102fab..00000000 --- a/lib/helpers/custom/resolve.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -var resolve = require('helper-resolve'); - -/** - * Return a property from the package.json of the specified module. - * _(The module must be installed in `node_modules`)_. - * - * ```js - * {%%= resolve("micromatch") %} - * //=> 'node_modules/micromatch/index.js' - * - * {%%= resolve("micromatch", "version") %} - * //=> '2.2.0' - * ``` - * - * @param {String} `fp` The path of the file to include. - * @param {String} `options` - * @option {String} `name` Replace `./` in `require('./')` with the given name. - * @return {String} - */ - -module.exports = function resolve_(name, key) { - return resolve.sync(name)[typeof key === 'string' ? key : 'main']; -}; diff --git a/lib/helpers/custom/shield.js b/lib/helpers/custom/shield.js deleted file mode 100644 index 3faa64e0..00000000 --- a/lib/helpers/custom/shield.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Transform for loading the `{%= shield() %}` helper - * These aren't really implemented yet. this is a - * placeholder for better logic - */ - -module.exports = function (verb) { - verb.badge('npm', '[![{%= name %}](http://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.org/package/{%= name %})'); - - verb.badge('npm2', '[![{%= name %}](http://img.shields.io/npm/dm/{%= name %}.svg)](https://www.npmjs.org/package/{%= name %})'); - - verb.badge('nodei', '[![{%= name %}](https://nodei.co/npm/{%= name %}.png?downloads=true "{%= name %}")](https://nodei.co/npm/{%= name %})'); - - verb.badge('travis', '[![Build Status](http://img.shields.io/travis/{%= name %}.svg)](https://travis-ci.org/{%= name %})'); - - return function shield(name, locals) { - var ctx = _.extend({}, this.context, locals); - return this.app.render(verb.views.badges[name], ctx); - }; -}; diff --git a/lib/helpers/custom/twitter.js b/lib/helpers/custom/twitter.js deleted file mode 100644 index b7d9f41c..00000000 --- a/lib/helpers/custom/twitter.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('../../utils').linkify('twitter'); diff --git a/lib/helpers/sync.js b/lib/helpers/sync.js deleted file mode 100644 index 6fa5d7f3..00000000 --- a/lib/helpers/sync.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -/** - * Transform for loading default sync helpers - */ - -module.exports = function(app) { - app.helper('date', require('helper-date')); - app.helper('apidocs', require('template-helper-apidocs')); - app.helper('multiToc', require('helper-toc')(app.option('toc'))); - app.helper('codelinks', require('helper-codelinks')(app)); - app.helper('changelog', require('helper-changelog')(app)); - app.helper('copyright', require('helper-copyright')); - app.helper('coverage', require('helper-coverage')); - app.helper('license', require('helper-license')); - app.helper('relative', require('relative')); - app.helper('year', require('year')); -}; diff --git a/lib/middleware/index.js b/lib/index.js similarity index 100% rename from lib/middleware/index.js rename to lib/index.js diff --git a/lib/init.js b/lib/init.js deleted file mode 100644 index 9cd013d3..00000000 --- a/lib/init.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; - -var init = require('./transforms/init'); -var config = require('./transforms/config'); -var mod = require('./transforms/modifiers'); -var env = require('./transforms/env'); - -/** - * Load initialization transforms - * - * | config - * | loaders - * | templates - * | options - * | middleware - * | plugins - * | load - * | engines - * | helpers - load helpers last - */ - -module.exports = function(app) { - app.transform('metadata', init.metadata); - app.transform('ignore', init.ignore); - app.transform('files', env.files); - - app.transform('env', env.env); - app.transform('pkg', env.pkg); - app.transform('keys', env.keys); - app.transform('paths', env.paths); - app.transform('cwd', env.cwd); - app.transform('repo', mod.repository); - app.transform('author', env.author); - app.transform('user', env.user); - app.transform('username', env.username); - app.transform('github', env.github); - app.transform('travis', env.travis); - app.transform('fork', env.fork); - app.transform('missing', env.missing); - - app.transform('github-url', mod.github_url); - app.transform('twitter-url', mod.twitter_url); - - app.once('loaded', function () { - app.transform('defaults', init.defaults); - app.transform('runner', init.runner); - app.transform('argv', init.argv); - app.transform('config', config); - app.transform('loaders', init.loaders); - app.transform('create', init.templates); - app.transform('engines', init.engines); - app.transform('middleware', init.middleware); - app.transform('helpers', init.helpers); - app.transform('load', init.load); - app.transform('plugins', init.plugins); - app.emit('init'); - }); - - app.once('init', function () { - app.transform('helpers', init.helpers); - app.emit('finished'); - }); - - app.once('finished', function () { - app.transform('checkup', init.checkup); - }); -}; diff --git a/lib/local.js b/lib/local.js new file mode 100644 index 00000000..2a7ea55d --- /dev/null +++ b/lib/local.js @@ -0,0 +1,22 @@ +'use strict'; + +var get = require('get-value'); + +function Local(locals) { + this.locals = locals || {}; +} + +Local.prototype.get = function(key) { + return get(this.locals, key); +}; + +Local.prototype.set = function(key, value) { + set(this.locals, key, value); + return this; +}; + +/** + * Expose Local + */ + +module.exports = Local; diff --git a/lib/middleware/append.js b/lib/middleware/append.js deleted file mode 100644 index 35485316..00000000 --- a/lib/middleware/append.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -/** - * `append` post-render middleware. - * - * If a string is defined on `verb.cache.data.append`, - * it will be appended to the content of every file - * that matches the route. - */ - -module.exports = function(app) { - return function (file, next) { - file.content += app.get('data.append') || ''; - next(); - }; -}; diff --git a/lib/middleware/assets.js b/lib/middleware/assets.js deleted file mode 100644 index 27abde0c..00000000 --- a/lib/middleware/assets.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -/** - * Module dependencies - */ - -var path = require('path'); -var compute = require('computed-property'); -var relative = require('relative-dest'); -var extend = require('extend-shallow'); - -/** - * Calculate the path from the `assets` or `public` - * path define on the options to the destination - * file. - */ - -module.exports = function(assemble) { - return function assetsPath(file, next) { - calculate(assemble, file, 'assets'); - calculate(assemble, file, 'public'); - next(); - }; -}; - -function calculate(assemble, file, target) { - compute(file.data, target, function () { - var opts = extend({}, assemble.options, file.options); - - // destination directory for the file - var dest = path.resolve(this.dest.dirname || process.cwd()); - target = path.resolve(dest, opts[target] || target); - - // look for `opts.assets`, fallback to `./assets` - this.assetsPath = relative(dest, target); - return this.assetsPath; - }); -} diff --git a/lib/middleware/conflict.js b/lib/middleware/conflict.js deleted file mode 100644 index 1c066634..00000000 --- a/lib/middleware/conflict.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -var _ = require('lodash'); -var chalk = require('chalk'); -var extend = require('extend-shallow'); -var symbol = require('log-symbols'); - -/** - * Resolve conflicts between helpers and data - * properties before rendering. - */ - -module.exports = function conflict_(app) { - var config = extend({}, app.options, app.get('argv')); - - if (config.verbose) { - console.log(); - console.log(chalk.bold.underline('Checking for conflicts…')); - console.log(); - } - - return function (file, next) { - if (config.nolint === true) { - return next(); - } - - var str = file.content; - var res = helpers(str); - var conflicting = problematic(app, file, res, config.verbose); - var h = {}; - - if (!conflicting.length) { - return next(); - } - - var ctx = {}; - ctx.options = _.extend({}, app.options, file.options); - ctx.context = _.merge({}, app.cache.data, file.data, file.locals); - ctx.app = app; - - file.locals = file.locals || {}; - file.locals.__ = file.locals.__ || {}; - - // console.log('actual:', app._.helpers.sync) - for (var i = 0; i < conflicting.length; i++) { - var name = conflicting[i]; - file.content = namespace(file.content, name); - - var syncFn = function () { - return app._.helpers.sync[name].apply(this, arguments); - }; - - if (syncFn) { - h[name] = _.bind(syncFn, ctx); - file.locals.__[name] = _.bind(syncFn, ctx); - app.helpers({__: h}); - delete app._.helpers.sync[name]; - } - - var asyncFn = app._.helpers.async[name]; - if (asyncFn) { - h[name] = _.bind(asyncFn, ctx); - file.locals.__[name] = _.bind(asyncFn, ctx); - app.asyncHelpers({__: h}); - delete app._.helpers.async[name]; - } - - if (!asyncFn && !syncFn) { - h[name] = noop(name); - file.locals.__[name] = noop(name); - app.helpers({__: h}); - } - } - - next(); - }; -}; - -function noop(name) { - return function () { - var msg = ''; - msg += 'ERROR! Cannot find the {%= ' + name + '() %} helper. Helpers may be '; - msg += 'registered using `app.helper()`.'; - // console.log(chalk.red(msg)); - }; -} - -function namespace(str, name) { - return str.split('{%= ' + name).join('{%= __.' + name); -} - -function helpers(str) { - var re = /\{%=\s*((?!__\.)[\w.]+)(?:\(\)|\(([^)]*?)\))\s*%}/gm; - var res = [], match; - - while (match = re.exec(str)) { - if (res.indexOf(match[1]) === -1) { - res.push(match[1].trim()); - } - } - return res; -} - -function problematic(app, file, helpers, verbose) { - var dataKeys = Object.keys(app.cache.data); - dataKeys = _.union(dataKeys, Object.keys(file.data)); - - var registered = Object.keys(app._.helpers.async); - registered = _.union(registered, Object.keys(app._.helpers.sync)); - - var h = [], d = []; - var len = helpers.length; - - while (len--) { - var helper = helpers[len]; - // if the helper name is also a data prop, it's a conflict - if (dataKeys.indexOf(helper) !== -1 && helper.indexOf('.') === -1) { - d.push(helper); - } - - // if the helper is not registered, it's a conflict - if (registered.indexOf(helper) === -1 && helper.indexOf('.') === -1) { - h.push(helper); - } - } - - message(h, d, verbose, file); - return _.union(h, d); -} - - -function message(h, d, verbose, file) { - if (!verbose) return; - var fp = file.path.split(/[\\\/]/).slice(-2).join('/'); - - var hlen = h.length; - var dlen = d.length; - - if (hlen > 0) { - console.log(symbol.error, '', chalk.red(hlen + ' missing helper' + s(hlen) + ' found in', fp)); - h.forEach(function (ele) { - console.log(' -', ele); - }); - console.log(); - } - - if (dlen > 0) { - console.log(symbol.warning, '', chalk.yellow(dlen + ' data/helper conflict' + s(dlen) + ' found in', fp)); - d.forEach(function (ele) { - console.log(' -', ele); - }); - console.log(); - } -} - -function s(len) { - return len > 1 ? 's' : ''; -} diff --git a/lib/middleware/copyright.js b/lib/middleware/copyright.js deleted file mode 100644 index 80a3cab4..00000000 --- a/lib/middleware/copyright.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var update = require('update-copyright'); -var parse = require('parse-copyright'); - -/** - * Add copyright information to the context. - * - * ```js - * // get - * console.log(verb.get('data.copyright')); - * // or directly - * console.log(verb.cache.data.copyright); - * // used by the copyright helper - * {%= copyright() %} - * ``` - */ - -module.exports = function copyright_(verb) { - var copyright = verb.get('data.copyright'); - var readme = verb.files('README.md'); - var parsed = false; - - if (!parsed && !copyright && readme.length) { - var str = fs.readFileSync(readme[0], 'utf8'); - var res = update(str); - if (res) { - parsed = true; - verb.set('data.copyright.statement', res); - } - } - - return function (file, next) { - if (typeof copyright === 'string') return next(); - if (typeof copyright === 'object' && Object.keys(copyright).length) { - return next(); - } - copyright = verb.get('data.copyright') || parse(file.content)[0] || {}; - verb.set('data.copyright', copyright); - file.data.copyright = copyright; - next(); - }; -}; diff --git a/lib/middleware/debug.js b/lib/middleware/debug.js deleted file mode 100644 index 34557cab..00000000 --- a/lib/middleware/debug.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -var get = require('get-value'); -var _ = require('lodash'); - -/** - * Convenience method for debugging. - * - * **Examples** - * - * ```js - * {%= log(debug("app")) %} - * {%= log(debug("app.cache.data")) %} - * - * {%= log(debug("file")) %} - * {%= log(debug("file.data")) %} - * - * {%= log(debug()) %} - * {%= log(debug("this")) %} - * {%= log(debug("this.dest")) %} - * ``` - * @todo move to a helper - */ - -module.exports = function (app) { - return function(file, next) { - file.data.debug = file.data.debug || {}; - - file.data.debug = function (prop) { - var segs, root, type = typeof prop; - if (type !== 'undefined') { - segs = prop.split('.'); - root = segs.shift(); - segs = segs.join('.'); - } - - // get the (pseudo) context - if (root === 'this' || root === 'context' || type === 'undefined') { - var ctx = app.cache.data; - _.merge(ctx, file.data); - return filter(ctx, segs); - } - // get the file object - if (root === 'file') { - return filter(file, segs); - } - // get the app - if (root === 'app') { - return filter(app, segs); - } - }; - next(); - }; -}; - -function filter(obj, prop) { - var omitKeys = ['debug', '_contents', 'fn']; - if (typeof prop !== 'string' || prop === '') { - return _.omit(_.cloneDeep(obj), omitKeys); - } - return _.omit(_.cloneDeep(get(obj, prop)), omitKeys); -} diff --git a/lib/middleware/dest.js b/lib/middleware/dest.js deleted file mode 100644 index 7556fcfb..00000000 --- a/lib/middleware/dest.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -/** - * Prime the `file.data.dest` object. - */ - -module.exports = function(file, next) { - file.data.dest = file.data.dest || {}; - next(); -}; diff --git a/lib/middleware/diff.js b/lib/middleware/diff.js deleted file mode 100644 index f5d04b53..00000000 --- a/lib/middleware/diff.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var chalk = require('chalk'); -var relative = require('relative'); - -/** - * Show a diff comparison of pre- and post-render content. - */ - -module.exports = function(app) { - return function (file, next) { - var diff = app.get('argv.diff'); - - if (!diff) return next(); - console.log(); - console.log('- end -'); - console.log('---------------------------------------------------------------'); - console.log(relative(file.path)); - console.log(); - - if (file.content === '') { - console.log(chalk.gray(' no content.')); - } else if (file.content === file.orig) { - console.log(chalk.gray(' content is unchanged.')); - } else { - app.diff(file.orig, file.content, 'diffLines'); - } - next(); - }; -}; diff --git a/lib/middleware/engine.js b/lib/middleware/engine.js deleted file mode 100644 index 88f358e5..00000000 --- a/lib/middleware/engine.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -/** - * Set the engine to use - */ - -module.exports = function engine_(file, next) { - file.options.engine = file.options.engine || file.ext || '.md'; - next(); -}; diff --git a/lib/middleware/examples.js b/lib/middleware/examples.js deleted file mode 100644 index 00be4c9a..00000000 --- a/lib/middleware/examples.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -var extract = require('gfm-code-blocks'); - -/** - * Detect the layout to use - */ - -module.exports = function (app) { - return function (file, next) { - app.cache.data.examples = app.cache.data.examples || {}; - var examples = {}; - - var str = file.content; - extract(str).forEach(function (block) { - block.orig = block.block; - var m = /^\/\/\s*example\.([^\n]+)([\s\S]+)/.exec(block.code); - if (!m) return next(); - var name = m[1]; - examples[name] = examples[name] || []; - file.content = file.content.split(block.block).join(''); - block.block = '```js\n' + m[2] + '\n```\n'; - examples[name].push(block); - }); - - app.cache.data.examples = examples; - next(); - }; -}; diff --git a/lib/middleware/ext.js b/lib/middleware/ext.js deleted file mode 100644 index 06e631f0..00000000 --- a/lib/middleware/ext.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('../utils'); - -/** - * Ensure that `ext` is on the file object. - */ - -module.exports = function(file, next) { - if (!file.hasOwnProperty('data')) { - throw new Error('ext middleware: file object should have a `data` property.'); - } - - if (!file.data.hasOwnProperty('src')) { - throw new Error('ext middleware: file.data object should have a `src` property.'); - } - - file.ext = file.ext ? utils.formatExt(file.ext) : (file.data.src.ext || path.extname(file.path)); - next(); -}; diff --git a/lib/middleware/layout.js b/lib/middleware/layout.js deleted file mode 100644 index 764b5c6e..00000000 --- a/lib/middleware/layout.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -/** - * Detect the layout to use - */ - -module.exports = function(file, next) { - file.layout = file.layout - || file.data.layout - || file.locals.layout - || file.options.layout; - next(); -}; diff --git a/lib/middleware/lint-after.js b/lib/middleware/lint-after.js deleted file mode 100644 index 20c39542..00000000 --- a/lib/middleware/lint-after.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -var extend = require('extend-shallow'); - -/** - * Lints post-render content to find potential errors - * or incorrect content. - */ - -module.exports = function(app) { - var config = extend({}, app.options, app.get('argv')); - var diff = config.diff; - var lint = config.lint; - - return function (file, next) { - if (!lint) return next(); - var str = file.content, error = {}; - - if (/\[object Object\]/.test(str)) { - // only show the last one or two path segments - error.path = file.path.split(/[\\\/]/).slice(-2).join('/'); - error.message = '`[object Object]`'; - error.content = str; - - if (diff) { - app.diff(str, file.orig); - } - app.union('messages.errors', error); - } - next(); - }; -}; diff --git a/lib/middleware/lint.js b/lib/middleware/lint.js deleted file mode 100644 index 18203136..00000000 --- a/lib/middleware/lint.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -/** - * Resolve conflicts between helpers and data - * properties before rendering. - */ - -var lint = require('lint-templates'); - -module.exports = function(app) { - return function (file, next) { - lint(app, file); - next(); - }; -}; diff --git a/lib/middleware/matter.js b/lib/middleware/matter.js deleted file mode 100644 index 67fbd82c..00000000 --- a/lib/middleware/matter.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -var parser = require('parser-front-matter'); -var extend = require('extend-shallow'); - -/** - * Default middleware for parsing front-matter - */ - -module.exports = function (app) { - return function (file, next) { - var opts = extend({}, file.options, app.option('matter')); - parser.parse(file, opts, function (err) { - if (err) return next(err); - next(); - }); - }; -}; diff --git a/lib/middleware/multi-toc.js b/lib/middleware/multi-toc.js deleted file mode 100644 index 8e9be546..00000000 --- a/lib/middleware/multi-toc.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var glob = require('globby'); -var relative = require('relative'); -var toc = require('markdown-toc'); -var mdu = require('markdown-utils'); - -/** - * Generate a Table of Contents for a glob of files. - */ - -module.exports = function multitoc_(file, next) { - var str = file.content; - var i = str.indexOf('') + 3); - var pattern = strip(tag); - - var args = toArgs(pattern); - var files = []; - - if (args.length && args[1]) { - var opts = args[1] || {}; - files = glob.sync(args[0], opts).map(function (fp) { - return path.join(opts.cwd || process.cwd(), fp); - }); - } else { - files = glob.sync(args[0]); - } - - if (!files.length) return next(); - var res = files.map(generate).join('\n'); - file.content = str.split(tag).join(res); - next(); -}; - -function generate(fp, options) { - options = options || {}; - fp = path.join(options.cwd || process.cwd(), fp); - var str = fs.readFileSync(fp, 'utf8'); - var first = str.split('\n')[0].trim().slice(2).trim(); - - // don't generate a TOC for a template - if (/\{%=/.test(first)) return ''; - var res = ''; - - res += mdu.h2(mdu.link(first, relative(fp))); - res += '__AFTER__'; - res += '\n'; - - var table = toc(str, { - linkify: function(tok, heading) { - var url = relative(fp); - url += '/#'; - url += toc.slugify(heading); - tok.content = mdu.link(tok.content, url); - return tok; - } - }); - - res += table.content; - res += '\n'; - - res = res.split(/\n{2,}/).join('\n'); - res = res.split('__AFTER__').join('\n'); - return res; -} - -function strip(str) { - str = str.replace(//, ''); - return str.trim(); -} - -function toArgs(pattern) { - var args = pattern.split(',').filter(Boolean); - args[0] = args[0].trim().replace(/^['"]|['"]$/g, ''); - args[1] = args[1] ? JSON.parse(args[1].trim()) : null; - return args; -} diff --git a/lib/middleware/props.js b/lib/middleware/props.js deleted file mode 100644 index d97fdba1..00000000 --- a/lib/middleware/props.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -/** - * Prime the `file` object with properties that - * can be extended in plugins. - */ - -module.exports = function props_(file, next) { - file.options = file.options || {}; - file.locals = file.locals || {}; - file.data = file.data || {}; - next(); -}; diff --git a/lib/middleware/readme.js b/lib/middleware/readme.js deleted file mode 100644 index c79394b1..00000000 --- a/lib/middleware/readme.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -/** - * Set the dest path to `README.md` for `.verb.md` templates - */ - -module.exports = function(app) { - return function(file, next) { - if (app.isFalse('readme') || app.isTrue('noreadme')) { - return next(); - } - - if (file.readme === false || file.noreadme === true) { - return next(); - } - - if (/\.(?:verb(rc)?|readme)\.md$/i.test(file.path)) { - file.path = 'README.md'; - } - next(); - }; -}; diff --git a/lib/middleware/src.js b/lib/middleware/src.js deleted file mode 100644 index 8f001a13..00000000 --- a/lib/middleware/src.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var parse = require('parse-filepath'); -var path = require('path'); - -/** - * Set properties on `file.data.src` to use in plugins, - * other middleware, helpers, templates etc. - */ - -module.exports = function(file, next) { - var orig = file.options.originalPath; - - file.data.src = file.data.src || {}; - file.data.src.path = orig; - - // look for native `path.parse` method first - var parsed = typeof path.parse === 'function' - ? path.parse(orig) - : parse(orig); - - file.data.src.dirname = parsed.dir; - file.data.src.filename = parsed.name; - file.data.src.basename = parsed.base; - file.data.src.extname = parsed.ext; - file.data.src.ext = parsed.ext; - - file.data.process = {}; - file.data.process.cwd = function () { - return process.cwd(); - }; - - file.data.resolve = path.resolve; - next(); -}; diff --git a/lib/pkg.json b/lib/pkg.json deleted file mode 100644 index 5fcbabed..00000000 --- a/lib/pkg.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "", - "description": "", - "version": "", - "homepage": "", - "authors": [ - { - "name": "", - "email": "", - "url": "" - } - ], - "author": { - "name": "", - "email": "", - "url": "" - }, - "contributors": [], - "repository": { - "type": "git", - "url": "" - }, - "bugs": { - "url": "" - }, - "licenses": [ - { - "type": "", - "url": "" - } - ], - "license": { - "type": "MIT", - "url": "" - }, - "files": [], - "main": "", - "bin": {}, - "engines": {}, - "scripts": {}, - "dependencies": {}, - "devDependencies": {}, - "keywords": [], - "verb": { - "data": {} - }, - "github": { - "user": "", - "repo": "" - } -} diff --git a/lib/plugins/comments.js b/lib/plugins/comments.js deleted file mode 100644 index c2729c5f..00000000 --- a/lib/plugins/comments.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var extend = require('extend-shallow'); -var through = require('through2'); -var PluginError = require('plugin-error'); - -module.exports = function() { - var opts = extend({}, this.options, this.get('argv')); - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - try { - // strip code comments - var str = file.contents.toString(); - str = stripHbsComments(str, opts); - str = stripTmplComments(str, opts); - str = stripHtmlComments(str, opts); - - // rebuffer contents - file.contents = new Buffer(str); - this.push(file); - return cb(); - } catch (err) { - this.emit('error', new PluginError('comments plugin', err, {stack: true})); - return cb(); - } - }); -}; - -function stripHbsComments(str, opts) { - return strip(str, '{{!', '}}', opts); -} - -function stripTmplComments(str, opts) { - return strip(str, '{{#', '#}}', opts); -} - -function stripHtmlComments(str, opts) { - str = strip(str, '', opts); - return strip(str, '', opts); -} - -function strip(str, lt, rt, opts) { - if (opts && opts.stripComments === false) return str; - var a = str.indexOf(lt); - var b = str.indexOf(rt, a); - - while (a !== -1 && b !== -1) { - str = str.slice(0, a) + str.slice(b + rt.length); - a = str.indexOf(lt); - b = str.indexOf(rt, a); - } - return str; -} diff --git a/lib/plugins/conflicts.js b/lib/plugins/conflicts.js deleted file mode 100644 index 513845dd..00000000 --- a/lib/plugins/conflicts.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict'; - -var PluginError = require('plugin-error'); -var symbol = require('log-symbols'); -var through = require('through2'); -var yellow = require('ansi-yellow'); -var red = require('ansi-red'); -var _ = require('lodash'); - -module.exports = function(options) { - var config = this.get('argv') || {}; - options = _.extend({}, config, options); - var app = this; - - if (config.conflicts && config.verbose) { - console.log(); - console.log(yellow('Checking for conflicts…')); - } - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - if (config.nocheck) { - this.push(file); - return cb(); - } - - try { - var str = file.contents.toString(); - var res = helpers(str); - var conflicting = problematic(app, file, res, config.verbose); - var h = {}; - - if (!conflicting.length) { - this.push(file); - return cb(); - } - - var ctx = {}; - ctx.options = _.extend({}, app.options, file.options); - ctx.context = file.locals || {}; - ctx.config = app.config; - ctx.app = app; - - file.locals = file.locals || {}; - file.locals.__ = file.locals.__ || {}; - - for (var i = 0; i < conflicting.length; i++) { - var name = conflicting[i]; - file.content = namespace(file.content, name); - var syncFn = app._.helpers.getHelper(name); - if (syncFn) { - h[name] = _.bind(syncFn, ctx); - file.locals.__[name] = _.bind(syncFn, ctx); - app.helpers({__: h}); - delete app._.helpers[name]; - } - - var asyncFn = app._.asyncHelpers.getHelper(name); - if (asyncFn) { - h[name] = _.bind(asyncFn, ctx); - file.locals.__[name] = _.bind(asyncFn, ctx); - app.asyncHelpers({__: h}); - delete app._.asyncHelpers[name]; - } - - if (!asyncFn && !syncFn) { - h[name] = noop(name); - file.locals.__[name] = noop(name); - app.helpers({__: h}); - } - } - - file.contents = new Buffer(file.content); - this.push(file); - return cb(); - } catch (err) { - this.emit('error', new PluginError('conflicts plugin', err, {stack: true})); - return cb(); - } - }); -}; - -function noop(name) { - return function () { - var msg = ''; - msg += 'ERROR! Cannot find the {%= '; - msg += name; - msg += '() %} helper. Helpers may be '; - msg += 'registered using `app.helper()`.'; - // console.log(red(msg)); - }; -} - -function namespace(str, name) { - return str.split('{%= ' + name).join('{%= __.' + name); -} - -function helpers(str) { - var re = /\{%=\s*((?!__\.)[\w.]+)(?:\(\)|\(([^)]*?)\))\s*%}/gm; - var res = [], - match; - - while (match = re.exec(str)) { - if (res.indexOf(match[1]) === -1) { - res.push(match[1].trim()); - } - } - return res; -} - -function problematic(app, file, helpers, verbose) { - var dataKeys = Object.keys(app.cache.data); - dataKeys = _.union(dataKeys, Object.keys(file.data)); - var registered = Object.keys(app._.asyncHelpers); - registered = _.union(registered, Object.keys(app._.helpers)); - - var h = [], - d = []; - var len = helpers.length; - - while (len--) { - var helper = helpers[len]; - // if the helper name is also a data prop, it's a conflict - if (dataKeys.indexOf(helper) !== -1 && helper.indexOf('.') === -1) { - d.push(helper); - } - - // if the helper is not registered, it's a conflict - if (registered.indexOf(helper) === -1 && helper.indexOf('.') === -1) { - h.push(helper); - } - } - - message(h, d, verbose, file); - return _.union(h, d); -} - -function message(h, d, verbose, file) { - if (!verbose) return; - var fp = file.path.split(/[\\\/]/).slice(-2).join('/'); - - var hlen = h.length; - var dlen = d.length; - - if (hlen > 0) { - console.log(symbol.error, '', red(hlen + ' missing helper' + s(hlen) + ' found in', fp)); - h.forEach(function (ele) { - console.log(' -', ele); - }); - console.log(); - } - - if (dlen > 0) { - console.log(symbol.warning, '', yellow(dlen + ' data/helper conflict' + s(dlen) + ' found in', fp)); - d.forEach(function (ele) { - console.log(' -', ele); - }); - console.log(); - } -} - -function s(len) { - return len > 1 ? 's' : ''; -} diff --git a/lib/plugins/dest.js b/lib/plugins/dest.js deleted file mode 100644 index 5606fd47..00000000 --- a/lib/plugins/dest.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var path = require('path'); -var through = require('through2'); -var PluginError = require('plugin-error'); -var utils = require('../utils'); - -/** - * Add dest properties to `file.data` - */ - -module.exports = function destPlugin(destDir) { - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - try { - var dest = file.data.dest || {}; - if (typeof destDir === 'function') { - dest.dirname = destDir(file); - } else if (typeof destDir === 'string') { - dest.dirname = path.resolve(destDir); - } else { - dest.dirname = path.dirname(file.path); - } - - dest.cwd = file.cwd; - dest.base = file.base; - dest.relative = file.relative; - dest.extname = path.extname(file.relative); - dest.basename = path.basename(file.relative); - dest.filename = utils.basename(dest.basename, dest.extname); - dest.path = path.join(dest.dirname, dest.basename); - - file.data.__filename = dest.path; - file.data.__dirname = dest.dirname; - file.data.dest = dest; - - this.push(file); - return cb(); - } catch (err) { - this.emit('error', new PluginError('dest plugin', err, {stack: true})); - return cb(); - } - }); -}; diff --git a/lib/plugins/format.js b/lib/plugins/format.js deleted file mode 100644 index 339353c2..00000000 --- a/lib/plugins/format.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -var through = require('through2'); -var Remarkable = require('remarkable'); -var PluginError = require('plugin-error'); -var prettify = require('pretty-remarkable'); -var extend = require('extend-shallow'); - - -module.exports = function(locals) { - var argv = this.get('argv'); - var app = this; - - // add a trailing newline to docs? - var newline = argv.newline || this.config.get('newline'); - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - try { - if (file.path.indexOf('README.md') === -1 || noformat(app, file, locals, argv)) { - this.push(file); - return cb(); - } - - // pass some extra formatting info to `pretty-remarkable` - var opts = extend({}, locals, file.options); - opts.username = app.get('data.username'); - opts.name = app.get('data.author.name'); - - // prettify - var str = pretty(file.contents.toString(), opts); - str = str.trim() + (newline ? '\n' : ''); - str = fixList(str); - - // rebuffer contents - file.contents = new Buffer(str); - this.push(file); - return cb(); - } catch(err) { - this.emit('error', new PluginError('formatter plugin', err, {stack: true})); - return cb(); - } - }); -}; - -/** - * Fix list formatting - */ - -function fixList(str) { - str = str.replace(/([ ]{1,4}[+-] \[?[^)]+\)?)\n\n\* /gm, '$1\n* '); - str = str.split('__{_}_*').join('**{*}**'); - return str; -} - -/** - * Instantiate `Remarkable` and use the `prettify` plugin - * on the given `str`. - * - * @param {String} `str` - * @param {Object} `options` - * @return {String} - */ - -function pretty(str, options) { - return new Remarkable(options) - .use(prettify) - .render(str); -} - -/** - * Push the `file` through if the user has specfied - * not to format it. - */ - -function noformat(app, file, locals, argv) { - return app.isTrue('noformat') || app.isFalse('format') - || file.noformat === true || file.format === false - || locals.noformat === true || locals.format === false - || argv.noformat === true || argv.format === false; -} diff --git a/lib/plugins/index.js b/lib/plugins/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/plugins/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/lib/plugins/init.js b/lib/plugins/init.js deleted file mode 100644 index 772bdd39..00000000 --- a/lib/plugins/init.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var gutil = require('gulp-util'); -var PluginError = gutil.PluginError; -var through = require('through2'); - -module.exports = function () { - var app = this; - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - if (file.isStream()) { - this.emit('error', new PluginError('init plugin', 'Streaming is not supported.')); - return cb(); - } - - try { - var stream = this; - file.content = file.contents.toString(); - app.handle('onLoad', file, function (err) { - if (err) { - stream.emit('error', new PluginError('renderFile', err, {stack: true})); - cb(err); - return; - } - }); - this.push(file); - cb(); - } catch (err) { - this.emit('error', new PluginError('init plugin', err, {stack: true})); - cb(); - } - }); -}; diff --git a/lib/plugins/lint.js b/lib/plugins/lint.js deleted file mode 100644 index 891d2525..00000000 --- a/lib/plugins/lint.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var PluginError = require('plugin-error'); -var lint = require('lint-templates'); -var through = require('through2'); - -module.exports = function(options) { - var app = this; - return through.obj(function (file, enc, cb) { - try { - lint(app, file); - this.push(file); - return cb(); - } catch(err) { - this.emit('error', new PluginError('lint plugin', err, {stack: true})); - return cb(); - } - }); -}; diff --git a/lib/plugins/reflinks.js b/lib/plugins/reflinks.js deleted file mode 100644 index 01d585d0..00000000 --- a/lib/plugins/reflinks.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; - -var through = require('through2'); -var PluginError = require('plugin-error'); -var reflinks = require('helper-reflinks'); -var extend = require('extend-shallow'); -var isTrue = require('is-true'); - -/** - * Expose `reflinks` plugin - */ - -module.exports = function(options) { - options = options || {}; - var config = this.config && this.config.get('reflinks') || {}; - var argv = this.get('argv'); - var app = this; - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - var disabled = !enabled(app, file, options, argv, config); - if (disabled || !config) { - this.push(file); - return cb(); - } - - try { - app.union('reflinks', file.data.reflinks || []); - var list = app.get('reflinks') || []; - - if (!list || !list.length) { - this.push(file); - return cb(); - } - - var stream = this; - var str = file.contents.toString(); - - reflinks(list, function (err, res) { - if (err) { - stream.emit('error', new PluginError('reflinks plugin', err)); - return cb(); - } - - file.contents = new Buffer(str + '\n' + res); - stream.push(file); - return cb(); - }); - - } catch (err) { - this.emit('error', new PluginError('reflinks plugin', err)); - return cb(); - } - }); -}; - -/** - * Push the `file` through if the user has specfied - * not to generate reflinks. - */ - -function enabled(app, file, options, argv) { - var template = extend({}, file.locals, file.options, file.data); - return isTrue(argv, 'reflinks') - || isTrue(template, 'reflinks') - || isTrue(options, 'reflinks') - || isTrue(app.options, 'reflinks'); -} diff --git a/lib/plugins/render.js b/lib/plugins/render.js deleted file mode 100644 index bb8d9173..00000000 --- a/lib/plugins/render.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var PluginError = require('plugin-error'); -var through = require('through2'); -var _ = require('lodash'); - -/** - * Expose `render` plugin - */ - -module.exports = function (locals) { - var app = this; - var opts = _.extend({showStack: true}, this.options); - - locals = locals || {}; - locals.options = locals.options || {}; - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - locals = _.merge({}, locals, file.locals); - locals.options = _.merge({}, app.options, locals.options); - file.content = file.contents.toString(); - - try { - var stream = this; - app.render(file, locals, function (err, content) { - if (err) { - stream.emit('error', new PluginError('render plugin', err, opts)); - return cb(); - } - file.contents = new Buffer(content); - stream.push(file); - return cb(); - }); - } catch (err) { - this.emit('error', new PluginError('render plugin', err, opts)); - return cb(); - } - }); -}; diff --git a/lib/stack.js b/lib/stack.js deleted file mode 100644 index f10acb4f..00000000 --- a/lib/stack.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict'; - -/** - * Module dependencies. - */ - -var through = require('through2'); -var drafts = require('gulp-drafts'); -var es = require('event-stream'); -var vfs = require('vinyl-fs'); -var _ = require('lodash'); -var cache = {}; - -/** - * Local dependencies - */ - -var plugins = require('./plugins'); - -/** - * Default `src` plugins to run. - * - * To disable a plugin: - * - * ```js - * app.disable('src:foo plugin'); - * ``` - */ - -exports.src = function(app, glob, opts) { - opts = _.merge({}, app.options, opts); - cache = opts; - - return createStack(app, { - 'src:vfs': vfs.src(glob, opts), - 'src:init': plugins.init.call(app, opts), - 'src:lint': plugins.lint.call(app, opts), - 'src:conflicts': plugins.conflicts.call(app, opts), - 'src:drafts': drafts.call(app, opts) - }); -}; - -/** - * Default `dest` plugins to run. - * - * To disable a plugin: - * - * ```js - * app.disable('dest:bar plugin'); - * ``` - */ - -exports.dest = function (app, dest, opts) { - opts = _.merge({}, app.options, cache, opts); - opts.locals = opts.locals || {}; - - return createStack(app, { - 'dest:paths': plugins.dest.call(app, dest, opts.locals, opts), - 'dest:render': plugins.render.call(app, opts.locals, opts), - 'dest:reflinks': plugins.reflinks.call(app, opts.locals, opts), - 'dest:comments': plugins.comments.call(app), - 'dest:format': plugins.format.call(app, opts.locals, opts), - 'dest:vfs': vfs.dest(dest, opts) - }); -}; - -/** - * Create the default plugin stack based on user settings. - * - * Disable a plugin by passing the name of the plugin + ` plugin` - * to `app.disable()`, - * - * **Example:** - * - * ```js - * app.disable('src:foo plugin'); - * app.disable('src:bar plugin'); - * ``` - */ - -function createStack(app, plugins) { - if (app.enabled('minimal config')) { - return es.pipe.apply(es, []); - } - function enabled(acc, plugin, name) { - if (plugin == null) { - acc.push(through.obj()); - } - if (app.enabled(name + ' plugin')) { - acc.push(plugin); - } - return acc; - } - var arr = _.reduce(plugins, enabled, []); - return es.pipe.apply(es, arr); -} diff --git a/lib/transforms/config/del.js b/lib/transforms/config/del.js deleted file mode 100644 index f66b2175..00000000 --- a/lib/transforms/config/del.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var mm = require('micromatch'); -var bold = require('ansi-bold'); - -/** - * Delete a value from the config store. - * - * ```sh - * $ --del foo - * - * # delete multiple values - * $ --del foo,bar,baz - * ``` - */ - -module.exports = function(app) { - var del = app.get('argv.del'); - var config = app.config; - var keys = mm(Object.keys(config.data), del); - - if (keys.length) { - config.omit.apply(config, keys); - console.log('deleted:', bold(keys.join(', '))); - } -}; diff --git a/lib/transforms/config/get.js b/lib/transforms/config/get.js deleted file mode 100644 index f143da20..00000000 --- a/lib/transforms/config/get.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var bold = require('ansi-bold'); -var cyan = require('ansi-cyan'); - -/** - * Get a value from the config store. - * - * ```sh - * $ --get one - * # or - * $ --get one,two,three - * ``` - */ - -module.exports = function(app) { - var config = app.config; - var get = app.get('argv.get'); - - if (get) { - if (get === true || get === 'true') { - console.log(cyan('config config:'), stringify(config.data)); - } else if (get.indexOf(',') !== -1) { - get.split(',').forEach(function (val) { - console.log(val, '=', stringify(config.get(val))); - }); - } else { - console.log(get, '=', stringify(config.get(get))); - } - } -}; - -function stringify(val) { - return bold(JSON.stringify(val)); -} diff --git a/lib/transforms/config/index.js b/lib/transforms/config/index.js deleted file mode 100644 index bd259188..00000000 --- a/lib/transforms/config/index.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -var Config = require('data-store'); -var config = require('export-files')(__dirname); - -/** - * Initialize a global config store, for persisting data - * that may be reused across projects. - * - * Initialized in the `init` transform. - */ - -module.exports = function(verb) { - verb.config = new Config('verb'); - - verb.transform('set', config.set); - verb.transform('get', config.get); - verb.transform('del', config.del); - verb.transform('option', config.option); - verb.transform('union', config.union); -}; diff --git a/lib/transforms/config/option.js b/lib/transforms/config/option.js deleted file mode 100644 index 85035f87..00000000 --- a/lib/transforms/config/option.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -/** - * Persist a value on the config store. - * - * ```sh - * $ --option one=abc - * #=> {one: 'abc'} - * - * $ --option one - * #=> {one: true} - * ``` - */ - -module.exports = function(app) { - var config = app.config; - var args; - - var option = app.get('argv.option'); - if (option) { - args = option.split('='); - if (args.length === 2) { - config.set(args[0], args[1]); - } else { - config.set(args[0], true); - } - } - - var enable = app.get('argv.enable'); - if (enable) { - config.set(enable, true); - } - - var disable = app.get('argv.disable'); - if (disable) { - config.set(disable, false); - } -}; diff --git a/lib/transforms/config/set.js b/lib/transforms/config/set.js deleted file mode 100644 index 773aec9c..00000000 --- a/lib/transforms/config/set.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -/** - * Persist a value on the config store. - * - * ```sh - * $ --set one=abc - * #=> {one: 'abc'} - * - * $ --set one - * #=> {one: true} - * ``` - */ - -module.exports = function(app) { - var config = app.config; - var args; - - var set = app.get('argv.set'); - if (set) { - args = set.split('='); - if (args.length === 2) { - config.set(args[0], args[1]); - } else { - config.set(args[0], true); - } - } -}; diff --git a/lib/transforms/config/union.js b/lib/transforms/config/union.js deleted file mode 100644 index 0abd17dc..00000000 --- a/lib/transforms/config/union.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var union = require('arr-union'); - -/** - * Get a value from the config store. - * - * ```sh - * $ --union one=a,b,c - * #=> {one: ['a', 'b', 'c']} - * - * $ --union one=d - * #=> {one: ['a', 'b', 'c', 'd']} - * ``` - */ - -module.exports = function(app) { - var arr = app.get('argv.union'); - var config = app.config; - var args; - - if (arr) { - args = arr.split('='); - if (args.length > 1) { - var val = config.get(args[1]); - args[2] = args[2].split(','); - config.set(args[1], union(val, args[2])); - } - } -}; diff --git a/lib/transforms/env/author.js b/lib/transforms/env/author.js deleted file mode 100644 index 6ecff18f..00000000 --- a/lib/transforms/env/author.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -var isPlainObject = require('is-plain-object'); -var author = require('parse-author'); - -/** - * Called in the `init` transform. Adds an `author` - * property to the context, or normalizes the existing one. - */ - -module.exports = function(app) { - var res = app.get('data.author'); - if (isPlainObject(res)) return; - - if (typeof res === 'string') { - app.data({author: author(res)}); - } else { - app.data({author: {}}); - } -}; diff --git a/lib/transforms/env/cwd.js b/lib/transforms/env/cwd.js deleted file mode 100644 index 46b0899c..00000000 --- a/lib/transforms/env/cwd.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -/** - * Get/set the current working directory - * - * ```js - * console.log(verb.cwd); - * //=> /dev/foo/bar/ - * ``` - * Or set: - * - * ```js - * verb.cwd = 'foo'; - * ``` - */ - -module.exports = function(app) { - var cwd = app.option('cwd') || process.cwd(); - - Object.defineProperty(app, 'cwd', { - configurable: true, - get: function () { - return cwd; - }, - set: function (val) { - cwd = val; - } - }); -}; diff --git a/lib/transforms/env/env.js b/lib/transforms/env/env.js deleted file mode 100644 index 3c54ac4f..00000000 --- a/lib/transforms/env/env.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var path = require('path'); -var merge = require('lodash')._.merge; - -/** - * Getter for getting stored values from `verb.env`, a read-only - * object that stores the project's package.json before it's modified. - * - * ```js - * console.log(verb.env.name); - * //=> 'my-project' - * ``` - * - * @return {*} The value of specified property. - * @api public - */ - -module.exports = function(app) { - var env = app.env || app.option('env') || require(path.resolve('package.json')); - merge(env, (env.verb && env.verb.data) || {}); - - Object.defineProperty(app, 'env', { - get: function () { - return env; - }, - set: function () { - console.log('verb.env is read-only and cannot be modified.'); - } - }); -}; diff --git a/lib/transforms/env/files.js b/lib/transforms/env/files.js deleted file mode 100644 index 80117b26..00000000 --- a/lib/transforms/env/files.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var mm = require('micromatch'); -var glob = require('globby'); - -/** - * Loads files from the root of the current project. - * Returns matching function bound to the list of files. - * - * ```js - * console.log(verb.files('*.js')); - * //=> ['foo.js', 'bar.js'] - * ``` - */ - -module.exports = function(verb) { - var patterns = ['*', 'lib/*', 'test/*']; - var files = glob.sync(patterns, {dot: true}); - - verb.files = function (pattern, options) { - return mm(files, pattern, options); - }; - - verb.fileExists = function (pattern, options) { - var res = verb.files(pattern, options); - return !!(res && res.length); - }; -}; diff --git a/lib/transforms/env/fork.js b/lib/transforms/env/fork.js deleted file mode 100644 index 0e1f6080..00000000 --- a/lib/transforms/env/fork.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -/** - * Adds a `fork` URL to the context - */ - -module.exports = function(app) { - var repo = app.get('data.repository'); - if (! repo) { - return; - } - var url = repo.url.replace(/\.git$/, ''); - url = url.replace(/git:\/\//, 'https://'); - app.data({fork: url + '/fork'}); -}; diff --git a/lib/transforms/env/git.js b/lib/transforms/env/git.js deleted file mode 100644 index 92440275..00000000 --- a/lib/transforms/env/git.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var username = require('git-user-name'); - -/** - * Called in the `username` transform, if a `username` - * cannot be determined from easier means, this attempts - * to get the `user.name` from global `.git config` - */ - -module.exports = function(verb) { - if (!verb.get('data.git.username')) { - verb.set('data.git.username', username()); - } -}; diff --git a/lib/transforms/env/github.js b/lib/transforms/env/github.js deleted file mode 100644 index 5adbc3d0..00000000 --- a/lib/transforms/env/github.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var parse = require('parse-github-url'); - -/** - * Called in the `init` transform. Adds a `github` - * property to the context. - * - * @param {Object} `verb` - */ - -module.exports = function(verb) { - var repo = verb.get('data.repository'); - var url = (repo && typeof repo === 'object') - ? repo.url - : repo; - - var github = parse(url); - if (github && Object.keys(github).length) { - verb.data({github: github}); - } -}; diff --git a/lib/transforms/env/ignore.js b/lib/transforms/env/ignore.js deleted file mode 100644 index cda93f2a..00000000 --- a/lib/transforms/env/ignore.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var parse = require('parse-gitignore'); -var union = require('lodash')._.union; -var utils = require('../../utils'); - -/** - * Read `.gitignore` and add patterns to - * `options.ignore` - */ - -module.exports = function(verb) { - if (typeof verb.cache.data.ignore === 'undefined') { - var ignore = union(verb.option('ignore'), verb.env.ignore); - verb.data({ignore: gitignore(verb.cwd, '.gitignore', ignore)}); - } -}; - -/** - * Parse the local `.gitignore` file and add - * the resulting ignore patterns. - */ - -function gitignore(cwd, fp, arr) { - fp = path.resolve(cwd, fp); - if (!fs.existsSync(fp)) { - return utils.defaultIgnores; - } - var str = fs.readFileSync(fp, 'utf8'); - return parse(str, arr); -} diff --git a/lib/transforms/env/index.js b/lib/transforms/env/index.js deleted file mode 100644 index 3a9b8a9b..00000000 --- a/lib/transforms/env/index.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -module.exports = require('export-files')(__dirname); - -/** - * Load the project environment. Starting with - * package.json and .gitignore. - */ - -// module.exports = function(app) { -// app.transform('env env', env.env); -// app.transform('env ignore', env.ignore); -// app.transform('env files', env.files); -// app.transform('env keys', env.keys); -// app.transform('env github', env.github); -// app.transform('env travis', env.travis); -// app.transform('env user', env.user); -// app.transform('env author', env.author); -// app.transform('env username', env.username); -// app.transform('env license', env.license); -// app.transform('env licenses', env.licenses); -// }; diff --git a/lib/transforms/env/keys.js b/lib/transforms/env/keys.js deleted file mode 100644 index e3fce9e6..00000000 --- a/lib/transforms/env/keys.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -/** - * Adds `Object.keys()` to `verb.cache.data`. Can be used - * for conflict resolution between helpers and context properties. - */ - -module.exports = function(verb) { - if (!verb.cache.data.hasOwnProperty('keys')) { - verb.cache.data.keys = Object.keys(verb.env); - } -}; diff --git a/lib/transforms/env/missing.js b/lib/transforms/env/missing.js deleted file mode 100644 index e332f605..00000000 --- a/lib/transforms/env/missing.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -/** - * Called in the `init` transform to ensure that certain - * fields are on the context. - * - * @param {Object} `verb` - */ - -module.exports = function(verb) { - verb.cache.missing = verb.cache.missing || {}; - - if (!verb.cache.data.hasOwnProperty('licenses')) { - verb.union('messages.missing.data', ['licenses']); - } - - if (!verb.cache.data.hasOwnProperty('license')) { - verb.union('messages.missing.data', ['license']); - } -}; diff --git a/lib/transforms/env/paths.js b/lib/transforms/env/paths.js deleted file mode 100644 index c1562bfb..00000000 --- a/lib/transforms/env/paths.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -/** - * Prime the `verb.paths` object. - */ - -module.exports = function(app) { - app.cache.paths = app.cache.paths || {}; -}; diff --git a/lib/transforms/env/pkg.js b/lib/transforms/env/pkg.js deleted file mode 100644 index 118b8646..00000000 --- a/lib/transforms/env/pkg.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var utils = require('../../utils'); -var cache = {}; - -/** - * Called in the `init` transform. A read-only copy of this data is also - * stored on `verb.env` - * - * @param {Object} `verb` - */ - -module.exports = function(verb) { - var filename = verb.option('config') || 'package.json'; - - verb.data(filename, function (fp) { - return cache[fp] || (cache[fp] = utils.tryRequire(fp) || {}); - }); -}; diff --git a/lib/transforms/env/templates.js b/lib/transforms/env/templates.js deleted file mode 100644 index 38110d2a..00000000 --- a/lib/transforms/env/templates.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -var path = require('path'); - -/** - * Get/set the current working directory - * - * ```js - * console.log(verb.templates); - * //=> /dev/foo/bar/ - * ``` - * Or set: - * - * ```js - * verb.templates = 'foo'; - * ``` - */ - -module.exports = function(app) { - var dir = app.option('templates'); - if (typeof dir === 'undefined') { - dir = path.join(process.cwd(), 'templates'); - } - app.set('paths.templates'); -}; diff --git a/lib/transforms/env/travis.js b/lib/transforms/env/travis.js deleted file mode 100644 index 30128f99..00000000 --- a/lib/transforms/env/travis.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var stringify = require('stringify-travis-url'); - -/** - * Called in the `init` transform. Adds a `travis_url` - * property to the context. - */ - -module.exports = function(app) { - var github = app.get('data.github'); - if (! github) { - return; - } - var travis = stringify(github.user, github.repo); - app.set('data.travis_url', travis); -}; diff --git a/lib/transforms/env/user.js b/lib/transforms/env/user.js deleted file mode 100644 index c4848d11..00000000 --- a/lib/transforms/env/user.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -/** - * Called in the `init` transform. Adds `user` and `username` - * for the current project to the context. - */ - -module.exports = function(app) { - app.set('data.username', app.get('data.github.username')); -}; diff --git a/lib/transforms/env/username.js b/lib/transforms/env/username.js deleted file mode 100644 index 8a28c5c6..00000000 --- a/lib/transforms/env/username.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -var github = require('parse-github-url'); - -/** - * Called in the `username` transform, if the `git` transform - * was not able to find anything, this attempts to generate a - * username from other fields. - */ - -module.exports = function(app) { - if (!app.get('data.github.username')) { - var author = app.get('data.author'); - if (typeof author.url === 'string' && /\/github/.test(author.url)) { - var parsed = github(author.url); - var user = (parsed && parsed.user) || ''; - app.set('data.github.username', user); - app.set('data.username', user); - } - } -}; diff --git a/lib/transforms/init/argv.js b/lib/transforms/init/argv.js deleted file mode 100644 index 1bd03b9f..00000000 --- a/lib/transforms/init/argv.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -/** - * Prime the `verb.cache.argv` object. Used for setting values - * that are passed from the command line. - */ - -module.exports = function(app) { - app.cache.argv = app.cache.argv || {}; -}; diff --git a/lib/transforms/init/data.js b/lib/transforms/init/data.js deleted file mode 100644 index e177388a..00000000 --- a/lib/transforms/init/data.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('../../utils'); - -/** - * Prime `verb.cache.data` with empty package.json fields that - * will be over-written by the user's environment. - */ - -module.exports = function(app) { - app.data('../../pkg.json', function (fp) { - return utils.tryRequire(path.resolve(__dirname, fp)) || {}; - }); -}; diff --git a/lib/transforms/init/defaults.js b/lib/transforms/init/defaults.js deleted file mode 100644 index c930639c..00000000 --- a/lib/transforms/init/defaults.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var cwd = require('cwd'); - -/** - * Define default options. - */ - -module.exports = function(app) { - app.defaults('templates.examples', cwd('.')); - app.defaults('templates.includes', require('readme-includes')); - app.defaults('templates.badges', require('readme-badges')); - app.defaults('templates.docs', cwd('docs')); - - app.option({ - toc: {append: '\n\n_(Table of contents generated by [verb])_'} - }); - - // engines - app.option('view engine', 'md'); - - // routing - app.option('router methods'); - app.enable('case sensitive routing'); - app.enable('strict routing'); - - // delimiters - app.option('layoutDelims', ['<<%', '%>>']); - app.option('escapeDelims', ['{%%', '{%']); -}; diff --git a/lib/transforms/init/engines.js b/lib/transforms/init/engines.js deleted file mode 100644 index a0baccbe..00000000 --- a/lib/transforms/init/engines.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -/** - * Load built-in engines - */ - -module.exports = function(app) { - app.engine('md', require('engine-lodash'), { - delims: ['{%', '%}'] - }); -}; diff --git a/lib/transforms/init/helpers.js b/lib/transforms/init/helpers.js deleted file mode 100644 index ec2685e8..00000000 --- a/lib/transforms/init/helpers.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -/** - * Load default helpers - */ - -module.exports = function(app) { - require('../../helpers/sync')(app); - require('../../helpers/async')(app); - require('../../helpers/collections')(app); -}; diff --git a/lib/transforms/init/index.js b/lib/transforms/init/index.js deleted file mode 100644 index 874ea554..00000000 --- a/lib/transforms/init/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('export-files')(__dirname); diff --git a/lib/transforms/init/load.js b/lib/transforms/init/load.js deleted file mode 100644 index 70ae2728..00000000 --- a/lib/transforms/init/load.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var utils = require('../../utils'); -var path = require('path'); -var cwd = require('cwd'); - -/** - * Pre-load templates for built-in template types: - * | examples - * | includes - * | badges - * | docs - */ - -module.exports = function(app) { - app.badges('**/*.md', opts(app, 'badges', 'readme-badges')); - app.includes('**/*.md', opts(app, 'includes', 'readme-includes')); - app.examples('example.js', { cwd: cwd('.'), cache: true }); - app.docs('**/*.md', opts(app, 'docs')); -}; - -function opts(app, name) { - var paths = app.config.get('templates'); - var defaults = app.option('defaults.templates'); - var res = { cwd: process.cwd(), cache: true }; - - if (paths && paths.hasOwnProperty(name)) { - res.cwd = utils.tryRequire(paths[name]); - if (res.cwd) return res; - res.cwd = path.resolve(process.cwd(), paths[name]); - if (res.cwd) return res; - } - - if (defaults.hasOwnProperty(name)) { - res.cwd = utils.tryRequire(defaults[name]); - if (res.cwd) return res; - res.cwd = path.resolve(process.cwd(), defaults[name]); - } - return res; -} diff --git a/lib/transforms/init/loaders.js b/lib/transforms/init/loaders.js deleted file mode 100644 index 455e82f9..00000000 --- a/lib/transforms/init/loaders.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -var base = require('base-loader'); -var task = require('init-file-loader'); - -/** - * Load built-in loaders - */ - -module.exports = function(app) { - app.loader('base', [base]); - app.loader('task', [task]); -}; diff --git a/lib/transforms/init/metadata.js b/lib/transforms/init/metadata.js deleted file mode 100644 index 190579f7..00000000 --- a/lib/transforms/init/metadata.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -var path = require('path'); - -/** - * Called in the `init` transform. Adds Verb's package.json - * data to `verb.metadata`. - * - * @param {Object} `app` - */ - -module.exports = function(app) { - Object.defineProperty(app, 'metadata', { - get: function () { - return require(path.resolve(__dirname, '../../..', 'package.json')); - }, - set: function () { - console.log('`verb.metadata` is read-only and cannot be modified.'); - } - }); -}; diff --git a/lib/transforms/init/middleware.js b/lib/transforms/init/middleware.js deleted file mode 100644 index 8f0cb5bb..00000000 --- a/lib/transforms/init/middleware.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -var middleware = require('../../middleware/'); -var mu = require('middleware-utils'); - -/** - * Initialize default middleware - */ - -module.exports = function(app) { - var delims = mu.delims(); - - app.onLoad(/./, mu.parallel([ - middleware.props, - middleware.engine, - middleware.src, - middleware.ext, - middleware.dest, - middleware.layout, - middleware.debug(app), - ]), mu.error('onLoad (js)')) - - .onLoad(/\.js$/, mu.parallel([ - middleware.copyright(app), - ]), mu.error('onLoad (js)')) - - .onLoad(/\.md$/, mu.series([ - middleware.copyright(app), - require('template-toc')(app), - middleware.examples(app), - delims.escape(), - ]), mu.error('onLoad (md)')) - - .preRender(/\.md$/, mu.parallel([ - middleware.readme(app), - ]), mu.error('preRender (md)')) - - .postRender(/\.md$/, mu.parallel([ - delims.unescape(), - middleware['lint-after'](app), - middleware.diff(app), - ]), mu.error('postRender')) -}; diff --git a/lib/transforms/init/plugins.js b/lib/transforms/init/plugins.js deleted file mode 100644 index 2821ecfd..00000000 --- a/lib/transforms/init/plugins.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -/** - * Load default settings - */ - -module.exports = function(app) { - // default `src` plugins - app.enable('src:vfs plugin'); - app.enable('src:init plugin'); - app.enable('src:conflicts plugin'); - app.enable('src:lint plugin'); - app.enable('src:drafts plugin'); - - // default `dest` plugins - app.enable('dest:todos plugin'); - app.enable('dest:paths plugin'); - app.enable('dest:render plugin'); - app.enable('dest:reflinks plugin'); - app.enable('dest:comments plugin'); - app.enable('dest:format plugin'); - app.enable('dest:vfs plugin'); -}; diff --git a/lib/transforms/init/runner.js b/lib/transforms/init/runner.js deleted file mode 100644 index dd33244e..00000000 --- a/lib/transforms/init/runner.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -/** - * Adds data to the context for the current `verb.runner`. - */ - -module.exports = function(app) { - app.data({ - runner: { - name: 'verb-cli', - url: 'https://github.com/assemble/verb-cli' - } - }); -}; diff --git a/lib/transforms/init/templates.js b/lib/transforms/init/templates.js deleted file mode 100644 index 84798667..00000000 --- a/lib/transforms/init/templates.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -/** - * Create built-in template types, using the `base` loader - */ - -module.exports = function(app) { - app.create('example', {isRenderable: true}, ['base']); - app.create('include', {isRenderable: true}, ['base']); - app.create('badge', {isRenderable: true}, ['base']); - app.create('doc', {isRenderable: true}, ['base']); -}; diff --git a/lib/transforms/modifiers/github_url.js b/lib/transforms/modifiers/github_url.js deleted file mode 100644 index 25b60865..00000000 --- a/lib/transforms/modifiers/github_url.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -/** - * Modifier to add a github URl if a github username - * exists on the context. - */ - -module.exports = function github_(verb) { - var username = verb.get('data.github.username'); - if (username) { - verb.set('data.github.url', 'https://github/' + username); - } -}; diff --git a/lib/transforms/modifiers/index.js b/lib/transforms/modifiers/index.js deleted file mode 100644 index 874ea554..00000000 --- a/lib/transforms/modifiers/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('export-files')(__dirname); diff --git a/lib/transforms/modifiers/repository.js b/lib/transforms/modifiers/repository.js deleted file mode 100644 index f9128bc2..00000000 --- a/lib/transforms/modifiers/repository.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -/** - * Called in the `init` transform. Normalizes the - * `repository` field. - */ - -module.exports = function(app) { - var repo = app.get('data.repository'); - if (typeof repo === 'string') { - repo = 'git://github.com/' + repo + '.git'; - app.data({repository: {url: repo}}); - } -}; diff --git a/lib/transforms/modifiers/twitter_url.js b/lib/transforms/modifiers/twitter_url.js deleted file mode 100644 index 2a346a99..00000000 --- a/lib/transforms/modifiers/twitter_url.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -/** - * Modifier to add a twitter URl if a twitter username - * exists on the context. - */ - -module.exports = function twitter_(verb) { - var username = verb.get('data.twitter.username'); - if (username) { - verb.set('data.twitter.url', 'http://twitter/' + username); - } -}; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 00000000..93af4293 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,112 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); + +/** + * Lazily required module dependencies + */ + +var lazy = require('lazy-cache')(require); + +// type utils +lazy('mixin-deep', 'merge'); +lazy('global-modules', 'gm'); +lazy('expand-tilde', 'tilde'); + +// file/view utils +lazy('stream-combiner', 'combine'); +lazy('through2', 'through'); +lazy('vinyl-fs', 'vfs'); +lazy('vinyl', 'Vinyl'); +lazy('to-vinyl'); + +// engine/template utiles +lazy('parser-front-matter', 'matter'); +lazy('engine-handlebars', 'engine'); + +// task utils +lazy('composer-runtimes'); +lazy('src-stream'); +lazy('dest'); + +var utils = lazy; + +utils.runtimes = function(app) { + if (app.options.runtimes !== false) { + utils.composerRuntimes(app); + } +}; + +utils.basename = function(key) { + return path.basename(key, path.extname(key)); +}; + +utils.arrayify = function(val) { + return Array.isArray(val) ? val : [val]; +}; + +utils.tryRequire = function(name) { + try { + return require(name); + } catch(err) {} + return null; +}; + +utils.tryRead = function(fp) { + try { + return fs.readFileSync(fp); + } catch(err) {} + return null; +}; + +utils.npm = function(name) { + return utils.tryRequire(name) || utils.tryRequire(path.resolve(name)); +}; + +utils.identity = function(val) { + return val; +}; + +utils.resolveConfig = function (config, fn) { + fn = fn || utils.identity; + for (var key in config) { + if (config.hasOwnProperty(key)) { + var val = utils.npm(resolveDir(config[key])); + fn(key, val); + } + } +}; + +utils.layoutFn = function(app, view, cb) { + if (app.option('layoutFn')) { + app.options.layoutFn(view); + return cb(null, view); + } + if (!view.layout) { + view.layout = view.locals.layout; + } + if (!view.layout) { + view.layout = view.data.layout; + } + cb(null, view); +}; + +utils.reloadViews = function reloadViews(app, key) { + for (var name in app.views) { + if (app.views.hasOwnProperty(name)) { + var views = app.views[name]; + + if (!key || typeof app[name][key] !== 'function') { + app.create(name, app[name].options); + app[name].addViews(views); + } + } + } +}; + +/** + * Expose utils + */ + +module.exports = utils; diff --git a/lib/utils/defaultIgnores.js b/lib/utils/defaultIgnores.js deleted file mode 100644 index 65516abd..00000000 --- a/lib/utils/defaultIgnores.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -module.exports = [ - '*.DS_Store', - '.git', - 'bower_components', - 'node_modules', - 'npm-debug.log', - 'actual', - 'fixtures', - 'test/actual', - 'test/fixtures', - 'temp', - 'tmp', - 'vendor', - 'wip' -]; diff --git a/lib/utils/index.js b/lib/utils/index.js deleted file mode 100644 index cb1cc7f8..00000000 --- a/lib/utils/index.js +++ /dev/null @@ -1,153 +0,0 @@ -'use strict'; - -/** - * Module dependencies - */ - -var path = require('path'); -var mdu = require('markdown-utils'); -var mm = require('micromatch'); - -/** - * Expose `utils` - */ - -var utils = require('export-files')(__dirname); - -/** - * File cache - */ - -var cache = {}; - -/** - * Create a url from the given `domain`, optionally pass `true` - * as the second argument to use `https` as the protocol. - * - * @param {String} `domain` - * @param {String} `https` - * @return {String} - */ - -utils.linkify = function linkify(domain, https) { - return function (author) { - if (typeof author !== 'object') { - throw new TypeError('utils.linkify expects author to be an object.'); - } - - var username = author.username; - if (typeof username === 'undefined') { - username = this.context[domain] && this.context[domain].username; - } - - if (typeof username === 'undefined') { - username = this.config.get(domain + '.username'); - } - - if (typeof username === 'object') { - var o = username; - username = o.username; - } - - if (!username) return ''; - - var protocol = https ? 'https://' : 'http://'; - var res = mdu.link(domain + '/' + username, protocol + domain + '.com/' + username); - return '+ ' + res; - }; -}; - -/** - * Returns a matching function to use against - * the list of given files. - * - * @param {Array} `files` The list of files to match against. - * @return {Array} Array of matching files - */ - -utils.files = function files(arr) { - return function(pattern, options) { - return mm(arr, pattern, options); - }; -}; - -/** - * Try to require a file, fail silently. Encapsulating try-catches - * also helps with v8 optimizations. - */ - -utils.tryRequire = function tryRequire(fp) { - if (typeof fp !== 'string') { - throw new Error('utils.tryRequire() expects a string.'); - } - var key = 'tryRequire:' + fp; - if (cache.hasOwnProperty(key)) return cache[key]; - try { - return (cache[key] = require(fp)); - } catch(err) {} - try { - return (cache[key] = require(path.resolve(fp))); - } catch(err) {} - return null; -}; - -/** - * Get the basename of a file path, excluding extension. - * - * @param {String} `fp` - * @param {String} `ext` Optionally pass the extension. - */ - -utils.basename = function basename(fp, ext) { - if (typeof fp === 'undefined') { - throw new Error('utils.basename() expects a string.'); - } - return fp.substr(0, fp.length - (ext || path.extname(fp)).length); -}; - -/** - * Ensure that a file extension is formatted properly. - * - * @param {String} `ext` - */ - -utils.formatExt = function formatExt(ext) { - // could be filepath with no ext, e.g. `LICENSE` - if (typeof ext === 'undefined') return; - if (Array.isArray(ext)) return ext.map(utils.formatExt); - if (ext.charAt(0) !== '.') ext = '.' + ext; - return ext; -}; - -/** - * Cast `val` to an array. - */ - -utils.arrayify = function arrayify(val) { - var isArray = Array.isArray(val); - if (typeof val !== 'string' && !isArray) { - throw new Error('utils.arrayify() expects a string or array.'); - } - return isArray ? val : [val]; -}; - -/** - * Detect if the user has specfied not to render a file. - */ - -utils.norender = function norender(verb, file, locals) { - var ext = file && file.ext ? utils.formatExt(ext) : null; - if (ext && !verb.engines.hasOwnProperty(ext)) { - return false; - } - - return verb.isTrue('norender') || verb.isFalse('render') - || file.norender === true || file.render === false - || locals.norender === true || locals.render === false; -}; - -/** - * Expose `utils` - */ - -module.exports = utils; diff --git a/package.json b/package.json index 79bb7d5d..3d1c1f34 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,10 @@ { - "name": "verb", + "name": "verb2", "description": "Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes.", "version": "0.8.8", - "homepage": "https://github.com/verbose/verb", - "author": { - "name": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert" - }, - "repository": { - "type": "git", - "url": "git://github.com/verbose/verb.git" - }, + "homepage": "https://github.com/verbose/verb2", + "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "repository": "verbose/verb2", "bugs": { "url": "https://github.com/verbose/verb/issues" }, @@ -27,82 +21,49 @@ "test": "mocha" }, "dependencies": { - "ansi-bold": "^0.1.1", - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "ansi-yellow": "^0.1.1", - "arr-union": "^3.0.0", - "async-helper-base": "^0.2.0", - "base-loader": "^0.1.0", - "chalk": "^1.1.1", - "composer": "^0.3.0", - "computed-property": "^0.1.2", - "cwd": "^0.8.0", - "data-store": "^0.8.2", - "diff": "^2.0.2", - "engine-lodash": "^0.8.0", - "event-stream": "^3.3.1", + "assemble-loader": "^0.1.3", + "composer": "^0.4.1", + "composer-runtimes": "^0.3.1", + "data-store": "^0.9.0", + "dest": "^0.2.1", + "engine-handlebars": "^0.8.0", + "expand-tilde": "^1.2.0", "export-files": "^2.1.0", - "extend-shallow": "^2.0.1", - "get-value": "^1.1.5", - "gfm-code-blocks": "^0.3.0", - "git-user-name": "^1.1.2", - "globby": "^2.1.0", - "gulp-drafts": "^0.2.0", - "helper-changelog": "^0.1.0", - "helper-codelinks": "^0.1.2", - "helper-copyright": "^1.4.0", - "helper-coverage": "^0.1.2", - "helper-date": "^0.2.2", - "helper-license": "^0.2.1", - "helper-reflinks": "^1.4.1", - "helper-related": "^0.10.0", - "helper-resolve": "^0.3.1", - "helper-toc": "^0.2.0", - "init-file-loader": "^0.1.1", - "is-plain-object": "^2.0.1", - "is-true": "^0.1.0", - "js-comments": "^0.5.4", - "lint-templates": "^0.1.2", - "lodash": "^3.10.1", - "log-symbols": "^1.0.2", - "logging-helpers": "^0.4.0", - "markdown-toc": "^0.11.5", + "global-modules": "^0.2.0", + "lazy-cache": "^0.2.3", + "map-config": "^0.1.0", "markdown-utils": "^0.7.1", - "micromatch": "^2.2.0", - "middleware-utils": "^0.1.4", - "parse-author": "^0.2.0", - "parse-copyright": "^0.4.0", - "parse-filepath": "^0.6.3", - "parse-github-url": "^0.1.1", - "parse-gitignore": "^0.2.0", + "mixin-deep": "^1.1.3", "parser-front-matter": "^1.2.5", - "plugin-error": "^0.1.2", - "pretty-remarkable": "^0.1.8", "readme-badges": "^0.3.3", "readme-includes": "^0.2.9", - "relative": "^3.0.1", - "relative-dest": "^0.1.0", - "remarkable": "^1.6.0", - "stringify-travis-url": "^0.1.0", - "template": "^0.14.3", - "template-helper-apidocs": "^0.4.4", - "template-helpers": "^0.3.2", - "template-toc": "^0.3.3", + "src-stream": "^0.1.1", + "stream-combiner": "^0.2.2", + "templates": "jonschlinkert/templates#collections", "through2": "^2.0.0", - "update-copyright": "^0.1.0", - "vinyl-fs": "^1.0.0", - "year": "^0.2.1" + "to-vinyl": "^0.2.0", + "vinyl": "wearefractal/vinyl", + "vinyl-fs": "wearefractal/vinyl-fs" }, "devDependencies": { + "buffer-equal": "0.0.1", "consolidate": "^0.13.1", + "engine-base": "^0.1.2", + "event-stream": "^3.3.1", + "for-own": "^0.1.3", + "get-value": "^1.2.1", + "globby": "^3.0.1", + "graceful-fs": "^4.1.2", + "gulp": "^3.9.0", "gulp-istanbul": "^0.10.0", "gulp-jshint": "^1.11.2", "gulp-mocha": "^2.1.3", - "gulp-util": "^3.0.6", - "handlebars": "^3.0.3", + "is-buffer": "^1.0.2", "jshint-stylish": "^2.0.1", + "minimist": "^1.2.0", "mocha": "*", + "object.omit": "^2.0.0", + "rimraf": "^2.4.3", "should": "*", "swig": "^1.4.2" }, @@ -141,6 +102,37 @@ "support" ] }, + "collections": { + "docs": { + "options": { + "cwd": "fixtures/foo" + }, + "set": { + "a.b.c": "d" + } + }, + "includes": { + "options": { + "cwd": "fixtures/bar" + } + }, + "badges": { + "options": { + "cwd": "fixtures/baz" + }, + "addViews": { + "foo": "this is a badge" + } + } + }, + "helpers": { + "helper": { + "whatever": "@/helper-related" + }, + "asyncHelper": { + "whatever": "@/helper-related" + } + }, "related": { "list": [ "composer", @@ -149,14 +141,12 @@ "engine" ] }, - "reflinks": { - "list": [ - "jsdiff", - "option-cache", - "template", - "plasma", - "config-cache" - ] - } + "reflinks": [ + "jsdiff", + "option-cache", + "template", + "plasma", + "config-cache" + ] } } diff --git a/recipes/api-docs.md b/recipes/api-docs.md deleted file mode 100644 index 045f73b0..00000000 --- a/recipes/api-docs.md +++ /dev/null @@ -1,13 +0,0 @@ - -```js -var verb = require('verb'); -var tap = require('gulp-tap'); - -verb.task('api', function () { - verb.src('api/**/*.md') - // .pipe(tap(function (file) { - // console.log('file', file.contents.toString()); - // })) - .pipe(verb.dest('api-dest')); -}); -``` \ No newline at end of file diff --git a/recipes/api-documentation.md b/recipes/api-documentation.md deleted file mode 100644 index dd699eaf..00000000 --- a/recipes/api-documentation.md +++ /dev/null @@ -1,17 +0,0 @@ -# API docs - -> Writing and maintaining API documentation with Verb - - -```js -var verb = require('verb'); -var tap = require('gulp-tap'); - -verb.task('api', function () { - verb.src('api/**/*.md') - // .pipe(tap(function (file) { - // console.log('file', file.contents.toString()); - // })) - .pipe(verb.dest('api-dest')); -}); -``` diff --git a/recipes/generate-a-table-of-contents.md b/recipes/generate-a-table-of-contents.md deleted file mode 100644 index da3ae28c..00000000 --- a/recipes/generate-a-table-of-contents.md +++ /dev/null @@ -1,25 +0,0 @@ -# Generate a Table of Contents - -> Single or multi-file! - -For a single-file TOC, add the following to the place where you want the TOC to render: - -```html - -``` - -For a multi-file TOC, add the following to the place where you want the TOC to render: - -```html - -``` - -**Escaping** - -If you ever need to add a TOC comment to documentation for illustrative purposes and you do not want it to render: - -Add double exclamation points, like this: - -```html - -``` diff --git a/recipes/template-data.md b/recipes/template-data.md deleted file mode 100644 index 0b92a62c..00000000 --- a/recipes/template-data.md +++ /dev/null @@ -1,38 +0,0 @@ -## Template data - -> Load data to pass to templates - - -```js -var verb = require('verb'); -``` - -```js -verb.data('data/*.yml'); -verb.layout('default.md', '\n<<% body %>>\n'); -verb.includes('includes/*.md'); -verb.docs('docs/*.md'); - -// verb.helper('comments', require('verb-helper-comments')); - -verb.helperAsync('docs', function (name, locals, cb) { - var doc = this.cache.docs[name]; - this.render(doc, locals, function (err, content) { - if (err) return cb(err); - cb(null, content); - }); -}); - -verb.task('foo', function() { - verb.src('.verbrc.md') - .pipe(verb.dest('temp')); -}); - -verb.task('readme', function() { - verb.src('.verbrc.md') - // .pipe(dest(':dest/:basename')) - .pipe(verb.dest('./')); -}); - -verb.task('default', ['foo', 'readme']); -``` \ No newline at end of file diff --git a/recipes/using-layouts.md b/recipes/using-layouts.md deleted file mode 100644 index 4154e9b9..00000000 --- a/recipes/using-layouts.md +++ /dev/null @@ -1,21 +0,0 @@ -## Using layouts - -> Layouts are used to wrap other templates with common or project-wide content. - -In your verbfile.js: - -```js -var verb = require('verb'); - -// register a layout with verb. -verb.layout('default.md', {content: '# {%= name %}\n\n<<% body %>>\n'}); - -// register a page, and use the layout by adding the `layout` property. -verb.page('api.md', {content: 'API docs...', layout: 'default.md'}); - -//=> '# {%= name %}\n\nAPI docs...\n' -``` - -**What's with the `body` syntax** - -Verb is a documentation generator, the crazy `body` syntax is verb's way of ensure that we avoid collision with templates that might be in code examples. \ No newline at end of file diff --git a/recipes/verbfile.md b/recipes/verbfile.md deleted file mode 100644 index fd241be6..00000000 --- a/recipes/verbfile.md +++ /dev/null @@ -1,15 +0,0 @@ -# verbfile - -> Creating a basic verbfile.js - -This is what the default `verbfile.js` looks like: - -```js -var verb = require('verb'); - -verb.task('default', function() { - verb.src('.verb.md') - .pipe(verb.dest('.')); -}); -``` - diff --git a/recipes/verbmd.md b/recipes/verbmd.md deleted file mode 100644 index 6772d44b..00000000 --- a/recipes/verbmd.md +++ /dev/null @@ -1,94 +0,0 @@ -# .verb.md - -> .verb.md is a markdown template that Verb uses to generate your project's readme. - -If your project has a `.verb.md` template, verb will automatically render it using data from your project's package.json file. - -## Example .verb.md - -You can use any variables, helpers, templates or data you want in Verb documentation. This example below just happens to be the template that Verb's maintainers like on their own projects. - -Below we'll describe each section. - -```markdown -# {%%= name %} {%%= badge("fury") %} - -> {%%= description %} - -## Install -{%%= include("install") %} - -## API -{%%= apidocs("index.js") %} - -## Author -{%%= include("author") %} - -## License -{%%= copyright() %} -{%%= license() %} - -*** - -{%%= include("footer") %} -``` - -_(before going further, please read [how verb works](./how-verb-works.md) if you need a primer on how these templates work.)_ - -There are only a couple of template variables used, the rest of example consists of [helpers](./helpers.md). Let's go over all of them. - - -## Variables - -Simple variables, like `name` are typically used when their values can be resolved using data from package.json without doing a lot of work to get the data. - -### name - -Uses the value of the `name` property from package.json. - -```js -{%%= name %} -``` - -Uses the `description` property from package.json. - -```js -> {%%= description %} -``` - -## Helpers - -Helpers are used when data needs to be updated dynamically, or when it needs to be calculated - -### badge - -This is a helper that gets badge templates from node_modules and renders them using data in package.json. There are other badges, like `travis`, but you can add your own if you want or do a pr to verb to add more. - -```js -{%%= badge("fury") %} -``` - -### description - -```js -{%%= include("install") %} -``` - -```js -{%%= apidocs("index.js") %} -``` - -```js -{%%= include("author") %} -``` - -```js -{%%= license() %} -``` -```js -{%%= copyright() %} -``` - -```js -{%%= include("footer") %} -``` \ No newline at end of file diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js new file mode 100644 index 00000000..0a8d94c1 --- /dev/null +++ b/test/app.applyLayout.js @@ -0,0 +1,106 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; +var page = { + content: '<%= name %>', + layout: 'default.tmpl', + locals: { + name: 'Halle' + } +}; + +describe('helpers', function () { + describe('rendering', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('layout', {viewType: 'layout'}); + app.create('page'); + }); + + it('should throw an error when a layout cannot be found:', function (done) { + app.layout('fofof.tmpl', {content: '..'}); + app.page('a.tmpl', page) + .render(function (err) { + assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); + done(); + }); + }); + + it('should emit an error when a layout cannot be found:', function (done) { + app.layout('fofof.tmpl', {content: '..'}); + app.on('error', function (err) { + assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); + done(); + }); + + app.page('a.tmpl', page) + .render(function () { + }); + }); + + it('should throw an error - layout defined but no layouts registered:', function (done) { + app.page('a.tmpl', page) + .render(function (err) { + assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); + done(); + }); + }); + + it('should emit an error - layout defined but no layouts registered:', function (done) { + app.on('error', function (err) { + assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); + done(); + }); + app.page('a.tmpl', page) + .render(function () { + }); + }); + + it('should wrap a view with a layout (view.render):', function (done) { + app.layout('default.tmpl', {content: 'before {% body %} after'}); + app.page('a.tmpl', page) + .render(function (err) { + if (err) return done(err); + done(); + }); + }); + + it('should wrap a view with a layout (app.render):', function (done) { + app.layout('default.tmpl', {content: 'before {% body %} after'}); + app.page('a.tmpl', page); + + var view = app.pages.getView('a.tmpl'); + app.render(view, function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'before Halle after'); + done(); + }); + }); + + it('should throw an error when a defined layout is not applied:', function (done) { + app.layout('no_body_tag.tmpl', {content: 'who? me?'}); + page.layout = 'no_body_tag.tmpl'; + app.page('a.tmpl', page) + .render(function (err) { + assert(err.message === 'cannot find layout tag "body" in "no_body_tag.tmpl"'); + done(); + }); + }); + + it('should emit an error when a defined layout is not applied:', function (done) { + app.on('error', function (err) { + assert(err.message === 'cannot find layout tag "body" in "no_body_tag.tmpl"'); + done(); + }); + page.layout = 'no_body_tag.tmpl'; + app.layout('no_body_tag.tmpl', {content: 'who? me?'}); + app.page('a.tmpl', page) + .render(function () { + }); + }); + }); +}); + diff --git a/test/app.collection.js b/test/app.collection.js new file mode 100644 index 00000000..ef1fe4e4 --- /dev/null +++ b/test/app.collection.js @@ -0,0 +1,131 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('collection', function () { + describe('method', function () { + beforeEach(function () { + app = new App(); + }); + + it('should expose the collection method', function () { + assert(typeof app.collection === 'function'); + }); + + it('should return a new collection', function () { + var collection = app.collection(); + assert(typeof collection === 'object'); + }); + + it('should have isCollection property', function () { + var collection = app.collection(); + assert(collection.isCollection === true); + }); + }); + + describe('adding views', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + }); + + it('should load a view onto the respective collection:', function () { + app.pages('test/fixtures/pages/a.hbs'); + app.views.pages.should.have.property('test/fixtures/pages/a.hbs'); + }); + + it('should allow collection methods to be chained:', function () { + app + .pages('test/fixtures/pages/a.hbs') + .pages('test/fixtures/pages/b.hbs') + .pages('test/fixtures/pages/c.hbs'); + + app.views.pages.should.have.properties([ + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' + ]); + }); + + it('should expose the `option` method:', function () { + app.pages.option('foo', 'bar') + .pages('test/fixtures/pages/a.hbs') + .pages('test/fixtures/pages/b.hbs') + .pages('test/fixtures/pages/c.hbs'); + + app.pages.options.should.have.property('foo', 'bar'); + app.views.pages.should.have.properties([ + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' + ]); + }); + + it('should expose the `option` method:', function () { + app.pages.option('foo', 'bar') + .pages('test/fixtures/pages/a.hbs') + .pages('test/fixtures/pages/b.hbs') + .pages('test/fixtures/pages/c.hbs'); + + assert(Object.keys(app.views.pages).length === 3); + }); + }); + + describe('rendering views', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + }); + + it('should render a view with inherited app.render', function (done) { + app.page('test/fixtures/templates/a.tmpl') + .use(function (view) { + if (!view.contents) { + view.contents = fs.readFileSync(view.path); + } + }) + .set('data.name', 'Brian') + .render(function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'Brian'); + done(); + }); + }); + }); +}); + +describe('collection singular method', function () { + describe('create', function () { + beforeEach(function () { + app = new App(); + }); + + it('should add a pluralized collection from singular name', function () { + app.create('page'); + assert(typeof app.views.pages === 'object'); + }); + }); + + describe('adding views', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should add a view to the created collection:', function () { + app.page('test/fixtures/pages/a.hbs'); + assert(typeof app.views.pages['test/fixtures/pages/a.hbs'] === 'object'); + }); + + it('should expose the `option` method:', function () { + app.pages.option('foo', 'bar'); + app.pages.options.should.have.property('foo', 'bar'); + }); + }); +}); diff --git a/test/app.compile.js b/test/app.compile.js new file mode 100644 index 00000000..0703d3c3 --- /dev/null +++ b/test/app.compile.js @@ -0,0 +1,43 @@ +require('should'); +var assert = require('assert'); +var App = require('..'); +var app; + +describe('compile', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + }); + + it('should throw an error when an engine cannot be found:', function () { + app.page('foo.bar', {content: '<%= name %>'}); + var page = app.pages.getView('foo.bar'); + (function() { + app.compile(page); + }).should.throw('Templates#compile cannot find an engine for: .bar'); + }); + + it('should compile a template:', function () { + app.engine('tmpl', require('engine-base')); + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); + + var page = app.pages.getView('a.tmpl'); + var view = app.compile(page); + assert.equal(typeof view.fn, 'function'); + }); + + it('should throw an error when a callback is given:', function () { + app.engine('md', require('engine-base')); + app.page('foo.md', {content: '<%= name %>'}); + var page = app.pages.getView('foo.md'); + (function() { + app.compile(page, function () { + }); + }).should.throw('Templates#compile is sync and does not take a callback function'); + + (function() { + app.compile(page, {}, function () { + }); + }).should.throw('Templates#compile is sync and does not take a callback function'); + }); +}); diff --git a/test/app.copy.js b/test/app.copy.js new file mode 100644 index 00000000..bf77b0e3 --- /dev/null +++ b/test/app.copy.js @@ -0,0 +1,12 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe.skip('app', function () { + describe('', function () { + it('should:', function () { + }); + }); +}); diff --git a/test/app.create.js b/test/app.create.js new file mode 100644 index 00000000..ec023d1c --- /dev/null +++ b/test/app.create.js @@ -0,0 +1,173 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('create', function () { + describe('inflections', function () { + beforeEach(function () { + app = new App(); + }); + + it('should expose the create method', function () { + assert(typeof app.create === 'function'); + }); + + it('should add a collection to `views`', function () { + app.create('pages'); + assert(typeof app.views.pages === 'object'); + }); + + it('should add a pluralized collection to `views`', function () { + app.create('page'); + assert(typeof app.views.pages === 'object'); + }); + }); + + describe('custom constructors', function () { + beforeEach(function () { + var Vinyl = require('vinyl'); + Vinyl.prototype.custom = function (key) { + this[key] = 'nonsense'; + return this; + }; + app = new App({View: Vinyl}); + app.create('pages'); + }); + + it('should create views from key-value pairs:', function () { + app.page('a.hbs', {path: 'a.hbs', content: 'a'}); + app.page('b.hbs', {path: 'b.hbs', content: 'b'}); + app.page('c.hbs', {path: 'c.hbs', content: 'c'}); + var a = app.pages.getView('a.hbs'); + a.custom('foo'); + a.foo.should.equal('nonsense'); + }); + }); + + describe('custom instances', function () { + it('should create views from custom `View` and `Views` instance/ctor:', function () { + var Vinyl = require('vinyl'); + Vinyl.prototype.read = function () { + return fs.readFileSync(file.path); + }; + + var Views = App.Views; + var views = new Views({View: Vinyl}); + + views.addView('a.hbs', {path: 'a.hbs', content: 'a'}); + views.addView('b.hbs', {path: 'b.hbs', content: 'b'}); + views.addView('c.hbs', {path: 'c.hbs', content: 'c'}); + + app = new App(); + app.create('pages', views); + + var a = app.pages.getView('a.hbs'); + assert(a instanceof Vinyl); + assert(Vinyl.isVinyl(a)); + assert(typeof a.read === 'function'); + + views.addView('d.hbs', {path: 'd.hbs', content: 'd'}); + var d = app.pages.getView('d.hbs'); + assert(d instanceof Vinyl); + assert(Vinyl.isVinyl(d)); + }); + }); + + describe('chaining', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should create views from key-value pairs:', function () { + app.page('a.hbs', {content: 'a'}); + app.page('b.hbs', {content: 'b'}); + app.page('c.hbs', {content: 'c'}); + app.views.pages.should.have.properties(['a.hbs', 'b.hbs', 'c.hbs']); + assert(app.views.pages['a.hbs'].contents.toString() === 'a'); + }); + + it('should create views from file paths:', function () { + app.page('test/fixtures/pages/a.hbs'); + app.page('test/fixtures/pages/b.hbs'); + app.page('test/fixtures/pages/c.hbs'); + + app.views.pages.should.have.properties([ + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' + ]); + }); + }); + + + describe('instance', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + }); + + it('should return the collection instance', function () { + var collection = app.create('pages'); + assert(collection instanceof App.Views); + + collection.option('renameKey', function (key) { + return path.basename(key); + }); + collection + .use(function (views) { + views.read = function (name) { + var view = this.getView(name); + if (!view.contents) { + view.contents = fs.readFileSync(view.path); + } + }; + }); + + collection.addView('test/fixtures/templates/a.tmpl'); + collection.read('a.tmpl'); + assert(collection.getView('a.tmpl').contents.toString() === '<%= name %>'); + }); + }); + + describe('viewType', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + }); + + it('should add collection to the given viewType', function () { + app.create('layout', {viewType: 'layout'}); + assert(app.layouts.options.viewType[0] === 'layout'); + }); + + it('should add a collection to multiple viewTypes', function () { + app.create('foo', {viewType: ['layout', 'renderable']}); + assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); + }); + }); + + describe('events', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + }); + + it('should emit `create` when a collection is created:', function () { + app.on('create', function (collection) { + if (collection.options.plural === 'layouts') { + collection.options.foo = 'bar'; + } + }); + + app.create('layout'); + app.layout('one', {path: 'two', contents: '...'}); + assert(app.layouts.options.foo === 'bar'); + }); + }); +}); diff --git a/test/app.data.js b/test/app.data.js new file mode 100644 index 00000000..86ced3bc --- /dev/null +++ b/test/app.data.js @@ -0,0 +1,93 @@ +require('mocha'); +require('should'); +var path = require('path'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('app.data', function () { + beforeEach(function () { + app = new App(); + }); + + it('should set a key-value pair on cache.data:', function () { + app.data('a', 'b'); + assert(app.cache.data.a === 'b'); + }); + + it('should set an object on cache.data:', function () { + app.data({c: 'd'}); + assert(app.cache.data.c === 'd'); + }); + + it('should load data from a file onto cache.data:', function () { + app.data('test/fixtures/data/a.json'); + assert(app.cache.data.a.one.a === 'aaa'); + }); + + it('should load a glob of data onto cache.data:', function () { + app.data('test/fixtures/data/*.json'); + assert(app.cache.data.a.one.a === 'aaa'); + assert(app.cache.data.b.two.b === 'bbb'); + assert(app.cache.data.c.three.c === 'ccc'); + }); + + it('should use `namespace` defined on global opts:', function () { + app.option('namespace', function (key) { + return 'prefix_' + path.basename(key, path.extname(key)); + }); + app.data('test/fixtures/data/*.json'); + assert(app.cache.data.prefix_a.one.a === 'aaa'); + assert(app.cache.data.prefix_b.two.b === 'bbb'); + assert(app.cache.data.prefix_c.three.c === 'ccc'); + }); + + it('should use `namespace` defined on data opts:', function () { + app.data('test/fixtures/data/*.json', { + namespace: function (key) { + return 'prefix_' + path.basename(key, path.extname(key)); + } + }); + assert(app.cache.data.prefix_a.one.a === 'aaa'); + assert(app.cache.data.prefix_b.two.b === 'bbb'); + assert(app.cache.data.prefix_c.three.c === 'ccc'); + }); + + it('should use `renameKey` defined on data opts:', function () { + app.data('test/fixtures/data/*.json', { + renameKey: function (key) { + return 'prefix_' + path.basename(key, path.extname(key)); + } + }); + assert(app.cache.data.prefix_a.one.a === 'aaa'); + assert(app.cache.data.prefix_b.two.b === 'bbb'); + assert(app.cache.data.prefix_c.three.c === 'ccc'); + }); + + it('should extend `cache.data`', function() { + app.data({a: 'aaa', b: 'bbb', c: 'ccc'}); + app.data({x: 'xxx', y: 'yyy', z: 'zzz'}); + assert(app.cache.data.a === 'aaa'); + assert(app.cache.data.b === 'bbb'); + assert(app.cache.data.c === 'ccc'); + assert(app.cache.data.x === 'xxx'); + assert(app.cache.data.y === 'yyy'); + assert(app.cache.data.z === 'zzz'); + }); + + it('should extend the `cache.data` object when the first param is a string.', function() { + app.data('foo', {x: 'xxx', y: 'yyy', z: 'zzz'}); + app.data('bar', {a: 'aaa', b: 'bbb', c: 'ccc'}); + assert(app.cache.data.foo.x === 'xxx'); + assert(app.cache.data.bar.a === 'aaa'); + }); + + it('should be chainable.', function() { + app + .data({x: 'xxx', y: 'yyy', z: 'zzz'}) + .data({a: 'aaa', b: 'bbb', c: 'ccc'}); + + assert(app.cache.data.x === 'xxx'); + assert(app.cache.data.a === 'aaa'); + }); +}); diff --git a/test/app.dest.js b/test/app.dest.js new file mode 100644 index 00000000..760a0eb2 --- /dev/null +++ b/test/app.dest.js @@ -0,0 +1,222 @@ +require('mocha'); +var path = require('path'); +var fs = require('graceful-fs'); +var should = require('should'); +var rimraf = require('rimraf'); +var App = require('..'); +var app; + +var outpath = path.join(__dirname, './out-fixtures'); + +describe('app output stream', function() { + describe('dest()', function() { + beforeEach(function (done) { + rimraf(outpath, done); + }); + afterEach(function (done) { + rimraf(outpath, done); + }); + + describe('minimal config - enabled', function () { + beforeEach(function () { + app = new App(); + }); + + it('should return a stream', function (done) { + var stream = app.dest(path.join(__dirname, 'fixtures/')); + should.exist(stream); + should.exist(stream.on); + done(); + }); + + it('should return an output stream that writes files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); + var outstream = app.dest(outpath); + instream.pipe(outstream); + + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + String(file.contents).should.equal('this is a test'); + }); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.not.exist(err); + should.exist(contents); + String(contents).should.equal('this is a test'); + done(); + }); + }); + }); + + it('should return an output stream that does not write non-read files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {read: false}); + var outstream = app.dest(outpath); + instream.pipe(outstream); + + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + }); + + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.exist(err); + should.not.exist(contents); + done(); + }); + }); + }); + + it('should return an output stream that writes streaming files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {buffer: false}); + var outstream = instream.pipe(app.dest(outpath)); + + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + }); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.not.exist(err); + should.exist(contents); + String(contents).should.equal('this is a test'); + done(); + }); + }); + }); + + it('should return an output stream that writes streaming files to new directories', function (done) { + testWriteDir({}, done); + }); + + it('should return an output stream that writes streaming files to new directories (buffer: false)', function (done) { + testWriteDir({buffer: false}, done); + }); + + it('should return an output stream that writes streaming files to new directories (read: false)', function (done) { + testWriteDir({read: false}, done); + }); + + it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function (done) { + testWriteDir({buffer: false, read: false}, done); + }); + + }); + + describe('minimal config - disabled', function () { + beforeEach(function () { + app = new App(); + app.set('ext', '.txt'); + }); + + afterEach(function () { + app.set('ext', '.html'); + }); + + it('should return a stream', function (done) { + var stream = app.dest(path.join(__dirname, 'fixtures/')); + should.exist(stream); + should.exist(stream.on); + done(); + }); + + it('should return an output stream that writes files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); + var outstream = app.dest(outpath); + instream.pipe(outstream); + + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + String(file.contents).should.equal('this is a test'); + }); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.not.exist(err); + should.exist(contents); + String(contents).should.equal('this is a test'); + done(); + }); + }); + }); + + it('should return an output stream that does not write non-read files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {read: false}); + var outstream = app.dest(outpath); + instream.pipe(outstream); + + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + }); + + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.exist(err); + should.not.exist(contents); + done(); + }); + }); + }); + + it('should return an output stream that writes streaming files to new directories', function (done) { + testWriteDir({}, done); + }); + + it('should return an output stream that writes streaming files to new directories (buffer: false)', function (done) { + testWriteDir({buffer: false}, done); + }); + + it('should return an output stream that writes streaming files to new directories (read: false)', function (done) { + testWriteDir({read: false}, done); + }); + + it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function (done) { + testWriteDir({buffer: false, read: false}, done); + }); + }); + + function testWriteDir(srcOptions, done) { + var instream = app.src(path.join(__dirname, 'fixtures/generic'), srcOptions); + var outstream = instream.pipe(app.dest(outpath)); + + outstream.on('error', done); + outstream.on('data', function(file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + path.join(file.path,'').should.equal(path.join(outpath, './generic')); + }); + + outstream.on('end', function() { + fs.exists(path.join(outpath, 'generic'), function(exists) { + /* jshint expr: true */ + should(exists).be.ok; + /* jshint expr: false */ + done(); + }); + }); + } + }); +}); diff --git a/test/app.doc.js b/test/app.doc.js new file mode 100644 index 00000000..abef13a8 --- /dev/null +++ b/test/app.doc.js @@ -0,0 +1,20 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var verb = require('..'); +var app; + +describe('app', function () { + beforeEach(function() { + app = verb(); + }); + + describe('add doc', function () { + it('should add docs to `app.views.docs`:', function () { + app.doc('a.hbs', {path: 'a.hbs', content: 'a'}); + app.doc('b.hbs', {path: 'b.hbs', content: 'b'}); + app.doc('c.hbs', {path: 'c.hbs', content: 'c'}); + assert(Object.keys(app.views.docs).length === 3); + }); + }); +}); diff --git a/test/app.docs.js b/test/app.docs.js new file mode 100644 index 00000000..13c12e0f --- /dev/null +++ b/test/app.docs.js @@ -0,0 +1,22 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var verb = require('..'); +var app; + +describe('app', function () { + beforeEach(function() { + app = verb(); + }); + + describe('add docs', function () { + it('should add docs to `app.views.docs`:', function () { + app.docs({ + 'a.hbs': {path: 'a.hbs', content: 'a'}, + 'b.hbs': {path: 'b.hbs', content: 'b'}, + 'c.hbs': {path: 'c.hbs', content: 'c'}, + }); + assert(Object.keys(app.views.docs).length === 3); + }); + }); +}); diff --git a/test/app.engines.js b/test/app.engines.js new file mode 100644 index 00000000..98205d09 --- /dev/null +++ b/test/app.engines.js @@ -0,0 +1,204 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('engines', function () { + describe('constructor', function () { + it('should create an instance of App:', function () { + app = new App(); + assert(app instanceof App); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof App.extend ==='function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function() { + app = new App(); + }); + + it('should expose `set`', function () { + assert(typeof app.set ==='function'); + }); + it('should expose `get`', function () { + assert(typeof app.get ==='function'); + }); + it('should expose `visit`', function () { + assert(typeof app.visit ==='function'); + }); + it('should expose `define`', function () { + assert(typeof app.define ==='function'); + }); + it('should expose `engine`', function () { + assert(typeof app.engine ==='function'); + }); + }); + + describe('instance', function () { + beforeEach(function() { + app = new App(); + }); + + it('should set an arbitrary value on the instance:', function () { + app.set('a', 'b'); + assert(app.a ==='b'); + }); + + it('should get an arbitrary value from the instance:', function () { + app.set('a', 'b'); + assert(app.get('a') ==='b'); + }); + }); + + describe('engines', function() { + beforeEach(function() { + app = new App(); + }); + + it('should throw an error when engine name is invalid:', function () { + (function () { + app.engine(null, {}); + }).should.throw('expected engine ext to be a string or array.'); + }); + + it('should register an engine to the given extension', function () { + app.engine('hbs', function () {}); + assert(typeof app.engines['.hbs'] === 'object'); + }); + + it('should set an engine with the given extension', function () { + var hbs = function() {}; + hbs.render = function() {}; + hbs.renderFile = function() {}; + app.engine('hbs', hbs); + assert(app.engines['.hbs']); + assert(app.engines['.hbs'].renderFile); + assert(app.engines['.hbs'].render); + }); + + it('should get an engine:', function () { + app.engine('hbs', function () {}); + var hbs = app.engine('hbs'); + assert(typeof hbs === 'object'); + assert(hbs.hasOwnProperty('render')); + assert(hbs.hasOwnProperty('compile')); + }); + + it('should register multiple engines to the given extension', function () { + app.engine(['hbs', 'md'], function () {}); + assert(typeof app.engines['.hbs'] === 'object'); + assert(typeof app.engines['.md'] === 'object'); + }); + }); +}); + +describe('engines', function () { + beforeEach(function () { + app = new App(); + app.create('pages'); + app.pages('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); + app.pages('bar.tmpl', {content: 'A <%= letter %> {{ letter }} C'}); + }); + + it('should register an engine:', function () { + app.engine('a', {render: function () {}}); + app.engines.should.have.property('.a'); + }); + + it('should use custom delimiters:', function (done) { + app.engine('tmpl', require('engine-base'), { + delims: ['{{', '}}'] + }); + app.render('foo.tmpl', {letter: 'B'}, function (err, res) { + if (err) return done(err); + res.contents.toString().should.equal('A <%= letter %> B C'); + done(); + }); + }); + + it('should override individual delims values:', function (done) { + app.engine('tmpl', require('engine-base'), { + interpolate: /\{{([^}]+)}}/g, + evaluate: /\{{([^}]+)}}/g, + escape: /\{{-([^}]+)}}/g + }); + app.render('bar.tmpl', {letter: 'B'}, function (err, res) { + if (err) return done(err); + res.contents.toString().should.equal('A <%= letter %> B C'); + done(); + }); + }); + + it('should get an engine:', function () { + app.engine('a', { + render: function () {} + }); + var a = app.engine('a'); + a.should.have.property('render'); + }); +}); + + +describe('engine selection:', function (done) { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.engine('hbs', require('engine-handlebars')); + app.create('pages'); + }); + + it('should get the engine from file extension:', function (done) { + app.page('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on the collection:', function (done) { + app.create('posts', {engine: 'hbs'}); + app.post('a', {content: '{{a}}', locals: {a: 'b'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on the view:', function (done) { + app.create('posts'); + app.post('a', {content: '{{a}}', engine: 'hbs', locals: {a: 'b'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on `view.data`:', function (done) { + app.create('posts'); + app.post('a', {content: '{{a}}', locals: {a: 'b'}, data: {engine: 'hbs'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on render locals:', function (done) { + app.create('posts'); + app.post('a', {content: '{{a}}', locals: {a: 'b'}}) + .render({engine: 'hbs'}, function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); +}); diff --git a/test/app.events.js b/test/app.events.js new file mode 100644 index 00000000..9bed9ade --- /dev/null +++ b/test/app.events.js @@ -0,0 +1,50 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('events', function () { + beforeEach(function () { + app = new App(); + }); + + it('should listen for an event:', function () { + var app = new App(); + app.on('foo', function () { + }); + assert(Array.isArray(app._callbacks['$foo'])); + }); + + it('should emit an event:', function (done) { + var app = new App(); + app.on('foo', function (val) { + assert(val === 'bar'); + done(); + }); + assert(Array.isArray(app._callbacks['$foo'])); + app.emit('foo', 'bar'); + }); + + it('should listen for error events:', function (done) { + var app = new App(); + app.on('foo', function (val) { + assert(val === 'bar'); + done(); + }); + assert(Array.isArray(app._callbacks['$foo'])); + app.emit('foo', 'bar'); + }); + + it('should listen for `view` events:', function () { + var app = new App(); + app.initialize(); + + app.on('view', function (view) { + view.foo = 'bar'; + }); + + var view = app.view({path: 'a', content: 'b'}); + assert(view.foo === 'bar'); + }); +}); diff --git a/test/app.get-set.js b/test/app.get-set.js new file mode 100644 index 00000000..cdc6353d --- /dev/null +++ b/test/app.get-set.js @@ -0,0 +1,71 @@ +require('should'); +var assert = require('assert'); +var App = require('..'); +var app; + +describe('app.set()', function () { + beforeEach(function() { + app = new App(); + }); + + it('should set a value', function () { + app.set('a', 'b'); + app.get('a').should.equal('b'); + }); + + it('should set properties on the instance.', function () { + app.set('a', 'b'); + app.a.should.equal('b'); + }); + + it('should allow an object to be set directly.', function () { + app.set({x: 'y'}); + app.x.should.equal('y'); + app.get('x').should.equal('y'); + }); + + it('should set nested properties on the instance.', function () { + app.set('c', {d: 'e'}); + app.get('c').d.should.equal('e'); + }); + + it('should use dot notation to `set` values.', function () { + app.set('h.i', 'j'); + app.get('h').should.eql({i: 'j'}); + }); + + it('should use dot notation to `get` values.', function () { + app.set('h', {i: 'j'}); + app.get('h.i').should.equal('j'); + }); + + it('should return `this` for chaining', function () { + app.set('a', 'b').should.equal(app); + app + .set('aa', 'bb') + .set('bb', 'cc') + .set('cc', 'dd'); + app.get('aa').should.equal('bb'); + app.get('bb').should.equal('cc'); + app.get('cc').should.equal('dd'); + }); + + it('should return undefined when not set', function () { + app.set('a', undefined).should.equal(app); + }); +}); + +describe('app.get()', function () { + beforeEach(function() { + app = new App(); + }); + + it('should return undefined when no set', function () { + assert(app.get('a') === undefined); + }); + + it('should otherwise return the value', function () { + app.set('a', 'b'); + app.get('a').should.equal('b'); + }); +}); diff --git a/test/app.handle.js b/test/app.handle.js new file mode 100644 index 00000000..795ff842 --- /dev/null +++ b/test/app.handle.js @@ -0,0 +1,22 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('handler', function () { + beforeEach(function () { + app = new App(); + app.create('pages'); + app.handlers(['foo']); + }); + + it('should support custom handle methods:', function (done) { + var page = app.page('foo', {contents: null}); + + app.handle('foo', page, function (err, view) { + assert(typeof view.path === 'string'); + done(); + }); + }); +}); diff --git a/test/app.handlers.js b/test/app.handlers.js new file mode 100644 index 00000000..27924f4d --- /dev/null +++ b/test/app.handlers.js @@ -0,0 +1,136 @@ +require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var utils = require('../lib/utils'); +var App = require('..'); +var app; + +function decorateViews(views) { + var fn = views.decorateView; + views.decorateView = function () { + var view = fn.apply(fn, arguments); + view.read = function () { + if (!this.contents) { + this.contents = fs.readFileSync(this.path); + } + }; + return view; + }; + views.loader = function (pattern) { + var files = utils.resolveGlob(pattern); + return files.reduce(function (acc, fp) { + acc[fp] = {path: fp}; + return acc; + }, {}); + }; + return views; +} + +describe.skip('handlers', function () { + describe('custom handlers', function () { + beforeEach(function () { + app = new App(); + app.create('pages') + .use(decorateViews) + .option('renameKey', function (key) { + return path.basename(key); + }); + }); + + it('should add custom middleware handlers:', function () { + app.handler('foo'); + app.router.should.have.property('foo'); + assert.equal(typeof app.router.foo, 'function'); + }); + + it('should add custom middleware handlers:', function () { + app.pages.on('view', function (key, val) { + val.read(); + }); + + app.handler('foo'); + app.handler('bar'); + + // app.on('foo', function () { + // console.log(arguments) + // }) + + app.foo(/./, function (view, next) { + view.one = 'aaa'; + next(); + }); + + app.bar(/./, function (view, next) { + view.two = 'zzz'; + next(); + }); + + var pages = app.pages('test/fixtures/templates/*.tmpl') + .use(function (pages) { + var fn = pages.decorateView; + pages.decorateView = function (view) { + view = fn(view); + app.handleView('foo', view); + return view; + }; + return pages; + }); + // .pages('test/fixtures/pages/*.hbs') + // .use(function (pages) { + // var fn = pages.decorateView; + // pages.decorateView = function (view) { + // view = fn(view); + // app.handleView('bar', view); + // return view; + // }; + // return pages; + // }) + + console.log(pages.getView('a.tmpl').one); + // console.log(app.pages.getView('a.tmpl').one) + // console.log(app.pages.getView('a.hbs').two) + + // app.pages.getView('a.txt').should.have.property('one'); + // app.pages.getView('a.txt').should.have.property('two'); + + // app.pages.getView('a.md').should.not.have.property('one'); + // app.pages.getView('a.md').should.have.property('two'); + }); + + // it('should add custom middleware handlers:', function () { + // app.handler('foo'); + // app.handler('bar'); + + // function handle(method) { + // return function (view) { + // return app.handle(method, view); + // } + // } + + // app.foo(/./, function (view, next) { + // view.one = 'aaa'; + // next(); + // }); + + // app.bar(/./, function (view, next) { + // view.two = 'zzz'; + // next(); + // }); + + // app.pages('test/fixtures/*.txt') + // .use(handle('foo')) + + // .pages('test/fixtures/*.md') + // .use(handle('bar')) + + // .use(utils.rename); + + // app.pages.getView('a.txt').should.have.property('one'); + // app.pages.getView('a.txt').should.have.property('two'); + + // app.pages.getView('a.md').should.not.have.property('one'); + // app.pages.getView('a.md').should.have.property('two'); + // }); + }); +}); diff --git a/test/app.include.js b/test/app.include.js new file mode 100644 index 00000000..510ec857 --- /dev/null +++ b/test/app.include.js @@ -0,0 +1,21 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var verb = require('..'); +var app, len; + +describe('app', function () { + beforeEach(function() { + app = verb(); + len = Object.keys(app.views.includes).length; + }); + + describe('add include', function () { + it('should add includes to `app.views.includes`:', function () { + app.include('a.hbs', {path: 'a.hbs', content: 'a'}); + app.include('b.hbs', {path: 'b.hbs', content: 'b'}); + app.include('c.hbs', {path: 'c.hbs', content: 'c'}); + assert((Object.keys(app.views.includes).length - len) === 3); + }); + }); +}); diff --git a/test/app.includes.js b/test/app.includes.js new file mode 100644 index 00000000..be651314 --- /dev/null +++ b/test/app.includes.js @@ -0,0 +1,23 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var verb = require('..'); +var app, len; + +describe('app', function () { + beforeEach(function() { + app = verb(); + len = Object.keys(app.views.includes).length; + }) + + describe('add includes', function () { + it('should add includes to `app.views.includes`:', function () { + app.includes({ + 'a.hbs': {path: 'a.hbs', content: 'a'}, + 'b.hbs': {path: 'b.hbs', content: 'b'}, + 'c.hbs': {path: 'c.hbs', content: 'c'}, + }); + assert((Object.keys(app.views.includes).length - len) === 3); + }); + }); +}); diff --git a/test/app.js b/test/app.js new file mode 100644 index 00000000..07c347cd --- /dev/null +++ b/test/app.js @@ -0,0 +1,94 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('app', function () { + describe('constructor', function () { + it('should create an instance of App:', function () { + app = new App(); + assert(app instanceof App); + }); + + it('should new up without new:', function () { + app = App(); + assert(app instanceof App); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof App.extend ==='function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function() { + app = new App(); + }); + + it('should expose `set`', function () { + assert(typeof app.set ==='function'); + }); + it('should expose `get`', function () { + assert(typeof app.get ==='function'); + }); + it('should expose `visit`', function () { + assert(typeof app.visit ==='function'); + }); + it('should expose `define`', function () { + assert(typeof app.define ==='function'); + }); + it('should expose `views`', function () { + assert(typeof app.views === 'object'); + }); + }); + + describe('instance', function () { + beforeEach(function() { + app = new App(); + }); + + it('should set a value on the instance:', function () { + app.set('a', 'b'); + assert(app.a ==='b'); + }); + + it('should get a value from the instance:', function () { + app.set('a', 'b'); + assert(app.get('a') ==='b'); + }); + }); + + describe('initialization', function () { + it('should listen for errors:', function (done) { + app = new App(); + app.on('error', function (err) { + assert(err.message === 'foo'); + done(); + }); + app.emit('error', new Error('foo')); + }); + + it('should mixin methods after init:', function () { + app = new App(); + app.option({ + mixins: { + foo: function () {} + } + }); + assert(typeof app.foo ==='function'); + }); + + it('should mixin prototype methods defined on options:', function () { + app = new App({ + mixins: { + foo: function () {} + } + }); + assert(typeof app.foo ==='function'); + delete App.prototype.foo; + }); + }); +}); diff --git a/test/app.layout.js b/test/app.layout.js new file mode 100644 index 00000000..036efa18 --- /dev/null +++ b/test/app.layout.js @@ -0,0 +1,20 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var verb = require('..'); +var app; + +describe('app', function () { + beforeEach(function() { + app = verb(); + }); + + describe('add layout', function () { + it('should add layouts to `app.views.layouts`:', function () { + app.layout('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); + app.layout('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); + app.layout('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); + assert(Object.keys(app.views.layouts).length === 3); + }); + }); +}); diff --git a/test/app.layouts.js b/test/app.layouts.js new file mode 100644 index 00000000..03a47bba --- /dev/null +++ b/test/app.layouts.js @@ -0,0 +1,22 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var verb = require('..'); +var app; + +describe('app', function () { + beforeEach(function() { + app = verb(); + }); + + describe('add layouts', function () { + it('should add layouts to `app.views.layouts`:', function () { + app.layouts({ + 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, + 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + }); + assert(Object.keys(app.views.layouts).length === 3); + }); + }); +}); diff --git a/test/app.lookups.js b/test/app.lookups.js new file mode 100644 index 00000000..b49a921d --- /dev/null +++ b/test/app.lookups.js @@ -0,0 +1,108 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var path = require('path'); +var globby = require('globby'); +var assert = require('assert'); +var Templates = require('../'); +var utils = require('../lib/utils'); +var app; + +function resolveGlob(patterns, options) { + var opts = utils.merge({cwd: process.cwd()}, options); + return globby.sync(patterns, opts).map(function (fp) { + return path.resolve(opts.cwd, fp); + }); +} + +describe('lookups', function () { + beforeEach(function () { + app = new Templates(); + app.option('renameKey', function (key) { + return path.basename(key); + }); + app.create('pages') + .use(function (pages) { + pages.on('addViews', function (glob) { + var files = resolveGlob(glob); + files.forEach(function (fp) { + pages.addView(fp, {path: fp}); + }); + pages.loaded = true; + }); + return function (view) { + view.read = function () { + this.contents = fs.readFileSync(this.path); + }; + return view; + }; + }); + + app.pages('test/fixtures/templates/*.tmpl'); + }); + + describe('getView', function () { + it('should find a view', function () { + var view = app.getView('pages', 'a.tmpl'); + assert(typeof view.path === 'string'); + }); + + it('should find a view using the renameKey function', function () { + var view = app.getView('pages', 'test/fixtures/templates/a.tmpl'); + assert(typeof view.path === 'string'); + }); + + it('should return null when nothing is found', function () { + var view = app.getView('pages', 'test/fixtures/templates/foo.tmpl'); + assert(view === null); + }); + + it('should find a view using a glob pattern', function () { + var view = app.getView('pages', 'a', function (key) { + return key + '.tmpl'; + }); + assert(typeof view.path === 'string'); + }); + }); + + describe('getViews', function () { + it('should return the collection object if passed:', function () { + var views = app.getViews(app.views.pages); + assert(Object.keys(views).length > 1); + }); + + it('should return the specified collection with the plural name:', function () { + var views = app.getViews('pages'); + assert(Object.keys(views).length > 1); + }); + + it('should return the specified collection with the singular name:', function () { + var views = app.getViews('page'); + assert(Object.keys(views).length > 1); + }); + + it('should return null when the collection is not found:', function () { + (function () { + app.getViews('nada'); + }).should.throw('getViews cannot find collection: nada'); + }); + }); + + describe('find', function () { + it('should return null when a view is not found:', function () { + (function () { + app.find({}); + }).should.throw('expected name to be a string.'); + }); + + it('should find a view by collection name:', function () { + var view = app.find('a.tmpl', 'pages'); + assert(typeof view.path === 'string'); + }); + + it('should find a view without a collection name:', function () { + var view = app.find('a.tmpl'); + assert(typeof view.path === 'string'); + }); + }); +}); diff --git a/test/app.middleware.js b/test/app.middleware.js new file mode 100644 index 00000000..479d6a85 --- /dev/null +++ b/test/app.middleware.js @@ -0,0 +1,59 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var Templates = require('../'); +var app; + +describe('middleware', function () { + beforeEach(function () { + app = new Templates(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + }); + + it('should call the all method for every middleware method:', function () { + var i = 0; + app.all(/./, function (view, next) { + assert(typeof view.path === 'string'); + i++; + next(); + }); + + assert(i === 0); + app.page('foo.tmpl', {content: 'foo'}); + assert(i === 1); + }); + + it('should call the onLoad method when a view is loaded:', function () { + var i = 0; + app.onLoad(/./, function (view, next) { + assert(typeof view.path === 'string'); + i++; + next(); + }); + + assert(i === 0); + app.page('foo.tmpl', {content: 'foo'}); + assert(i === 1); + }); + + it('should emit an event when a handler is called:', function (done) { + var i = 0; + app.on('onLoad', function () { + i++; + }); + app.on('preRender', function () { + i++; + }); + app.on('preCompile', function () { + i++; + }); + + app.page('foo.tmpl', {content: 'foo'}) + .render(function (err) { + if (err) return done(err); + assert(i === 3); + done(); + }); + }); +}); diff --git a/test/app.option.js b/test/app.option.js new file mode 100644 index 00000000..0a0821be --- /dev/null +++ b/test/app.option.js @@ -0,0 +1,103 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('app.option', function () { + beforeEach(function () { + app = new App(); + }); + + it('should set a key-value pair on options:', function () { + app.option('a', 'b'); + assert(app.options.a === 'b'); + }); + + it('should set an object on options:', function () { + app.option({c: 'd'}); + assert(app.options.c === 'd'); + }); + + it('should throw on invalid args:', function () { + (function () { + app.option(function () {}); + }).should.throw('expected a string or object.'); + }); + + it('should set an option.', function() { + app.option('a', 'b'); + app.options.should.have.property('a'); + }); + + it('should get an option.', function() { + app.option('a', 'b'); + app.option('a').should.equal('b'); + }); + + it('should extend the `options` object.', function() { + app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); + app.option('x').should.equal('xxx'); + app.option('y').should.equal('yyy'); + app.option('z').should.equal('zzz'); + }); + + it('options should be on the `options` object.', function() { + app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); + app.options.x.should.equal('xxx'); + app.options.y.should.equal('yyy'); + app.options.z.should.equal('zzz'); + }); + + it('should be chainable.', function() { + app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); + app.option({a: 'aaa', b: 'bbb', c: 'ccc'}); + + app.option('x').should.equal('xxx'); + app.option('a').should.equal('aaa'); + }); + + it('should extend the `options` object when the first param is a string.', function() { + app.option('foo', {x: 'xxx', y: 'yyy', z: 'zzz'}); + app.option('bar', {a: 'aaa', b: 'bbb', c: 'ccc'}); + + app.option('foo').should.have.property('x'); + app.option('bar').should.have.property('a'); + + app.options.foo.should.have.property('x'); + app.options.bar.should.have.property('a'); + }); + + it('should set an option.', function() { + app.option('a', 'b'); + app.options.should.have.property('a'); + }); + + it('should get an option.', function() { + app.option('a', 'b'); + app.option('a').should.equal('b'); + }); + + it('should extend the `options` object.', function() { + app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); + app.option('x').should.equal('xxx'); + app.option('y').should.equal('yyy'); + app.option('z').should.equal('zzz'); + }); + + it('options should be on the `options` object.', function() { + app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); + app.options.x.should.equal('xxx'); + app.options.y.should.equal('yyy'); + app.options.z.should.equal('zzz'); + }); + + it('should be chainable.', function() { + app + .option({x: 'xxx', y: 'yyy', z: 'zzz'}) + .option({a: 'aaa', b: 'bbb', c: 'ccc'}); + + app.option('x').should.equal('xxx'); + app.option('a').should.equal('aaa'); + }); +}); diff --git a/test/app.render.js b/test/app.render.js new file mode 100644 index 00000000..c22e0862 --- /dev/null +++ b/test/app.render.js @@ -0,0 +1,87 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('render', function () { + describe('rendering', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should throw an error when no callback is given:', function () { + (function() { + app.render({}); + }).should.throw('Templates#render is async and expects a callback function'); + }); + + it('should throw an error when an engine is not defined:', function (done) { + app.page('foo.bar', {content: '<%= name %>'}); + var page = app.pages.getView('foo.bar'); + + app.render(page, function(err) { + assert(err.message === 'Templates#render cannot find an engine for: .bar'); + done(); + }); + }); + + it('should use helpers to render a view:', function (done) { + var locals = {name: 'Halle'}; + + app.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, res) { + if (err) return done(err); + + assert(res.contents.toString() === 'a HALLE b'); + done(); + }); + }); + + it('should use helpers when rendering a view:', function (done) { + var locals = {name: 'Halle'}; + app.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a HALLE b'); + done(); + }); + }); + + it('should render a template when contents is a buffer:', function (done) { + app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = app.pages.getView('a.tmpl'); + + app.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a template when content is a string:', function (done) { + app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = app.pages.getView('a.tmpl'); + + app.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + }); +}); diff --git a/test/app.route.js b/test/app.route.js new file mode 100644 index 00000000..b68878c4 --- /dev/null +++ b/test/app.route.js @@ -0,0 +1,92 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('routes', function () { + beforeEach(function() { + app = new App(); + }); + + describe('routes', function() { + it('should create a route for the given path:', function (done) { + app = new App(); + app.create('posts'); + + app.on('all', function(msg) { + assert(msg === 'done'); + done(); + }); + + app.route('blog/:title') + .all(function(view, next) { + app.emit('all', 'done'); + next(); + }); + + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + + it('should emit events when a route method is called:', function (done) { + app = new App(); + app.create('posts'); + + app.on('onLoad', function(view) { + assert(view.path === 'blog/foo.js'); + done(); + }); + + app.param('title', function (view, next, title) { + assert(title === 'foo.js'); + next(); + }); + + app.onLoad('blog/:title', function (view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + + it('should emit errors', function (done) { + app = new App(); + app.create('posts'); + + app.on('error', function(err) { + assert(err.message === 'false == true'); + done(); + }); + + // wrong... + app.param('title', function (view, next, title) { + assert(title === 'fo.js'); + next(); + }); + + app.onLoad('/blog/:title', function (view, next) { + assert(view.path === '/blog/foo.js'); + next(); + }); + + app.post('whatever', {path: '/blog/foo.js', content: 'bar baz'}); + }); + + it('should have path property', function () { + var route = new app.Route('/blog/:year/:month/:day/:slug').all([ + function () {} + ]); + route.path.should.equal('/blog/:year/:month/:day/:slug'); + }); + + it('should have stack property', function () { + var route = new app.Route('/blog/:year/:month/:day/:slug').all([ + function () {} + ]); + + route.stack.should.be.instanceof(Array); + route.stack.should.have.length(1); + }); + }); +}); diff --git a/test/app.src.js b/test/app.src.js new file mode 100644 index 00000000..aae44fde --- /dev/null +++ b/test/app.src.js @@ -0,0 +1,296 @@ +'use strict'; + +var App = require('..'); +var should = require('should'); +var join = require('path').join; +var app; + +describe('app input stream', function() { + + describe('src()', function() { + beforeEach(function () { + app = new App(); + }); + + it('should return a stream', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + should.exist(stream); + should.exist(stream.on); + done(); + }); + + it('should return an input stream from a flat glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('this is a test'); + }); + stream.on('end', function () { + done(); + }); + }); + + it('should return an input stream for multiple globs', function (done) { + var globArray = [ + join(__dirname, './fixtures/generic/run.dmc'), + join(__dirname, './fixtures/generic/test.dmc') + ]; + var stream = app.src(globArray); + + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(2); + files[0].path.should.equal(globArray[0]); + files[1].path.should.equal(globArray[1]); + done(); + }); + }); + + it('should return an input stream for multiple globs with negation', function (done) { + var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); + var globArray = [ + join(__dirname, './fixtures/generic/*.dmc'), + '!' + join(__dirname, './fixtures/generic/test.dmc'), + ]; + var stream = app.src(globArray); + + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(1); + files[0].path.should.equal(expectedPath); + done(); + }); + }); + + it('should return an input stream with no contents when read is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + }); + stream.on('end', function () { + done(); + }); + }); + + it('should return an input stream with contents as stream when buffer is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + var buf = ''; + file.contents.on('data', function (d) { + buf += d; + }); + file.contents.on('end', function () { + buf.should.equal('this is a test'); + done(); + }); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + }); + }); + + it('should return an input stream from a deep glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.jade')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); + String(file.contents).should.equal('test template'); + }); + stream.on('end', function () { + done(); + }); + }); + + it('should return an input stream from a deeper glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); + var a = 0; + stream.on('error', done); + stream.on('data', function () { + ++a; + }); + stream.on('end', function () { + a.should.equal(2); + done(); + }); + }); + + it('should return a file stream from a flat path', function (done) { + var a = 0; + var stream = app.src(join(__dirname, './fixtures/test.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + ++a; + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('this is a test'); + }); + stream.on('end', function () { + a.should.equal(1); + done(); + }); + }); + + it('should return a stream', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + should.exist(stream); + should.exist(stream.on); + done(); + }); + + it('should return an input stream from a flat glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('this is a test'); + }); + stream.on('end', function () { + done(); + }); + }); + + it('should return an input stream for multiple globs', function (done) { + var globArray = [ + join(__dirname, './fixtures/generic/run.dmc'), + join(__dirname, './fixtures/generic/test.dmc') + ]; + var stream = app.src(globArray); + + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(2); + files[0].path.should.equal(globArray[0]); + files[1].path.should.equal(globArray[1]); + done(); + }); + }); + + it('should return an input stream for multiple globs, with negation', function (done) { + var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); + var globArray = [ + join(__dirname, './fixtures/generic/*.dmc'), + '!' + join(__dirname, './fixtures/generic/test.dmc'), + ]; + var stream = app.src(globArray); + + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(1); + files[0].path.should.equal(expectedPath); + done(); + }); + }); + + it('should return an input stream with no contents when read is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + }); + stream.on('end', function () { + done(); + }); + }); + + it.skip('should return a throw an error when buffer is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); + stream.on('error', function () { + done(); + }); + stream.on('data', function () { + done(new Error('should have thrown an error')); + }); + }); + + it('should return an input stream from a deep glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.jade')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); + String(file.contents).should.equal('test template'); + }); + stream.on('end', function () { + done(); + }); + }); + + it('should return an input stream from a deeper glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); + var a = 0; + stream.on('error', done); + stream.on('data', function () { + ++a; + }); + stream.on('end', function () { + a.should.equal(2); + done(); + }); + }); + + it('should return a file stream from a flat path', function (done) { + var a = 0; + var stream = app.src(join(__dirname, './fixtures/test.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + ++a; + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('this is a test'); + }); + stream.on('end', function () { + a.should.equal(1); + done(); + }); + }); + }); +}); diff --git a/test/app.task.js b/test/app.task.js new file mode 100644 index 00000000..f7f4f131 --- /dev/null +++ b/test/app.task.js @@ -0,0 +1,149 @@ +var assert = require('assert'); +var App = require('../'); +var app; + +describe('app', function () { + beforeEach(function () { + app = new App(); + }); + + it('should register a task', function () { + var fn = function (done) { + done(); + }; + app.task('default', fn); + assert.equal(typeof app.tasks.default, 'object'); + assert.equal(app.tasks.default.fn, fn); + }); + + it('should register a task with an array of dependencies', function () { + app.task('default', ['foo', 'bar'], function (done) { + done(); + }); + assert.equal(typeof app.tasks.default, 'object'); + assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + }); + + it('should register a task with a list of strings as dependencies', function () { + app.task('default', 'foo', 'bar', function (done) { + done(); + }); + assert.equal(typeof app.tasks.default, 'object'); + assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + }); + + it('should run a task', function (done) { + var count = 0; + app.task('default', function (cb) { + count++; + cb(); + }); + + app.run('default', function (err) { + if (err) return done(err); + assert.equal(count, 1); + done(); + }); + }); + + it('should throw an error when a task with unregistered dependencies is run', function (done) { + var count = 0; + app.task('default', ['foo', 'bar'], function (cb) { + count++; + cb(); + }); + + app.run('default', function (err) { + if (!err) return done(new Error('Expected an error to be thrown.')); + assert.equal(count, 0); + done(); + }); + }); + + it('should throw an error when `.run` is called without a callback function.', function () { + try { + app.run('default'); + throw new Error('Expected an error to be thrown.'); + } catch (err) { + } + }); + + it('should emit task events', function (done) { + var events = []; + app.on('starting', function (task) { + events.push('starting.' + task.name); + }); + app.on('finished', function (task) { + events.push('finished.' + task.name); + }); + app.on('error', function (err, task) { + events.push('error.' + task.name); + }); + + app.task('foo', function (cb) { + cb(); + }); + app.task('bar', ['foo'], function (cb) { + cb(); + }); + app.task('default', ['bar']); + app.run('default', function (err) { + if (err) return done(err); + assert.deepEqual(events, ['starting.foo','finished.foo','starting.bar','finished.bar','starting.default','finished.default']); + done(); + }); + }); + + it('should emit an error event when an error is passed back in a task', function (done) { + app.on('error', function (err) { + assert(err); + assert.equal(err.message, 'This is an error'); + }); + app.task('default', function (cb) { + return cb(new Error('This is an error')); + }); + app.run('default', function (err) { + if (err) return done(); + done(new Error('Expected an error')); + }); + }); + + it('should emit an error event when an error is thrown in a task', function (done) { + var errors = 0; + app.on('error', function (err) { + errors++; + assert(err); + assert.equal(err.message, 'This is an error'); + }); + app.task('default', function (cb) { + cb(new Error('This is an error')); + }); + app.run('default', function (err) { + assert.equal(errors, 1); + if (err) return done(); + done(new Error('Expected an error')); + }); + }); + + it('should run dependencies before running the dependent task.', function (done) { + var seq = []; + app.task('foo', function (cb) { + seq.push('foo'); + cb(); + }); + app.task('bar', function (cb) { + seq.push('bar'); + cb(); + }); + app.task('default', ['foo', 'bar'], function (cb) { + seq.push('default'); + cb(); + }); + + app.run('default', function (err) { + if (err) return done(err); + assert.deepEqual(seq, ['foo', 'bar', 'default']); + done(); + }); + }); +}); diff --git a/test/app.use.js b/test/app.use.js new file mode 100644 index 00000000..bb059506 --- /dev/null +++ b/test/app.use.js @@ -0,0 +1,280 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var Views = App.Views; +var View = App.View; +var app; + +describe('app.use', function () { + beforeEach(function () { + app = new App(); + }); + + it('should expose the instance to `use`:', function (done) { + app.use(function (inst) { + assert(inst instanceof App); + done(); + }); + }); + + it('should be chainable:', function (done) { + app.use(function (inst) { + assert(inst instanceof App); + }) + .use(function (inst) { + assert(inst instanceof App); + }) + .use(function (inst) { + assert(inst instanceof App); + done(); + }); + }); + + it('should pass to collection `use` if a function is returned:', function () { + app.use(function (inst) { + assert(inst instanceof App); + return function (collection) { + collection.foo = collection.addView; + assert(collection instanceof Views); + return collection; + }; + }); + + app.create('pages') + .foo({path: 'a.md', content: '...'}) + .addView({path: 'b.md', content: '...'}) + .addView({path: 'c.md', content: '...'}); + + assert(app.views.pages.hasOwnProperty('a.md')); + assert(app.views.pages.hasOwnProperty('b.md')); + assert(app.views.pages.hasOwnProperty('c.md')); + }); + + it('should be chainable when a collection function is returned:', function () { + app + .use(function (inst) { + assert(inst instanceof App); + return function (collection) { + collection.foo = collection.addView; + assert(collection instanceof Views); + return collection; + }; + }) + .use(function (inst) { + assert(inst instanceof App); + return function (collection) { + collection.bar = collection.addView; + assert(collection instanceof Views); + return collection; + }; + }) + .use(function (inst) { + assert(inst instanceof App); + return function (collection) { + collection.baz = collection.addView; + assert(collection instanceof Views); + return collection; + }; + }); + + var pages = app.create('pages'); + + pages.foo({path: 'a.md', content: '...'}); + pages.bar({path: 'b.md', content: '...'}); + pages.baz({path: 'c.md', content: '...'}); + + assert(app.views.pages.hasOwnProperty('a.md')); + assert(app.views.pages.hasOwnProperty('b.md')); + assert(app.views.pages.hasOwnProperty('c.md')); + }); + + it('should pass to view `use` if collection.use returns a function:', function () { + app.use(function (inst) { + assert(inst instanceof App); + + return function (collection) { + assert(collection instanceof Views); + collection.foo = collection.addView; + + return function (view) { + assert(view instanceof View); + view.foo = collection.addView.bind(collection); + return view; + }; + }; + }); + + app.create('pages') + .foo({path: 'a.md', content: '...'}) + .foo({path: 'b.md', content: '...'}) + .foo({path: 'c.md', content: '...'}); + + assert(app.views.pages.hasOwnProperty('a.md')); + assert(app.views.pages.hasOwnProperty('b.md')); + assert(app.views.pages.hasOwnProperty('c.md')); + }); + + it('should be chainable when a view function is returned:', function () { + app + .use(function (inst) { + assert(inst instanceof App); + + return function (collection) { + assert(collection instanceof Views); + collection.foo = collection.addView; + + return function (view) { + assert(view instanceof View); + view.a = collection.addView.bind(collection); + return view; + }; + }; + }) + .use(function (inst) { + assert(inst instanceof App); + + return function (collection) { + assert(collection instanceof Views); + collection.bar = collection.addView; + + return function (view) { + assert(view instanceof View); + view.b = collection.addView.bind(collection); + return view; + }; + }; + }) + .use(function (inst) { + assert(inst instanceof App); + + return function (collection) { + assert(collection instanceof Views); + collection.baz = collection.addView; + + return function (view) { + assert(view instanceof View); + view.c = collection.addView.bind(collection); + return view; + }; + }; + }); + + var pages = app.create('pages'); + + pages.foo({path: 'a.md', content: '...'}); + pages.bar({path: 'b.md', content: '...'}); + pages.baz({path: 'c.md', content: '...'}) + .a({path: 'x.md', content: '...'}) + .b({path: 'y.md', content: '...'}) + .c({path: 'z.md', content: '...'}); + + assert(app.views.pages.hasOwnProperty('a.md')); + assert(app.views.pages.hasOwnProperty('b.md')); + assert(app.views.pages.hasOwnProperty('c.md')); + + assert(app.views.pages.hasOwnProperty('x.md')); + assert(app.views.pages.hasOwnProperty('y.md')); + assert(app.views.pages.hasOwnProperty('z.md')); + }); + + it('should work with multiple collections:', function () { + app + .use(function (inst) { + assert(inst instanceof App); + + return function (collection) { + assert(collection instanceof Views); + collection.foo = collection.addView; + + return function (view) { + assert(view instanceof View); + view.a = collection.addView.bind(collection); + return view; + }; + }; + }) + .use(function (inst) { + assert(inst instanceof App); + + return function (collection) { + assert(collection instanceof Views); + collection.bar = collection.addView; + + return function (view) { + assert(view instanceof View); + view.b = collection.addView.bind(collection); + return view; + }; + }; + }) + .use(function (inst) { + assert(inst instanceof App); + assert(this instanceof App); + + return function (collection) { + collection.baz = collection.addView; + assert(collection instanceof Views); + assert(this instanceof Views); + + return function (view) { + assert(this instanceof View); + assert(view instanceof View); + view.c = collection.addView.bind(collection); + return view; + }; + }; + }); + + var pages = app.create('pages'); + + pages.foo({path: 'a.md', content: '...'}); + pages.bar({path: 'b.md', content: '...'}); + pages.baz({path: 'c.md', content: '...'}) + .a({path: 'x.md', content: '...'}) + .b({path: 'y.md', content: '...'}) + .c({path: 'z.md', content: '...'}); + + assert(app.views.pages.hasOwnProperty('a.md')); + assert(app.views.pages.hasOwnProperty('b.md')); + assert(app.views.pages.hasOwnProperty('c.md')); + + assert(app.views.pages.hasOwnProperty('x.md')); + assert(app.views.pages.hasOwnProperty('y.md')); + assert(app.views.pages.hasOwnProperty('z.md')); + + var posts = app.create('posts'); + + posts.foo({path: 'a.md', content: '...'}); + posts.bar({path: 'b.md', content: '...'}); + posts.baz({path: 'c.md', content: '...'}) + .a({path: 'x.md', content: '...'}) + .b({path: 'y.md', content: '...'}) + .c({path: 'z.md', content: '...'}); + + assert(app.views.posts.hasOwnProperty('a.md')); + assert(app.views.posts.hasOwnProperty('b.md')); + assert(app.views.posts.hasOwnProperty('c.md')); + + assert(app.views.posts.hasOwnProperty('x.md')); + assert(app.views.posts.hasOwnProperty('y.md')); + assert(app.views.posts.hasOwnProperty('z.md')); + + var docs = app.create('docs'); + + docs.foo({path: 'a.md', content: '...'}); + docs.bar({path: 'b.md', content: '...'}); + docs.baz({path: 'c.md', content: '...'}) + .a({path: 'x.md', content: '...'}) + .b({path: 'y.md', content: '...'}) + .c({path: 'z.md', content: '...'}); + + assert(app.views.docs.hasOwnProperty('a.md')); + assert(app.views.docs.hasOwnProperty('b.md')); + assert(app.views.docs.hasOwnProperty('c.md')); + + assert(app.views.docs.hasOwnProperty('x.md')); + assert(app.views.docs.hasOwnProperty('y.md')); + assert(app.views.docs.hasOwnProperty('z.md')); + }); +}); diff --git a/test/app.view.compile.js b/test/app.view.compile.js new file mode 100644 index 00000000..a2172584 --- /dev/null +++ b/test/app.view.compile.js @@ -0,0 +1,23 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('app view', function () { + describe('compile method', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should use helpers to render a view:', function () { + var buffer = new Buffer('a b c'); + var view = app.page('a.tmpl', {contents: buffer}) + .compile(); + assert(typeof view.fn === 'function'); + }); + }); +}); + diff --git a/test/app.view.render.js b/test/app.view.render.js new file mode 100644 index 00000000..694ca868 --- /dev/null +++ b/test/app.view.render.js @@ -0,0 +1,72 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('helpers', function () { + describe('rendering', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should use helpers to render a view:', function (done) { + var locals = {name: 'Halle'}; + + app.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + var buffer = new Buffer('a <%= upper(name) %> b'); + app.page('a.tmpl', {contents: buffer, locals: locals}) + .render(function (err, res) { + if (err) return done(err); + + assert(res.contents.toString() === 'a HALLE b'); + done(); + }); + }); + + it('should support helpers as an array:', function (done) { + var locals = {name: 'Halle'}; + + app.helpers([ + { + lower: function (str) { + return str.toLowerCase(str); + } + } + ]); + + var buffer = new Buffer('a <%= lower(name) %> b'); + app.page('a.tmpl', {contents: buffer, locals: locals}) + .render(function (err, res) { + if (err) return done(err); + + assert(res.contents.toString() === 'a halle b'); + done(); + }); + }); + + it('should support helpers as an object:', function (done) { + var locals = {name: 'Halle'}; + + app.helpers({ + prepend: function (prefix, str) { + return prefix + str; + } + }); + + var buffer = new Buffer('a <%= prepend("foo ", name) %> b'); + app.page('a.tmpl', {contents: buffer, locals: locals}) + .render(function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a foo Halle b'); + done(); + }); + }); + }); +}); + diff --git a/test/app.watch.js b/test/app.watch.js new file mode 100644 index 00000000..33f4ec9c --- /dev/null +++ b/test/app.watch.js @@ -0,0 +1,23 @@ +var path = require('path'); +var fs = require('fs'); + +var fixture = path.join(__dirname, 'fixtures/watch'); +var App = require('../'); +var app; + +describe('app', function () { + beforeEach(function () { + app = new App({runtimes: false}); + }); + + it('should run a task when a file changes', function (done) { + var fn = function () { + done(); + }; + app.task('watch-test', fn); + app.watch(fixture + '/**/*.*', ['watch-test']); + setImmediate(function () { + fs.writeFileSync(path.join(fixture, 'test.txt'), 'test'); + }); + }); +}); diff --git a/test/collection.events.js b/test/collection.events.js new file mode 100644 index 00000000..bec1e5ac --- /dev/null +++ b/test/collection.events.js @@ -0,0 +1,26 @@ +require('should'); +var App = require('..'); +var app; + +describe('collection events', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + }); + + it('should emit events:', function () { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); + var events = []; + + app.pages.on('option', function (key) { + events.push(key); + }); + + app.pages.option('a', 'b'); + app.pages.option('c', 'd'); + app.pages.option('e', 'f'); + app.pages.option({g: 'h'}); + + events.should.eql(['a', 'c', 'e', 'g']); + }); +}); diff --git a/test/collection.js b/test/collection.js new file mode 100644 index 00000000..f8a7fc47 --- /dev/null +++ b/test/collection.js @@ -0,0 +1,123 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('..'); +var Views = App.Views; +var collection; + +describe('collection', function () { + describe('method', function () { + beforeEach(function () { + collection = new Views(); + }); + + it('should expose the collection method', function () { + assert(typeof Views === 'function'); + }); + + it('should return a new collection', function () { + var collection = new Views(); + assert(typeof collection === 'object'); + }); + + it('should have isCollection property', function () { + var collection = new Views(); + assert(collection.isCollection === true); + }); + }); + + describe('adding views', function () { + beforeEach(function () { + collection = new Views(); + }); + + it('should load a view onto the respective collection:', function () { + collection.addView('a.hbs'); + collection.views.should.have.property('a.hbs'); + }); + + it('should allow collection methods to be chained:', function () { + collection + .addViews({'a.hbs': {path: 'a.hbs'}}) + .addViews({'b.hbs': {path: 'b.hbs'}}) + .addViews({'c.hbs': {path: 'c.hbs'}}); + + collection.views.should.have.properties([ + 'a.hbs', + 'b.hbs', + 'c.hbs' + ]); + }); + + it('should expose the `option` method:', function () { + collection.option('foo', 'bar') + .addViews('a.hbs') + .addViews('b.hbs') + .addViews('c.hbs'); + + collection.options.should.have.property('foo', 'bar'); + collection.views.should.have.properties([ + 'a.hbs', + 'b.hbs', + 'c.hbs' + ]); + }); + }); + + describe('queue', function () { + beforeEach(function () { + collection = new Views(); + }); + + it('should emit arguments on addView:', function (done) { + collection.on('addView', function (a, b, c, d, e) { + assert(a === 'a'); + assert(b === 'b'); + assert(c === 'c'); + assert(d === 'd'); + assert(e === 'e'); + done(); + }); + + collection.addView('a', 'b', 'c', 'd', 'e'); + }); + + it('should expose the `queue` property for loading views:', function () { + collection.queue.push(collection.view('b', {path: 'b'})); + + collection.addView('a', {path: 'a'}); + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + }); + + it('should load all views on the queue when addView is called:', function () { + collection.on('addView', function (key, value) { + collection.queue.push(collection.view(key, {content: value})); + }); + + collection.addView('a.html', 'aaa'); + collection.addView('b.html', 'bbb'); + collection.addView('c.html', 'ccc'); + assert(collection.views.hasOwnProperty('a.html')); + assert(collection.getView('a.html').content === 'aaa'); + assert(collection.views.hasOwnProperty('b.html')); + assert(collection.getView('b.html').content === 'bbb'); + assert(collection.views.hasOwnProperty('c.html')); + assert(collection.getView('c.html').content === 'ccc'); + }); + + it('should expose the `option` method:', function () { + collection.option('foo', 'bar') + .addViews('a.hbs') + .addViews('b.hbs') + .addViews('c.hbs'); + + collection.options.should.have.property('foo', 'bar'); + collection.views.should.have.properties([ + 'a.hbs', + 'b.hbs', + 'c.hbs' + ]); + }); + }); +}); diff --git a/test/collection.options.js b/test/collection.options.js new file mode 100644 index 00000000..e58cf617 --- /dev/null +++ b/test/collection.options.js @@ -0,0 +1,24 @@ +require('should'); +var App = require('..'); +var app; + +describe('collection.option()', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + }); + + it('should set an option:', function () { + app.pages.options.should.not.have.property('foo'); + app.pages.option('foo', 'bar'); + app.pages.options.should.have.property('foo'); + }); + + it('should extend options:', function () { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); + app.pages.option('a', 'b'); + app.pages.option('c', 'd'); + app.pages.option('e', 'f'); + app.pages.options.should.have.properties(['a', 'c', 'e']); + }); +}); diff --git a/test/collection.use.js b/test/collection.use.js new file mode 100644 index 00000000..05f49331 --- /dev/null +++ b/test/collection.use.js @@ -0,0 +1,155 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var Views = App.Views; +var View = App.View; +var collection; + +describe('collection.use', function () { + beforeEach(function () { + collection = new Views(); + }); + + it('should expose the instance to `use`:', function (done) { + collection.use(function (inst) { + assert(inst instanceof Views); + done(); + }); + }); + + it('should be chainable:', function (done) { + collection.use(function (inst) { + assert(inst instanceof Views); + }) + .use(function (inst) { + assert(inst instanceof Views); + }) + .use(function (inst) { + assert(inst instanceof Views); + done(); + }); + }); + + it('should expose the collection to a plugin:', function () { + collection.use(function (views) { + assert(views instanceof Views); + views.foo = views.addView.bind(views); + }); + + collection.foo('a', {content: '...'}); + assert(collection.views.hasOwnProperty('a')); + }); + + it('should expose collection when chained:', function () { + collection + .use(function (views) { + assert(views instanceof Views); + views.foo = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.bar = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.baz = views.addView.bind(views); + }); + + var pages = collection; + + pages.foo({path: 'a', content: '...'}); + pages.bar({path: 'b', content: '...'}); + pages.baz({path: 'c', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + }); + + it('should work when a custom `View` constructor is passed:', function () { + collection = new Views({View: require('vinyl')}); + collection + .use(function (views) { + assert(views instanceof Views); + views.foo = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.bar = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.baz = views.addView.bind(views); + }); + + var pages = collection; + + pages.foo({path: 'a', content: '...'}); + pages.bar({path: 'b', content: '...'}); + pages.baz({path: 'c', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + }); + + it('should pass to view `use` if a function is returned:', function () { + collection.use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.foo = views.addView.bind(views); + assert(view instanceof View); + }; + }); + + collection.addView('a', {content: '...'}) + .foo({path: 'b', content: '...'}) + .foo({path: 'c', content: '...'}) + .foo({path: 'd', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + assert(collection.views.hasOwnProperty('d')); + }); + + it('should be chainable when a view function is returned:', function () { + collection + .use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.foo = views.addView.bind(views); + assert(view instanceof View); + }; + }) + .use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.bar = views.addView.bind(views); + assert(view instanceof View); + }; + }) + .use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.baz = views.addView.bind(views); + assert(view instanceof View); + }; + }); + + collection.addView('a', {content: '...'}) + .foo({path: 'b', content: '...'}) + .bar({path: 'c', content: '...'}) + .baz({path: 'd', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + assert(collection.views.hasOwnProperty('d')); + }); +}); diff --git a/test/dest.js b/test/dest.js new file mode 100644 index 00000000..3f077c06 --- /dev/null +++ b/test/dest.js @@ -0,0 +1,908 @@ +var spies = require('./support/spy'); +var chmodSpy = spies.chmodSpy; +var statSpy = spies.statSpy; + +var assert = require('assert'); +var App = require('../'); +var app; + +var path = require('path'); +var fs = require('graceful-fs'); +var rimraf = require('rimraf'); + +var bufferStream; +var bufEqual = require('buffer-equal'); +var through = require('through2'); +var File = require('vinyl'); + +var should = require('should'); +require('mocha'); + +var wipeOut = function(cb) { + app = new App(); + rimraf(path.join(__dirname, './out-fixtures/'), cb); + spies.setError('false'); + statSpy.reset(); + chmodSpy.reset(); +}; + +var dataWrap = function(fn) { + return function(data, enc, cb) { + fn(data); + cb(); + }; +}; + +var realMode = function(n) { + return n & 07777; +}; + +describe('dest stream', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should explode on invalid folder (empty)', function(done) { + var stream; + try { + stream = app.dest(); + } catch (err) { + should.exist(err); + should.not.exist(stream); + done(); + } + }); + + it('should explode on invalid folder (empty string)', function(done) { + var stream; + try { + stream = app.dest(''); + } catch (err) { + should.exist(err); + should.not.exist(stream); + done(); + } + }); + + it('should pass through writes with cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should pass through writes with default cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = app.dest(path.join(__dirname, './out-fixtures/')); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not write null files', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(false); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with relative cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: path.relative(process.cwd(), __dirname)}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with function and relative cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = app.dest(function(file){ + should.exist(file); + file.should.equal(expectedFile); + return './out-fixtures'; + }, {cwd: path.relative(process.cwd(), __dirname)}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0655; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write streaming files to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0655; + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + setTimeout(function(){ + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should write directories to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0655; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function(){ + return true; + }, + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + fs.lstatSync(expectedPath).isDirectory().should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should allow piping multiple dests in streaming mode', function(done) { + var inputPath1 = path.join(__dirname, './out-fixtures/multiple-first'); + var inputPath2 = path.join(__dirname, './out-fixtures/multiple-second'); + var inputBase = path.join(__dirname, './out-fixtures/'); + var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var stream1 = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream2 = app.dest('./out-fixtures/', {cwd: __dirname}); + var content = fs.readFileSync(srcPath); + var rename = through.obj(function(file, _, next) { + file.path = inputPath2; + this.push(file); + next(); + }); + + stream1.on('data', function(file) { + file.path.should.equal(inputPath1); + }); + + stream1.pipe(rename).pipe(stream2); + stream2.on('data', function(file) { + file.path.should.equal(inputPath2); + }).once('end', function() { + fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); + fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); + done(); + }); + + var file = new File({ + base: inputBase, + path: inputPath1, + cwd: __dirname, + contents: content + }); + + stream1.write(file); + stream1.end(); + }); + + it('should write new files with the default user mode', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = 0666 & (~process.umask()); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write new files with the specified mode', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = 0744; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname, mode:expectedMode}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should update file mode to match the vinyl mode', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var startMode = 0655; + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + assert(chmodSpy.called); + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, startMode); + + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should use different modes for files and directories', function(done) { + var inputBase = path.join(__dirname, './fixtures/vinyl'); + var inputPath = path.join(__dirname, './fixtures/vinyl/wow/suchempty'); + var expectedBase = path.join(__dirname, './out-fixtures/wow'); + var expectedDirMode = 0755; + var expectedFileMode = 0655; + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function(){ + realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); + realMode(buffered[0].stat.mode).should.equal(expectedFileMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', { + cwd: __dirname, + mode: expectedFileMode, + dirMode: expectedDirMode + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should change to the specified base as string', function(done) { + var inputBase = path.join(__dirname, './fixtures/vinyl'); + var inputPath = path.join(__dirname, './fixtures/vinyl/wow/suchempty'); + + var firstFile = new File({ + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function(){ + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = app.dest('./out-fixtures/', { + cwd: __dirname, + base: inputBase + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should change to the specified base as function', function(done) { + var inputBase = path.join(__dirname, './fixtures/vinyl'); + var inputPath = path.join(__dirname, './fixtures/vinyl/wow/suchempty'); + + var firstFile = new File({ + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function() { + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = app.dest('./out-fixtures/', { + cwd: __dirname, + base: function(file){ + should.exist(file); + file.path.should.equal(inputPath); + return inputBase; + } + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should report IO errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, 0); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + stream.on('error', function(err) { + err.code.should.equal('EACCES'); + done(); + }); + stream.write(expectedFile); + }); + + it('should report stat errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + spies.setError(function(mod, fn) { + if (fn === 'stat' && arguments[2] === expectedPath) { + return new Error('stat error'); + } + }); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + stream.on('error', function(err) { + err.message.should.equal('stat error'); + done(); + }); + stream.write(expectedFile); + }); + + it('should report chmod errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + spies.setError(function(mod, fn) { + if (fn === 'chmod' && arguments[2] === expectedPath) { + return new Error('chmod error'); + } + }); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + stream.on('error', function(err) { + err.message.should.equal('chmod error'); + done(); + }); + stream.write(expectedFile); + }); + + it('should not chmod a matching file', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var expectedCount = 0; + spies.setError(function(mod, fn) { + if (fn === 'stat' && arguments[2] === expectedPath) { + expectedCount++; + } + }); + + var onEnd = function(){ + expectedCount.should.equal(1); + assert(!chmodSpy.called); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, expectedMode); + + statSpy.reset(); + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = 03722; + var normalMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: normalMode + } + }); + + var expectedCount = 0; + spies.setError(function(mod, fn) { + if (fn === 'stat' && arguments[2] === expectedPath) { + expectedCount++; + } + }); + + var onEnd = function(){ + expectedCount.should.equal(1); + assert(!chmodSpy.called); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, expectedMode); + + statSpy.reset(); + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not overwrite files with overwrite option set to false', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, './out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); + done(); + }; + + // Write expected file which should not be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: false}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should overwrite files with overwrite option set to true', function(done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, './out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); + done(); + }; + + // This should be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: true}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should create symlinks when the `symlink` attribute is set on the file', function (done) { + var inputPath = path.join(__dirname, './fixtures/vinyl/test-create-dir-symlink'); + var inputBase = path.join(__dirname, './fixtures/vinyl/'); + var inputRelativeSymlinkPath = 'wow'; + + var expectedPath = path.join(__dirname, './out-fixtures/test-create-dir-symlink'); + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, //'' + }); + + // `src()` adds this side-effect with `keepSymlinks` option set to false + inputFile.symlink = inputRelativeSymlinkPath; + + var onEnd = function(){ + fs.readlink(buffered[0].path, function () { + buffered[0].symlink.should.equal(inputFile.symlink); + buffered[0].path.should.equal(expectedPath); + done(); + }); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should emit finish event', function(done) { + var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + stream.once('finish', function() { + done(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: new Buffer("1234567890") + }); + + stream.write(file); + stream.end(); + }); +}); diff --git a/test/fixtures/apidocs-comments.js b/test/fixtures/apidocs-comments.js deleted file mode 100644 index ebb9c1ae..00000000 --- a/test/fixtures/apidocs-comments.js +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * Banner - */ - -/** - * Assign `value` to `key` - * - * @param {String} a - * @param {*} b - * @api public - */ - -function set(a, b, c) { - // do stuff -} diff --git a/test/fixtures/assets/a.hbs b/test/fixtures/assets/a.hbs new file mode 100644 index 00000000..d67fcdf4 --- /dev/null +++ b/test/fixtures/assets/a.hbs @@ -0,0 +1,8 @@ +--- +title: A +draft: true +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/assets/b.hbs b/test/fixtures/assets/b.hbs new file mode 100644 index 00000000..89bbba52 --- /dev/null +++ b/test/fixtures/assets/b.hbs @@ -0,0 +1,8 @@ +--- +title: B +draft: true +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/assets/c.hbs b/test/fixtures/assets/c.hbs new file mode 100644 index 00000000..6ba18f52 --- /dev/null +++ b/test/fixtures/assets/c.hbs @@ -0,0 +1,8 @@ +--- +title: C +draft: false +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/assets/d.hbs b/test/fixtures/assets/d.hbs new file mode 100644 index 00000000..a67a85c6 --- /dev/null +++ b/test/fixtures/assets/d.hbs @@ -0,0 +1,7 @@ +--- +title: D +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/auto-loading/a.js b/test/fixtures/auto-loading/a.js deleted file mode 100644 index 98594c1b..00000000 --- a/test/fixtures/auto-loading/a.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = function a(noun) { - noun.a = 'aaa'; - return noun; -}; diff --git a/test/fixtures/auto-loading/a.md b/test/fixtures/auto-loading/a.md deleted file mode 100644 index 5f898d8d..00000000 --- a/test/fixtures/auto-loading/a.md +++ /dev/null @@ -1 +0,0 @@ -# this is a fixture \ No newline at end of file diff --git a/test/fixtures/auto-loading/b.js b/test/fixtures/auto-loading/b.js deleted file mode 100644 index de8fa98a..00000000 --- a/test/fixtures/auto-loading/b.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = function b(noun) { - noun.b = 'bbb'; - return noun; -}; diff --git a/test/fixtures/auto-loading/c.js b/test/fixtures/auto-loading/c.js deleted file mode 100644 index bddcc0bb..00000000 --- a/test/fixtures/auto-loading/c.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = function c(noun) { - noun.c = 'ccc'; - return noun; -}; diff --git a/test/fixtures/auto-loading/package.json b/test/fixtures/auto-loading/package.json deleted file mode 100644 index 2a7aa652..00000000 --- a/test/fixtures/auto-loading/package.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "verb", - "description": "Verb makes it dead simple to generate markdown documentation, using simple templates, with zero configuration required. A project without documentation is like a project that doesn't exist.", - "version": "0.4.5", - "homepage": "https://github.com/verbose/verb", - "author": { - "name": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert" - }, - "repository": "verbose/verb", - "bugs": { - "url": "https://github.com/verbose/verb/issues" - }, - "license": "MIT", - "files": [ - "lib/", - "index.js" - ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, - "dependencies": { - "async": "^0.9.0", - "chalk": "^0.5.1", - "cwd": "^0.4.0", - "debug": "^2.1.1", - "engine-lodash": "^0.5.0", - "event-stream": "^3.2.2", - "export-files": "^1.0.0", - "extend-shallow": "^0.2.0", - "for-in": "^0.1.3", - "globby": "^1.1.0", - "gulp-util": "^3.0.3", - "helper-apidocs": "^0.2.1", - "helper-copyright": "^1.1.1", - "helper-date": "^0.2.0", - "helper-license": "^0.1.3", - "load-plugins": "^1.0.1", - "logging-helpers": "^0.3.0", - "map-files": "^0.3.0", - "merge-deep": "^0.1.3", - "object.reduce": "^0.1.3", - "orchestrator": "^0.3.7", - "parse-author": "^0.1.0", - "parse-copyright": "^0.3.1", - "repo-utils": "^0.1.1", - "session-cache": "^0.1.3", - "template": "^0.10.1", - "template-utils": "^0.4.1", - "through2": "^0.6.3", - "verb-readme-badges": "^0.2.0", - "verb-readme-includes": "^0.3.0", - "vinyl-fs": "^0.3.13", - "write-yaml": "^0.1.2" - }, - "devDependencies": { - "consolidate": "^0.11.0", - "gulp-istanbul": "^0.6.0", - "gulp-jshint": "^1.9.2", - "gulp-mocha": "^2.0.0", - "handlebars": "^3.0.0", - "jshint-stylish": "^1.0.0", - "lodash": "^3.2.0", - "mocha": "^2.0.1", - "should": "^4.3.0", - "swig": "^1.4.2" - }, - "keywords": [ - "comment", - "comments", - "doc", - "docs", - "document", - "documentation", - "generate", - "generator", - "gh", - "gh-pages", - "markdown", - "md", - "pages", - "readme", - "repo", - "repository", - "template", - "templates", - "verb", - "verbiage" - ] -} \ No newline at end of file diff --git a/test/fixtures/copy/example.txt b/test/fixtures/copy/example.txt new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/copy/example.txt @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/data/a.json b/test/fixtures/data/a.json new file mode 100644 index 00000000..8470de46 --- /dev/null +++ b/test/fixtures/data/a.json @@ -0,0 +1,3 @@ +{ + "one": {"a": "aaa"} +} \ No newline at end of file diff --git a/test/fixtures/data/a.yml b/test/fixtures/data/a.yml deleted file mode 100644 index 718c772c..00000000 --- a/test/fixtures/data/a.yml +++ /dev/null @@ -1 +0,0 @@ -one: two diff --git a/test/fixtures/data/alert.json b/test/fixtures/data/alert.json new file mode 100644 index 00000000..31767e67 --- /dev/null +++ b/test/fixtures/data/alert.json @@ -0,0 +1,7 @@ +{ + "success": { + "test": true, + "strong": "Heads up! This is a warning!", + "text": "You forgot a field!" + } +} \ No newline at end of file diff --git a/test/fixtures/data/b.json b/test/fixtures/data/b.json new file mode 100644 index 00000000..5f2fde78 --- /dev/null +++ b/test/fixtures/data/b.json @@ -0,0 +1,3 @@ +{ + "two": {"b": "bbb"} +} \ No newline at end of file diff --git a/test/fixtures/data/c.json b/test/fixtures/data/c.json new file mode 100644 index 00000000..7c274f3e --- /dev/null +++ b/test/fixtures/data/c.json @@ -0,0 +1,3 @@ +{ + "three": {"c": "ccc"} +} \ No newline at end of file diff --git a/test/fixtures/data/data.json b/test/fixtures/data/data.json new file mode 100644 index 00000000..7cd0452e --- /dev/null +++ b/test/fixtures/data/data.json @@ -0,0 +1,3 @@ +{ + "root": "Whoa, I should be at the root!" +} \ No newline at end of file diff --git a/test/fixtures/data/test.json b/test/fixtures/data/test.json new file mode 100644 index 00000000..c8e407ba --- /dev/null +++ b/test/fixtures/data/test.json @@ -0,0 +1,4 @@ +{ + "alpha": "one", + "beta": "two" +} \ No newline at end of file diff --git a/test/fixtures/dest-path/a.hbs b/test/fixtures/dest-path/a.hbs new file mode 100644 index 00000000..d67fcdf4 --- /dev/null +++ b/test/fixtures/dest-path/a.hbs @@ -0,0 +1,8 @@ +--- +title: A +draft: true +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/dest-path/b.hbs b/test/fixtures/dest-path/b.hbs new file mode 100644 index 00000000..89bbba52 --- /dev/null +++ b/test/fixtures/dest-path/b.hbs @@ -0,0 +1,8 @@ +--- +title: B +draft: true +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/dest-path/c.hbs b/test/fixtures/dest-path/c.hbs new file mode 100644 index 00000000..6ba18f52 --- /dev/null +++ b/test/fixtures/dest-path/c.hbs @@ -0,0 +1,8 @@ +--- +title: C +draft: false +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/dest-path/d.hbs b/test/fixtures/dest-path/d.hbs new file mode 100644 index 00000000..a67a85c6 --- /dev/null +++ b/test/fixtures/dest-path/d.hbs @@ -0,0 +1,7 @@ +--- +title: D +--- + +{{title}} + +{{assets}} \ No newline at end of file diff --git a/test/fixtures/docs/a.md b/test/fixtures/docs/a.md deleted file mode 100644 index f7d1f887..00000000 --- a/test/fixtures/docs/a.md +++ /dev/null @@ -1,17 +0,0 @@ -# AAA - -> blockquote - -some content - -## One - -Section one - -## Two - -Section two - -## Three - -Section three. diff --git a/test/fixtures/docs/b.md b/test/fixtures/docs/b.md deleted file mode 100644 index 1905bbee..00000000 --- a/test/fixtures/docs/b.md +++ /dev/null @@ -1,17 +0,0 @@ -# BBB - -> blockquote - -some content - -## One - -Section one - -## Two - -Section two - -## Three - -Section three. diff --git a/test/fixtures/drafts/a.hbs b/test/fixtures/drafts/a.hbs new file mode 100644 index 00000000..4f65e369 --- /dev/null +++ b/test/fixtures/drafts/a.hbs @@ -0,0 +1,6 @@ +--- +title: A +draft: true +--- + +{{title}} \ No newline at end of file diff --git a/test/fixtures/drafts/b.hbs b/test/fixtures/drafts/b.hbs new file mode 100644 index 00000000..9af946b5 --- /dev/null +++ b/test/fixtures/drafts/b.hbs @@ -0,0 +1,6 @@ +--- +title: B +draft: true +--- + +{{title}} \ No newline at end of file diff --git a/test/fixtures/drafts/c.hbs b/test/fixtures/drafts/c.hbs new file mode 100644 index 00000000..6366989e --- /dev/null +++ b/test/fixtures/drafts/c.hbs @@ -0,0 +1,6 @@ +--- +title: C +draft: false +--- + +{{title}} \ No newline at end of file diff --git a/test/fixtures/drafts/d.hbs b/test/fixtures/drafts/d.hbs new file mode 100644 index 00000000..8fb914b6 --- /dev/null +++ b/test/fixtures/drafts/d.hbs @@ -0,0 +1,5 @@ +--- +title: D +--- + +{{title}} \ No newline at end of file diff --git a/test/fixtures/front-matter/autodetect-no-lang.md b/test/fixtures/front-matter/autodetect-no-lang.md new file mode 100644 index 00000000..99d72210 --- /dev/null +++ b/test/fixtures/front-matter/autodetect-no-lang.md @@ -0,0 +1,5 @@ +--- +title: autodetect-no-lang +user: jonschlinkert +--- +Content \ No newline at end of file diff --git a/test/fixtures/front-matter/autodetect-yaml.md b/test/fixtures/front-matter/autodetect-yaml.md new file mode 100644 index 00000000..12eb7682 --- /dev/null +++ b/test/fixtures/front-matter/autodetect-yaml.md @@ -0,0 +1,5 @@ +---yaml +title: autodetect-yaml +user: jonschlinkert +--- +Content \ No newline at end of file diff --git a/test/fixtures/front-matter/lang-yaml.md b/test/fixtures/front-matter/lang-yaml.md new file mode 100644 index 00000000..414d6390 --- /dev/null +++ b/test/fixtures/front-matter/lang-yaml.md @@ -0,0 +1,5 @@ +--- +title: YAML +--- + +# This page has YAML front matter! diff --git a/test/fixtures/generic/run.dmc b/test/fixtures/generic/run.dmc new file mode 100644 index 00000000..f5925c06 --- /dev/null +++ b/test/fixtures/generic/run.dmc @@ -0,0 +1 @@ +# run.dmc \ No newline at end of file diff --git a/test/fixtures/generic/test.dmc b/test/fixtures/generic/test.dmc new file mode 100644 index 00000000..2286d5d4 --- /dev/null +++ b/test/fixtures/generic/test.dmc @@ -0,0 +1 @@ +# test.dmc \ No newline at end of file diff --git a/test/fixtures/helpers/a.js b/test/fixtures/helpers/a.js new file mode 100644 index 00000000..44919f59 --- /dev/null +++ b/test/fixtures/helpers/a.js @@ -0,0 +1,3 @@ +module.exports = function aaa() { + +}; \ No newline at end of file diff --git a/test/fixtures/helpers/b.js b/test/fixtures/helpers/b.js new file mode 100644 index 00000000..73142e0c --- /dev/null +++ b/test/fixtures/helpers/b.js @@ -0,0 +1,3 @@ +module.exports = function bbb() { + +}; \ No newline at end of file diff --git a/test/fixtures/helpers/c.js b/test/fixtures/helpers/c.js new file mode 100644 index 00000000..ae7c3579 --- /dev/null +++ b/test/fixtures/helpers/c.js @@ -0,0 +1,3 @@ +module.exports = function ccc() { + +}; \ No newline at end of file diff --git a/test/fixtures/helpers/obj.js b/test/fixtures/helpers/obj.js new file mode 100644 index 00000000..a139721f --- /dev/null +++ b/test/fixtures/helpers/obj.js @@ -0,0 +1,9 @@ +exports.one = function one() { + +}; +exports.two = function two() { + +}; +exports.three = function three() { + +}; \ No newline at end of file diff --git a/test/fixtures/includes/a.md b/test/fixtures/includes/a.md deleted file mode 100644 index 64b375b1..00000000 --- a/test/fixtures/includes/a.md +++ /dev/null @@ -1 +0,0 @@ -This is a.md diff --git a/test/fixtures/includes/footer.hbs b/test/fixtures/includes/footer.hbs new file mode 100644 index 00000000..decae978 --- /dev/null +++ b/test/fixtures/includes/footer.hbs @@ -0,0 +1,4 @@ +--- +message: Message from the footer +--- +
Footer {{message}}
\ No newline at end of file diff --git a/test/fixtures/includes/header.hbs b/test/fixtures/includes/header.hbs new file mode 100644 index 00000000..7845f006 --- /dev/null +++ b/test/fixtures/includes/header.hbs @@ -0,0 +1,4 @@ +--- +message: Message from the header +--- +
Header {{message}}
\ No newline at end of file diff --git a/test/fixtures/includes/info.hbs b/test/fixtures/includes/info.hbs new file mode 100644 index 00000000..26802499 --- /dev/null +++ b/test/fixtures/includes/info.hbs @@ -0,0 +1 @@ +
(From info.hbs) - Title: {{site.title}} - {{title}}
\ No newline at end of file diff --git a/test/fixtures/includes/post.hbs b/test/fixtures/includes/post.hbs new file mode 100644 index 00000000..818df44a --- /dev/null +++ b/test/fixtures/includes/post.hbs @@ -0,0 +1,11 @@ +
{{author}}
+
{{timestamp}}
+{{#if ../show-full}} +
+ {{content}} +
+{{else}} +
+ {{summary}} +
+{{/if}} diff --git a/test/fixtures/layouts/base.hbs b/test/fixtures/layouts/base.hbs new file mode 100644 index 00000000..eca79131 --- /dev/null +++ b/test/fixtures/layouts/base.hbs @@ -0,0 +1,10 @@ + + + + + {{site.title}} + + +{% body %} + + \ No newline at end of file diff --git a/test/fixtures/layouts/default.hbs b/test/fixtures/layouts/default.hbs new file mode 100644 index 00000000..a806196c --- /dev/null +++ b/test/fixtures/layouts/default.hbs @@ -0,0 +1,6 @@ +--- +layout: base +--- +{{> header }} +{% body %} +{{> footer }} \ No newline at end of file diff --git a/test/fixtures/layouts/post.hbs b/test/fixtures/layouts/post.hbs new file mode 100644 index 00000000..7a02efbc --- /dev/null +++ b/test/fixtures/layouts/post.hbs @@ -0,0 +1,13 @@ +--- +layout: default +--- +
+ {% body %} +
+ \ No newline at end of file diff --git a/test/fixtures/middleware/copyright.js b/test/fixtures/middleware/copyright.js deleted file mode 100644 index 77eef3bd..00000000 --- a/test/fixtures/middleware/copyright.js +++ /dev/null @@ -1,12 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -/** - * This is a fixture for the middleware copyright test - */ diff --git a/test/fixtures/middleware/multi-toc-options.md b/test/fixtures/middleware/multi-toc-options.md deleted file mode 100644 index 4a1a8a7f..00000000 --- a/test/fixtures/middleware/multi-toc-options.md +++ /dev/null @@ -1,21 +0,0 @@ -# TOC fixture - -> random comments in a blockquote because I don't know how to be creative when I'm writing fixtures. - -## Table of Contents - - - -## Heading 1 - -Some content. - -## Heading 2 - -More content. - -### Heading 3 - -More content. - -### Heading 4 diff --git a/test/fixtures/middleware/multi-toc.md b/test/fixtures/middleware/multi-toc.md deleted file mode 100644 index c238eeca..00000000 --- a/test/fixtures/middleware/multi-toc.md +++ /dev/null @@ -1,21 +0,0 @@ -# TOC fixture - -> random comments in a blockquote because I don't know how to be creative when I'm writing fixtures. - -## Table of Contents - - - -## Heading 1 - -Some content. - -## Heading 2 - -More content. - -### Heading 3 - -More content. - -### Heading 4 diff --git a/test/fixtures/middleware/toc.md b/test/fixtures/middleware/toc.md deleted file mode 100644 index d8e20182..00000000 --- a/test/fixtures/middleware/toc.md +++ /dev/null @@ -1,21 +0,0 @@ -# TOC fixture - -> random comments in a blockquote because I don't know how to be creative when I'm writing fixtures. - -## Table of Contents - - - -## Heading 1 - -Some content. - -## Heading 2 - -More content. - -### Heading 3 - -More content. - -### Heading 4 diff --git a/test/fixtures/middleware/todo.js b/test/fixtures/middleware/todo.js deleted file mode 100644 index 175048ce..00000000 --- a/test/fixtures/middleware/todo.js +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * Random banner - */ - -function foo() {} - -/** - * @TODO: this is todo #1 --> - * - * some random text - * @todo:this is todo #2 --> - * @todo: this is todo #3 --> - */ - -function bar() {} diff --git a/test/fixtures/package.json b/test/fixtures/package.json deleted file mode 100644 index 01265e5f..00000000 --- a/test/fixtures/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "foo", - "description": "bar.", - "version": "25.0", - "homepage": "https://github.com/verbose/verb", - "author": { - "name": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert" - }, - "repository": "verbose/verb", - "bugs": { - "url": "https://github.com/verbose/verb/issues" - }, - "license": "MIT", - "files": [ - "lib/", - "index.js" - ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, - "dependencies": { - "async": "^0.9.0", - "chalk": "^0.5.1", - "cwd": "^0.4.0", - "debug": "^2.1.1", - "diff": "^1.2.2" - }, - "devDependencies": { - "lodash": "^3.3.0", - "should": "^5.0.1", - "swig": "^1.4.2" - }, - "keywords": [ - "comment", - "comments", - "doc", - "docs", - "document", - "documentation", - "generate", - "generator", - "gh", - "gh-pages", - "markdown", - "md", - "pages", - "readme", - "repo", - "repository", - "template", - "templates", - "verb", - "verbiage" - ] -} diff --git a/test/fixtures/pages/a.hbs b/test/fixtures/pages/a.hbs new file mode 100644 index 00000000..0ec5d8d8 --- /dev/null +++ b/test/fixtures/pages/a.hbs @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/fixtures/pages/b.hbs b/test/fixtures/pages/b.hbs new file mode 100644 index 00000000..0ec5d8d8 --- /dev/null +++ b/test/fixtures/pages/b.hbs @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/fixtures/pages/c.hbs b/test/fixtures/pages/c.hbs new file mode 100644 index 00000000..0ec5d8d8 --- /dev/null +++ b/test/fixtures/pages/c.hbs @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/fixtures/parsers/a.a b/test/fixtures/parsers/a.a new file mode 100644 index 00000000..f2ba8f84 --- /dev/null +++ b/test/fixtures/parsers/a.a @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/test/fixtures/parsers/a.js b/test/fixtures/parsers/a.js new file mode 100644 index 00000000..ab2588a1 --- /dev/null +++ b/test/fixtures/parsers/a.js @@ -0,0 +1,3 @@ +module.exports = function a(file, enc, opts) { + return file; +}; diff --git a/test/fixtures/parsers/b.js b/test/fixtures/parsers/b.js new file mode 100644 index 00000000..96d66979 --- /dev/null +++ b/test/fixtures/parsers/b.js @@ -0,0 +1,3 @@ +module.exports = function b(file, enc, opts) { + return file; +}; diff --git a/test/fixtures/parsers/c.js b/test/fixtures/parsers/c.js new file mode 100644 index 00000000..a47440ae --- /dev/null +++ b/test/fixtures/parsers/c.js @@ -0,0 +1,3 @@ +module.exports = function c(file, enc, opts) { + return file; +}; diff --git a/test/fixtures/parsers/x.x b/test/fixtures/parsers/x.x new file mode 100644 index 00000000..d66d9d75 --- /dev/null +++ b/test/fixtures/parsers/x.x @@ -0,0 +1 @@ +xyz \ No newline at end of file diff --git a/test/fixtures/posts/a.txt b/test/fixtures/posts/a.txt new file mode 100644 index 00000000..bca29ee6 --- /dev/null +++ b/test/fixtures/posts/a.txt @@ -0,0 +1,4 @@ +--- +title: AAA +--- +This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/b.txt b/test/fixtures/posts/b.txt new file mode 100644 index 00000000..1e128c70 --- /dev/null +++ b/test/fixtures/posts/b.txt @@ -0,0 +1,4 @@ +--- +title: BBB +--- +This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/c.txt b/test/fixtures/posts/c.txt new file mode 100644 index 00000000..32f91870 --- /dev/null +++ b/test/fixtures/posts/c.txt @@ -0,0 +1,4 @@ +--- +title: CCC +--- +This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/routes/example.hbs b/test/fixtures/routes/example.hbs new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/routes/example.hbs @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/routes/example.txt b/test/fixtures/routes/example.txt new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/routes/example.txt @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/scaffolds/test.md b/test/fixtures/scaffolds/test.md new file mode 100644 index 00000000..104a7cfb --- /dev/null +++ b/test/fixtures/scaffolds/test.md @@ -0,0 +1 @@ +{{name}} \ No newline at end of file diff --git a/test/fixtures/scaffolds/test.txt b/test/fixtures/scaffolds/test.txt new file mode 100644 index 00000000..5b88ae15 --- /dev/null +++ b/test/fixtures/scaffolds/test.txt @@ -0,0 +1 @@ +test file! \ No newline at end of file diff --git a/test/fixtures/templates/README-with-include.md b/test/fixtures/templates/README-with-include.md deleted file mode 100644 index e20115b3..00000000 --- a/test/fixtures/templates/README-with-include.md +++ /dev/null @@ -1,3 +0,0 @@ -# Success! - -{%= include("a.md") %} diff --git a/test/fixtures/templates/README.md b/test/fixtures/templates/README.md deleted file mode 100644 index 6199cb82..00000000 --- a/test/fixtures/templates/README.md +++ /dev/null @@ -1 +0,0 @@ -# Success! \ No newline at end of file diff --git a/test/fixtures/templates/a.tmpl b/test/fixtures/templates/a.tmpl new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/test/fixtures/templates/a.tmpl @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/b.tmpl b/test/fixtures/templates/b.tmpl new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/test/fixtures/templates/b.tmpl @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/c.tmpl b/test/fixtures/templates/c.tmpl new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/test/fixtures/templates/c.tmpl @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/helpers.md b/test/fixtures/templates/helpers.md deleted file mode 100644 index be146064..00000000 --- a/test/fixtures/templates/helpers.md +++ /dev/null @@ -1,42 +0,0 @@ - -{%%= badge() %} - -{%%= changelog() %} - -{%%= changelog("history.yml") %} - -{%%= contrib('authors') %} - -{%%= copyright() %} - -{%%= copyright('2010') %} - -{%%= date('YYYY-MM-DD') %} - -{%= date('YYYY-MM-DD') %} - -{%%= docs('install') %} - -{%%= html() %} - -{%%= include('footer.md') %} - -{%%= log() %} - -{%%= methods('foo.js') %} - -{%%= methods('foo.js', {template: 'yaml'}) %} - -{%%= moment() %} - -{%%= raw() %} - -# Table of Contents - -{%%= toc() %} - -{%%= comments('foo/*.js') %} - -{%%= authors() %} - -{%%= foo('name') %} diff --git a/test/fixtures/test.coffee b/test/fixtures/test.coffee new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/test.coffee @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/test/run.jade b/test/fixtures/test/run.jade new file mode 100644 index 00000000..f6418020 --- /dev/null +++ b/test/fixtures/test/run.jade @@ -0,0 +1 @@ +test template \ No newline at end of file diff --git a/test/fixtures/vinyl/bom-utf16be.txt b/test/fixtures/vinyl/bom-utf16be.txt new file mode 100644 index 0000000000000000000000000000000000000000..b9dce78a5d31af4803acd1a0f0dfc14f064a5de1 GIT binary patch literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 literal 0 HcmV?d00001 diff --git a/test/fixtures/vinyl/bom-utf16le.txt b/test/fixtures/vinyl/bom-utf16le.txt new file mode 100644 index 0000000000000000000000000000000000000000..07cc600c98675d221bb56d10af38e650538734c9 GIT binary patch literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15lThis is an alert diff --git a/test/fixtures/yfm/yfm.hbs b/test/fixtures/yfm/yfm.hbs new file mode 100644 index 00000000..88d2411f --- /dev/null +++ b/test/fixtures/yfm/yfm.hbs @@ -0,0 +1,5 @@ +--- +foo: bar +--- + +
This is an alert
diff --git a/test/group.js b/test/group.js new file mode 100644 index 00000000..6df21072 --- /dev/null +++ b/test/group.js @@ -0,0 +1,130 @@ +require('mocha'); +require('should'); +var List = require('..').List; +var Group = require('..').Group; +var assert = require('./support/'); +var group; + +describe('group', function () { + describe('constructor', function () { + it('should create an instance of Group:', function () { + var group = new Group(); + assert(group instanceof Group); + }); + + it('should create an instance of Group with default List:', function () { + var group = new Group(); + assert.deepEqual(group.List, List); + }); + + it('should create an instance of Group with custom List:', function () { + function CustomList () { + List.apply(this, arguments); + } + List.extend(CustomList); + var group = new Group({List: CustomList}); + assert.deepEqual(group.List, CustomList); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof Group.extend ==='function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function() { + group = new Group(); + }); + + it('should expose `use`', function () { + assert(typeof group.use ==='function'); + }); + it('should expose `set`', function () { + assert(typeof group.set ==='function'); + }); + it('should expose `get`', function () { + assert(typeof group.get ==='function'); + }); + it('should expose `visit`', function () { + assert(typeof group.visit ==='function'); + }); + it('should expose `define`', function () { + assert(typeof group.define ==='function'); + }); + }); + + describe('instance', function () { + beforeEach(function() { + group = new Group(); + }); + + it('should set a value on the instance:', function () { + group.set('a', 'b'); + assert(group.a ==='b'); + }); + + it('should get a value from the instance:', function () { + group.set('a', 'b'); + assert(group.get('a') ==='b'); + }); + }); + + describe('get', function() { + it('should get a normal value when not an array', function () { + var group = new Group({'foo': {items: [1,2,3]}}); + assert.deepEqual(group.get('foo'), {items: [1,2,3]}); + }); + + it('should get an instance of List when value is an array', function () { + var group = new Group({'foo': {items: [{path: 'one.hbs'},{path: 'two.hbs'}, {path: 'three.hbs'}]}}); + var list = group.get('foo.items'); + assert(list instanceof List); + assert.deepEqual(list.items.length, 3); + }); + + it('should throw an error when trying to use a List method on a non List value', function () { + (function () { + var group = new Group({'foo': {items: [1,2,3]}}); + var foo = group.get('foo'); + foo.paginate(); + }).should.throw('paginate can only be used with an array of `List` items.'); + }); + + it('should not override properties already existing on non List values', function (done) { + var group = new Group({'foo': {items: [1,2,3], paginate: function () { + assert(true); + done(); + }}}); + var foo = group.get('foo'); + foo.paginate(); + }); + }); + + describe('use', function() { + beforeEach(function() { + group = new Group(); + }); + + it('should use middleware on a group:', function () { + group.set('one', {contents: new Buffer('aaa')}); + group.set('two', {contents: new Buffer('zzz')}); + + group + .use(function (group) { + group.options = {}; + }) + .use(function (group) { + group.options.foo = 'bar'; + }) + .use(function () { + this.set('one', 'two'); + }); + + assert(group.one === 'two'); + assert(group.options.foo === 'bar'); + }); + }); +}); + diff --git a/test/helpers.builtin.js b/test/helpers.builtin.js deleted file mode 100644 index ae6797b9..00000000 --- a/test/helpers.builtin.js +++ /dev/null @@ -1,155 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -/* deps: mocha lodash swig template */ -require('should'); -var Verb = require('..'); -var orig = process.cwd(); -var verb; - -describe('built-in helpers', function () { - before(function () { - process.chdir(__dirname + '/fixtures'); - }); - - after(function () { - process.chdir(orig); - }); - - beforeEach(function (cb) { - verb = new Verb.Verb(); - - verb.engine('*', require('engine-lodash')); - verb.option('cwd', __dirname + '/fixtures'); - verb.data({'__dirname': verb.option('cwd')}); - cb(); - }); - - describe('when automatically generated helpers are used:', function () { - it.only('should use them in templates:', function (cb) { - verb.helper('upper', function (str) { - return str.toUpperCase(); - }); - - verb.render('{%= upper(name) %}', {name: 'Jon Schlinkert'}, function (err, content) { - if (err) console.log(err); - content.should.equal('JON SCHLINKERT'); - cb(); - }); - }); - }); - - describe('apidocs helper:', function () { - it('should use the `apidocs` helper:', function (cb) { - var str = '{%= apidocs("apidocs-comments.js") %}'; - verb.render(str, function (err, content) { - if (err) console.log(err); - content.should.match(/apidocs-comments.js#L/i); - cb(); - }); - }); - }); - - describe('badge helper:', function () { - it('should use the `badge` helper:', function (cb) { - verb.render('{%= badge("travis") %}', function (err, content) { - if (err) console.log(err); - content.should.equal(' [![Build Status](https://travis-ci.org/verbose/verb.svg)](https://travis-ci.org/verbose/verb) '); - cb(); - }); - }); - - it('should use context pass to the helper:', function (cb) { - var str = '{%= badge("travis", {travis_url: "https://travis-ci.org/foo/bar"}) %}'; - var expected = ' [![Build Status](https://travis-ci.org/foo/bar.svg)](https://travis-ci.org/foo/bar) '; - - verb.render(str, function (err, content) { - if (err) console.log(err); - content.should.equal(expected); - cb(); - }); - }); - }); - - describe('include:', function () { - it('should use the `include` helper:', function (cb) { - verb.render('{%= include("tests") %}', function (err, content) { - if (err) console.log(err); - content.should.match(/Install dev dependencies:/i); - cb(); - }); - }); - - describe('escaping:', function () { - it('should not try to render escaped templates', function (cb) { - verb.page('foo.md', {content: '{%%= include("tests") %}'}); - verb.render('foo.md', function (err, content) { - if (err) console.log(err); - content.should.equal('{%= include("tests") %}'); - cb(); - }); - }); - }); - - it('should support using `includes` as deeply nested includes:', function (cb) { - verb.include('one', {content: '{%= include("a", {cwd: "auto-loading"}) %}'}); - verb.include('two', {content: 'c {%= include("one") %} d'}); - verb.include('three', {content: 'b {%= include("two") %} e'}); - verb.include('four', {content: 'a {%= include("three") %} f'}); - - verb.render('four', function (err, content) { - if (err) console.log(err); - content.should.match(/a b c # this is a fixture d e f/i); - cb(); - }); - }); - }); - - describe('docs:', function () { - it('should use the `docs` helper to get files without extension:', function (cb) { - var str = '{%= docs("README", {cwd: __dirname + "/templates"}) %}'; - verb.render(str, function (err, content) { - if (err) console.log(err); - content.should.match(/Success!/i); - cb(); - }); - }); - - it('should use the `docs` helper to get files with extension:', function (cb) { - var str = '{%= docs("README", {cwd: __dirname + "/templates"}) %}'; - verb.render(str, function (err, content) { - if (err) console.log(err); - content.should.match(/Success!/i); - cb(); - }); - }); - - it('should use the `docs` helper to get files with extension:', function (cb) { - var str = '{%= docs("README.md", {cwd: __dirname + "/templates"}) %}'; - verb.render(str, function (err, content) { - if (err) console.log(err); - content.should.match(/Success!/i); - cb(); - }); - }); - - it('should support using `doc` as deeply nested includes:', function (cb) { - verb.doc('one.md', {content: '{%= docs("a.md", {cwd: "auto-loading"}) %}'}); - verb.doc('two.md', {content: 'c {%= docs("one.md") %} d'}); - verb.doc('three.md', {content: 'b {%= docs("two.md") %} e'}); - verb.doc('four.md', {content: 'a {%= docs("three.md") %} f'}); - - verb.render('four', function (err, content) { - if (err) console.log(err); - content.should.match(/a b c # this is a fixture d e f/i); - cb(); - }); - }); - }); -}); diff --git a/test/helpers.collections.js b/test/helpers.collections.js deleted file mode 100644 index 0e6659bb..00000000 --- a/test/helpers.collections.js +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('loaded helpers', function () { - it('should use helpers from the template-helpers library:', function (done) { - verb.render('{%= add(1, 2) %}', function (err, content) { - if (err) console.log(err); - content.should.equal('3'); - done(); - }); - }); - - it('should use helpers from the markdown-utils library:', function (done) { - verb.render('{%= mdu.link("a", "b") %}', function (err, content) { - if (err) console.log(err); - content.should.equal('[a](b)'); - done(); - }); - }); -}); diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 00000000..b03321fe --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,594 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var forOwn = require('for-own'); +var consolidate = require('consolidate'); +var handlebars = require('engine-handlebars'); +var matter = require('parser-front-matter'); +var rimraf = require('rimraf'); +var swig = consolidate.swig; +require('swig'); + +function load(fp) { + fp = path.join(__dirname, 'fixtures', fp); + var str = fs.readFileSync(fp, 'utf8'); + return str; +} + +var App = require('..'); +var app; + +describe('helpers', function () { + describe('constructor', function () { + it('should create an instance of Helpers:', function () { + app = new App(); + assert(app instanceof App); + }); + }); + + describe('prototype methods', function () { + beforeEach(function() { + app = new App(); + }); + it('should expose `helper`', function () { + assert(typeof app.helper ==='function'); + }); + it('should expose `asyncHelper`', function () { + assert(typeof app.asyncHelper ==='function'); + }); + }); + + describe('helpers', function() { + beforeEach(function() { + app = new App(); + }); + + it('should add a sync helper to the `sync` object:', function () { + app.helper('one', function () {}); + assert(typeof app._.helpers.sync.one === 'function'); + }); + + it('should load a glob of sync helper functions:', function () { + app.helpers('test/fixtures/helpers/[a-c].js'); + assert(typeof app._.helpers.sync.a === 'function'); + assert(typeof app._.helpers.sync.b === 'function'); + assert(typeof app._.helpers.sync.c === 'function'); + }); + + it('should fail gracefully on bad globs:', function (done) { + try { + app.helpers('test/fixtures/helpers/*.foo'); + done(); + } catch(err) { + done(new Error('should not throw an error.')); + } + }); + + it('should add a glob of sync helper objects:', function () { + app.helpers('test/fixtures/helpers/!([a-c]).js'); + assert(typeof app._.helpers.sync.one === 'function'); + assert(typeof app._.helpers.sync.two === 'function'); + assert(typeof app._.helpers.sync.three === 'function'); + }); + + it('should add a glob with mixed helper objects and functions:', function () { + app.helpers('test/fixtures/helpers/*.js'); + assert(typeof app._.helpers.sync.a === 'function'); + assert(typeof app._.helpers.sync.b === 'function'); + assert(typeof app._.helpers.sync.c === 'function'); + assert(typeof app._.helpers.sync.one === 'function'); + assert(typeof app._.helpers.sync.two === 'function'); + assert(typeof app._.helpers.sync.three === 'function'); + }); + + it('should add an object of sync helpers to the `sync` object:', function () { + app.helpers({ + x: function () {}, + y: function () {}, + z: function () {} + }); + + assert(typeof app._.helpers.sync.x === 'function'); + assert(typeof app._.helpers.sync.y === 'function'); + assert(typeof app._.helpers.sync.z === 'function'); + }); + }); + + describe('async helpers', function() { + beforeEach(function() { + app = new App(); + }); + + it('should add an async helper to the `async` object:', function () { + app.asyncHelper('two', function () {}); + assert(typeof app._.helpers.async.two === 'function'); + }); + + it('should load a glob of async helper functions:', function () { + app.asyncHelpers('test/fixtures/helpers/[a-c].js'); + assert(typeof app._.helpers.async.a === 'function'); + assert(typeof app._.helpers.async.b === 'function'); + assert(typeof app._.helpers.async.c === 'function'); + }); + + it('should add a glob of async helper objects:', function () { + app.asyncHelpers('test/fixtures/helpers/!([a-c]).js'); + assert(typeof app._.helpers.async.one === 'function'); + assert(typeof app._.helpers.async.two === 'function'); + assert(typeof app._.helpers.async.three === 'function'); + }); + + it('should fail gracefully on bad globs:', function (done) { + try { + app.asyncHelpers('test/fixtures/helpers/*.foo'); + done(); + } catch(err) { + done(new Error('should not throw an error.')); + } + }); + + it('should add a glob with mixed helper objects and functions:', function () { + app.asyncHelpers('test/fixtures/helpers/*.js'); + assert(typeof app._.helpers.async.a === 'function'); + assert(typeof app._.helpers.async.b === 'function'); + assert(typeof app._.helpers.async.c === 'function'); + assert(typeof app._.helpers.async.one === 'function'); + assert(typeof app._.helpers.async.two === 'function'); + assert(typeof app._.helpers.async.three === 'function'); + }); + + it('should add an object of async helpers to the `async` object:', function () { + app.asyncHelpers({ + x: function () {}, + y: function () {}, + z: function () {} + }); + + assert(typeof app._.helpers.async.x === 'function'); + assert(typeof app._.helpers.async.y === 'function'); + assert(typeof app._.helpers.async.z === 'function'); + }); + }); +}); + +describe('sync helpers', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should register a helper:', function () { + app.helper('a', function () {}); + app.helper('b', function () {}); + assert(app._.helpers.sync.hasOwnProperty('a')); + assert(app._.helpers.sync.hasOwnProperty('b')); + }); + + it('should use a helper:', function (done) { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= upper(a) %>', locals: {a: 'bbb'}}); + app.helper('upper', function (str) { + return str.toUpperCase(); + }); + + var page = app.pages.getView('a.tmpl'); + app.render(page, function (err, view) { + if (err) return done(err); + + assert.equal(typeof view.contents.toString(), 'string'); + assert.equal(view.contents.toString(), 'BBB'); + done(); + }); + }); +}); + +describe('async helpers', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + it('should register an async helper:', function () { + app.asyncHelper('a', function () {}); + app.asyncHelper('b', function () {}); + app._.helpers.async.should.have.property('a'); + app._.helpers.async.should.have.property('b'); + }); + + it('should use an async helper:', function (done) { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= lower(a) %>', locals: {a: 'BBB'}}); + app.asyncHelper('lower', function (str, next) { + if (typeof next !== 'function') return str; + next(null, str.toLowerCase()); + }); + + var page = app.pages.getView('a.tmpl'); + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'bbb'); + done(); + }); + }); +}); + +describe('built-in helpers:', function () { + describe('automatically generated helpers for default view types:', function () { + beforeEach(function () { + app = new App({rethrow: false}); + app.engine('md', require('engine-base')); + app.engine('tmpl', require('engine-base')); + app.create('partials', { viewType: 'partial' }); + app.create('pages'); + + // parse front matter + app.onLoad(/./, function (view, next) { + matter.parse(view, next); + }); + }); + + it('should expose front matter to the `partial` helper.', function (done) { + app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); + app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); + + app.render('b.md', function (err, res) { + if (err) return done(err); + res.content.should.equal('foo AAA bar'); + done(); + }); + }); + + it('should use helper locals.', function (done) { + app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo CCC bar'); + done(); + }); + }); + + it('should use front matter data.', function (done) { + app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>'}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo AAA bar'); + done(); + }); + }); + + it('should use partial locals:', function (done) { + app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); + + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) + .render({name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo EEE bar'); + done(); + }); + }); + + it.skip('should use locals from the `view.render` method:', function (done) { + app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); + + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) + .render({name: 'DDD'}, function (err, res) { + if (err) return done(err); + + res.content.should.equal('foo EEE bar'); + done(); + }); + }); + + it('should use locals from the `app.render` method:', function (done) { + app.partial('abc.md', {content: '<%= name %>'}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo DDD bar'); + done(); + }); + }); + + it('should return an empty string when the partial is missing.', function (done) { + app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("def.md", { name: "CCC" }) %> bar'}); + app.render('xyz.md', {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo bar'); + done(); + }); + }); + }); + + describe('helper context:', function () { + beforeEach(function () { + app = new App({rethrow: false}); + app.engine(['tmpl', 'md'], require('engine-base')); + app.create('partial', { viewType: 'partial' }); + app.create('page'); + + // parse front matter + app.onLoad(/./, function (view, next) { + matter.parse(view, next); + }); + }); + + it.skip('should prefer helper locals over view locals.', function (done) { + app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo CCC bar'); + done(); + }); + }); + + it('should give preference to view locals over render locals.', function (done) { + app.partial('abc.md', {content: '<%= name %>', locals: {name: 'BBB'}}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); + + var page = app.pages.getView('xyz.md'); + + app.render(page, {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo BBB bar'); + done(); + }); + }); + + it('should use render locals when other locals are not defined.', function (done) { + app.partial('abc.md', {content: '<%= name %>'}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo DDD bar'); + done(); + }); + }); + }); + + describe('user-defined engines:', function () { + beforeEach(function () { + app = new App({rethrow: false}); + app.create('partial', { viewType: 'partial' }); + app.create('page'); + + // parse front matter + app.onLoad(/./, function (view, next) { + matter.parse(view, next); + }); + }); + + it('should use the `partial` helper with handlebars.', function (done) { + app.engine(['tmpl', 'md'], require('engine-base')); + app.engine('hbs', handlebars); + + app.partial('title.hbs', {content: '{{name}}', locals: {name: 'BBB'}}); + app.page('a.hbs', {path: 'a.hbs', content: 'foo {{{partial "title.hbs" this}}} bar'}); + + app.render('a.hbs', {name: 'Halle Nicole'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('foo Halle Nicole bar'); + done(); + }); + }); + + it('should use the `partial` helper with any engine.', function (done) { + app.engine('hbs', handlebars); + app.engine('md', handlebars); + app.engine('swig', swig); + app.engine('tmpl', require('engine-base')); + + app.partial('a.hbs', {content: '---\nname: "AAA"\n---\n{{name}}', locals: {name: 'BBB'}}); + app.page('a.hbs', {path: 'a.hbs', content: '{{author}}', locals: {author: 'Halle Nicole'}}); + app.page('b.tmpl', {path: 'b.tmpl', content: '<%= author %>', locals: {author: 'Halle Nicole'}}); + app.page('d.swig', {path: 'd.swig', content: '{{author}}', locals: {author: 'Halle Nicole'}}); + app.page('e.swig', {path: 'e.swig', content: '{{author}}', locals: {author: 'Halle Nicole'}}); + app.page('f.hbs', {content: '{{author}}', locals: {author: 'Halle Nicole'}}); + app.page('g.md', {content: '---\nauthor: Brian Woodward\n---\n{{author}}', locals: {author: 'Halle Nicole'}}); + app.page('with-partial.hbs', {path: 'with-partial.hbs', content: '{{{partial "a.hbs" custom.locals}}}'}); + + var locals = {custom: {locals: {name: 'Halle Nicole' }}}; + app.render('a.hbs', locals, function (err, res) { + if (err) return console.log(err); + res.content.should.equal('Halle Nicole'); + }); + + app.render('with-partial.hbs', locals, function (err, res) { + if (err) return console.log(err); + res.content.should.equal('Halle Nicole'); + }); + + var page = app.pages.getView('g.md'); + locals.author = page.data.author || locals.author; + page.render(locals, function (err, res) { + if (err) return done(err); + res.content.should.equal('Brian Woodward'); + done(null, res.content); + }); + }); + }); +}); + +describe('helpers integration', function () { + beforeEach(function () { + app = new App(); + app.create('pages'); + app.engine('md', require('engine-base')); + }); + + describe('.helpers()', function () { + it('should add helpers and use them in templates.', function (done) { + app.helpers({ + upper: function (str) { + return str.toUpperCase(); + } + }); + + app.page('doc.md', {content: 'a <%= upper(name) %> b'}) + .render({name: 'Halle'}, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE b'); + done(); + }); + }); + }); + + describe('helper options:', function () { + it('should expose `this.options` to helpers:', function (done) { + app.helper('cwd', function (fp) { + return path.join(this.options.cwd, fp); + }); + + app.option('cwd', 'foo/bar'); + app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) + .render(function (err, res) { + if (err) return done(err); + assert(res.content === 'a foo/bar/baz b'); + done(); + }); + }); + + it('should pass helper options to helpers:', function (done) { + app.helper('cwd', function (fp) { + return path.join(this.options.cwd, fp); + }); + + app.option('helper.cwd', 'foo/bar'); + + app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) + .render(function (err, res) { + if (err) return done(err); + assert(res.content === 'a foo/bar/baz b'); + done(); + }); + }); + }); + + describe('options.helpers', function () { + it('should register helpers passed on the options:', function (done) { + app.option({ + helpers: { + upper: function (str) { + return str.toUpperCase(); + }, + foo: function (str) { + return 'foo' + str; + } + } + }); + + app.page('doc.md', {content: 'a <%= upper(name) %> <%= foo("bar") %> b'}) + .render({name: 'Halle'}, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE foobar b'); + done(); + }); + }); + }); + + describe('options.helpers', function () { + it('should add helpers and use them in templates.', function (done) { + app.options.helpers = { + upper: function (str) { + return str.toUpperCase(); + }, + foo: function (str) { + return 'foo' + str; + } + }; + + app.page('doc.md', {content: 'a <%= upper(name) %> b'}) + .render({name: 'Halle'}, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE b'); + done(); + }); + }); + }); +}); + +describe('collection helpers', function () { + beforeEach(function () { + app = new App(); + app.create('posts'); + app.create('pages', {engine: 'hbs'}); + app.create('partials', {viewType: 'partial', engine: 'hbs'}); + app.create('snippet', {viewType: 'partial'}); + app.engine('hbs', require('engine-handlebars')); + app.helper('log', function (ctx) { + console.log(ctx); + }); + }); + + describe('plural', function () { + it('should get the given collection', function (done) { + app.post('a.hbs', {content: 'foo'}); + app.post('b.hbs', {content: 'bar'}); + app.post('c.hbs', {content: 'baz'}); + + app.partial('list.hbs', { + content: '{{#posts}}{{#each items}}{{content}}{{/each}}{{/posts}}' + }); + + app.page('index.hbs', { + content: '{{> list.hbs }}' + }) + .render(function (err, res) { + if (err) return done(err); + assert(res.content === 'foobarbaz'); + done(); + }); + }); + }); + + describe('single', function () { + it('should get a view from an unspecified collection', function (done) { + app.post('a.hbs', {content: 'post-a'}); + app.post('b.hbs', {content: 'post-b'}); + + var one = app.page('one', {content: '{{view "a.hbs"}}'}) + .compile() + .fn() + + var two = app.page('two', {content: '{{view "b.hbs"}}'}) + .compile() + .fn() + + assert(one === 'post-a'); + assert(two === 'post-b'); + done(); + }); + + it('should get a specific view from the given collection', function (done) { + app.post('a.hbs', {content: 'post-a'}); + app.post('b.hbs', {content: 'post-b'}); + app.post('c.hbs', {content: 'post-c'}); + app.page('a.hbs', {content: 'page-a'}); + app.page('b.hbs', {content: 'page-b'}); + app.page('c.hbs', {content: 'page-c'}); + + var one = app.page('one', {content: '{{view "a.hbs" "posts"}}'}) + .compile() + .fn() + + var two = app.page('two', {content: '{{view "b.hbs" "pages"}}'}) + .compile() + .fn() + + assert(one === 'post-a'); + assert(two === 'page-b'); + done(); + }); + }); +}); diff --git a/test/helpers.user-defined.js b/test/helpers.user-defined.js deleted file mode 100644 index e4218480..00000000 --- a/test/helpers.user-defined.js +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('helpers', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - verb.engine('*', require('engine-lodash')); - done(); - }); - - describe('when helpers are registered with the `.helper()` method:', function () { - it('should use them in templates:', function (done) { - verb.helper('upper', function (str) { - return str.toUpperCase(); - }); - verb.render('{%= upper(name) %}', {name: 'Jon Schlinkert'}, function (err, content) { - if (err) console.log(err); - content.should.equal('JON SCHLINKERT'); - done(); - }); - }); - }); -}); diff --git a/test/includes.js b/test/includes.js deleted file mode 100644 index 437d0ab7..00000000 --- a/test/includes.js +++ /dev/null @@ -1,122 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('includes', function () { - describe('when the author include is used:', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - verb.engine('*', require('engine-lodash')); - done(); - }); - - it('should render only the name when `username` is not defined:', function (done) { - verb.render('{%= include("author") %}', function (err, content) { - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - '' - ].join('\n')); - done(); - }); - }); - - it('should render github and twitter url when `username` is passed:', function (done) { - verb.render('{%= include("author", {username: "jonschlinkert"}) %}', function (err, content) { - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - '', - ' + [github/jonschlinkert](https://github.com/jonschlinkert) ', - ' + [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ', - '' - ].join('\n')); - done(); - }); - }); - - it('should render github url only when `author.username` is passed:', function (done) { - verb.render('{%= include("author", {author: {username: "jonschlinkert"}}) %}', function (err, content) { - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - '', - ' + [github/jonschlinkert](https://github.com/jonschlinkert) ', - '' - ].join('\n')); - done(); - }); - }); - - it('should render twitter url only when `author.username` is passed:', function (done) { - verb.render('{%= include("author", {twitter: {username: "jonschlinkert"}}) %}', function (err, content) { - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - ' + [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ', - '' - ].join('\n')); - done(); - }); - }); - - it('should render github and twitter urls when `author.username` is passed:', function (done) { - verb.render('{%= include("author", {author: {username: "jonschlinkert"}}) %}', function (err, content) { - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - '', - ' + [github/jonschlinkert](https://github.com/jonschlinkert) \n', - ].join('\n')); - done(); - }); - }); - - it('should work when `username` is on the context:', function (done) { - verb.data({username: 'jonschlinkert'}); - - verb.render('{%= include("author") %}', function (err, content) { - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - '', - ' + [github/jonschlinkert](https://github.com/jonschlinkert) ', - ' + [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ', - '' - ].join('\n')); - done(); - }); - }); - - it('should work when `username` is defined in front-matter:', function (done) { - verb.doc('foo.md', {content: '---\nusername: jonschlinkert\n---\n{%= include("author") %}'}); - verb.render('foo', function (err, content) { - console.log(verb.cache) - if (err) console.log(err); - content.should.equal([ - '', - '**Jon Schlinkert**', - '', - ' + [github/jonschlinkert](https://github.com/jonschlinkert) ', - ' + [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ', - '' - ].join('\n')); - done(); - }); - }); - }); -}); diff --git a/test/layouts.js b/test/layouts.js new file mode 100644 index 00000000..31381842 --- /dev/null +++ b/test/layouts.js @@ -0,0 +1,112 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('..'); +var app; + +describe('layouts', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('layout', { viewType: 'layout' }); + app.create('page'); + }); + + it('should apply a layout to a view:', function (done) { + app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); + app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c'); + done(); + }); + }); + + it('should not apply a layout to itself:', function (done) { + app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c', layout: 'base'}); + app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c'); + done(); + }); + }); + + it('should apply nested layouts to a view:', function (done) { + app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); + app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); + app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); + app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); + + app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); + var page = app.pages.getView('z.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'outter c b a inner a b c outter'); + done(); + }); + }); + + it('should track layout stack history on `layoutStack`:', function (done) { + app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); + app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); + app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); + app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); + + app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); + var page = app.pages.getView('z.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert(view.layoutStack.length === 4); + assert(typeof view.layoutStack[0] === 'object'); + assert(typeof view.layoutStack[0].depth === 'number'); + done(); + }); + }); + + it('should track layout stack history on `layoutStack`:', function (done) { + app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); + app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); + app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); + app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); + + app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); + var page = app.pages.getView('z.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'outter c b a inner a b c outter'); + done(); + }); + }); + + it('should get layouts from `layout` viewTypes:', function (done) { + app.create('section', { viewType: 'layout' }); + app.create('block', { viewType: 'layout' }); + + app.section('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); + app.block('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); + app.section('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); + app.block('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); + + app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); + var page = app.pages.getView('z.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'outter c b a inner a b c outter'); + done(); + }); + }); +}); diff --git a/test/list.js b/test/list.js new file mode 100644 index 00000000..068b9d83 --- /dev/null +++ b/test/list.js @@ -0,0 +1,499 @@ +require('mocha'); +require('should'); +var path = require('path'); +var get = require('get-value'); +var List = require('../').List; +var Views = require('../').Views; +var View = require('../').View; +var assert = require('./support/'); +var list, views; + +describe('list', function () { + describe('constructor', function () { + it('should create an instance of List:', function () { + var list = new List(); + assert(list instanceof List); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof List.extend ==='function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function() { + list = new List(); + }); + + it('should expose `set`', function () { + assert(typeof list.set ==='function'); + }); + it('should expose `get`', function () { + assert(typeof list.get ==='function'); + }); + it('should expose `visit`', function () { + assert(typeof list.visit ==='function'); + }); + it('should expose `define`', function () { + assert(typeof list.define ==='function'); + }); + it('should expose `addItem`', function () { + assert(typeof list.addItem ==='function'); + }); + + it('should expose `items`', function () { + assert(Array.isArray(list.items)); + }); + it('should expose `keys`', function () { + assert(Array.isArray(list.keys)); + }); + }); + + describe('instance', function () { + beforeEach(function() { + list = new List(); + }); + + it('should set a value on the instance:', function () { + list.set('a', 'b'); + assert(list.a ==='b'); + }); + + it('should get a value from the instance:', function () { + list.set('a', 'b'); + assert(list.get('a') ==='b'); + }); + }); + + describe('addItem', function() { + beforeEach(function() { + list = new List(); + }); + + it('should add an item to `items`:', function () { + list.addItem('one', {content: '...'}); + assert(list.items.length === 1); + assert(Buffer.isBuffer(list.items[0].contents)); + }); + + it('should create an instance of `Item`:', function () { + list.addItem('one', {content: '...'}); + assert(list.items[0] instanceof list.Item); + }); + + it('should allow an `Item` constructor to be passed:', function () { + var Vinyl = require('vinyl'); + Vinyl.prototype.foo = function(key, value) { + this[key] = value; + }; + list = new List({Item: Vinyl}); + list.addItem('one', {content: '...'}); + list.items[0].foo('bar', 'baz'); + assert(list.items[0].bar === 'baz'); + }); + + it('should allow an instance of `Item` to be passed:', function () { + var list = new List({Item: View}); + var view = new View({content: '...'}); + list.addItem('one', view); + view.set('abc', 'xyz'); + assert(list.items[0] instanceof list.Item); + assert(Buffer.isBuffer(list.items[0].contents)); + assert(list.items[0].abc === 'xyz'); + }); + }); + + describe('addItems', function() { + beforeEach(function() { + list = new List(); + }); + + it('should add an object with multiple items:', function () { + list.addItems({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + assert(Buffer.isBuffer(list.items[0].contents)); + assert(Buffer.isBuffer(list.items[1].contents)); + }); + }); + + describe('addItems', function() { + beforeEach(function() { + list = new List(); + }); + + it('should add an array with multiple items:', function () { + list.addList([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + assert(Buffer.isBuffer(list.items[0].contents)); + assert(Buffer.isBuffer(list.items[1].contents)); + }); + + it('should take a sync callback on `addList`:', function () { + function addContents(item) { + item.contents = new Buffer(item.path.charAt(0)); + } + + list.addList([ + { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + ], addContents); + + assert(Buffer.isBuffer(list.items[0].contents)); + assert(Buffer.isBuffer(list.items[1].contents)); + assert(Buffer.isBuffer(list.items[2].contents)); + }); + }); + + describe('sortBy', function() { + var items = [ + { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + ]; + + it('should sort a list:', function () { + list = new List(); + list.addList(items); + + var compare = function(prop) { + return function (a, b, fn) { + var valA = get(a, prop); + var valB = get(b, prop); + return fn(valA, valB); + }; + }; + + var res = list.sortBy('locals.date', 'doesnt.exist', [ + compare('locals.foo'), + compare('locals.bar') + ]); + + assert.containEql(res.items, [ + { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } } + ]); + }); + + it('should not sort the (original) instance list `items`:', function () { + list = new List(); + list.addList(items); + + var compare = function(prop) { + return function (a, b, fn) { + var valA = get(a, prop); + var valB = get(b, prop); + return fn(valA, valB); + }; + }; + + var res = list.sortBy('locals.date', 'doesnt.exist', [ + compare('locals.foo'), + compare('locals.bar') + ]); + + // should not be sorted + assert.containEql(list.items, items); + + // should be sorted + assert.containEql(res.items, [ + { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } } + ]); + }); + + it('should pass options to array-sort from the constructor:', function () { + list = new List({sort: {reverse: true}}); + list.addList(items); + + var compare = function(prop) { + return function (a, b, fn) { + var valA = get(a, prop); + var valB = get(b, prop); + return fn(valA, valB); + }; + }; + + var res = list.sortBy('locals.date', 'doesnt.exist', [ + compare('locals.foo'), + compare('locals.bar') + ]); + + assert.containEql(res.items, [ + { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, + { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } } + ]); + }); + + it('should pass options to array-sort from the sortBy method:', function () { + list = new List(); + list.addList(items); + + var compare = function(prop) { + return function (a, b, fn) { + var valA = get(a, prop); + var valB = get(b, prop); + return fn(valA, valB); + }; + }; + + var res = list.sortBy('locals.date', 'doesnt.exist', [ + compare('locals.foo'), + compare('locals.bar') + ], {reverse: true}); + + assert.containEql(res.items, [ + { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, + { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } } + ]); + }); + }); + + describe('groupBy', function() { + var items = [ + { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + ]; + + it('should group a list by a property:', function () { + list = new List(); + list.addList(items); + + var res = list.groupBy('locals.foo'); + var keys = ['zzz', 'mmm', 'xxx', 'aaa', 'ccc', 'rrr', 'ttt', 'yyy']; + assert.deepEqual(Object.keys(res), keys); + }); + }); + + describe('sort and group', function() { + var items = [ + { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { path: 'd.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 3 } }, + { path: 'i.md', locals: { date: '2013-02-01', foo: 'xxx', bar: 5 } }, + { path: 'i.md', locals: { date: '2013-02-01', foo: 'lll', bar: 5 } }, + { path: 'k.md', locals: { date: '2013-03-01', foo: 'xxx', bar: 1 } }, + { path: 'j.md', locals: { date: '2013-02-01', foo: 'xxx', bar: 4 } }, + { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { path: 'm.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { path: 'n.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 7 } }, + { path: 'o.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 7 } }, + { path: 'p.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 7 } }, + { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + ]; + + it('should group a list by a property:', function () { + list = new List(items); + + var context = list + .sortBy('locals.date') + .groupBy(function (view) { + var date = view.locals.date; + view.locals.year = date.slice(0, 4); + view.locals.month = date.slice(5, 7); + view.locals.day = date.slice(8, 10); + return view.locals.year; + }, 'locals.month'); + + var keys = Object.keys(context); + assert(keys[0] === '2012'); + assert(keys[1] === '2013'); + assert(keys[2] === '2014'); + assert(keys[3] === '2015'); + }); + }); + + describe('paginate', function() { + var items = [ + { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, + { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, + { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, + { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, + { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, + { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, + { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + ]; + + it('should paginate a list:', function () { + list = new List(items); + + var res = list.paginate(); + assert.equal(res.length, 2); + assert.containEql(res[0].items, items.slice(0, 10)); + assert.containEql(res[1].items, items.slice(10)); + }); + + it('should paginate a list with given options:', function () { + list = new List(items); + var res = list.paginate({limit: 5}); + + assert.equal(res.length, 3); + assert.containEql(res[0].items, items.slice(0, 5)); + assert.containEql(res[1].items, items.slice(5, 10)); + assert.containEql(res[2].items, items.slice(10)); + }); + }); + + describe('Views instance', function() { + beforeEach(function() { + views = new Views(); + }); + + it('should add views from an instance of Views:', function () { + views.addViews({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + + list = new List(views); + assert(Buffer.isBuffer(list.items[0].contents)); + assert(Buffer.isBuffer(list.items[1].contents)); + }); + }); + + describe('getIndex', function() { + beforeEach(function() { + list = new List(); + }); + it('should get the index of a key when key is not renamed:', function () { + list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); + list.addItem('a/b/c/eee.hbs', {content: 'eee'}); + assert(list.getIndex('a/b/c/ddd.hbs') === 0); + assert(list.getIndex('a/b/c/eee.hbs') === 1); + }); + + it('should get the index of a key when key is renamed:', function () { + list = new List({ + renameKey: function (key) { + return path.basename(key); + } + }); + list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); + list.addItem('a/b/c/eee.hbs', {content: 'eee'}); + assert(list.getIndex('a/b/c/ddd.hbs') === 0); + assert(list.getIndex('ddd.hbs') === 0); + assert(list.getIndex('a/b/c/eee.hbs') === 1); + assert(list.getIndex('eee.hbs') === 1); + }); + }); + + describe('getItem', function() { + beforeEach(function() { + list = new List(); + }); + + it('should get an view from `views`:', function () { + list.addItem('one', {content: 'aaa'}); + list.addItem('two', {content: 'zzz'}); + assert(list.items.length === 2); + assert(Buffer.isBuffer(list.items[0].contents)); + assert(Buffer.isBuffer(list.getItem('one').contents)); + assert(list.getItem('one').contents.toString() === 'aaa'); + assert(list.getItem('two').contents.toString() === 'zzz'); + }); + }); + + describe('use', function() { + beforeEach(function() { + list = new List(); + }); + + it('should use middleware on a list:', function () { + list.addItem('one', {content: 'aaa'}); + list.addItem('two', {content: 'zzz'}); + + list + .use(function (list, options) { + options.foo = 'bar'; + }) + .use(function () { + this.set('one', 'two'); + }); + + assert(list.one === 'two'); + assert(list.options.foo === 'bar'); + }); + }); +}); + diff --git a/test/mergePartials.js b/test/mergePartials.js new file mode 100644 index 00000000..463c86c5 --- /dev/null +++ b/test/mergePartials.js @@ -0,0 +1,118 @@ +require('should'); +var omit = require('object.omit'); +var App = require('..'); +var app, keys; + +describe('mergePartials', function () { + beforeEach(function () { + app = new App(); + }); + + it('should merge multiple partials collections onto one collection:', function () { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials(); + actual.should.have.property('partials'); + actual.partials.should.have.properties(['a', 'b', 'c']); + }); + + it('should keep partials collections on seperate collections:', function () { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({ + mergePartials: false, + mergeTypes: ['foos', 'bars', 'bazs'] + }); + + actual.should.not.have.property('partials'); + actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); + }); + + it('should emit `mergePartials`:', function () { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + var arr = []; + + app.on('onMerge', function (view) { + arr.push(view.content); + }); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({ + mergePartials: false, + mergeTypes: ['foos', 'bars', 'bazs'] + }); + + actual.should.not.have.property('partials'); + actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); + arr.should.eql(['aaa', 'bbb', 'ccc']); + }); + + it('should handle `onMerge` middleware:', function () { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.onMerge(/./, function (view, next) { + view.content += ' onMerge'; + next(); + }); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({ + mergePartials: false, + mergeTypes: ['foos', 'bars', 'bazs'] + }); + + actual.should.eql({ + foos: {a: 'aaa onMerge'}, + bars: {b: 'bbb onMerge'}, + bazs: {c: 'ccc onMerge'} + }); + }); + + it('should skip views with `nomerge=true`:', function () { + var opts = { viewType: 'partial' }; + + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.onMerge(/[ab]/, function (view, next) { + view.options.nomerge = true; + next(); + }); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({mergePartials: false}); + actual.should.have.property('bazs', { c: 'ccc' }); + }); +}); + + diff --git a/test/middleware.js b/test/middleware.js deleted file mode 100644 index 8fb6e36b..00000000 --- a/test/middleware.js +++ /dev/null @@ -1,143 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('middleware', function () { - var orig = process.cwd(); - beforeEach(function (done) { - verb = new verb.Verb(); - before(function () { - process.chdir(__dirname + '/fixtures'); - }); - - after(function () { - process.chdir(orig); - }); - - verb.engine('md', require('engine-lodash')); - verb.option('cwd', __dirname); - verb.data({'__dirname': verb.option('cwd')}); - done(); - }); - - describe('todos:', function () { - it('should add a todos list to a markdown file\'s `todos` property:', function (done) { - verb.src('test/fixtures/middleware/todo.md') - .on('data', function (file) { - if (/\/todo\.md$/.test(file.path)) { - file.todos.should.be.an.array; - } - }) - .on('end', done); - }); - - it('should add a todos list to a javascript file\'s `todos` property:', function (done) { - verb.src('test/fixtures/middleware/todo.js') - .on('data', function (file) { - if (/\/todo\.js$/.test(file.path)) { - file.todos.should.be.an.array; - } - }) - .on('end', done); - }); - }); - - describe('multi-toc:', function () { - it('should generate a multi-file table of contents:', function (done) { - verb.doc('multi-toc.md', {cwd: __dirname + '/fixtures/middleware'}); - verb.views.docs.should.have.property('multi-toc'); - - verb.render('multi-toc', function (err, content) { - if (err) console.log(err); - content.should.match(/docs\/_verb\/[^\/]+\/#/); - done(); - }); - }); - - it('should strip the toc comment marker from the generated file:', function (done) { - verb.doc('multi-toc.md', {cwd: __dirname + '/fixtures/middleware'}); - verb.views.docs.should.have.property('multi-toc'); - - verb.render('multi-toc', function (err, content) { - if (err) console.log(err); - content.should.not.match(//); - done(); - }); - }); - - it('should generate a markdown table of contents for an `include`:', function (done) { - verb.include('*.md', {cwd: __dirname + '/fixtures/middleware'}); - verb.views.includes.should.have.property('toc'); - - verb.render('toc', function (err, content) { - if (err) console.log(err); - content.should.match(/<\!-- tocstop -->/); - done(); - }); - }); - - it('should add a toc to a file\'s `toc` property:', function (done) { - verb.src('test/fixtures/middleware/toc.md') - .on('data', function (file) { - if (/\/toc\.md$/.test(file.path)) { - console.log(file.toc) - file.toc.should.be.a.string; - } - }) - .on('end', done); - }); - - it('should generate a markdown table of contents for a `src` file:', function (done) { - verb.src('test/fixtures/middleware/toc.md') - .on('data', function (file) { - if (/\/toc\.md$/.test(file.path)) { - file.contents.toString().should.match(/<\!-- tocstop -->/); - } - }) - .on('end', done); - }); - }); - - describe('copyright:', function () { - it('should add copyright data to the context:', function (done) { - verb.src('test/fixtures/middleware/copyright.js') - .on('data', function (file) { - verb.cache.data.should.have.property('copyright'); - }) - .on('end', done); - }); - }); -}); diff --git a/test/out-fixtures/fixtures/vinyl/test.coffee b/test/out-fixtures/fixtures/vinyl/test.coffee new file mode 100644 index 00000000..6a537b5b --- /dev/null +++ b/test/out-fixtures/fixtures/vinyl/test.coffee @@ -0,0 +1 @@ +1234567890 \ No newline at end of file diff --git a/test/plugin.todos.js b/test/plugin.todos.js deleted file mode 100644 index 1236c491..00000000 --- a/test/plugin.todos.js +++ /dev/null @@ -1,77 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe.skip('default helpers:', function () { - var orig = process.cwd(); - - beforeEach(function (done) { - before(function () { - process.chdir(__dirname + '/fixtures'); - }); - - after(function () { - process.chdir(orig); - }); - - verb = new verb.Verb(); - verb.engine('*', require('engine-lodash')); - verb.option('cwd', __dirname); - verb.data({'__dirname': verb.option('cwd')}); - done(); - }); - - describe('todos:', function () { - it('should add todos to `file.todos` for markdown files:', function (done) { - verb.doc('abc.md', {cwd: __dirname + '/fixtures/todos'}); - verb.views.docs.should.have.property('abc'); - - verb.render('abc', function (err, content) { - if (err) console.log(err); - // verb.cache.data.should.have.property('todos'); - done(); - }); - }); - - // it('should generate a list of todos:', function (done) { - // verb.doc('abc.md', {cwd: __dirname + '/fixtures/todos'}); - // verb.doc('xyz.js', {cwd: __dirname + '/fixtures/todos'}); - // verb.views.docs.should.have.property('abc'); - // verb.views.docs.should.have.property('xyz'); - // console.log(verb.views.docs.xyz) - // }); - // stream.write(new File({ - // base: __dirname, - // path: __dirname + '/post.md' - // })); - - // stream.on('data', function (file) { - // buffer.push(file); - // }); - - // stream.on('end', function () { - // assert.equal(buffer.length, 1); - // assert.equal(buffer[0].relative, 'post.md'); - // cb(); - // }); - // stream.end(); - // it('should strip the toc marker from the generated file:', function (done) { - // verb.doc('todos.md', {cwd: __dirname + '/fixtures/middleware'}); - // verb.views.docs.should.have.property('todos'); - - // verb.render('todos', function (err, content) { - // if (err) console.log(err); - // content.should.not.match(//); - // done(); - // }); - // }); - }); -}); diff --git a/test/renameKey.js b/test/renameKey.js new file mode 100644 index 00000000..13a8e080 --- /dev/null +++ b/test/renameKey.js @@ -0,0 +1,349 @@ +var path = require('path'); +var App = require('..'); +var app; + +function renameKey(key) { + return path.basename(key, path.extname(key)); +} + +describe('renameKey', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + app.create('posts'); + }); + + describe('global options:', function () { + it('should use `renameKey` function defined on global opts:', function () { + app.option('renameKey', renameKey); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.posts.should.have.property('a'); + app.views.posts.should.have.property('b'); + app.views.posts.should.have.property('c'); + app.views.posts.should.have.property('d'); + app.views.posts.should.have.property('e'); + }); + + it('should not have conflicts when view name is the collection name:', function () { + app.option('renameKey', renameKey); + + app.post('a/b/c/post.txt', {content: 'this is contents'}); + app.page('a/b/c/page.txt', {content: 'this is contents'}); + + app.views.posts.should.have.property('post'); + app.views.pages.should.have.property('page'); + }); + }); + + describe('create method:', function () { + it('should use `renameKey` option chained from the `create` method:', function () { + app.create('post') + .option('renameKey', function (key) { + return 'posts/' + path.basename(key); + }); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.posts.should.have.property('posts/a.txt'); + app.views.posts.should.have.property('posts/b.txt'); + app.views.posts.should.have.property('posts/c.txt'); + app.views.posts.should.have.property('posts/d.txt'); + app.views.posts.should.have.property('posts/e.txt'); + }); + }); + + describe('create method:', function () { + it('should use `renameKey` defined on the `create` method:', function () { + app.create('post', { + renameKey: function (key) { + return 'posts/' + path.basename(key); + } + }); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.posts.should.have.property('posts/a.txt'); + app.views.posts.should.have.property('posts/b.txt'); + app.views.posts.should.have.property('posts/c.txt'); + app.views.posts.should.have.property('posts/d.txt'); + app.views.posts.should.have.property('posts/e.txt'); + }); + }); + + describe('collections:', function () { + describe('setting:', function () { + it('should get a view with the `renameKey` defined on app.options:', function () { + app.option('renameKey', function (key) { + return 'foo/' + path.basename(key); + }); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.post('a/b/c/c.txt', {content: '...'}); + + app.views.posts.should.have.property('foo/a.txt'); + app.views.posts.should.have.property('foo/b.txt'); + app.views.posts.should.have.property('foo/c.txt'); + }); + + it('should use `renameKey` defined on collection.options:', function () { + app.pages.option('renameKey', function (key) { + return 'page/' + path.basename(key); + }); + + app.posts.option('renameKey', function (key) { + return 'post/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('page/a.txt'); + app.views.pages.should.have.property('page/b.txt'); + app.views.pages.should.have.property('page/c.txt'); + app.views.pages.should.have.property('page/d.txt'); + app.views.pages.should.have.property('page/e.txt'); + + app.views.posts.should.have.property('post/a.txt'); + app.views.posts.should.have.property('post/b.txt'); + app.views.posts.should.have.property('post/c.txt'); + app.views.posts.should.have.property('post/d.txt'); + app.views.posts.should.have.property('post/e.txt'); + }); + + it('should use the `collection.renameKey()` method:', function () { + app.pages.renameKey(function (key) { + return 'baz/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('baz/a.txt'); + app.views.pages.should.have.property('baz/b.txt'); + app.views.pages.should.have.property('baz/c.txt'); + app.views.pages.should.have.property('baz/d.txt'); + app.views.pages.should.have.property('baz/e.txt'); + }); + + it('should use the `app.renameKey()` method:', function () { + app.renameKey(function (key) { + return 'app/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('app/a.txt'); + app.views.pages.should.have.property('app/b.txt'); + app.views.pages.should.have.property('app/c.txt'); + app.views.pages.should.have.property('app/d.txt'); + app.views.pages.should.have.property('app/e.txt'); + }); + + it('should prefer collection method over app.options:', function () { + // this works when you switch the order around... + app.pages.renameKey(function pagesRenameKey(key) { + return 'aaa/' + path.basename(key); + }); + app.option('renameKey', function optsRenameKey(key) { + return 'foo/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('aaa/a.txt'); + app.views.pages.should.have.property('aaa/b.txt'); + app.views.pages.should.have.property('aaa/c.txt'); + app.views.pages.should.have.property('aaa/d.txt'); + app.views.pages.should.have.property('aaa/e.txt'); + }); + + it('should prefer collection method over app method:', function () { + app.pages.renameKey(function (key) { + return 'aaa/' + path.basename(key); + }); + app.renameKey(function (key) { + return 'zzz/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('aaa/a.txt'); + app.views.pages.should.have.property('aaa/b.txt'); + app.views.pages.should.have.property('aaa/c.txt'); + app.views.pages.should.have.property('aaa/d.txt'); + app.views.pages.should.have.property('aaa/e.txt'); + }); + + it('should prefer collection options over app.options:', function () { + app.pages.option('renameKey', function (key) { + return 'collection/' + path.basename(key); + }); + app.option('renameKey', function (key) { + return 'app/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('collection/a.txt'); + app.views.pages.should.have.property('collection/b.txt'); + app.views.pages.should.have.property('collection/c.txt'); + app.views.pages.should.have.property('collection/d.txt'); + app.views.pages.should.have.property('collection/e.txt'); + }); + + it('should prefer collection options over app method:', function () { + app.pages.option('renameKey', function (key) { + return 'collection/' + path.basename(key); + }); + app.renameKey(function (key) { + return 'app/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('collection/a.txt'); + app.views.pages.should.have.property('collection/b.txt'); + app.views.pages.should.have.property('collection/c.txt'); + app.views.pages.should.have.property('collection/d.txt'); + app.views.pages.should.have.property('collection/e.txt'); + }); + + it('should use renameKey on chained methods:', function () { + app.page('test/fixtures/pages/a.txt', { + options: { + renameKey: function foo(key) { + return 'foo/' + path.basename(key); + } + } + }); + + app.page('test/fixtures/pages/a.hbs', { + options: { + renameKey: function bar(key) { + return 'bar/' + path.basename(key); + } + } + }); + + app.views.pages.should.have.properties([ + 'foo/a.txt', + 'bar/a.hbs' + ]); + }); + }); + + describe('getting', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('post'); + app.create('page'); + }); + + it('should get a view with the `renameKey` defined on the `create` method:', function () { + app.create('post', { + renameKey: function createRenameKey(key) { + return 'posts/' + path.basename(key); + } + }); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.post('a/b/c/c.txt', {content: '...'}); + + app.posts.getView('a.txt').should.have.property('path', 'a/b/c/a.txt'); + app.posts.getView('posts/a.txt').should.have.property('path', 'a/b/c/a.txt'); + }); + + it('should get a view with `renameKey` on collection.options:', function () { + app.pages.option('renameKey', function (key) { + return 'bar/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.page('a/b/c/c.txt', {content: '...'}); + + app.views.pages.should.have.property('bar/a.txt'); + app.views.pages.should.have.property('bar/b.txt'); + app.views.pages.should.have.property('bar/c.txt'); + }); + + it('should get a view with the the `app.renameKey()` method:', function () { + app.renameKey(function (key) { + return 'baz/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.page('a/b/c/c.txt', {content: '...'}); + + app.views.pages.should.have.property('baz/a.txt'); + app.views.pages.should.have.property('baz/b.txt'); + app.views.pages.should.have.property('baz/c.txt'); + }); + + it('should get a view with the the `collection.renameKey()` method:', function () { + app.pages.renameKey(function (key) { + return 'baz/' + path.basename(key); + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.page('a/b/c/c.txt', {content: '...'}); + + app.views.pages.should.have.property('baz/a.txt'); + app.views.pages.should.have.property('baz/b.txt'); + app.views.pages.should.have.property('baz/c.txt'); + }); + }); + }); +}); diff --git a/test/render.js b/test/render.js new file mode 100644 index 00000000..2883877c --- /dev/null +++ b/test/render.js @@ -0,0 +1,69 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('render', function () { + describe('engine', function () { + var view; + + beforeEach(function () { + app = new App({silent: true}); + app.engine('tmpl', require('engine-base')); + app.create('page'); + view = {contents: new Buffer('a <%= name %> b'), locals: {name: 'Halle'}}; + }); + + it('should render a view from an object:', function (done) { + app.page('a.tmpl', view) + .render(function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a Halle b'); + done(); + }); + }); + + it('should throw an error when a variable is undefined:', function (done) { + delete view.locals.name; + + app.page('a.tmpl', view) + .render(function (err) { + assert(err.message === 'name is not defined'); + done(); + }); + }); + + it('should re-throw an error when rethrow is true:', function (done) { + delete view.locals.name; + + app = new App({rethrow: true, silent: true}); + app.engine('tmpl', require('engine-base')); + app.create('page'); + + app.page('a.tmpl', view) + .render(function (err) { + assert(err.message === 'name is not defined'); + done(); + }); + }); + + it('should emit a re-thrown error when rethrow is true:', function (done) { + delete view.locals.name; + + app = new App({rethrow: true, silent: true}); + app.engine('tmpl', require('engine-base')); + app.create('page'); + + app.on('error', function(err) { + assert(err.message === 'name is not defined'); + done(); + }); + + app.page('a.tmpl', view) + .render(function (err) { + assert(err.message === 'name is not defined'); + }); + }); + }); +}); diff --git a/test/routes.js b/test/routes.js new file mode 100644 index 00000000..e56ba519 --- /dev/null +++ b/test/routes.js @@ -0,0 +1,97 @@ +require('should'); +var assert = require('assert'); +var App = require('..'); +var app; + +function append(str) { + return function(view, next) { + var content = view.contents.toString(); + view.contents = new Buffer(content + ' ' + str); + next(); + }; +} +function prepend(str) { + return function(view, next) { + var content = view.contents.toString(); + view.contents = new Buffer(str + ' ' + content); + next(); + }; +} + +describe('routes', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + describe('params', function () { + it('should call param function when routing', function(done) { + app.param('id', function(view, next, id) { + assert.equal(id, '123'); + next(); + }); + + app.all('/foo/:id/bar', function(view, next) { + assert.equal(view.options.params.id, '123'); + next(); + }); + + app.router.handle({ path: '/foo/123/bar' }, done); + }); + }); + + describe('onLoad middleware', function () { + it('should run when templates are loaded:', function () { + app.onLoad(/\.tmpl/, prepend('onLoad')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>'}); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('onLoad <%= name %>'); + }); + }); + + describe('preCompile middleware', function () { + it('should run before templates are compiled:', function () { + + }); + }); + + describe('postCompile middleware', function () { + it('should run after templates are compiled:', function () { + + }); + }); + + describe('preRender middleware', function () { + it('should run before templates are rendered:', function (done) { + app.preRender(/\.tmpl/, prepend('preRender')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('<%= name %>'); + + page.render({}, function (err, res) { + if (err) return done(err); + res.contents.toString().should.equal('preRender aaa'); + done(); + }); + }); + }); + + describe('postRender middleware', function () { + it('should run after templates are rendered:', function (done) { + app.postRender(/\.tmpl/, append('postRender')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('<%= name %>'); + + page.render({}, function (err, res) { + if (err) return done(err); + res.contents.toString().should.equal('aaa postRender'); + done(); + }); + }); + }); +}); diff --git a/test/store.js b/test/store.js new file mode 100644 index 00000000..627da3f3 --- /dev/null +++ b/test/store.js @@ -0,0 +1,230 @@ +'use strict'; + +require('mocha'); +require('should'); +var fs = require('fs'); +var path = require('path'); +var Store = require('data-store'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('store', function () { + beforeEach(function () { + app = new App(); + }); + + afterEach(function () { + app.store.del({force: true}); + }); + + it('should create an instance of Store', function () { + assert(app.store instanceof Store); + }); + + it('should create a store at the given `cwd`', function () { + app = new App({store: {cwd: __dirname + '/actual'}}); + + app.store.set('foo', 'bar'); + assert(path.basename(app.store.path) === 'verb.json'); + assert(app.store.data.hasOwnProperty('foo')); + assert(app.store.data.foo === 'bar'); + assert(fs.existsSync(path.join(__dirname, 'actual', 'verb.json'))); + }); + + it('should create a store using the given `indent` value', function () { + app = new App({store: {cwd: __dirname + '/actual', indent: 0}}); + app.store.set('foo', 'bar'); + var contents = fs.readFileSync(path.join(__dirname, 'actual', 'verb.json'), 'utf8'); + assert(contents === '{"foo":"bar"}'); + }); + + it('should set a value on the store', function () { + app.store.set('one', 'two'); + app.store.data.one.should.equal('two'); + }); + + it('should set an object', function () { + app.store.set({four: 'five', six: 'seven'}); + app.store.data.four.should.equal('five'); + app.store.data.six.should.equal('seven'); + }); + + it('should set a nested value', function () { + app.store.set('a.b.c.d', {e: 'f'}); + app.store.data.a.b.c.d.e.should.equal('f'); + }); + + it('should union a value onto an array on the store', function () { + app.store.union('one', 'two'); + app.store.data.one.should.eql(['two']); + }); + + it('should not union duplicate values', function () { + app.store.union('one', 'two'); + app.store.data.one.should.eql(['two']); + + app.store.union('one', ['two']); + app.store.data.one.should.eql(['two']); + }); + + it('should concat an existing array:', function () { + app.store.union('one', 'a'); + app.store.data.one.should.eql(['a']); + + app.store.union('one', ['b']); + app.store.data.one.should.eql(['a', 'b']); + + app.store.union('one', ['c', 'd']); + app.store.data.one.should.eql(['a', 'b', 'c', 'd']); + }); + + it('should return true if a key `.has()` on the store', function () { + app.store.set('foo', 'bar'); + app.store.set('baz', null); + app.store.set('qux', undefined); + + app.store.has('foo').should.eql(true); + app.store.has('bar').should.eql(false); + app.store.has('baz').should.eql(false); + app.store.has('qux').should.eql(false); + }); + + it('should return true if a nested key `.has()` on the store', function () { + app.store.set('a.b.c.d', {x: 'zzz'}); + app.store.set('a.b.c.e', {f: null}); + app.store.set('a.b.g.j', {k: undefined}); + + app.store.has('a.b.bar').should.eql(false); + app.store.has('a.b.c.d').should.eql(true); + app.store.has('a.b.c.d.x').should.eql(true); + app.store.has('a.b.c.d.z').should.eql(false); + app.store.has('a.b.c.e').should.eql(true); + app.store.has('a.b.c.e.f').should.eql(false); + app.store.has('a.b.c.e.z').should.eql(false); + app.store.has('a.b.g.j').should.eql(true); + app.store.has('a.b.g.j.k').should.eql(false); + app.store.has('a.b.g.j.z').should.eql(false); + }); + + it('should return true if a key exists `.hasOwn()` on the store', function () { + app.store.set('foo', 'bar'); + app.store.set('baz', null); + app.store.set('qux', undefined); + + app.store.hasOwn('foo').should.eql(true); + app.store.hasOwn('bar').should.eql(false); + app.store.hasOwn('baz').should.eql(true); + app.store.hasOwn('qux').should.eql(true); + }); + + it('should return true if a nested key exists `.hasOwn()` on the store', function () { + app.store.set('a.b.c.d', {x: 'zzz'}); + app.store.set('a.b.c.e', {f: null}); + app.store.set('a.b.g.j', {k: undefined}); + + app.store.hasOwn('a.b.bar').should.eql(false); + app.store.hasOwn('a.b.c.d').should.eql(true); + app.store.hasOwn('a.b.c.d.x').should.eql(true); + app.store.hasOwn('a.b.c.d.z').should.eql(false); + app.store.has('a.b.c.e.f').should.eql(false); + app.store.hasOwn('a.b.c.e.f').should.eql(true); + app.store.hasOwn('a.b.c.e.bar').should.eql(false); + app.store.has('a.b.g.j.k').should.eql(false); + app.store.hasOwn('a.b.g.j.k').should.eql(true); + app.store.hasOwn('a.b.g.j.foo').should.eql(false); + }); + + it('should `.get()` a stored value', function () { + app.store.set('three', 'four'); + app.store.get('three').should.equal('four'); + }); + + it('should `.get()` a nested value', function () { + app.store.set({a: {b: {c: 'd'}}}); + app.store.get('a.b.c').should.equal('d'); + }); + + it('should `.del()` a stored value', function () { + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.del('a'); + app.store.should.not.have.property('a'); + }); + + it('should `.del()` multiple stored values', function () { + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.set('e', 'f'); + app.store.del(['a', 'c', 'e']); + app.store.data.should.eql({}); + }); +}); + +describe('events', function () { + it('should emit `set` when an object is set:', function () { + var keys = []; + app.store.on('set', function (key) { + keys.push(key); + }); + + app.store.set({a: {b: {c: 'd'}}}); + keys.should.eql(['a']); + }); + + it('should emit `set` when a key/value pair is set:', function () { + var keys = []; + app.store.on('set', function (key) { + keys.push(key); + }); + + app.store.set('a', 'b'); + keys.should.eql(['a']); + }); + + it('should emit `set` when an object value is set:', function () { + var keys = []; + app.store.on('set', function (key) { + keys.push(key); + }); + + app.store.set('a', {b: 'c'}); + keys.should.eql(['a']); + }); + + it('should emit `set` when an array of objects is passed:', function () { + var keys = []; + app.store.on('set', function (key) { + keys.push(key); + }); + + app.store.set([{a: 'b'}, {c: 'd'}]); + keys.should.eql(['a', 'c']); + }); + + it('should emit `del` when a value is delted:', function () { + var res; + app.store.on('del', function (keys) { + keys.should.eql(['a']); + assert(typeof app.store.get('a') === 'undefined'); + }); + + app.store.set('a', {b: 'c'}); + app.store.get('a').should.eql({b: 'c'}); + app.store.del('a'); + }); + + it('should emit deleted keys on `del`:', function () { + var res; + app.store.on('del', function (keys) { + keys.should.eql(['a', 'c', 'e']); + assert(Object.keys(app.store.data).length === 0); + }); + + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.set('e', 'f'); + app.store.data.should.have.properties(['a', 'c', 'e']); + app.store.del({force: true}); + }); +}); diff --git a/test/support/index.js b/test/support/index.js new file mode 100644 index 00000000..f0ecd8fa --- /dev/null +++ b/test/support/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var assert = module.exports = require('assert'); +require('should'); + +assert.containEql = function containEql(actual, expected) { + if (Array.isArray(expected)) { + var len = expected.length; + while (len--) { + assert.containEql(actual[len], expected[len]); + } + } else { + for (var key in expected) { + assert.deepEqual(actual[key], expected[key]); + } + } +}; diff --git a/test/support/spy.js b/test/support/spy.js new file mode 100644 index 00000000..e14512b2 --- /dev/null +++ b/test/support/spy.js @@ -0,0 +1,27 @@ +var fs = require('fs'); +var sinon = require('sinon'); + +var errorfn = false; + +function maybeCallAsync(module, func) { + var original = module[func]; + return sinon.stub(module, func, function() { + var args = Array.prototype.slice.call(arguments); + args.unshift(module, func); + var err = typeof errorfn === 'function' && + errorfn.apply(this, args); + if (!err) { + original.apply(this, arguments); + } else { + arguments[arguments.length - 1](err); + } + }); +} + +module.exports = { + setError: function(fn) { + errorfn = fn; + }, + chmodSpy: maybeCallAsync(fs, 'chmod'), + statSpy: maybeCallAsync(fs, 'stat') +}; diff --git a/test/verb.create.js b/test/verb.create.js deleted file mode 100644 index ce26c48c..00000000 --- a/test/verb.create.js +++ /dev/null @@ -1,147 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('engine create:', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - verb.engine('*', require('engine-lodash')); - done(); - }); - - describe('.create():', function () { - it('should create a new template `type`:', function () { - verb.create('include', 'includes'); - verb.should.have.properties('include', 'includes'); - }); - }); - - describe('when a new template type is created:', function () { - it('should add methods to the views for that type:', function () { - verb.create('apple', 'apples'); - verb.should.have.properties('apple', 'apples'); - }); - - it('should add templates to the views for a given template type:', function () { - verb.create('apple', 'apples'); - - verb.apple('a', 'one'); - verb.apple('b', 'two'); - verb.apple('c', 'three'); - - verb.views.should.have.property('apples'); - verb.views.apples.should.have.properties('a', 'b', 'c'); - }); - - describe('.decorate()', function () { - /* setup */ - beforeEach(function () { - verb = new verb.Verb(); - // create some custom template types - verb.create('block', 'blocks', { isLayout: true }); - verb.create('include', 'includes', { isPartial: true }); - verb.create('post', 'posts', { isRenderable: true }); - verb.create('doc', 'docs', { isRenderable: true }); - - // intentionally create dupes using different renderable types - verb.page('aaa.md', '<%= name %>', {name: 'Jon Schlinkert'}); - verb.post('aaa.md', '<%= name %>', {name: 'Brian Woodward'}); - verb.docs('aaa.md', '<%= name %>', {name: 'Halle Nicole'}); - - verb.include('sidebar.md', ''); - verb.block('default.md', 'abc {% body %} xyz'); - }); - - /* tests */ - it('should decorate the type with a `get` method:', function () { - verb.should.have.properties(['getPage', 'getPost', 'getDoc', 'getInclude']); - }); - - it('should decorate the type with a `render` method:', function () { - verb.should.have.properties(['renderPage', 'renderPost', 'renderDoc']); - }); - }); - }); - - describe('when the `isRenderable` flag is set on the options:', function () { - it('should push the name of the type into the `isRenderable` array:', function () { - verb.create('apple', 'apples', { isRenderable: true }); - - verb.type.renderable.should.containEql('pages'); - verb.type.renderable.should.containEql('apples'); - verb.type.renderable.should.containEql('apples'); - }); - }); - - describe('when the `isLayout` flag is set on the options:', function () { - it('should push the name of the type into the `isLayout` array:', function () { - verb.create('orange', 'oranges', { isLayout: true }); - - verb.type.layout.should.containEql('layouts'); - verb.type.layout.should.containEql('oranges'); - }); - }); - - describe('when no type flag is set on the options:', function () { - it('should push the name of the type into the `isPartial` array:', function () { - verb.create('banana', 'bananas'); - - verb.type.partial.should.containEql('partials'); - verb.type.partial.should.containEql('bananas'); - }); - }); - - describe('when the `isPartial` flag is set on the options:', function () { - it('should push the name of the type into the `isPartial` array:', function () { - verb.create('banana', 'bananas', { isPartial: true }); - - verb.type.partial.should.containEql('partials'); - verb.type.partial.should.containEql('bananas'); - }); - }); - - describe('when both the `isPartial` and the `isLayout` flags are set:', function () { - it('should push the type into both arrays:', function () { - verb.create('banana', 'bananas', { isPartial: true, isLayout: true }); - - verb.type.partial.should.containEql('bananas'); - verb.type.layout.should.containEql('bananas'); - }); - }); - - describe('when both the `isPartial` and the `isRenderable` flags are set:', function () { - it('should push the type into both arrays:', function () { - verb.create('banana', 'bananas', { isPartial: true, isRenderable: true }); - - verb.type.partial.should.containEql('bananas'); - verb.type.renderable.should.containEql('bananas'); - }); - }); - - describe('when both the `isLayout` and the `isRenderable` flags are set:', function () { - it('should push the type into both arrays:', function () { - verb.create('banana', 'bananas', { isLayout: true, isRenderable: true }); - - verb.type.layout.should.containEql('bananas'); - verb.type.renderable.should.containEql('bananas'); - }); - }); - - describe('when all three types flags are set:', function () { - it('should push the type into all three arrays:', function () { - verb.create('banana', 'bananas', { isPartial: true, isLayout: true, isRenderable: true }); - - verb.type.layout.should.containEql('bananas'); - verb.type.partial.should.containEql('bananas'); - verb.type.renderable.should.containEql('bananas'); - }); - }); -}); diff --git a/test/verb.cwd.js b/test/verb.cwd.js deleted file mode 100644 index afd5249d..00000000 --- a/test/verb.cwd.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('cwd', function () { - it('should set the cwd:', function () { - verb.option('cwd', __dirname); - verb.options.cwd.should.equal(__dirname); - }); -}); diff --git a/test/verb.data.js b/test/verb.data.js deleted file mode 100644 index 0b284759..00000000 --- a/test/verb.data.js +++ /dev/null @@ -1,147 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('verb.data()', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - verb.engine('*', require('engine-lodash')); - verb.data({name: 'Halle'}); - verb.emit('loaded'); - done(); - }); - - describe('`.data()` method:', function () { - it('should store data on `verb.cache.data`:', function () { - verb.cache.data.name.should.equal('Halle'); - }); - - it('should pass be passed to templates as context:', function (done) { - verb.render('<%= name %>', function (err, content) { - if (err) console.log(err); - content.should.equal('Halle'); - done(); - }); - }); - - it('should be overridden by data passed on the render method:', function (done) { - verb.render('<%= name %>', {name: 'Brooke'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Brooke'); - done(); - }); - }); - - it.only('should be overridden by data passed on template locals:', function (done) { - verb.data({name: 'Halle'}); - verb.doc('foo.md', {content: '<%= name %>', locals: {name: 'Brooke'}}); - - verb.render('foo', function (err, content) { - if (err) console.log(err); - // content.should.equal('Brooke'); - done(); - }); - }); - - it('should be overridden by data passed on template data:', function (done) { - verb.doc('foo.md', {content: '<%= name %>', data: {name: 'Brooke'}}); - - verb.render('foo', function (err, content) { - if (err) console.log(err); - content.should.equal('Brooke'); - done(); - }); - }); - - it('should be overridden by data passed on front-matter:', function (done) { - verb.doc('foo.md', {content: '---\nname: Brooke\n---\n{%= name %}'}); - verb.render('foo', function (err, content) { - if (err) console.log(err); - content.should.equal('Brooke'); - done(); - }); - }); - }); -}); - - -describe('verb data', function() { - beforeEach(function (done) { - verb = new verb.Verb(); - done(); - }); - - - describe('.data()', function() { - it('should set properties on the `data` object.', function() { - verb.set('data.foo', 'bar'); - verb.get('data').foo.should.equal('bar'); - verb.get('data.foo').should.equal('bar'); - }); - - it('should read files and merge data onto `cache.data`', function() { - verb.data('package.json', { namespace: false }); - verb.get('data.name').should.equal('verb'); - }); - - it('should read files and merge data onto `cache.data.package`', function() { - verb.data('package.json', { namespace: true }); - verb.get('data.package.name').should.equal('verb'); - }); - - it('should read files and merge data onto `cache.data`', function() { - verb.data({xyz: 'abc'}); - verb.get('data.xyz').should.equal('abc'); - }); - - it('should read files and merge data onto `cache.data`', function() { - verb.data([{aaa: 'bbb', ccc: 'ddd'}]); - verb.get('data.aaa').should.equal('bbb'); - verb.get('data.ccc').should.equal('ddd'); - }); - }); - - describe('.extendData()', function() { - it('should extend the `data` object.', function() { - verb.extendData({x: 'x', y: 'y', z: 'z'}); - verb.get('data').should.have.property('x'); - verb.get('data').should.have.property('y'); - verb.get('data').should.have.property('z'); - }); - }); - - describe('.flattenData()', function() { - it('should merge the value of a nested `data` property onto the root of the given object.', function() { - var root = verb.flattenData({data: {x: 'x'}, y: 'y', z: 'z'}); - root.should.have.property('x'); - root.should.have.property('y'); - root.should.have.property('z'); - root.should.not.have.property('data'); - }); - }); - - describe('.plasma()', function() { - it('should read JSON files and return an object.', function() { - var pkg = verb.plasma('package.json', { namespace: false }); - pkg.name.should.equal('verb'); - }); - - it('should expand a glob pattern, read JSON/YAML files and return an object.', function() { - var pkg = verb.plasma('p*.json', { namespace: false }); - pkg.name.should.equal('verb'); - }); - - it('should accept and object and return an object.', function() { - var foo = verb.plasma({a: 'b'}); - foo.a.should.equal('b'); - }); - }); -}); diff --git a/test/verb.defaults.js b/test/verb.defaults.js deleted file mode 100644 index 92205913..00000000 --- a/test/verb.defaults.js +++ /dev/null @@ -1,39 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -var orig = process.cwd(); -require('should'); - -describe('verb defaults', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - done(); - }); - - before(function () { - process.chdir(__dirname + '/fixtures'); - }); - - after(function () { - process.chdir(orig); - }); - - describe('when an instance of verb is initialized:', function () { - it('should load the verb package.json onto the `verb` object:', function () { - verb.should.have.property('verb'); - verb.verb.should.have.property('name', 'verb'); - }); - - it('should load the user\'s package.json onto the `env` object:', function () { - verb.should.have.property('env'); - verb.env.should.have.property('name', 'foo'); - }); - }); -}); diff --git a/test/verb.js b/test/verb.js deleted file mode 100644 index ea69465f..00000000 --- a/test/verb.js +++ /dev/null @@ -1,19 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('verb', function () { - it('should expose verb\'s package.json on `verb`:', function () { - verb.should.have.property('verb'); - verb.verb.should.be.an.object; - verb.verb.name.should.be.verb; // ;) - }); -}); diff --git a/test/verb.pkg.js b/test/verb.pkg.js deleted file mode 100644 index d9e73602..00000000 --- a/test/verb.pkg.js +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -require('should'); - -describe('package.json', function () { - it('should set a project\'s package.json on `verb.cache.data`:', function () { - verb.cache.data.name.should.equal('verb'); - }); -}); diff --git a/test/verb.render.js b/test/verb.render.js deleted file mode 100644 index 47269507..00000000 --- a/test/verb.render.js +++ /dev/null @@ -1,182 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var hbs = require('handlebars'); // keep -var consolidate = require('consolidate'); -var verb = require('..'); -require('should'); - -describe('verb.render()', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - verb.engine('html', require('engine-lodash')); - done(); - }); - - describe('`this` object:', function () { - it('should expose `this` to the .render() method:', function (done) { - verb.render('<%= name %>', {name: 'Jon Schlinkert'}, function (err, content) { - if (err) console.log(err); - this.should.have.properties(['cache', 'options', 'engines', 'delims']); - done(); - }); - }); - }); - - describe.skip('when an un-cached string is passed to `.render()`:', function () { - it('should detect the engine from `ext` (with dot) on locals:', function (done) { - verb.render('<%= name %>', {name: 'Jon Schlinkert', engine: '.html'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should detect the engine from `ext` (without dot) on locals:', function (done) { - verb.render('<%= name %>', {name: 'Jon Schlinkert', ext: 'html'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should detect the engine from `engine` (with dot) on locals:', function (done) { - verb.render('<%= name %>', {name: 'Jon Schlinkert', engine: '.html'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should detect the engine from `engine` (without dot) on locals:', function (done) { - verb.render('<%= name %>', {name: 'Jon Schlinkert', engine: 'html'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - }); - - describe('when the name of a cached template is passed to `.render()`:', function () { - it('should find the template and render it:', function (done) { - verb.page('aaa.md', '{%= name %}', {name: 'Jon Schlinkert'}); - - verb.render('aaa.md', function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should render the first matching template if dupes are found:', function (done) { - verb.page('aaa.md', '{%= name %}', {name: 'Brian Woodward'}); - verb.create('post', 'posts', { isRenderable: true }); - verb.post('aaa.md', '{%= name %}', {name: 'Jon Schlinkert'}); - - verb.render('aaa.md', function (err, content) { - if (err) console.log(err); - content.should.equal('Brian Woodward'); - done(); - }); - }); - }); - - describe('engine render:', function () { - it('should determine the engine from the `path` on the given object:', function (done) { - var file = {path: 'a/b/c.md', content: '{%= name %}', locals: {name: 'Jon Schlinkert'}}; - - verb.render(file, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should determine the engine from the `path` on the given object:', function (done) { - var file = {path: 'a/b/c.md', content: '{%= name %}'}; - - verb.render(file, {name: 'Jon Schlinkert'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should render content with an engine from [consolidate].', function (done) { - verb.engine('hbs', consolidate.handlebars); - var hbs = verb.getEngine('hbs'); - - hbs.render('{{name}}', {name: 'Jon Schlinkert'}, function (err, content) { - if (err) console.log(err); - content.should.equal('Jon Schlinkert'); - done(); - }); - }); - - it('should use `file.path` to determine the correct consolidate engine to render content:', function (done) { - verb.engine('hbs', consolidate.handlebars); - verb.engine('swig', consolidate.swig); - verb.engine('tmpl', consolidate.lodash); - - var files = [ - {path: 'fixture.hbs', content: '{{title}}', locals: {title: 'Handlebars'}}, - {path: 'fixture.tmpl', content: '<%= title %>', locals: {title: 'Lo-Dash'}}, - {path: 'fixture.swig', content: '{{title}}', locals: {title: 'Swig'}} - ]; - - files.forEach(function(file) { - verb.render(file, function (err, content) { - if (err) console.log(err); - - if (file.path === 'fixture.hbs') { - content.should.equal('Handlebars'); - } - if (file.path === 'fixture.tmpl') { - content.should.equal('Lo-Dash'); - } - if (file.path === 'fixture.swig') { - content.should.equal('Swig'); - } - }); - }); - - done(); - }); - - it('should use the key of a cached template to determine the consolidate engine to use:', function (done) { - verb.engine('hbs', consolidate.handlebars); - verb.engine('swig', consolidate.swig); - verb.engine('tmpl', consolidate.lodash); - - verb.page('a.hbs', {content: '{{title}}', title: 'Handlebars'}); - verb.page('b.tmpl', {content: '<%= title %>', title: 'Lo-Dash'}); - verb.page('d.swig', {content: '{{title}}', title: 'Swig'}); - - Object.keys(verb.views.pages).forEach(function(file) { - verb.render(file, function (err, content) { - if (err) console.log(err); - - if (file.path === 'fixture.hbs') { - content.should.equal('Handlebars'); - } - if (file.path === 'fixture.tmpl') { - content.should.equal('Lo-Dash'); - } - if (file.path === 'fixture.swig') { - content.should.equal('Swig'); - } - }); - }); - - done(); - }); - }); -}); diff --git a/test/verb.variables.js b/test/verb.variables.js deleted file mode 100644 index 07fca5bd..00000000 --- a/test/verb.variables.js +++ /dev/null @@ -1,37 +0,0 @@ -/*! - * verb - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var verb = require('..'); -var orig = process.cwd(); -require('should'); - -describe('verb defaults', function () { - beforeEach(function (done) { - verb = new verb.Verb(); - done(); - }); - before(function () { - process.chdir(__dirname + '/fixtures'); - }); - after(function () { - process.chdir(orig); - }); - - describe('when an instance of verb is initialized:', function () { - it('should load the verb package.json onto the `verb` object:', function () { - verb.should.have.property('verb'); - verb.verb.should.have.property('name', 'verb'); - }); - - it('should load the user\'s package.json onto the `env` object:', function () { - verb.should.have.property('env'); - verb.env.should.have.property('name', 'foo'); - }); - }); -}); diff --git a/test/view.content.js b/test/view.content.js new file mode 100644 index 00000000..86cd43b3 --- /dev/null +++ b/test/view.content.js @@ -0,0 +1,28 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('content', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + app.engine('tmpl', require('engine-base')); + }); + + it('should normalize the `content` property on a view to a string:', function (done) { + app.page('abc', {path: 'test/fixtures/templates/a.tmpl'}) + .set('read', function () { + this.contents = fs.readFileSync(this.path); + return this; + }); + + app.views.pages.abc.read(); + + assert('content' in app.views.pages.abc); + assert(typeof app.views.pages.abc.content === 'string'); + done(); + }); +}); diff --git a/test/view.events.js b/test/view.events.js new file mode 100644 index 00000000..f2d52bdf --- /dev/null +++ b/test/view.events.js @@ -0,0 +1,27 @@ +require('should'); +var App = require('..'); +var app; + +describe('view.option()', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + }); + + it('should emit events:', function () { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); + var page = app.pages.getView('a.tmpl'); + var events = []; + + page.on('option', function (key) { + events.push(key); + }); + + page.option('a', 'b'); + page.option('c', 'd'); + page.option('e', 'f'); + page.option({g: 'h'}); + + events.should.eql(['a', 'c', 'e', 'g']); + }); +}); diff --git a/test/view.js b/test/view.js new file mode 100644 index 00000000..1ce9d88d --- /dev/null +++ b/test/view.js @@ -0,0 +1,1149 @@ +require('mocha'); +var should = require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var Stream = require('stream'); +var es = require('event-stream'); +var View = require('../').View; +var view; + +describe('View', function () { + describe('instance', function () { + it('should create an instance of View:', function () { + view = new View(); + assert(view instanceof View); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof View.extend === 'function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function () { + view = new View(); + }); + + it('should expose `set`:', function () { + assert(typeof view.set === 'function'); + }); + it('should expose `get`:', function () { + assert(typeof view.get === 'function'); + }); + it('should expose `del`:', function () { + assert(typeof view.del === 'function'); + }); + it('should expose `define`:', function () { + assert(typeof view.define === 'function'); + }); + it('should expose `visit`:', function () { + assert(typeof view.visit === 'function'); + }); + it('should expose `compile`:', function () { + assert(typeof view.compile === 'function'); + }); + it('should expose `render`:', function () { + assert(typeof view.render === 'function'); + }); + }); + + describe('properties', function () { + it('should expose an `options` property', function () { + view = new View({}); + assert.deepEqual(view.options, {}); + assert(view.hasOwnProperty('options')); + }); + + it('should add `options` when passed on the constructor', function () { + view = new View({options: {foo: 'bar'}}); + assert(view.options.foo === 'bar'); + }); + + it('should expose a `data` property', function () { + view = new View({app: {}}); + assert.deepEqual(view.data, {}); + assert(view.hasOwnProperty('data')); + }); + + it('should add `data` when passed on the constructor', function () { + view = new View({data: {foo: 'bar'}}); + assert(view.data.foo === 'bar'); + }); + + it('should add `locals` when passed on the constructor', function () { + view = new View({locals: {foo: 'bar'}}); + assert(view.locals.foo === 'bar'); + }); + }); + + describe('set', function () { + it('should set properties on the object', function () { + view = new View(); + view.set('foo', 'bar'); + assert.equal(view.foo, 'bar'); + }); + }); + + describe('get', function () { + it('should get properties from the object', function () { + view = new View(); + view.set('foo', 'bar'); + assert.equal(view.get('foo'), 'bar'); + }); + }); + + describe('cwd', function () { + it('should get properties from the object', function () { + view = new View({cwd: 'test/fixtures'}); + assert(view.cwd === 'test/fixtures'); + }); + }); + + describe('clone', function () { + it('should clone the view:', function () { + view = new View({content: 'foo'}); + view.set({path: 'foo/bar'}); + view.set('options.one', 'two'); + var clone = view.clone(); + assert(clone.contents); + clone.set('baz', 'quux'); + clone.set('options.three', 'four'); + assert.equal(clone.get('foo'), view.get('foo')); + assert(clone.get('baz') === 'quux'); + assert(!view.get('baz')); + // not deep cloned + assert(clone.get('options.three') === 'four'); + assert(view.get('options.three') === 'four'); + }); + + it('should deep clone the entire object', function () { + view = new View({content: 'foo'}); + view.set({path: 'foo/bar'}); + view.set('options.one', 'two'); + var clone = view.clone({deep: true}); + clone.set('options.three', 'four'); + assert(view.get('options.one') === 'two'); + assert(clone.get('options.one') === 'two'); + assert(clone.get('options.three') === 'four'); + assert(!view.get('options.three')); + }); + }); + + describe('visit', function () { + it('should visit all properties on an object and call the specified method', function () { + view = new View(); + var obj = { + foo: 'bar', + bar: 'baz', + baz: 'bang' + }; + view.visit('set', obj); + assert.equal(view.get('foo'), 'bar'); + assert.equal(view.get('bar'), 'baz'); + assert.equal(view.get('baz'), 'bang'); + }); + + it('should visit all properties on all objects in an array and call the specified method', function () { + view = new View(); + var arr = [{foo: 'bar', bar: 'baz', baz: 'bang'}]; + view.visit('set', arr); + assert.equal(view.get('foo'), 'bar'); + assert.equal(view.get('bar'), 'baz'); + assert.equal(view.get('baz'), 'bang'); + }); + }); + + describe('compile', function () { + it('should get view.layout from view.data.layout', function () { + view = new View({path: 'foo', contents: 'a b c', data: {layout: 'default'}}); + assert(view.layout === 'default'); + }); + it('should get view.layout from view.options.layout', function () { + view = new View({path: 'foo', contents: 'a b c', options: {layout: 'default'}}); + assert(view.layout === 'default'); + }); + it('should get view.layout from view.locals.layout', function () { + view = new View({path: 'foo', contents: 'a b c', locals: {layout: 'default'}}); + assert(view.layout === 'default'); + }); + it('should get view.layout from the view', function () { + view = new View({path: 'foo', contents: 'a b c', layout: 'default'}); + assert(view.layout === 'default'); + }); + }); + + describe('compile', function () { + it('should add a compiled function to `view.fn`', function () { + view = new View({path: 'foo', contents: 'a <%= name %> z'}); + view.compile(); + assert(typeof view.fn === 'function'); + }); + + it('should render a compiled template', function (done) { + view = new View({path: 'foo', contents: 'a <%= name %> z'}); + view.compile(); + view.render({name: 'Halle'}, function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a Halle z'); + done(); + }); + }); + + it('should render a template', function (done) { + view = new View({path: 'foo', contents: 'a <%= name %> z'}); + view.render({name: 'Halle'}, function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a Halle z'); + done(); + }); + }); + + it('should render fn using data passed on the constructor', function (done) { + view = new View({ + path: 'foo', + contents: 'a <%= name %> z', + data: { + name: 'Brooke' + } + }); + + view.compile(); + view.render(function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a Brooke z'); + done(); + }); + }); + + it('should render fn using data passed on the constructor', function (done) { + view = new View({ + path: 'foo', + contents: 'a <%= name %> z', + data: { + name: 'Brooke' + } + }); + + view.render(function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a Brooke z'); + done(); + }); + }); + + it('should pass errors in the callback.', function (done) { + view = new View({ + path: 'foo', + contents: 'a <%= name %> z' + }); + + view.render(function (err) { + assert(err.message === 'name is not defined'); + done(); + }); + }); + }); +}); + +/** + * The following unit tests are from Vinyl + * Since we inherit vinyl in View, we need + * to ensure that these still pass. + */ + +describe('View', function() { + describe('isVinyl()', function() { + it('should return true on a vinyl object', function(done) { + var view = new View(); + View.isVinyl(view).should.equal(true); + done(); + }); + it('should return false on a normal object', function(done) { + View.isVinyl({}).should.equal(false); + done(); + }); + it('should return false on a null object', function(done) { + View.isVinyl({}).should.equal(false); + done(); + }); + }); + + describe('constructor()', function() { + it('should default cwd to process.cwd', function(done) { + var view = new View(); + view.cwd.should.equal(process.cwd()); + done(); + }); + + it('should default base to cwd', function(done) { + var cwd = '/'; + var view = new View({cwd: cwd}); + view.base.should.equal(cwd); + done(); + }); + + it('should default base to cwd even when none is given', function(done) { + var view = new View(); + view.base.should.equal(process.cwd()); + done(); + }); + + it('should default path to null', function(done) { + var view = new View(); + should.not.exist(view.path); + done(); + }); + + it('should default history to []', function(done) { + var view = new View(); + view.history.should.eql([]); + done(); + }); + + it('should default stat to null', function(done) { + var view = new View(); + should.not.exist(view.stat); + done(); + }); + + it('should default contents to null', function(done) { + var view = new View(); + should.not.exist(view.contents); + done(); + }); + + it('should set base to given value', function(done) { + var val = '/'; + var view = new View({base: val}); + view.base.should.equal(val); + done(); + }); + + it('should set cwd to given value', function(done) { + var val = '/'; + var view = new View({cwd: val}); + view.cwd.should.equal(val); + done(); + }); + + it('should set path to given value', function(done) { + var val = '/test.coffee'; + var view = new View({path: val}); + view.path.should.equal(val); + view.history.should.eql([val]); + done(); + }); + + it('should set history to given value', function(done) { + var val = '/test.coffee'; + var view = new View({history: [val]}); + view.path.should.equal(val); + view.history.should.eql([val]); + done(); + }); + + it('should set stat to given value', function(done) { + var val = {}; + var view = new View({stat: val}); + view.stat.should.equal(val); + done(); + }); + + it('should set contents to given value', function(done) { + var val = new Buffer('test'); + var view = new View({contents: val}); + view.contents.should.equal(val); + done(); + }); + }); + + describe('isBuffer()', function() { + it('should return true when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var view = new View({contents: val}); + view.isBuffer().should.equal(true); + done(); + }); + + it('should return false when the contents are a Stream', function(done) { + var val = new Stream(); + var view = new View({contents: val}); + view.isBuffer().should.equal(false); + done(); + }); + + it('should return false when the contents are a null', function(done) { + var view = new View({contents: null}); + view.isBuffer().should.equal(false); + done(); + }); + }); + + describe('isStream()', function() { + it('should return false when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var view = new View({contents: val}); + view.isStream().should.equal(false); + done(); + }); + + it('should return true when the contents are a Stream', function(done) { + var val = new Stream(); + var view = new View({contents: val}); + view.isStream().should.equal(true); + done(); + }); + + it('should return false when the contents are a null', function(done) { + var view = new View({contents: null}); + view.isStream().should.equal(false); + done(); + }); + }); + + describe('isNull()', function() { + it('should return false when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var view = new View({contents: val}); + view.isNull().should.equal(false); + done(); + }); + + it('should return false when the contents are a Stream', function(done) { + var val = new Stream(); + var view = new View({contents: val}); + view.isNull().should.equal(false); + done(); + }); + + it('should return true when the contents are a null', function(done) { + var view = new View({contents: null}); + view.isNull().should.equal(true); + done(); + }); + }); + + describe('isDirectory()', function() { + var fakeStat = { + isDirectory: function() { + return true; + } + }; + + it('should return false when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var view = new View({contents: val, stat: fakeStat}); + view.isDirectory().should.equal(false); + done(); + }); + + it('should return false when the contents are a Stream', function(done) { + var val = new Stream(); + var view = new View({contents: val, stat: fakeStat}); + view.isDirectory().should.equal(false); + done(); + }); + + it('should return true when the contents are a null', function(done) { + var view = new View({contents: null, stat: fakeStat}); + view.isDirectory().should.equal(true); + done(); + }); + }); + + describe('clone()', function() { + it('should copy all attributes over with Buffer', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Buffer('test') + }; + var view = new View(options); + var view2 = view.clone(); + + view2.should.not.equal(view, 'refs should be different'); + view2.cwd.should.equal(view.cwd); + view2.base.should.equal(view.base); + view2.path.should.equal(view.path); + view2.contents.should.not.equal(view.contents, 'buffer ref should be different'); + view2.contents.toString('utf8').should.equal(view.contents.toString('utf8')); + done(); + }); + + it('should copy buffer\'s reference with option contents: false', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.js', + contents: new Buffer('test') + }; + + var view = new View(options); + + var copy1 = view.clone({ contents: false }); + copy1.contents.should.equal(view.contents); + + var copy2 = view.clone({}); + copy2.contents.should.not.equal(view.contents); + + var copy3 = view.clone({ contents: 'any string' }); + copy3.contents.should.not.equal(view.contents); + + done(); + }); + + it('should copy all attributes over with Stream', function(done) { + var contents = new Stream.PassThrough(); + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: contents + }; + var view = new View(options); + var view2 = view.clone(); + + contents.write(new Buffer('wa')); + + process.nextTick(function() { + contents.write(new Buffer('dup')); + contents.end(); + }); + + view2.should.not.equal(view, 'refs should be different'); + view2.cwd.should.equal(view.cwd); + view2.base.should.equal(view.base); + view2.path.should.equal(view.path); + view2.contents.should.not.equal(view.contents, 'stream ref should not be the same'); + view.contents.pipe(es.wait(function(err, data) { + view2.contents.pipe(es.wait(function(err, data2) { + data2.should.not.equal(data, 'stream contents ref should not be the same'); + data2.should.eql(data, 'stream contents should be the same'); + })); + })); + done(); + }); + + it('should copy all attributes over with null', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + var view = new View(options); + var view2 = view.clone(); + + view2.should.not.equal(view, 'refs should be different'); + view2.cwd.should.equal(view.cwd); + view2.base.should.equal(view.base); + view2.path.should.equal(view.path); + should.not.exist(view2.contents); + done(); + }); + + it('should properly clone the `stat` property', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.js', + contents: new Buffer('test'), + stat: fs.statSync(__filename) + }; + + var view = new View(options); + var copy = view.clone(); + + assert(copy.stat.isFile()); + assert(!copy.stat.isDirectory()); + assert(view.stat instanceof fs.Stats); + assert(copy.stat instanceof fs.Stats); + done(); + }); + + it('should properly clone the `history` property', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.js', + contents: new Buffer('test'), + stat: fs.statSync(__filename) + }; + + var view = new View(options); + var copy = view.clone(); + + copy.history[0].should.equal(options.path); + copy.path = 'lol'; + view.path.should.not.equal(copy.path); + done(); + }); + + it('should copy custom properties', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + + var view = new View(options); + view.custom = { a: 'custom property' }; + var view2 = view.clone(); + + view2.should.not.equal(view, 'refs should be different'); + view2.cwd.should.equal(view.cwd); + view2.base.should.equal(view.base); + view2.path.should.equal(view.path); + view2.custom.should.equal(view.custom); + view2.custom.a.should.equal(view.custom.a); + + done(); + }); + + it('should copy history', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + + var view = new View(options); + view.path = '/test/test.js'; + view.path = '/test/test-938di2s.js'; + var view2 = view.clone(); + + view2.history.should.eql([ + '/test/test.coffee', + '/test/test.js', + '/test/test-938di2s.js' + ]); + view2.history.should.not.equal([ + '/test/test.coffee', + '/test/test.js', + '/test/test-938di2s.js' + ]); + view2.path.should.eql('/test/test-938di2s.js'); + + done(); + }); + + it('should copy all attributes deeply', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + + var view = new View(options); + view.custom = { a: 'custom property' }; + + var view2 = view.clone(true); + view2.custom.should.eql(view.custom); + view2.custom.should.not.equal(view.custom); + view2.custom.a.should.equal(view.custom.a); + + var view3 = view.clone({ deep: true }); + view3.custom.should.eql(view.custom); + view3.custom.should.not.equal(view.custom); + view3.custom.a.should.equal(view.custom.a); + + var view4 = view.clone(false); + view4.custom.should.eql(view.custom); + view4.custom.should.equal(view.custom); + view4.custom.a.should.equal(view.custom.a); + + var view5 = view.clone({ deep: false }); + view5.custom.should.eql(view.custom); + view5.custom.should.equal(view.custom); + view5.custom.a.should.equal(view.custom.a); + + done(); + }); + }); + + describe('pipe()', function() { + it('should write to stream with Buffer', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Buffer('test') + }; + var view = new View(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(options.contents.toString('utf8')); + }); + stream.on('end', function() { + done(); + }); + var ret = view.pipe(stream); + ret.should.equal(stream, 'should return the stream'); + }); + + it('should pipe to stream with Stream', function(done) { + var testChunk = new Buffer('test'); + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Stream.PassThrough() + }; + var view = new View(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(testChunk.toString('utf8')); + done(); + }); + var ret = view.pipe(stream); + ret.should.equal(stream, 'should return the stream'); + + view.contents.write(testChunk); + }); + + it('should do nothing with null', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + var view = new View(options); + var stream = new Stream.PassThrough(); + stream.on('data', function() { + throw new Error('should not write'); + }); + stream.on('end', function() { + done(); + }); + var ret = view.pipe(stream); + ret.should.equal(stream, 'should return the stream'); + }); + + it('should write to stream with Buffer', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Buffer('test') + }; + var view = new View(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(options.contents.toString('utf8')); + done(); + }); + stream.on('end', function() { + throw new Error('should not end'); + }); + var ret = view.pipe(stream, {end: false}); + ret.should.equal(stream, 'should return the stream'); + }); + + it('should pipe to stream with Stream', function(done) { + var testChunk = new Buffer('test'); + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Stream.PassThrough() + }; + var view = new View(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(testChunk.toString('utf8')); + done(); + }); + stream.on('end', function() { + throw new Error('should not end'); + }); + var ret = view.pipe(stream, {end: false}); + ret.should.equal(stream, 'should return the stream'); + + view.contents.write(testChunk); + }); + + it('should do nothing with null', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + var view = new View(options); + var stream = new Stream.PassThrough(); + stream.on('data', function() { + throw new Error('should not write'); + }); + stream.on('end', function() { + throw new Error('should not end'); + }); + var ret = view.pipe(stream, {end: false}); + ret.should.equal(stream, 'should return the stream'); + process.nextTick(done); + }); + }); + + describe('inspect()', function() { + it('should return correct format when no contents and no path', function(done) { + var view = new View(); + view.inspect().should.equal(''); + done(); + }); + + it('should return correct format when Buffer and no path', function(done) { + var val = new Buffer('test'); + var view = new View({ + contents: val + }); + view.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when Buffer and relative path', function(done) { + var val = new Buffer('test'); + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: val + }); + view.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when Buffer and only path and no base', function(done) { + var val = new Buffer('test'); + var view = new View({ + cwd: '/', + path: '/test/test.coffee', + contents: val + }); + delete view.base; + view.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when Stream and relative path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Stream.PassThrough() + }); + view.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when null and relative path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }); + view.inspect().should.equal(''); + done(); + }); + }); + + describe('contents get/set', function() { + it('should work with Buffer', function(done) { + var val = new Buffer('test'); + var view = new View(); + view.contents = val; + view.contents.should.equal(val); + done(); + }); + + it('should work with Stream', function(done) { + var val = new Stream.PassThrough(); + var view = new View(); + view.contents = val; + view.contents.should.equal(val); + done(); + }); + + it('should work with null', function(done) { + var val = null; + var view = new View(); + view.contents = val; + (view.contents === null).should.equal(true); + done(); + }); + + it('should work with string', function(done) { + var val = 'test'; + var view = new View(); + view.contents = val; + view.contents.should.deepEqual(new Buffer(val)); + done(); + }); + }); + + describe('relative get/set', function() { + it('should error on set', function(done) { + var view = new View(); + try { + view.relative = 'test'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should error on get when no base', function(done) { + var a; + var view = new View(); + delete view.base; + try { + a = view.relative; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should error on get when no path', function(done) { + var a; + var view = new View(); + try { + a = view.relative; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return a relative path from base', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.relative.should.equal('test.coffee'); + done(); + }); + + it('should return a relative path from cwd', function(done) { + var view = new View({ + cwd: '/', + path: '/test/test.coffee' + }); + view.relative.should.equal(path.join('test','test.coffee')); + done(); + }); + }); + + describe('dirname get/set', function() { + it('should error on get when no path', function(done) { + var a; + var view = new View(); + try { + a = view.dirname; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return the dirname of the path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.dirname.should.equal('/test'); + done(); + }); + + it('should error on set when no path', function(done) { + var view = new View(); + try { + view.dirname = '/test'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should set the dirname of the path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.dirname = '/test/foo'; + view.path.should.equal('/test/foo/test.coffee'); + done(); + }); + }); + + describe('basename get/set', function() { + it('should error on get when no path', function(done) { + var a; + var view = new View(); + try { + a = view.basename; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return the basename of the path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.basename.should.equal('test.coffee'); + done(); + }); + + it('should error on set when no path', function(done) { + var view = new View(); + try { + view.basename = 'test.coffee'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should set the basename of the path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.basename = 'foo.png'; + view.path.should.equal('/test/foo.png'); + done(); + }); + }); + + describe('extname get/set', function() { + it('should error on get when no path', function(done) { + var a; + var view = new View(); + try { + a = view.extname; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return the extname of the path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.extname.should.equal('.coffee'); + done(); + }); + + it('should error on set when no path', function(done) { + var view = new View(); + try { + view.extname = '.coffee'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should set the extname of the path', function(done) { + var view = new View({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + view.extname = '.png'; + view.path.should.equal('/test/test.png'); + done(); + }); + }); + + describe('path get/set', function() { + + it('should record history when instantiation', function() { + var view = new View({ + cwd: '/', + path: '/test/test.coffee' + }); + + view.path.should.eql('/test/test.coffee'); + view.history.should.eql(['/test/test.coffee']); + }); + + it('should record history when path change', function() { + var view = new View({ + cwd: '/', + path: '/test/test.coffee' + }); + + view.path = '/test/test.js'; + view.path.should.eql('/test/test.js'); + view.history.should.eql(['/test/test.coffee', '/test/test.js']); + + view.path = '/test/test.coffee'; + view.path.should.eql('/test/test.coffee'); + view.history.should.eql(['/test/test.coffee', '/test/test.js', '/test/test.coffee']); + }); + + it('should not record history when set the same path', function() { + var view = new View({ + cwd: '/', + path: '/test/test.coffee' + }); + + view.path = '/test/test.coffee'; + view.path = '/test/test.coffee'; + view.path.should.eql('/test/test.coffee'); + view.history.should.eql(['/test/test.coffee']); + + // ignore when set empty string + view.path = ''; + view.path.should.eql('/test/test.coffee'); + view.history.should.eql(['/test/test.coffee']); + }); + + it('should throw when set path null in constructor', function() { + (function() { + new View({ + cwd: '/', + path: null + }); + }).should.throw('path should be string'); + }); + + it('should throw when set path null', function() { + var view = new View({ + cwd: '/', + path: 'foo' + }); + + (function() { + view.path = null; + }).should.throw('path should be string'); + }); + }); +}); diff --git a/test/view.methods.js b/test/view.methods.js new file mode 100644 index 00000000..635d9922 --- /dev/null +++ b/test/view.methods.js @@ -0,0 +1,39 @@ +require('should'); +var App = require('..'); +var app; + +describe('view.option()', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); + + + describe('.use', function () { + it('should expose `.use` for running plugins on a view:', function () { + app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) + .use(function () { + this.options.foo = 'bar'; + }) + .use(function () { + this.options.bar = 'baz'; + }); + + var page = app.pages.getView('a.tmpl'); + page.options.should.have.property('foo'); + page.options.should.have.property('bar'); + }); + }); + + describe('.render:', function () { + it('should expose `.render` for rendering a view:', function (done) { + app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', locals: {a: 'bbb'}}) + .render({}, function (err, res) { + if (err) return done(err); + res.contents.toString().should.equal('bbb'); + done(); + }); + }); + }); +}); diff --git a/test/view.option.js b/test/view.option.js new file mode 100644 index 00000000..ffc185ea --- /dev/null +++ b/test/view.option.js @@ -0,0 +1,28 @@ +require('should'); +var App = require('..'); +var app; + +describe('view.option()', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + }); + + it('should set an option:', function () { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); + var page = app.pages.getView('a.tmpl'); + + page.options.should.not.have.property('foo'); + page.option('foo', 'bar'); + page.options.should.have.property('foo'); + }); + + it('should extend options:', function () { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); + var page = app.pages.getView('a.tmpl'); + page.option('a', 'b'); + page.option('c', 'd'); + page.option('e', 'f'); + page.options.should.have.properties(['a', 'c', 'e']); + }); +}); diff --git a/test/view.render.js b/test/view.render.js new file mode 100644 index 00000000..e537654f --- /dev/null +++ b/test/view.render.js @@ -0,0 +1,26 @@ +require('mocha'); +require('should'); +var View = require('../').View; +var App = require('..'); +var view, app; + +describe.skip('helpers', function () { + describe('rendering', function () { + beforeEach(function () { + app = new App(); + view = new View(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + }); + + it('should expose `.render` for rendering a view:', function (done) { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) + .render({a: 'bbb'}, function (err, res) { + if (err) return done(err); + res.contents.toString().should.equal('bbb'); + done(); + }); + }); + }); +}); + diff --git a/test/view.set.js b/test/view.set.js new file mode 100644 index 00000000..9828c518 --- /dev/null +++ b/test/view.set.js @@ -0,0 +1,33 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var assert = require('assert'); +var App = require('../'); +var app; + +describe('set', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + app.engine('tmpl', require('engine-base')); + }); + + it('should set a property on a view:', function (done) { + app.page('abc', {path: 'test/fixtures/templates/a.tmpl'}) + .set('read', function () { + this.contents = fs.readFileSync(this.path); + return this; + }); + + assert('read' in app.views.pages.abc); + app.views.pages.abc + .read() + .set('data.name', 'Brooke') + .render(function (err, res) { + if (err) return done(err); + + assert(res.contents.toString() === 'Brooke'); + done(); + }); + }); +}); diff --git a/test/view.use.js b/test/view.use.js new file mode 100644 index 00000000..a17efff0 --- /dev/null +++ b/test/view.use.js @@ -0,0 +1,59 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var App = require('../'); +var View = App.View; +var view; + +describe('view.use', function () { + beforeEach(function () { + view = new View(); + }); + + it('should expose the instance to `use`:', function (done) { + view.use(function (inst) { + assert(inst instanceof View); + done(); + }); + }); + + it('should be chainable:', function (done) { + view.use(function (inst) { + assert(inst instanceof View); + }) + .use(function (inst) { + assert(inst instanceof View); + }) + .use(function (inst) { + assert(inst instanceof View); + done(); + }); + }); + + it('should expose the view to a plugin:', function () { + view.use(function (view) { + assert(view instanceof View); + view.foo = function (str) { + return str + ' ' + 'bar'; + }; + }); + assert(view.foo('foo') === 'foo bar'); + }); + + it('should be chainable:', function () { + view + .use(function (view) { + view.a = 'aaa'; + }) + .use(function (view) { + view.b = 'bbb'; + }) + .use(function (view) { + view.c = 'ccc'; + }); + + assert(view.a === 'aaa'); + assert(view.b === 'bbb'); + assert(view.c === 'ccc'); + }); +}); diff --git a/test/viewTypes.js b/test/viewTypes.js new file mode 100644 index 00000000..a6c89e9f --- /dev/null +++ b/test/viewTypes.js @@ -0,0 +1,33 @@ +var assert = require('assert'); +var App = require('../'); +var app; + + +describe('viewType', function () { + describe('view types', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + }); + + it('should add collection (plural) to the `viewTypes` object', function () { + app.viewTypes = []; // reset + app.create('foo', {viewType: 'layout'}); + app.create('bar', {viewType: 'layout'}); + assert.deepEqual(app.viewTypes.layout, [ 'foos', 'bars' ]); + + app.create('baz', {viewType: 'renderable'}); + assert.deepEqual(app.viewTypes.renderable, [ 'bazs' ]); + }); + + it('should add collection to the given viewType', function () { + app.create('layout', {viewType: 'layout'}); + assert(app.layouts.options.viewType[0] === 'layout'); + }); + + it('should add a collection to multiple viewTypes', function () { + app.create('foo', {viewType: ['layout', 'renderable']}); + assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); + }); + }); +}); diff --git a/test/views.js b/test/views.js new file mode 100644 index 00000000..ce516bee --- /dev/null +++ b/test/views.js @@ -0,0 +1,340 @@ +require('mocha'); +require('should'); +var path = require('path'); +var assert = require('assert'); +var isBuffer = require('is-buffer'); +var List = require('..').List; +var View = require('..').View; +var Views = require('..').Views; +var collection; + +describe('views', function () { + describe('constructor', function () { + it('should create an instance of Views:', function () { + var collection = new Views(); + assert(collection instanceof Views); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof Views.extend ==='function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function() { + collection = new Views(); + }); + + it('should expose `set`', function () { + assert(typeof collection.set ==='function'); + }); + it('should expose `get`', function () { + assert(typeof collection.get ==='function'); + }); + it('should expose `visit`', function () { + assert(typeof collection.visit ==='function'); + }); + it('should expose `define`', function () { + assert(typeof collection.define ==='function'); + }); + it('should expose `addView`', function () { + assert(typeof collection.addView ==='function'); + }); + }); + + describe('instance', function () { + beforeEach(function() { + collection = new Views(); + }); + + it('should set a value on the instance:', function () { + collection.set('a', 'b'); + assert(collection.a ==='b'); + }); + + it('should get a value from the instance:', function () { + collection.set('a', 'b'); + assert(collection.get('a') ==='b'); + }); + }); + + describe('option', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should set a key/value pair on options:', function () { + collection.option('a', 'b'); + assert(collection.options.a === 'b'); + }); + + it('should set an object on options:', function () { + collection.option({c: 'd'}); + assert(collection.options.c === 'd'); + }); + + it('should get an option:', function () { + collection.option({c: 'd'}); + var c = collection.option('c'); + assert(c === 'd'); + }); + }); + + describe('addView', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should throw an error when args are invalid:', function () { + (function () { + collection.addView(function() {}); + }).should.throw('expected value to be an object.'); + }); + + it('should add a view to `views`:', function () { + collection.addView('foo'); + collection.views.should.have.property('foo'); + + collection.addView('one', {content: '...'}); + assert(typeof collection.views.one === 'object'); + assert(isBuffer(collection.views.one.contents)); + }); + + it('should create an instance of `View`:', function () { + collection.addView('one', {content: '...'}); + assert(collection.views.one instanceof collection.View); + }); + + it('should allow an `View` constructor to be passed:', function () { + View.prototype.foo = function(key, value) { + this[key] = value; + }; + collection = new Views({View: View}); + collection.addView('one', {content: '...'}); + collection.views.one.foo('bar', 'baz'); + assert(collection.views.one.bar === 'baz'); + }); + + it('should allow an instance of `View` to be passed:', function () { + var collection = new Views({View: View}); + var view = new View({content: '...'}); + collection.addView('one', view); + view.set('abc', 'xyz'); + assert(collection.views.one instanceof collection.View); + assert(isBuffer(collection.views.one.contents)); + assert(collection.views.one.abc === 'xyz'); + }); + }); + + describe('addViews', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should add multiple views:', function () { + collection.addViews({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + assert(isBuffer(collection.views.one.contents)); + assert(isBuffer(collection.views.two.contents)); + }); + + it('should create views from an instance of Views', function () { + collection.addViews({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + var pages = new Views(collection); + assert(isBuffer(pages.views.one.contents)); + assert(isBuffer(pages.views.two.contents)); + }); + + it('should add an array of views:', function () { + collection.addViews([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + assert(isBuffer(collection.views.one.contents)); + assert(isBuffer(collection.views.two.contents)); + }); + }); + + describe('view', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should return a single collection view from a key-value pair', function () { + var one = collection.view('one', {content: 'foo'}); + var two = collection.view('two', {content: 'bar'}); + + assert(one instanceof View); + assert(one.path === 'one'); + assert(two instanceof View); + assert(two.path === 'two'); + }); + + it('should return a single collection view from an object', function () { + var one = collection.view({path: 'one', content: 'foo'}); + var two = collection.view({path: 'two', content: 'bar'}); + + assert(one instanceof View); + assert(one.path === 'one'); + assert(two instanceof View); + assert(two.path === 'two'); + }); + }); + + describe('addList', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should add a list of views:', function () { + collection.addList([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + assert(isBuffer(collection.views.one.contents)); + assert(isBuffer(collection.views.two.contents)); + }); + + it('should add a list of views from the constructor:', function () { + var list = new List([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + + collection = new Views(list); + assert(isBuffer(collection.views.one.contents)); + assert(isBuffer(collection.views.two.contents)); + }); + + it('should throw an error when list is not an array:', function () { + var views = new Views(); + (function () { + views.addList(); + }).should.throw('expected list to be an array.'); + + (function () { + views.addList({}); + }).should.throw('expected list to be an array.'); + + (function () { + views.addList('foo'); + }).should.throw('expected list to be an array.'); + }); + + it('should load an array of items from an event:', function () { + var collection = new Views(); + + collection.on('addList', function (list) { + while (list.length) { + collection.addView({path: list.pop()}); + } + }); + + collection.addList(['a.txt', 'b.txt', 'c.txt']); + assert(collection.views.hasOwnProperty('a.txt')); + assert(collection.views['a.txt'].path === 'a.txt'); + }); + + it('should load an object of views from an event:', function () { + var collection = new Views(); + + collection.on('addViews', function (views) { + for (var key in views) { + collection.addView('foo/' + key, views[key]); + delete views[key]; + } + }); + + collection.addViews({ + a: {path: 'a.txt'}, + b: {path: 'b.txt'}, + c: {path: 'c.txt'} + }); + + assert(collection.views.hasOwnProperty('foo/a')); + assert(collection.views['foo/a'].path === 'a.txt'); + }); + + it('should signal `loaded` when finished:', function () { + var collection = new Views(); + + collection.on('addViews', function (views) { + for (var key in views) { + if (key === 'c') break; + collection.addView('foo/' + key, views[key]); + } + }); + + collection.addViews({ + a: {path: 'a.txt'}, + b: {path: 'b.txt'}, + c: {path: 'c.txt'} + }); + + assert(collection.views.hasOwnProperty('foo/a')); + assert(!collection.views.hasOwnProperty('foo/c')); + assert(collection.views['foo/a'].path === 'a.txt'); + }); + }); + + describe('getView', function() { + beforeEach(function() { + collection = new Views(); + }); + it('should get a view from `views`:', function () { + collection.addView('one', {content: 'aaa'}); + collection.addView('two', {content: 'zzz'}); + assert(isBuffer(collection.views.one.contents)); + assert(isBuffer(collection.getView('one').contents)); + assert(collection.getView('one').contents.toString() === 'aaa'); + assert(collection.getView('two').contents.toString() === 'zzz'); + }); + }); + + describe('count', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should get the number of views:', function () { + collection.addView('one', {content: 'aaa'}); + collection.addView('two', {content: 'zzz'}); + assert(Object.keys(collection.views).length === 2); + }); + }); +}); + +describe('options', function() { + describe('options.renameKey', function() { + beforeEach(function() { + collection = new Views({ + renameKey: function (key) { + return path.basename(key); + } + }); + }); + + it('should use a custom rename key function on view keys', function() { + collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); + assert(collection.views['d.hbs'].contents.toString() === 'foo bar baz'); + }); + + it('should get a view with the renamed key:', function () { + collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); + assert(collection.getView('d.hbs').contents.toString() === 'foo bar baz'); + }); + + it('should get a view with the original key:', function () { + collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); + assert(collection.getView('a/b/c/d.hbs').contents.toString() === 'foo bar baz'); + }); + }); +}); + diff --git a/verbfile.js b/verbfile.js deleted file mode 100644 index 7015f040..00000000 --- a/verbfile.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -var gutil = require('gulp-util'); -var istanbul = require('gulp-istanbul'); -var jshint = require('gulp-jshint'); -var mocha = require('gulp-mocha'); -var through = require('through2'); -var verb = require('./'); - -verb.disable('debugEngine'); -verb.disable('reflinks'); - -verb.task('readme', function () { - verb.src('.verb.md') - .pipe(verb.dest('.')) -}); - -verb.task('docs', function () { - verb.src('docs/_templates/[e-g]*.md') - .pipe(verb.dest('test/actual')) -}); - -verb.task('lint', function () { - /* deps: jshint-stylish */ - verb.src(['index.js', 'lib/**/*.js']) - .on('error', gutil.log) - .pipe(jshint('.jshintrc')) - .pipe(jshint.reporter('jshint-stylish')); -}); - -verb.task('test', ['lint'], function (cb) { - verb.src(['index.js', 'lib/**/*.js']) - .on('error', gutil.log) - .pipe(istanbul()) - .pipe(istanbul.hookRequire()) - .on('finish', function () { - verb.src('test/*.js') - .pipe(mocha()) - .pipe(istanbul.writeReports({ - reporters: [ 'text' ], - reportOpts: {dir: 'coverage', file: 'summary.txt'} - })) - .on('end', function () { - verb.diff(); - cb(); - }); - }); -}); - -verb.task('default', ['readme']); From 7b08a59605877f64d9e7b01e613ccc339fa44832 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Sep 2015 06:24:30 -0400 Subject: [PATCH 002/282] rename local to locals --- index.js | 10 ++++++---- lib/local.js | 22 ---------------------- lib/locals.js | 22 ++++++++++++++++++++++ lib/utils.js | 2 -- package.json | 5 ++--- 5 files changed, 30 insertions(+), 31 deletions(-) delete mode 100644 lib/local.js create mode 100644 lib/locals.js diff --git a/index.js b/index.js index 725d6c91..b41956e4 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ var path = require('path'); var store = require('data-store'); -var Local = require('./lib/local'); +var Locals = require('./lib/locals'); var loader = require('assemble-loader'); var includes = require('readme-includes'); var badges = require('readme-badges'); @@ -54,7 +54,7 @@ Templates.extend(Verb, { initVerb: function(app, opts) { this.store = store('verb', opts.store); - this.local = new Local(this.cache.data.verb); + this.locals = new Locals(this.cache.data.verb); // console.log(this.local.locals) @@ -207,6 +207,8 @@ Templates.extend(Verb, { return '\n' + keys.join('\n'); }); + this.asyncHelper('related', require('helper-related')); + this.asyncHelper('trim', function (str) { return str.trim(); }); @@ -219,10 +221,10 @@ Templates.extend(Verb, { }); - lib.config(this, this.local.locals); + lib.config(this, this.locals.cache); }, - local: function (key) { + locals: function (key) { return this.get('cache.data.verb.' + key); }, diff --git a/lib/local.js b/lib/local.js deleted file mode 100644 index 2a7ea55d..00000000 --- a/lib/local.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var get = require('get-value'); - -function Local(locals) { - this.locals = locals || {}; -} - -Local.prototype.get = function(key) { - return get(this.locals, key); -}; - -Local.prototype.set = function(key, value) { - set(this.locals, key, value); - return this; -}; - -/** - * Expose Local - */ - -module.exports = Local; diff --git a/lib/locals.js b/lib/locals.js new file mode 100644 index 00000000..73a25b92 --- /dev/null +++ b/lib/locals.js @@ -0,0 +1,22 @@ +'use strict'; + +var get = require('get-value'); + +function Locals(cache) { + this.cache = cache || {}; +} + +Locals.prototype.get = function(key) { + return get(this.cache, key); +}; + +Locals.prototype.set = function(key, value) { + set(this.cache, key, value); + return this; +}; + +/** + * Expose Locals + */ + +module.exports = Locals; diff --git a/lib/utils.js b/lib/utils.js index 93af4293..d194558c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -11,8 +11,6 @@ var lazy = require('lazy-cache')(require); // type utils lazy('mixin-deep', 'merge'); -lazy('global-modules', 'gm'); -lazy('expand-tilde', 'tilde'); // file/view utils lazy('stream-combiner', 'combine'); diff --git a/package.json b/package.json index 3d1c1f34..89e863cd 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,8 @@ "data-store": "^0.9.0", "dest": "^0.2.1", "engine-handlebars": "^0.8.0", - "expand-tilde": "^1.2.0", "export-files": "^2.1.0", - "global-modules": "^0.2.0", + "helper-related": "^0.10.0", "lazy-cache": "^0.2.3", "map-config": "^0.1.0", "markdown-utils": "^0.7.1", @@ -149,4 +148,4 @@ "config-cache" ] } -} +} \ No newline at end of file From f0fe6945d8c5b528a09f37cde51a80e77aefafbb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Sep 2015 07:03:42 -0400 Subject: [PATCH 003/282] remove old fixtures --- test/fixtures/assets/a.hbs | 8 -------- test/fixtures/assets/b.hbs | 8 -------- test/fixtures/assets/c.hbs | 8 -------- test/fixtures/assets/d.hbs | 7 ------- test/fixtures/copy/example.txt | 1 - test/fixtures/data/a.json | 3 --- test/fixtures/data/alert.json | 7 ------- test/fixtures/data/b.json | 3 --- test/fixtures/data/c.json | 3 --- test/fixtures/data/data.json | 3 --- test/fixtures/data/test.json | 4 ---- test/fixtures/dest-path/a.hbs | 8 -------- test/fixtures/dest-path/b.hbs | 8 -------- test/fixtures/dest-path/c.hbs | 8 -------- test/fixtures/dest-path/d.hbs | 7 ------- test/fixtures/drafts/a.hbs | 6 ------ test/fixtures/drafts/b.hbs | 6 ------ test/fixtures/drafts/c.hbs | 6 ------ test/fixtures/drafts/d.hbs | 5 ----- test/fixtures/front-matter/autodetect-no-lang.md | 5 ----- test/fixtures/front-matter/autodetect-yaml.md | 5 ----- test/fixtures/front-matter/lang-yaml.md | 5 ----- test/fixtures/generic/run.dmc | 1 - test/fixtures/generic/test.dmc | 1 - test/fixtures/helpers/a.js | 3 --- test/fixtures/helpers/b.js | 3 --- test/fixtures/helpers/c.js | 3 --- test/fixtures/helpers/obj.js | 9 --------- test/fixtures/includes/footer.hbs | 4 ---- test/fixtures/includes/header.hbs | 4 ---- test/fixtures/includes/info.hbs | 1 - test/fixtures/includes/post.hbs | 11 ----------- test/fixtures/layouts/base.hbs | 10 ---------- test/fixtures/layouts/default.hbs | 6 ------ test/fixtures/layouts/post.hbs | 13 ------------- test/fixtures/pages/a.hbs | 1 - test/fixtures/pages/b.hbs | 1 - test/fixtures/pages/c.hbs | 1 - test/fixtures/parsers/a.a | 1 - test/fixtures/parsers/a.js | 3 --- test/fixtures/parsers/b.js | 3 --- test/fixtures/parsers/c.js | 3 --- test/fixtures/parsers/x.x | 1 - test/fixtures/posts/a.txt | 4 ---- test/fixtures/posts/b.txt | 4 ---- test/fixtures/posts/c.txt | 4 ---- test/fixtures/routes/example.hbs | 1 - test/fixtures/routes/example.txt | 1 - test/fixtures/scaffolds/test.md | 1 - test/fixtures/scaffolds/test.txt | 1 - test/fixtures/templates/a.tmpl | 1 - test/fixtures/templates/b.tmpl | 1 - test/fixtures/templates/c.tmpl | 1 - test/fixtures/test.coffee | 1 - test/fixtures/test/run.jade | 1 - test/fixtures/vinyl/bom-utf16be.txt | Bin 244 -> 0 bytes test/fixtures/vinyl/bom-utf16le.txt | Bin 244 -> 0 bytes test/fixtures/vinyl/bom-utf8.txt | 1 - test/fixtures/vinyl/test-symlink | 1 - test/fixtures/vinyl/test-symlink-dir | 1 - test/fixtures/vinyl/test.coffee | 1 - test/fixtures/vinyl/wow/suchempty | 1 - test/fixtures/watch/test.txt | 1 - test/fixtures/yfm/complex.hbs | 12 ------------ test/fixtures/yfm/yfm.hbs | 5 ----- 65 files changed, 251 deletions(-) delete mode 100644 test/fixtures/assets/a.hbs delete mode 100644 test/fixtures/assets/b.hbs delete mode 100644 test/fixtures/assets/c.hbs delete mode 100644 test/fixtures/assets/d.hbs delete mode 100644 test/fixtures/copy/example.txt delete mode 100644 test/fixtures/data/a.json delete mode 100644 test/fixtures/data/alert.json delete mode 100644 test/fixtures/data/b.json delete mode 100644 test/fixtures/data/c.json delete mode 100644 test/fixtures/data/data.json delete mode 100644 test/fixtures/data/test.json delete mode 100644 test/fixtures/dest-path/a.hbs delete mode 100644 test/fixtures/dest-path/b.hbs delete mode 100644 test/fixtures/dest-path/c.hbs delete mode 100644 test/fixtures/dest-path/d.hbs delete mode 100644 test/fixtures/drafts/a.hbs delete mode 100644 test/fixtures/drafts/b.hbs delete mode 100644 test/fixtures/drafts/c.hbs delete mode 100644 test/fixtures/drafts/d.hbs delete mode 100644 test/fixtures/front-matter/autodetect-no-lang.md delete mode 100644 test/fixtures/front-matter/autodetect-yaml.md delete mode 100644 test/fixtures/front-matter/lang-yaml.md delete mode 100644 test/fixtures/generic/run.dmc delete mode 100644 test/fixtures/generic/test.dmc delete mode 100644 test/fixtures/helpers/a.js delete mode 100644 test/fixtures/helpers/b.js delete mode 100644 test/fixtures/helpers/c.js delete mode 100644 test/fixtures/helpers/obj.js delete mode 100644 test/fixtures/includes/footer.hbs delete mode 100644 test/fixtures/includes/header.hbs delete mode 100644 test/fixtures/includes/info.hbs delete mode 100644 test/fixtures/includes/post.hbs delete mode 100644 test/fixtures/layouts/base.hbs delete mode 100644 test/fixtures/layouts/default.hbs delete mode 100644 test/fixtures/layouts/post.hbs delete mode 100644 test/fixtures/pages/a.hbs delete mode 100644 test/fixtures/pages/b.hbs delete mode 100644 test/fixtures/pages/c.hbs delete mode 100644 test/fixtures/parsers/a.a delete mode 100644 test/fixtures/parsers/a.js delete mode 100644 test/fixtures/parsers/b.js delete mode 100644 test/fixtures/parsers/c.js delete mode 100644 test/fixtures/parsers/x.x delete mode 100644 test/fixtures/posts/a.txt delete mode 100644 test/fixtures/posts/b.txt delete mode 100644 test/fixtures/posts/c.txt delete mode 100644 test/fixtures/routes/example.hbs delete mode 100644 test/fixtures/routes/example.txt delete mode 100644 test/fixtures/scaffolds/test.md delete mode 100644 test/fixtures/scaffolds/test.txt delete mode 100644 test/fixtures/templates/a.tmpl delete mode 100644 test/fixtures/templates/b.tmpl delete mode 100644 test/fixtures/templates/c.tmpl delete mode 100644 test/fixtures/test.coffee delete mode 100644 test/fixtures/test/run.jade delete mode 100644 test/fixtures/vinyl/bom-utf16be.txt delete mode 100644 test/fixtures/vinyl/bom-utf16le.txt delete mode 100644 test/fixtures/vinyl/bom-utf8.txt delete mode 120000 test/fixtures/vinyl/test-symlink delete mode 120000 test/fixtures/vinyl/test-symlink-dir delete mode 100644 test/fixtures/vinyl/test.coffee delete mode 100644 test/fixtures/vinyl/wow/suchempty delete mode 100644 test/fixtures/watch/test.txt delete mode 100644 test/fixtures/yfm/complex.hbs delete mode 100644 test/fixtures/yfm/yfm.hbs diff --git a/test/fixtures/assets/a.hbs b/test/fixtures/assets/a.hbs deleted file mode 100644 index d67fcdf4..00000000 --- a/test/fixtures/assets/a.hbs +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: A -draft: true ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/assets/b.hbs b/test/fixtures/assets/b.hbs deleted file mode 100644 index 89bbba52..00000000 --- a/test/fixtures/assets/b.hbs +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: B -draft: true ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/assets/c.hbs b/test/fixtures/assets/c.hbs deleted file mode 100644 index 6ba18f52..00000000 --- a/test/fixtures/assets/c.hbs +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: C -draft: false ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/assets/d.hbs b/test/fixtures/assets/d.hbs deleted file mode 100644 index a67a85c6..00000000 --- a/test/fixtures/assets/d.hbs +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: D ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/copy/example.txt b/test/fixtures/copy/example.txt deleted file mode 100644 index a8a94062..00000000 --- a/test/fixtures/copy/example.txt +++ /dev/null @@ -1 +0,0 @@ -this is a test \ No newline at end of file diff --git a/test/fixtures/data/a.json b/test/fixtures/data/a.json deleted file mode 100644 index 8470de46..00000000 --- a/test/fixtures/data/a.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "one": {"a": "aaa"} -} \ No newline at end of file diff --git a/test/fixtures/data/alert.json b/test/fixtures/data/alert.json deleted file mode 100644 index 31767e67..00000000 --- a/test/fixtures/data/alert.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "success": { - "test": true, - "strong": "Heads up! This is a warning!", - "text": "You forgot a field!" - } -} \ No newline at end of file diff --git a/test/fixtures/data/b.json b/test/fixtures/data/b.json deleted file mode 100644 index 5f2fde78..00000000 --- a/test/fixtures/data/b.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "two": {"b": "bbb"} -} \ No newline at end of file diff --git a/test/fixtures/data/c.json b/test/fixtures/data/c.json deleted file mode 100644 index 7c274f3e..00000000 --- a/test/fixtures/data/c.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "three": {"c": "ccc"} -} \ No newline at end of file diff --git a/test/fixtures/data/data.json b/test/fixtures/data/data.json deleted file mode 100644 index 7cd0452e..00000000 --- a/test/fixtures/data/data.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "root": "Whoa, I should be at the root!" -} \ No newline at end of file diff --git a/test/fixtures/data/test.json b/test/fixtures/data/test.json deleted file mode 100644 index c8e407ba..00000000 --- a/test/fixtures/data/test.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "alpha": "one", - "beta": "two" -} \ No newline at end of file diff --git a/test/fixtures/dest-path/a.hbs b/test/fixtures/dest-path/a.hbs deleted file mode 100644 index d67fcdf4..00000000 --- a/test/fixtures/dest-path/a.hbs +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: A -draft: true ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/dest-path/b.hbs b/test/fixtures/dest-path/b.hbs deleted file mode 100644 index 89bbba52..00000000 --- a/test/fixtures/dest-path/b.hbs +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: B -draft: true ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/dest-path/c.hbs b/test/fixtures/dest-path/c.hbs deleted file mode 100644 index 6ba18f52..00000000 --- a/test/fixtures/dest-path/c.hbs +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: C -draft: false ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/dest-path/d.hbs b/test/fixtures/dest-path/d.hbs deleted file mode 100644 index a67a85c6..00000000 --- a/test/fixtures/dest-path/d.hbs +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: D ---- - -{{title}} - -{{assets}} \ No newline at end of file diff --git a/test/fixtures/drafts/a.hbs b/test/fixtures/drafts/a.hbs deleted file mode 100644 index 4f65e369..00000000 --- a/test/fixtures/drafts/a.hbs +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: A -draft: true ---- - -{{title}} \ No newline at end of file diff --git a/test/fixtures/drafts/b.hbs b/test/fixtures/drafts/b.hbs deleted file mode 100644 index 9af946b5..00000000 --- a/test/fixtures/drafts/b.hbs +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: B -draft: true ---- - -{{title}} \ No newline at end of file diff --git a/test/fixtures/drafts/c.hbs b/test/fixtures/drafts/c.hbs deleted file mode 100644 index 6366989e..00000000 --- a/test/fixtures/drafts/c.hbs +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: C -draft: false ---- - -{{title}} \ No newline at end of file diff --git a/test/fixtures/drafts/d.hbs b/test/fixtures/drafts/d.hbs deleted file mode 100644 index 8fb914b6..00000000 --- a/test/fixtures/drafts/d.hbs +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: D ---- - -{{title}} \ No newline at end of file diff --git a/test/fixtures/front-matter/autodetect-no-lang.md b/test/fixtures/front-matter/autodetect-no-lang.md deleted file mode 100644 index 99d72210..00000000 --- a/test/fixtures/front-matter/autodetect-no-lang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: autodetect-no-lang -user: jonschlinkert ---- -Content \ No newline at end of file diff --git a/test/fixtures/front-matter/autodetect-yaml.md b/test/fixtures/front-matter/autodetect-yaml.md deleted file mode 100644 index 12eb7682..00000000 --- a/test/fixtures/front-matter/autodetect-yaml.md +++ /dev/null @@ -1,5 +0,0 @@ ----yaml -title: autodetect-yaml -user: jonschlinkert ---- -Content \ No newline at end of file diff --git a/test/fixtures/front-matter/lang-yaml.md b/test/fixtures/front-matter/lang-yaml.md deleted file mode 100644 index 414d6390..00000000 --- a/test/fixtures/front-matter/lang-yaml.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: YAML ---- - -# This page has YAML front matter! diff --git a/test/fixtures/generic/run.dmc b/test/fixtures/generic/run.dmc deleted file mode 100644 index f5925c06..00000000 --- a/test/fixtures/generic/run.dmc +++ /dev/null @@ -1 +0,0 @@ -# run.dmc \ No newline at end of file diff --git a/test/fixtures/generic/test.dmc b/test/fixtures/generic/test.dmc deleted file mode 100644 index 2286d5d4..00000000 --- a/test/fixtures/generic/test.dmc +++ /dev/null @@ -1 +0,0 @@ -# test.dmc \ No newline at end of file diff --git a/test/fixtures/helpers/a.js b/test/fixtures/helpers/a.js deleted file mode 100644 index 44919f59..00000000 --- a/test/fixtures/helpers/a.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function aaa() { - -}; \ No newline at end of file diff --git a/test/fixtures/helpers/b.js b/test/fixtures/helpers/b.js deleted file mode 100644 index 73142e0c..00000000 --- a/test/fixtures/helpers/b.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function bbb() { - -}; \ No newline at end of file diff --git a/test/fixtures/helpers/c.js b/test/fixtures/helpers/c.js deleted file mode 100644 index ae7c3579..00000000 --- a/test/fixtures/helpers/c.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function ccc() { - -}; \ No newline at end of file diff --git a/test/fixtures/helpers/obj.js b/test/fixtures/helpers/obj.js deleted file mode 100644 index a139721f..00000000 --- a/test/fixtures/helpers/obj.js +++ /dev/null @@ -1,9 +0,0 @@ -exports.one = function one() { - -}; -exports.two = function two() { - -}; -exports.three = function three() { - -}; \ No newline at end of file diff --git a/test/fixtures/includes/footer.hbs b/test/fixtures/includes/footer.hbs deleted file mode 100644 index decae978..00000000 --- a/test/fixtures/includes/footer.hbs +++ /dev/null @@ -1,4 +0,0 @@ ---- -message: Message from the footer ---- -
Footer {{message}}
\ No newline at end of file diff --git a/test/fixtures/includes/header.hbs b/test/fixtures/includes/header.hbs deleted file mode 100644 index 7845f006..00000000 --- a/test/fixtures/includes/header.hbs +++ /dev/null @@ -1,4 +0,0 @@ ---- -message: Message from the header ---- -
Header {{message}}
\ No newline at end of file diff --git a/test/fixtures/includes/info.hbs b/test/fixtures/includes/info.hbs deleted file mode 100644 index 26802499..00000000 --- a/test/fixtures/includes/info.hbs +++ /dev/null @@ -1 +0,0 @@ -
(From info.hbs) - Title: {{site.title}} - {{title}}
\ No newline at end of file diff --git a/test/fixtures/includes/post.hbs b/test/fixtures/includes/post.hbs deleted file mode 100644 index 818df44a..00000000 --- a/test/fixtures/includes/post.hbs +++ /dev/null @@ -1,11 +0,0 @@ -
{{author}}
-
{{timestamp}}
-{{#if ../show-full}} -
- {{content}} -
-{{else}} -
- {{summary}} -
-{{/if}} diff --git a/test/fixtures/layouts/base.hbs b/test/fixtures/layouts/base.hbs deleted file mode 100644 index eca79131..00000000 --- a/test/fixtures/layouts/base.hbs +++ /dev/null @@ -1,10 +0,0 @@ - - - - - {{site.title}} - - -{% body %} - - \ No newline at end of file diff --git a/test/fixtures/layouts/default.hbs b/test/fixtures/layouts/default.hbs deleted file mode 100644 index a806196c..00000000 --- a/test/fixtures/layouts/default.hbs +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: base ---- -{{> header }} -{% body %} -{{> footer }} \ No newline at end of file diff --git a/test/fixtures/layouts/post.hbs b/test/fixtures/layouts/post.hbs deleted file mode 100644 index 7a02efbc..00000000 --- a/test/fixtures/layouts/post.hbs +++ /dev/null @@ -1,13 +0,0 @@ ---- -layout: default ---- -
- {% body %} -
- \ No newline at end of file diff --git a/test/fixtures/pages/a.hbs b/test/fixtures/pages/a.hbs deleted file mode 100644 index 0ec5d8d8..00000000 --- a/test/fixtures/pages/a.hbs +++ /dev/null @@ -1 +0,0 @@ -

\ No newline at end of file diff --git a/test/fixtures/pages/b.hbs b/test/fixtures/pages/b.hbs deleted file mode 100644 index 0ec5d8d8..00000000 --- a/test/fixtures/pages/b.hbs +++ /dev/null @@ -1 +0,0 @@ -

\ No newline at end of file diff --git a/test/fixtures/pages/c.hbs b/test/fixtures/pages/c.hbs deleted file mode 100644 index 0ec5d8d8..00000000 --- a/test/fixtures/pages/c.hbs +++ /dev/null @@ -1 +0,0 @@ -

\ No newline at end of file diff --git a/test/fixtures/parsers/a.a b/test/fixtures/parsers/a.a deleted file mode 100644 index f2ba8f84..00000000 --- a/test/fixtures/parsers/a.a +++ /dev/null @@ -1 +0,0 @@ -abc \ No newline at end of file diff --git a/test/fixtures/parsers/a.js b/test/fixtures/parsers/a.js deleted file mode 100644 index ab2588a1..00000000 --- a/test/fixtures/parsers/a.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function a(file, enc, opts) { - return file; -}; diff --git a/test/fixtures/parsers/b.js b/test/fixtures/parsers/b.js deleted file mode 100644 index 96d66979..00000000 --- a/test/fixtures/parsers/b.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function b(file, enc, opts) { - return file; -}; diff --git a/test/fixtures/parsers/c.js b/test/fixtures/parsers/c.js deleted file mode 100644 index a47440ae..00000000 --- a/test/fixtures/parsers/c.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function c(file, enc, opts) { - return file; -}; diff --git a/test/fixtures/parsers/x.x b/test/fixtures/parsers/x.x deleted file mode 100644 index d66d9d75..00000000 --- a/test/fixtures/parsers/x.x +++ /dev/null @@ -1 +0,0 @@ -xyz \ No newline at end of file diff --git a/test/fixtures/posts/a.txt b/test/fixtures/posts/a.txt deleted file mode 100644 index bca29ee6..00000000 --- a/test/fixtures/posts/a.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: AAA ---- -This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/b.txt b/test/fixtures/posts/b.txt deleted file mode 100644 index 1e128c70..00000000 --- a/test/fixtures/posts/b.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: BBB ---- -This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/c.txt b/test/fixtures/posts/c.txt deleted file mode 100644 index 32f91870..00000000 --- a/test/fixtures/posts/c.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: CCC ---- -This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/routes/example.hbs b/test/fixtures/routes/example.hbs deleted file mode 100644 index a8a94062..00000000 --- a/test/fixtures/routes/example.hbs +++ /dev/null @@ -1 +0,0 @@ -this is a test \ No newline at end of file diff --git a/test/fixtures/routes/example.txt b/test/fixtures/routes/example.txt deleted file mode 100644 index a8a94062..00000000 --- a/test/fixtures/routes/example.txt +++ /dev/null @@ -1 +0,0 @@ -this is a test \ No newline at end of file diff --git a/test/fixtures/scaffolds/test.md b/test/fixtures/scaffolds/test.md deleted file mode 100644 index 104a7cfb..00000000 --- a/test/fixtures/scaffolds/test.md +++ /dev/null @@ -1 +0,0 @@ -{{name}} \ No newline at end of file diff --git a/test/fixtures/scaffolds/test.txt b/test/fixtures/scaffolds/test.txt deleted file mode 100644 index 5b88ae15..00000000 --- a/test/fixtures/scaffolds/test.txt +++ /dev/null @@ -1 +0,0 @@ -test file! \ No newline at end of file diff --git a/test/fixtures/templates/a.tmpl b/test/fixtures/templates/a.tmpl deleted file mode 100644 index 36f1f1b5..00000000 --- a/test/fixtures/templates/a.tmpl +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/b.tmpl b/test/fixtures/templates/b.tmpl deleted file mode 100644 index 36f1f1b5..00000000 --- a/test/fixtures/templates/b.tmpl +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/c.tmpl b/test/fixtures/templates/c.tmpl deleted file mode 100644 index 36f1f1b5..00000000 --- a/test/fixtures/templates/c.tmpl +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/test/fixtures/test.coffee b/test/fixtures/test.coffee deleted file mode 100644 index a8a94062..00000000 --- a/test/fixtures/test.coffee +++ /dev/null @@ -1 +0,0 @@ -this is a test \ No newline at end of file diff --git a/test/fixtures/test/run.jade b/test/fixtures/test/run.jade deleted file mode 100644 index f6418020..00000000 --- a/test/fixtures/test/run.jade +++ /dev/null @@ -1 +0,0 @@ -test template \ No newline at end of file diff --git a/test/fixtures/vinyl/bom-utf16be.txt b/test/fixtures/vinyl/bom-utf16be.txt deleted file mode 100644 index b9dce78a5d31af4803acd1a0f0dfc14f064a5de1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 diff --git a/test/fixtures/vinyl/bom-utf16le.txt b/test/fixtures/vinyl/bom-utf16le.txt deleted file mode 100644 index 07cc600c98675d221bb56d10af38e650538734c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15lThis is an alert diff --git a/test/fixtures/yfm/yfm.hbs b/test/fixtures/yfm/yfm.hbs deleted file mode 100644 index 88d2411f..00000000 --- a/test/fixtures/yfm/yfm.hbs +++ /dev/null @@ -1,5 +0,0 @@ ---- -foo: bar ---- - -
This is an alert
From 303f5230217f147d0b7bebe8bd2fafd02b8c73af Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Sep 2015 07:04:06 -0400 Subject: [PATCH 004/282] move helpers into `lib/helpers` --- index.js | 77 +++++++++++++------------------------------------- lib/helpers.js | 40 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 lib/helpers.js diff --git a/index.js b/index.js index b41956e4..d9745e8c 100644 --- a/index.js +++ b/index.js @@ -56,8 +56,6 @@ Templates.extend(Verb, { this.store = store('verb', opts.store); this.locals = new Locals(this.cache.data.verb); - // console.log(this.local.locals) - this.initEngines(this); this.initMiddleware(this); this.initViewTypes(this); @@ -73,6 +71,8 @@ Templates.extend(Verb, { this.on('use', function () { utils.reloadViews(app); }); + + lib.config(app, app.locals.cache); }, /** @@ -109,7 +109,7 @@ Templates.extend(Verb, { initViewTypes: function () { // this.use(loader()); - this.defaultHelpers(); + lib.helpers(this); this .use(loader()) @@ -127,19 +127,14 @@ Templates.extend(Verb, { }; }); - this.data('author', { - name: 'Jon Schlinkert' - }); - this.data('twitter', { - username: 'jonschlinkert' - }); - this.data('github', { - username: 'jonschlinkert' - }); - - this.data('runner', { - name: this.get('cache.data.name'), - url: this.get('cache.data.repository.url'), + this.data({ + author: {name: 'Jon Schlinkert'}, + twitter: {username: 'jonschlinkert'}, + github: {username: 'jonschlinkert'}, + runner: { + name: this.get('cache.data.name'), + url: this.get('cache.data.repository'), + } }); this.create('includes', { @@ -159,7 +154,10 @@ Templates.extend(Verb, { this.create('badges', { viewType: ['partial'], renameKey: function (key) { - var cwd = this.options.cwd || ''; + var cwd = this.options.cwd; + if (!cwd || key.indexOf(cwd) === -1) { + return key; + } var len = cwd.length + 1; return key.slice(len); }, @@ -183,49 +181,12 @@ Templates.extend(Verb, { content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' }); - this.create('files'); - }, - - defaultHelpers: function () { - this.helper('date', function () { - return new Date(); - }); - - var mdu = require('markdown-utils'); - this.helperGroup('mdu', mdu); - - this.helper('log', function (msg) { - console.log.apply(console, arguments); - }); - - this.helper('related', function (keys) { - var url = 'https://www.npmjs.com/package/'; - keys = utils.arrayify(keys); - keys.forEach(function (key, i) { - keys[i] = '+ ' + mdu.link(key, url + key); - }); - return '\n' + keys.join('\n'); - }); - - this.asyncHelper('related', require('helper-related')); - - this.asyncHelper('trim', function (str) { - return str.trim(); - }); - - this.helper('license', function () { - - }); - - this.helper('copyright', function () { - + this.include('license', { + content: 'Copyright © 2015 {%= author.name %}\nReleased under the {%= license %} license.' }); - lib.config(this, this.locals.cache); - }, - - locals: function (key) { - return this.get('cache.data.verb.' + key); + this.create('files'); + console.log(this._.helpers) }, // userConfig: function () { diff --git a/lib/helpers.js b/lib/helpers.js new file mode 100644 index 00000000..8a9de6a2 --- /dev/null +++ b/lib/helpers.js @@ -0,0 +1,40 @@ +'use strict'; + +var utils = require('./'); + +module.exports = function (app) { + app.helper('date', function () { + return new Date(); + }); + + var mdu = require('markdown-utils'); + app.helperGroup('mdu', mdu); + + app.helper('log', function (msg) { + console.log.apply(console, arguments); + }); + + // app.helper('npm', function (names) { + // var url = 'https://www.npmjs.com/package/'; + // var res = ''; + // utils.arrayify(names).forEach(function (name) { + // res += '+ ' + mdu.link(name, url + name) + '\n'; + // }); + // return res; + // }); + + app.asyncHelper('related', require('helper-related')); + + app.asyncHelper('trim', function (str) { + return str.trim(); + }); + + // app.helper('license', function (locals) { + // // var ctx = utils.merge({}, this.context, locals); + + // }); + + app.helper('copyright', function () { + + }); +}; From de3d135ff1e76e4002b615dd05ef0e4fae1d06dd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Sep 2015 07:12:19 -0400 Subject: [PATCH 005/282] re-add fixtures --- test/fixtures/copy/example.txt | 1 + test/fixtures/data/a.json | 3 +++ test/fixtures/data/alert.json | 7 +++++++ test/fixtures/data/b.json | 3 +++ test/fixtures/data/c.json | 3 +++ test/fixtures/data/data.json | 3 +++ test/fixtures/data/test.json | 4 ++++ test/fixtures/generic/run.dmc | 1 + test/fixtures/generic/test.dmc | 1 + test/fixtures/helpers/a.js | 3 +++ test/fixtures/helpers/b.js | 3 +++ test/fixtures/helpers/c.js | 3 +++ test/fixtures/helpers/obj.js | 9 +++++++++ test/fixtures/pages/a.hbs | 1 + test/fixtures/pages/b.hbs | 1 + test/fixtures/pages/c.hbs | 1 + test/fixtures/templates/a.tmpl | 1 + test/fixtures/templates/b.tmpl | 1 + test/fixtures/templates/c.tmpl | 1 + test/fixtures/test.coffee | 1 + test/fixtures/vinyl/bom-utf16be.txt | Bin 0 -> 244 bytes test/fixtures/vinyl/bom-utf16le.txt | Bin 0 -> 244 bytes test/fixtures/vinyl/bom-utf8.txt | 1 + test/fixtures/vinyl/test-symlink | 1 + test/fixtures/vinyl/test-symlink-dir | 1 + test/fixtures/vinyl/test.coffee | 1 + test/fixtures/vinyl/wow/suchempty | 1 + test/fixtures/watch/test.txt | 1 + 28 files changed, 57 insertions(+) create mode 100644 test/fixtures/copy/example.txt create mode 100644 test/fixtures/data/a.json create mode 100644 test/fixtures/data/alert.json create mode 100644 test/fixtures/data/b.json create mode 100644 test/fixtures/data/c.json create mode 100644 test/fixtures/data/data.json create mode 100644 test/fixtures/data/test.json create mode 100644 test/fixtures/generic/run.dmc create mode 100644 test/fixtures/generic/test.dmc create mode 100644 test/fixtures/helpers/a.js create mode 100644 test/fixtures/helpers/b.js create mode 100644 test/fixtures/helpers/c.js create mode 100644 test/fixtures/helpers/obj.js create mode 100644 test/fixtures/pages/a.hbs create mode 100644 test/fixtures/pages/b.hbs create mode 100644 test/fixtures/pages/c.hbs create mode 100644 test/fixtures/templates/a.tmpl create mode 100644 test/fixtures/templates/b.tmpl create mode 100644 test/fixtures/templates/c.tmpl create mode 100644 test/fixtures/test.coffee create mode 100644 test/fixtures/vinyl/bom-utf16be.txt create mode 100644 test/fixtures/vinyl/bom-utf16le.txt create mode 100644 test/fixtures/vinyl/bom-utf8.txt create mode 120000 test/fixtures/vinyl/test-symlink create mode 120000 test/fixtures/vinyl/test-symlink-dir create mode 100644 test/fixtures/vinyl/test.coffee create mode 100644 test/fixtures/vinyl/wow/suchempty create mode 100644 test/fixtures/watch/test.txt diff --git a/test/fixtures/copy/example.txt b/test/fixtures/copy/example.txt new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/copy/example.txt @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/data/a.json b/test/fixtures/data/a.json new file mode 100644 index 00000000..8470de46 --- /dev/null +++ b/test/fixtures/data/a.json @@ -0,0 +1,3 @@ +{ + "one": {"a": "aaa"} +} \ No newline at end of file diff --git a/test/fixtures/data/alert.json b/test/fixtures/data/alert.json new file mode 100644 index 00000000..31767e67 --- /dev/null +++ b/test/fixtures/data/alert.json @@ -0,0 +1,7 @@ +{ + "success": { + "test": true, + "strong": "Heads up! This is a warning!", + "text": "You forgot a field!" + } +} \ No newline at end of file diff --git a/test/fixtures/data/b.json b/test/fixtures/data/b.json new file mode 100644 index 00000000..5f2fde78 --- /dev/null +++ b/test/fixtures/data/b.json @@ -0,0 +1,3 @@ +{ + "two": {"b": "bbb"} +} \ No newline at end of file diff --git a/test/fixtures/data/c.json b/test/fixtures/data/c.json new file mode 100644 index 00000000..7c274f3e --- /dev/null +++ b/test/fixtures/data/c.json @@ -0,0 +1,3 @@ +{ + "three": {"c": "ccc"} +} \ No newline at end of file diff --git a/test/fixtures/data/data.json b/test/fixtures/data/data.json new file mode 100644 index 00000000..7cd0452e --- /dev/null +++ b/test/fixtures/data/data.json @@ -0,0 +1,3 @@ +{ + "root": "Whoa, I should be at the root!" +} \ No newline at end of file diff --git a/test/fixtures/data/test.json b/test/fixtures/data/test.json new file mode 100644 index 00000000..c8e407ba --- /dev/null +++ b/test/fixtures/data/test.json @@ -0,0 +1,4 @@ +{ + "alpha": "one", + "beta": "two" +} \ No newline at end of file diff --git a/test/fixtures/generic/run.dmc b/test/fixtures/generic/run.dmc new file mode 100644 index 00000000..f5925c06 --- /dev/null +++ b/test/fixtures/generic/run.dmc @@ -0,0 +1 @@ +# run.dmc \ No newline at end of file diff --git a/test/fixtures/generic/test.dmc b/test/fixtures/generic/test.dmc new file mode 100644 index 00000000..2286d5d4 --- /dev/null +++ b/test/fixtures/generic/test.dmc @@ -0,0 +1 @@ +# test.dmc \ No newline at end of file diff --git a/test/fixtures/helpers/a.js b/test/fixtures/helpers/a.js new file mode 100644 index 00000000..44919f59 --- /dev/null +++ b/test/fixtures/helpers/a.js @@ -0,0 +1,3 @@ +module.exports = function aaa() { + +}; \ No newline at end of file diff --git a/test/fixtures/helpers/b.js b/test/fixtures/helpers/b.js new file mode 100644 index 00000000..73142e0c --- /dev/null +++ b/test/fixtures/helpers/b.js @@ -0,0 +1,3 @@ +module.exports = function bbb() { + +}; \ No newline at end of file diff --git a/test/fixtures/helpers/c.js b/test/fixtures/helpers/c.js new file mode 100644 index 00000000..ae7c3579 --- /dev/null +++ b/test/fixtures/helpers/c.js @@ -0,0 +1,3 @@ +module.exports = function ccc() { + +}; \ No newline at end of file diff --git a/test/fixtures/helpers/obj.js b/test/fixtures/helpers/obj.js new file mode 100644 index 00000000..a139721f --- /dev/null +++ b/test/fixtures/helpers/obj.js @@ -0,0 +1,9 @@ +exports.one = function one() { + +}; +exports.two = function two() { + +}; +exports.three = function three() { + +}; \ No newline at end of file diff --git a/test/fixtures/pages/a.hbs b/test/fixtures/pages/a.hbs new file mode 100644 index 00000000..0ec5d8d8 --- /dev/null +++ b/test/fixtures/pages/a.hbs @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/fixtures/pages/b.hbs b/test/fixtures/pages/b.hbs new file mode 100644 index 00000000..0ec5d8d8 --- /dev/null +++ b/test/fixtures/pages/b.hbs @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/fixtures/pages/c.hbs b/test/fixtures/pages/c.hbs new file mode 100644 index 00000000..0ec5d8d8 --- /dev/null +++ b/test/fixtures/pages/c.hbs @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/test/fixtures/templates/a.tmpl b/test/fixtures/templates/a.tmpl new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/test/fixtures/templates/a.tmpl @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/b.tmpl b/test/fixtures/templates/b.tmpl new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/test/fixtures/templates/b.tmpl @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/c.tmpl b/test/fixtures/templates/c.tmpl new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/test/fixtures/templates/c.tmpl @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/test/fixtures/test.coffee b/test/fixtures/test.coffee new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/test.coffee @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/vinyl/bom-utf16be.txt b/test/fixtures/vinyl/bom-utf16be.txt new file mode 100644 index 0000000000000000000000000000000000000000..b9dce78a5d31af4803acd1a0f0dfc14f064a5de1 GIT binary patch literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 literal 0 HcmV?d00001 diff --git a/test/fixtures/vinyl/bom-utf16le.txt b/test/fixtures/vinyl/bom-utf16le.txt new file mode 100644 index 0000000000000000000000000000000000000000..07cc600c98675d221bb56d10af38e650538734c9 GIT binary patch literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15l Date: Tue, 22 Sep 2015 07:12:38 -0400 Subject: [PATCH 006/282] remove old code from test --- test/app.dest.js | 59 ++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/test/app.dest.js b/test/app.dest.js index 760a0eb2..50501b6d 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -11,45 +11,40 @@ var outpath = path.join(__dirname, './out-fixtures'); describe('app output stream', function() { describe('dest()', function() { beforeEach(function (done) { + app = new App(); rimraf(outpath, done); }); afterEach(function (done) { rimraf(outpath, done); }); - describe('minimal config - enabled', function () { - beforeEach(function () { - app = new App(); - }); - - it('should return a stream', function (done) { - var stream = app.dest(path.join(__dirname, 'fixtures/')); - should.exist(stream); - should.exist(stream.on); - done(); - }); + it('should return a stream', function (done) { + var stream = app.dest(path.join(__dirname, 'fixtures/')); + should.exist(stream); + should.exist(stream.on); + done(); + }); - it('should return an output stream that writes files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); - var outstream = app.dest(outpath); - instream.pipe(outstream); + it('should return an output stream that writes files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); + var outstream = app.dest(outpath); + instream.pipe(outstream); - outstream.on('error', done); - outstream.on('data', function (file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); - String(file.contents).should.equal('this is a test'); - }); - outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { - should.not.exist(err); - should.exist(contents); - String(contents).should.equal('this is a test'); - done(); - }); + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + String(file.contents).should.equal('this is a test'); + }); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.not.exist(err); + should.exist(contents); + String(contents).should.equal('this is a test'); + done(); }); }); @@ -116,7 +111,7 @@ describe('app output stream', function() { }); - describe('minimal config - disabled', function () { + describe('exts', function () { beforeEach(function () { app = new App(); app.set('ext', '.txt'); From 7b04da482d234a4de635a3a926f02e51febf4814 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Sep 2015 07:24:15 -0400 Subject: [PATCH 007/282] move `views`, cleanup init code --- index.js | 151 ++++++++------------------------------------------- lib/views.js | 92 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 128 deletions(-) create mode 100644 lib/views.js diff --git a/index.js b/index.js index d9745e8c..acb8a917 100644 --- a/index.js +++ b/index.js @@ -6,14 +6,11 @@ var path = require('path'); var store = require('data-store'); -var Locals = require('./lib/locals'); -var loader = require('assemble-loader'); -var includes = require('readme-includes'); -var badges = require('readme-badges'); var Templates = require('templates'); var Composer = require('composer'); var proto = Composer.prototype; var lib = require('./lib'); +var Locals = lib.locals; var utils = lib.utils; /** @@ -32,11 +29,10 @@ function Verb(options) { if (!(this instanceof Verb)) { return new Verb(options); } - this.isVerb = true; Templates.apply(this, arguments); Composer.apply(this, arguments); this.options = options || {}; - this.initVerb(this, this.options); + this.initVerb(this.options); } Templates.inherit(Verb, Composer); @@ -52,18 +48,34 @@ Templates.extend(Verb, { * Initialize Verb defaults */ - initVerb: function(app, opts) { + initVerb: function(opts) { this.store = store('verb', opts.store); this.locals = new Locals(this.cache.data.verb); this.initEngines(this); this.initMiddleware(this); - this.initViewTypes(this); + this.initListeners(this); - this.option('view', function (view) { - if (view.src) view.path = view.src; - }); + lib.helpers(this); + lib.views(this); + }, + + /** + * Initialize the user's config. + */ + + initUserConfig: function () { + lib.config(this, this.locals.cache); + }, + + /** + * Initialize event listeners. The default listeners + * are setup to listen for events that indicate if + * something needs to be re-initialized based on user + * options. + */ + initListeners: function (app) { this.on('option', function (key) { utils.reloadViews(app, key); }); @@ -71,8 +83,6 @@ Templates.extend(Verb, { this.on('use', function () { utils.reloadViews(app); }); - - lib.config(app, app.locals.cache); }, /** @@ -99,121 +109,6 @@ Templates.extend(Verb, { }); }, - /** - * Default `viewTypes` - * | includes - * | layouts - * | pages - * | files - */ - - initViewTypes: function () { - // this.use(loader()); - lib.helpers(this); - - this - .use(loader()) - .use(function (app) { - return function (collection) { - if (!collection.isCollection) return collection; - var fn = this.getView; - - this.getView = function (name) { - return fn.apply(this, arguments) || this.loadView(name, { - ext: '.md' - }); - }; - return this; - }; - }); - - this.data({ - author: {name: 'Jon Schlinkert'}, - twitter: {username: 'jonschlinkert'}, - github: {username: 'jonschlinkert'}, - runner: { - name: this.get('cache.data.name'), - url: this.get('cache.data.repository'), - } - }); - - this.create('includes', { - viewType: ['partial'], - renameKey: function (key) { - var cwd = this.options.cwd; - if (!cwd || key.indexOf(cwd) === -1) { - return key; - } - var len = cwd.length + 1; - return key.slice(len); - }, - cwd: includes, - engine: 'md', - }); - - this.create('badges', { - viewType: ['partial'], - renameKey: function (key) { - var cwd = this.options.cwd; - if (!cwd || key.indexOf(cwd) === -1) { - return key; - } - var len = cwd.length + 1; - return key.slice(len); - }, - cwd: badges, - engine: 'md' - }); - - this.create('docs', { - viewType: ['partial'], - renameKey: utils.basename, - engine: 'md' - }); - - this.create('layouts', { - viewType: ['layout'], - renameKey: utils.basename, - engine: 'md' - }); - - this.include('npm', { - content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); - - this.include('license', { - content: 'Copyright © 2015 {%= author.name %}\nReleased under the {%= license %} license.' - }); - - this.create('files'); - console.log(this._.helpers) - }, - - // userConfig: function () { - // // this.helpers(this.store.get('helpers')); - // // this.helpers(this.local.get('helpers')); - - // // this.asyncHelpers(this.store.get('asyncHelpers')); - // // this.asyncHelpers(this.local.get('asyncHelpers')); - - // // console.log(this._.helpers) - - // // var len = paths.length; - // // while(len--) { - // // var fp = tryRead(path.join(paths[len], name)); - // // if (fp) return fp; - // // } - // }, - - config: function (name, fn) { - var self = this; - fn = fn || utils.identity; - var config = this.get('cache.data.verb.' + name); - return utils.resolveConfig(config, function (key, val) { - fn(key, val(self.options)); - }); - }, - /** * Glob patterns or filepaths to source files. * diff --git a/lib/views.js b/lib/views.js new file mode 100644 index 00000000..56ad931e --- /dev/null +++ b/lib/views.js @@ -0,0 +1,92 @@ +'use strict'; + +var includes = require('readme-includes'); +var badges = require('readme-badges'); +var loader = require('assemble-loader'); +var utils = require('./'); + +/** + * Default `viewTypes` + * | includes + * | layouts + * | pages + * | files + */ + +module.exports = function (app) { + app + .use(loader()) + .use(function (app) { + return function (collection) { + if (!collection.isCollection) return collection; + var fn = this.getView; + + this.getView = function (name) { + return fn.apply(this, arguments) || this.loadView(name, { + ext: '.md' + }); + }; + return this; + }; + }); + + app.data({ + author: {name: 'Jon Schlinkert'}, + twitter: {username: 'jonschlinkert'}, + github: {username: 'jonschlinkert'}, + runner: { + name: app.get('cache.data.name'), + url: app.get('cache.data.repository'), + } + }); + + app.create('includes', { + viewType: ['partial'], + renameKey: function (key) { + var cwd = app.options.cwd; + if (!cwd || key.indexOf(cwd) === -1) { + return key; + } + var len = cwd.length + 1; + return key.slice(len); + }, + cwd: includes, + engine: 'md', + }); + + app.create('badges', { + viewType: ['partial'], + renameKey: function (key) { + var cwd = app.badges.options.cwd; + if (!cwd || key.indexOf(cwd) === -1) { + return key; + } + var len = cwd.length + 1; + return key.slice(len); + }, + cwd: badges, + engine: 'md' + }); + + app.create('docs', { + viewType: ['partial'], + renameKey: utils.basename, + engine: 'md' + }); + + app.create('layouts', { + viewType: ['layout'], + renameKey: utils.basename, + engine: 'md' + }); + + app.include('npm', { + content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); + + app.include('license', { + content: 'Copyright © 2015 {%= author.name %}\nReleased under the {%= license %} license.' + }); + + app.create('files'); +}; From dd2c1a95c2543d02b6fc7e97de864e341162167c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 23 Sep 2015 12:00:45 -0400 Subject: [PATCH 008/282] update helpers, config, utils --- lib/config.js | 3 ++- lib/helpers.js | 35 +++++++++---------------- lib/utils.js | 47 +++++++++++++++++++++++---------- lib/views.js | 64 ++++++++++++++++++++++----------------------- test/view.render.js | 4 +-- 5 files changed, 80 insertions(+), 73 deletions(-) diff --git a/lib/config.js b/lib/config.js index aafb2033..f72cbd89 100644 --- a/lib/config.js +++ b/lib/config.js @@ -14,6 +14,8 @@ module.exports = function (app, config) { set: 'set', addViews: 'addViews' }, instance); + + console.log('loading templates from config: "' + key + '"'); return mapper.process(config); }; }); @@ -28,7 +30,6 @@ module.exports = function (app, config) { }, app); configMap.process(config); - // console.log(app._.helpers) // configMap.process({ // "ignore": [ diff --git a/lib/helpers.js b/lib/helpers.js index 8a9de6a2..cd354739 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,40 +1,29 @@ 'use strict'; -var utils = require('./'); +var utils = require('./utils'); module.exports = function (app) { + app.asyncHelper('ask', function () { + var fn = app.ask(); + return fn.apply(app, arguments); + }); + + app.asyncHelper('related', require('helper-related')(app.options)); + app.asyncHelper('reflinks', require('helper-reflinks')); + app.helper('date', function () { return new Date(); }); - var mdu = require('markdown-utils'); - app.helperGroup('mdu', mdu); - app.helper('log', function (msg) { console.log.apply(console, arguments); }); - // app.helper('npm', function (names) { - // var url = 'https://www.npmjs.com/package/'; - // var res = ''; - // utils.arrayify(names).forEach(function (name) { - // res += '+ ' + mdu.link(name, url + name) + '\n'; - // }); - // return res; - // }); - - app.asyncHelper('related', require('helper-related')); - - app.asyncHelper('trim', function (str) { + app.helper('trim', function (str) { return str.trim(); }); - // app.helper('license', function (locals) { - // // var ctx = utils.merge({}, this.context, locals); - - // }); - - app.helper('copyright', function () { - + app.helper('apidocs', function () { + // app.ask(locals) }); }; diff --git a/lib/utils.js b/lib/utils.js index d194558c..07894c73 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -22,6 +22,8 @@ lazy('to-vinyl'); // engine/template utiles lazy('parser-front-matter', 'matter'); lazy('engine-handlebars', 'engine'); +lazy('question-cache', 'questions'); +lazy('ask-once', 'ask'); // task utils lazy('composer-runtimes'); @@ -66,6 +68,15 @@ utils.identity = function(val) { return val; }; +utils.rename = function(key) { + var cwd = this.options.cwd; + if (!cwd || key.indexOf(cwd) === -1) { + return key; + } + var len = cwd.length + 1; + return key.slice(len); +}; + utils.resolveConfig = function (config, fn) { fn = fn || utils.identity; for (var key in config) { @@ -76,20 +87,6 @@ utils.resolveConfig = function (config, fn) { } }; -utils.layoutFn = function(app, view, cb) { - if (app.option('layoutFn')) { - app.options.layoutFn(view); - return cb(null, view); - } - if (!view.layout) { - view.layout = view.locals.layout; - } - if (!view.layout) { - view.layout = view.data.layout; - } - cb(null, view); -}; - utils.reloadViews = function reloadViews(app, key) { for (var name in app.views) { if (app.views.hasOwnProperty(name)) { @@ -103,6 +100,28 @@ utils.reloadViews = function reloadViews(app, key) { } }; +/** + * Override the native `getView` method from `templates` + * with a custom method that calls `loadView` if the + * view isn't already cached. + */ + +utils.getView = function (options) { + return function (app) { + return function (collection) { + if (!collection.isCollection) return collection; + var fn = this.getView; + + this.getView = function (name) { + return fn.apply(this, arguments) || this.loadView(name, { + ext: '.md' + }); + }; + return this; + }; + }; +}; + /** * Expose utils */ diff --git a/lib/views.js b/lib/views.js index 56ad931e..c01cd88e 100644 --- a/lib/views.js +++ b/lib/views.js @@ -3,7 +3,7 @@ var includes = require('readme-includes'); var badges = require('readme-badges'); var loader = require('assemble-loader'); -var utils = require('./'); +var utils = require('./utils'); /** * Default `viewTypes` @@ -14,21 +14,8 @@ var utils = require('./'); */ module.exports = function (app) { - app - .use(loader()) - .use(function (app) { - return function (collection) { - if (!collection.isCollection) return collection; - var fn = this.getView; - - this.getView = function (name) { - return fn.apply(this, arguments) || this.loadView(name, { - ext: '.md' - }); - }; - return this; - }; - }); + app.use(loader()); + app.use(utils.getView()); app.data({ author: {name: 'Jon Schlinkert'}, @@ -42,28 +29,14 @@ module.exports = function (app) { app.create('includes', { viewType: ['partial'], - renameKey: function (key) { - var cwd = app.options.cwd; - if (!cwd || key.indexOf(cwd) === -1) { - return key; - } - var len = cwd.length + 1; - return key.slice(len); - }, + renameKey: utils.rename, cwd: includes, engine: 'md', }); app.create('badges', { viewType: ['partial'], - renameKey: function (key) { - var cwd = app.badges.options.cwd; - if (!cwd || key.indexOf(cwd) === -1) { - return key; - } - var len = cwd.length + 1; - return key.slice(len); - }, + renameKey: utils.rename, cwd: badges, engine: 'md' }); @@ -84,8 +57,33 @@ module.exports = function (app) { content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' }); + app.include('copyright', { + content: 'Copyright © 2015, {%= author.name %}.' + }); + app.include('license', { - content: 'Copyright © 2015 {%= author.name %}\nReleased under the {%= license %} license.' + content: 'Released under the {%= ask("lslsl") %} license.' + }); + + app.include('author', { + locals: { + author: {name: 'Brian Woodward'}, + username: 'doowb' + }, + content: [ + '**{%= author.name %}**', + '', + '+ [github/{%= username %}](https://github.com/{%= username %})', + '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', + ].join('\n') + }); + + app.badge('travis', { + content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' + }); + + app.badge('fury', { + content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' }); app.create('files'); diff --git a/test/view.render.js b/test/view.render.js index e537654f..d3a84d56 100644 --- a/test/view.render.js +++ b/test/view.render.js @@ -4,7 +4,7 @@ var View = require('../').View; var App = require('..'); var view, app; -describe.skip('helpers', function () { +describe('helpers', function () { describe('rendering', function () { beforeEach(function () { app = new App(); @@ -17,7 +17,7 @@ describe.skip('helpers', function () { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) .render({a: 'bbb'}, function (err, res) { if (err) return done(err); - res.contents.toString().should.equal('bbb'); + res.content.should.equal('bbb'); done(); }); }); From fbd2f10fd10cf5a10d364d97324cd2f7f8c19405 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 23 Sep 2015 12:00:58 -0400 Subject: [PATCH 009/282] docs fixes --- .verb.md | 14 +++++++------- README.md | 36 ++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.verb.md b/.verb.md index 4442d3e8..88af70b5 100644 --- a/.verb.md +++ b/.verb.md @@ -1,4 +1,4 @@ -# {%= name %} {%= badge("fury") %} +# verb {%= badge("fury") %} > {%= description %} @@ -10,19 +10,19 @@ ## Usage ```js -var assemble = require('{%= name %}'); -var app = assemble(); +var verb = require('verb'); +var app = verb(); ``` -## assemblefile.js +## verbfile.js -The following example `assemblefile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. +The following example `verbfile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. ```js -var assemble = require('assemble'); +var verb = require('verb'); var extname = require('gulp-extname'); var less = require('gulp-less'); -var app = assemble(); +var app = verb(); app.task('html', function() { app.src('templates/*.hbs') diff --git a/README.md b/README.md index d4ac75d5..15637a7f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# verb2 [![NPM version](https://badge.fury.io/js/verb2.svg)](http://badge.fury.io/js/verb2) +# verb [![NPM version](https://badge.fury.io/js/verb2.svg)](http://badge.fury.io/js/verb2) > Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes. @@ -6,7 +6,7 @@ * [Install](#install) * [Usage](#usage) -* [assemblefile.js](#assemblefilejs) +* [verbfile.js](#verbfilejs) * [API](#api) * [Related projects](#related-projects) * [Running tests](#running-tests) @@ -29,19 +29,19 @@ $ npm i verb2 --save ## Usage ```js -var assemble = require('verb2'); -var app = assemble(); +var verb = require('verb'); +var app = verb(); ``` -## assemblefile.js +## verbfile.js -The following example `assemblefile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. +The following example `verbfile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. ```js -var assemble = require('assemble'); +var verb = require('verb'); var extname = require('gulp-extname'); var less = require('gulp-less'); -var app = assemble(); +var app = verb(); app.task('html', function() { app.src('templates/*.hbs') @@ -60,7 +60,7 @@ app.task('default', ['html', 'css']); ## API -### [Verb](index.js#L27) +### [Verb](index.js#L28) Create an `verb` application. This is the main function exported by the verb module. @@ -75,7 +75,7 @@ var verb = require('verb'); var app = verb(); ``` -### [.src](index.js#L158) +### [.src](index.js#L147) Glob patterns or filepaths to source files. @@ -90,7 +90,7 @@ Glob patterns or filepaths to source files. app.src('src/*.hbs', {layout: 'default'}); ``` -### [.symlink](index.js#L173) +### [.symlink](index.js#L162) Glob patterns or paths for symlinks. @@ -104,7 +104,7 @@ Glob patterns or paths for symlinks. app.symlink('src/**'); ``` -### [.dest](index.js#L189) +### [.dest](index.js#L178) Specify a destination for processed files. @@ -119,7 +119,7 @@ Specify a destination for processed files. app.dest('dist/'); ``` -### [.copy](index.js#L210) +### [.copy](index.js#L199) Copy files with the given glob `patterns` to the specified `dest`. @@ -137,7 +137,7 @@ app.task('assets', function() { }); ``` -### [.toStream](index.js#L230) +### [.toStream](index.js#L219) Push a view collection into a vinyl stream. @@ -155,7 +155,7 @@ app.toStream('posts', function(file) { }) ``` -### [.renderFile](index.js#L258) +### [.renderFile](index.js#L247) Render a vinyl file. @@ -171,7 +171,7 @@ app.src('*.hbs') .pipe(app.renderFile()); ``` -### [.task](index.js#L294) +### [.task](index.js#L289) Define a task to be run when the task is called. @@ -189,7 +189,7 @@ app.task('default', function() { }); ``` -### [.run](index.js#L313) +### [.run](index.js#L308) Run one or more tasks. @@ -256,7 +256,7 @@ Released under the MIT license. *** -_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 20, 2015._ +_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 23, 2015._ [config-cache]: https://github.com/jonschlinkert/config-cache [jsdiff]: https://github.com/nathan7/jsdiff From 4aaf3592aac5da8a21de5e485d9e152d307cf1ec Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 23 Sep 2015 12:01:13 -0400 Subject: [PATCH 010/282] adds `ask` and `question` methods --- index.js | 29 ++++++++++++++++++++++++++--- package.json | 29 ++++------------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/index.js b/index.js index acb8a917..2e800448 100644 --- a/index.js +++ b/index.js @@ -50,14 +50,15 @@ Templates.extend(Verb, { initVerb: function(opts) { this.store = store('verb', opts.store); - this.locals = new Locals(this.cache.data.verb); - this.initEngines(this); this.initMiddleware(this); this.initListeners(this); - lib.helpers(this); lib.views(this); + + this.locals = new Locals(this.cache.data.verb); + this.questions = utils.questions(); + this.initUserConfig(); }, /** @@ -109,6 +110,28 @@ Templates.extend(Verb, { }); }, + ask: function (locals) { + var ctx = utils.merge({}, this.cache.data, locals || {}); + var ask = utils.ask({ + questions: this.questions, + store: this.store, + data: ctx + }); + + return function () { + return ask.apply(ask, arguments); + } + }, + + /** + * Set a question to ask at a later point. + */ + + question: function () { + this.questions.set.apply(this.questions, arguments); + return this; + }, + /** * Glob patterns or filepaths to source files. * diff --git a/package.json b/package.json index 89e863cd..01299aae 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "test": "mocha" }, "dependencies": { + "ask-once": "^0.4.3", "assemble-loader": "^0.1.3", "composer": "^0.4.1", "composer-runtimes": "^0.3.1", @@ -28,12 +29,13 @@ "dest": "^0.2.1", "engine-handlebars": "^0.8.0", "export-files": "^2.1.0", + "helper-reflinks": "^1.4.1", "helper-related": "^0.10.0", "lazy-cache": "^0.2.3", "map-config": "^0.1.0", - "markdown-utils": "^0.7.1", "mixin-deep": "^1.1.3", "parser-front-matter": "^1.2.5", + "question-cache": "^0.3.0", "readme-badges": "^0.3.3", "readme-includes": "^0.2.9", "src-stream": "^0.1.1", @@ -101,29 +103,6 @@ "support" ] }, - "collections": { - "docs": { - "options": { - "cwd": "fixtures/foo" - }, - "set": { - "a.b.c": "d" - } - }, - "includes": { - "options": { - "cwd": "fixtures/bar" - } - }, - "badges": { - "options": { - "cwd": "fixtures/baz" - }, - "addViews": { - "foo": "this is a badge" - } - } - }, "helpers": { "helper": { "whatever": "@/helper-related" @@ -148,4 +127,4 @@ "config-cache" ] } -} \ No newline at end of file +} From be990db65f73ebe1ac9892aef02bdc4f563ec341 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 23 Sep 2015 12:02:06 -0400 Subject: [PATCH 011/282] remove docs temporarily --- .verb.md | 68 +------------- README.md | 264 +----------------------------------------------------- 2 files changed, 2 insertions(+), 330 deletions(-) diff --git a/.verb.md b/.verb.md index 88af70b5..95964a45 100644 --- a/.verb.md +++ b/.verb.md @@ -1,69 +1,3 @@ # verb {%= badge("fury") %} -> {%= description %} - - - -## Install -{%= include("install-npm", {save: true}) %} - -## Usage - -```js -var verb = require('verb'); -var app = verb(); -``` - -## verbfile.js - -The following example `verbfile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. - -```js -var verb = require('verb'); -var extname = require('gulp-extname'); -var less = require('gulp-less'); -var app = verb(); - -app.task('html', function() { - app.src('templates/*.hbs') - .pipe(extname('.html')) - .pipe(app.dest('dist/')); -}); - -app.task('css', function () { - app.src('styles/*.less') - .pipe(less()) - .pipe(app.dest('dist/assets/css')); -}); - -app.task('default', ['html', 'css']); -``` - -## API -{%= apidocs("index.js") %} - -## Related projects - -Assemble is built on top of these great projects: - -{%= related(verb.related.list) %} - -## Running tests -{%= include("tests") %} - -## Contributing -{%= include("contributing") %} - -If Assemble doesn't do what you need, [please let us know][issue]. - -## Author -{%= include("author") %} - -## License -{%= copyright() %} -{%= license() %} - -*** - -{%= include("footer") %} -{%= reflinks(verb.reflinks) %} +wip \ No newline at end of file diff --git a/README.md b/README.md index 15637a7f..b89dfbc7 100644 --- a/README.md +++ b/README.md @@ -1,265 +1,3 @@ # verb [![NPM version](https://badge.fury.io/js/verb2.svg)](http://badge.fury.io/js/verb2) -> Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes. - - - -* [Install](#install) -* [Usage](#usage) -* [verbfile.js](#verbfilejs) -* [API](#api) -* [Related projects](#related-projects) -* [Running tests](#running-tests) -* [Contributing](#contributing) -* [Author](#author) -* [License](#license) - -_(Table of contents generated by [verb])_ - - - -## Install - -Install with [npm](https://www.npmjs.com/) - -```sh -$ npm i verb2 --save -``` - -## Usage - -```js -var verb = require('verb'); -var app = verb(); -``` - -## verbfile.js - -The following example `verbfile.js` includes tasks for generating `.html` files from templates and `.css` stylesheets from `.less`. - -```js -var verb = require('verb'); -var extname = require('gulp-extname'); -var less = require('gulp-less'); -var app = verb(); - -app.task('html', function() { - app.src('templates/*.hbs') - .pipe(extname('.html')) - .pipe(app.dest('dist/')); -}); - -app.task('css', function () { - app.src('styles/*.less') - .pipe(less()) - .pipe(app.dest('dist/assets/css')); -}); - -app.task('default', ['html', 'css']); -``` - -## API - -### [Verb](index.js#L28) - -Create an `verb` application. This is the main function exported by the verb module. - -**Params** - -* `options` **{Object}**: Optionally pass default options to use. - -**Example** - -```js -var verb = require('verb'); -var app = verb(); -``` - -### [.src](index.js#L147) - -Glob patterns or filepaths to source files. - -**Params** - -* `glob` **{String|Array}**: Glob patterns or file paths to source files. -* `options` **{Object}**: Options or locals to merge into the context and/or pass to `src` plugins - -**Example** - -```js -app.src('src/*.hbs', {layout: 'default'}); -``` - -### [.symlink](index.js#L162) - -Glob patterns or paths for symlinks. - -**Params** - -* `glob` **{String|Array}** - -**Example** - -```js -app.symlink('src/**'); -``` - -### [.dest](index.js#L178) - -Specify a destination for processed files. - -**Params** - -* `dest` **{String|Function}**: File path or rename function. -* `options` **{Object}**: Options and locals to pass to `dest` plugins - -**Example** - -```js -app.dest('dist/'); -``` - -### [.copy](index.js#L199) - -Copy files with the given glob `patterns` to the specified `dest`. - -**Params** - -* `patterns` **{String|Array}**: Glob patterns of files to copy. -* `dest` **{String|Function}**: Desination directory. -* `returns` **{Stream}**: Stream, to continue processing if necessary. - -**Example** - -```js -app.task('assets', function() { - app.copy('assets/**', 'dist/'); -}); -``` - -### [.toStream](index.js#L219) - -Push a view collection into a vinyl stream. - -**Params** - -* `collection` **{String}**: The name of the view collection to push into the stream. -* **{Function}**: Optionally pass a filter function to use for filtering views. -* `returns` **{Stream}** - -**Example** - -```js -app.toStream('posts', function(file) { - return file.path !== 'index.hbs'; -}) -``` - -### [.renderFile](index.js#L247) - -Render a vinyl file. - -**Params** - -* `locals` **{Object}**: Optionally locals to pass to the template engine for rendering. -* `returns` **{Object}** - -**Example** - -```js -app.src('*.hbs') - .pipe(app.renderFile()); -``` - -### [.task](index.js#L289) - -Define a task to be run when the task is called. - -**Params** - -* `name` **{String}**: Task name -* `fn` **{Function}**: function that is called when the task is run. - -**Example** - -```js -app.task('default', function() { - app.src('templates/*.hbs') - .pipe(app.dest('dist/')); -}); -``` - -### [.run](index.js#L308) - -Run one or more tasks. - -**Params** - -* `tasks` **{Array|String}**: Task name or array of task names. -* `cb` **{Function}**: callback function that exposes `err` - -**Example** - -```js -app.run(['foo', 'bar'], function(err) { - if (err) console.error('ERROR:', err); -}); -``` - -**Params** - -* `glob` **{String|Array}**: Filepaths or glob patterns. -* `tasks` **{Array}**: Task(s) to watch. - -**Example** - -```js -app.task('watch', function() { - app.watch('docs/*.md', ['docs']); -}); -``` - -## Related projects - -Assemble is built on top of these great projects: - -* [assemble](https://www.npmjs.com/package/assemble): Static site generator for Grunt.js, Yeoman and Node.js. Used by Zurb Foundation, Zurb Ink, H5BP/Effeckt,… [more](https://www.npmjs.com/package/assemble) | [homepage](http://assemble.io) -* [composer](https://www.npmjs.com/package/composer): API-first task runner with three methods: task, run and watch. | [homepage](https://github.com/jonschlinkert/composer) -* [engine](https://www.npmjs.com/package/engine): Template engine based on Lo-Dash template, but adds features like the ability to register helpers… [more](https://www.npmjs.com/package/engine) | [homepage](https://github.com/jonschlinkert/engine) -* [template](https://www.npmjs.com/package/template): Render templates using any engine. Supports, layouts, pages, partials and custom template types. Use template… [more](https://www.npmjs.com/package/template) | [homepage](https://github.com/jonschlinkert/template) - -## Running tests - -Install dev dependencies: - -```sh -$ npm i -d && npm test -``` - -## Contributing - -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/verbose/verb/issues/new). - -If Assemble doesn't do what you need, [please let us know][issue]. - -## Author - -**Jon Schlinkert** - -+ [github/jonschlinkert](https://github.com/jonschlinkert) -+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) - -## License - -Copyright © 2015 Jon Schlinkert -Released under the MIT license. - -*** - -_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 23, 2015._ - -[config-cache]: https://github.com/jonschlinkert/config-cache -[jsdiff]: https://github.com/nathan7/jsdiff -[option-cache]: https://github.com/jonschlinkert/option-cache -[plasma]: https://github.com/jonschlinkert/plasma -[template]: https://github.com/jonschlinkert/template \ No newline at end of file +wip \ No newline at end of file From c506271f6c79494c0b430a136ea2c332c64612e7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 23 Sep 2015 12:10:45 -0400 Subject: [PATCH 012/282] bump to latest templates --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01299aae..ee9a8dc4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "readme-includes": "^0.2.9", "src-stream": "^0.1.1", "stream-combiner": "^0.2.2", - "templates": "jonschlinkert/templates#collections", + "templates": "^0.1.26", "through2": "^2.0.0", "to-vinyl": "^0.2.0", "vinyl": "wearefractal/vinyl", From 3b0f643d74416edb8327523531ede1def4019277 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 14 Oct 2015 05:49:41 -0400 Subject: [PATCH 013/282] setup plugins --- contributing.md | 43 ++++++++++++++++++ gulpfile.js | 16 +++---- index.js | 90 +++++++++--------------------------- lib/config.js | 1 + lib/locals.js | 5 +- lib/pipeline/format.js | 85 ++++++++++++++++++++++++++++++++++ lib/pipeline/index.js | 1 + lib/plugins/collections.js | 93 ++++++++++++++++++++++++++++++++++++++ lib/plugins/get-view.js | 30 ++++++++++++ lib/plugins/index.js | 1 + lib/plugins/questions.js | 45 ++++++++++++++++++ lib/utils.js | 25 ---------- lib/views.js | 90 ------------------------------------ package.json | 4 +- 14 files changed, 332 insertions(+), 197 deletions(-) create mode 100644 contributing.md create mode 100644 lib/pipeline/format.js create mode 100644 lib/pipeline/index.js create mode 100644 lib/plugins/collections.js create mode 100644 lib/plugins/get-view.js create mode 100644 lib/plugins/index.js create mode 100644 lib/plugins/questions.js delete mode 100644 lib/views.js diff --git a/contributing.md b/contributing.md new file mode 100644 index 00000000..5c557bae --- /dev/null +++ b/contributing.md @@ -0,0 +1,43 @@ +# Contributing to Assemble + +First and foremost, thank you! We appreciate that you want to contribute to Assemble, your time is valuable, and your contributions mean a lot to us. + +**What does "contributing" mean?** + +Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following: + +- Updating or correcting documentation +- Feature requests +- Bug reports + +## Issues + +**Before creating an issue** + +Please make sure you're creating one in the right place: + +- do you have a template syntax question? Like how to accomplish something with handlebars? The best place to get answers for this is [stackoverflow.com][], the [handlebars docs](handlebarsjs.com), or the documentation for the template engine you're using. +- Are you having an issue with an Assemble feature that is powered by an underlying lib? This is sometimes difficult to know, but sometimes it can be pretty easy to find out. For example, if you use a glob pattern somewhere and you found what you believe to be a matching bug, that would probably be an issue for [node-glob][] or [micromatch][] + +**Creating an issue** + +Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue: + +- what version of assemble are you using? +- is the issue helper-related? If so, this issue should probably be opened on the repo related to the helper being used. +- do you have any custom helpers defined? Is the issue related to the helper itself, data (context) being passed to the helper, or actually registering the helper in the first place? +- are you using middleware? +- any plugins? + + +## Above and beyond + +Here are some tips for creating idiomatic issues. Taking just a little bit extra time will make your issue easier to read, easier to resolve, more likely to be found by others who have the same or similar issue in the future. + +- take some time to learn basic markdown. This [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) is super helpful, as is the GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/). +- Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/). +- use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others +- use syntax highlighting by adding the correct language name after the first "code fence" + +[node-glob]: https://github.com/isaacs/node-glob +[micromatch]: https://github.com/jonschlinkert/micromatch \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 9ae53e91..cdbcc986 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,7 +4,7 @@ var istanbul = require('gulp-istanbul'); var jshint = require('gulp-jshint'); require('jshint-stylish'); -var lint = ['index.js', 'lib/utils.js', 'test/*.js']; +var lint = ['index.js', 'lib/*.js']; gulp.task('coverage', function () { return gulp.src(lint) @@ -15,7 +15,10 @@ gulp.task('coverage', function () { gulp.task('mocha', ['coverage'], function () { return gulp.src('test/*.js') .pipe(mocha({reporter: 'spec'})) - .pipe(istanbul.writeReports()); + .pipe(istanbul.writeReports({ + reporters: [ 'text' ], + reportOpts: {dir: 'coverage', file: 'summary.txt'} + })) }); gulp.task('jshint', function () { @@ -24,11 +27,4 @@ gulp.task('jshint', function () { .pipe(jshint.reporter('jshint-stylish')) }); -gulp.task('default', ['mocha', 'jshint'], function (cb) { - console.log('Finished "default"'); - - // force the process to end since `verb.watch` - // holds it open in the tests - process.exit(); - cb(); -}); +gulp.task('default', ['mocha', 'jshint']); diff --git a/index.js b/index.js index 2e800448..72954e59 100644 --- a/index.js +++ b/index.js @@ -6,9 +6,11 @@ var path = require('path'); var store = require('data-store'); +// var Assemble = require('assemble'); var Templates = require('templates'); var Composer = require('composer'); var proto = Composer.prototype; +var plugin = require('./lib/plugins'); var lib = require('./lib'); var Locals = lib.locals; var utils = lib.utils; @@ -49,33 +51,19 @@ Templates.extend(Verb, { */ initVerb: function(opts) { - this.store = store('verb', opts.store); + this.use(plugin.collections()); + this.use(plugin.questions()); + this.initEngines(this); this.initMiddleware(this); this.initListeners(this); lib.helpers(this); - lib.views(this); - - this.locals = new Locals(this.cache.data.verb); - this.questions = utils.questions(); - this.initUserConfig(); - }, - - /** - * Initialize the user's config. - */ - initUserConfig: function () { - lib.config(this, this.locals.cache); + this.store = store('verb', opts.store); + this.locals = new Locals('verb', this); + lib.config(this); }, - /** - * Initialize event listeners. The default listeners - * are setup to listen for events that indicate if - * something needs to be re-initialized based on user - * options. - */ - initListeners: function (app) { this.on('option', function (key) { utils.reloadViews(app, key); @@ -91,17 +79,14 @@ Templates.extend(Verb, { */ initEngines: function () { - this.option('rethrow', {regex: /\{%=?([^%]*)%}/}); - this.engine('hbs', require('engine-handlebars')); + this.option('rethrow', { regex: /\{%=?([^%]*)%}/ }); this.engine('md', require('engine-base'), { delims: ['{%', '%}'] }); }, /** - * Default middleware - * | Ensure user-defined layout is recognized - * | Parse front-matter + * Default middleware for parsing front matter */ initMiddleware: function (app) { @@ -110,28 +95,6 @@ Templates.extend(Verb, { }); }, - ask: function (locals) { - var ctx = utils.merge({}, this.cache.data, locals || {}); - var ask = utils.ask({ - questions: this.questions, - store: this.store, - data: ctx - }); - - return function () { - return ask.apply(ask, arguments); - } - }, - - /** - * Set a question to ask at a later point. - */ - - question: function () { - this.questions.set.apply(this.questions, arguments); - return this; - }, - /** * Glob patterns or filepaths to source files. * @@ -176,9 +139,8 @@ Templates.extend(Verb, { */ dest: function (dest) { - if (!dest) throw new Error('expected dest to be a string.'); - return utils.vfs.dest.apply(utils.vfs, arguments) - .on('data', function () {}); // TODO: fix this + if (!dest) throw new Error('expected dest to be a string or function.'); + return utils.vfs.dest.apply(utils.vfs, arguments); }, /** @@ -221,9 +183,7 @@ Templates.extend(Verb, { var stream = utils.through.obj(); setImmediate(function () { Object.keys(views).forEach(function (key) { - var view = views[key]; - var file = utils.toVinyl(view); - stream.write(file); + stream.write(views[key]); }); stream.end(); }); @@ -246,28 +206,22 @@ Templates.extend(Verb, { renderFile: function (locals) { var app = this; - var collection = this.viewCollection(); - + var collection = this.collection(); return utils.through.obj(function (file, enc, cb) { if (typeof locals === 'function') { cb = locals; locals = {}; } - try { - var view = collection.addView(file); - var ctx = utils.merge({}, app.cache.data, locals, view.data); + var view = collection.setView(file); + app.handleView('onLoad', view); - app.render(view, ctx, function (err, res) { - if (err) return cb(err); - file = app.view(res); - cb(null, file); - }); - - } catch(err) { - err.method = 'renderFile'; - app.emit('error', err); - } + var ctx = utils.merge({}, app.cache.data, locals, view.data); + app.render(view, ctx, function (err, res) { + if (err) return cb(err); + file = new utils.Vinyl(res); + cb(null, file); + }); }); }, diff --git a/lib/config.js b/lib/config.js index f72cbd89..12c64b15 100644 --- a/lib/config.js +++ b/lib/config.js @@ -2,6 +2,7 @@ var Config = require('map-config'); module.exports = function (app, config) { + var config = app.locals.cache; var keys = Object.keys(app.views); var views = {}; diff --git a/lib/locals.js b/lib/locals.js index 73a25b92..76bef6da 100644 --- a/lib/locals.js +++ b/lib/locals.js @@ -1,9 +1,10 @@ 'use strict'; var get = require('get-value'); +var set = require('set-value'); -function Locals(cache) { - this.cache = cache || {}; +function Locals(name, app) { + this.cache = app.cache.data[name] || (app.cache.data[name] = {}); } Locals.prototype.get = function(key) { diff --git a/lib/pipeline/format.js b/lib/pipeline/format.js new file mode 100644 index 00000000..6bd74098 --- /dev/null +++ b/lib/pipeline/format.js @@ -0,0 +1,85 @@ +'use strict'; + +var through = require('through2'); +var Remarkable = require('remarkable'); +var PluginError = require('plugin-error'); +var prettify = require('pretty-remarkable'); +var extend = require('extend-shallow'); + + +module.exports = function(locals) { + var argv = this.get('argv'); + var app = this; + + // add a trailing newline to docs? + var newline = argv.newline || this.config.get('newline'); + + return through.obj(function (file, enc, cb) { + if (file.isNull()) { + this.push(file); + return cb(); + } + + try { + if (file.path.indexOf('README.md') === -1 || noformat(app, file, locals, argv)) { + this.push(file); + return cb(); + } + + // pass some extra formatting info to `pretty-remarkable` + var opts = extend({}, locals, file.options); + opts.username = app.get('data.username'); + opts.name = app.get('data.author.name'); + + // prettify + var str = pretty(file.contents.toString(), opts); + str = str.trim() + (newline ? '\n' : ''); + str = fixList(str); + + // rebuffer contents + file.contents = new Buffer(str); + this.push(file); + return cb(); + } catch(err) { + this.emit('error', new PluginError('formatter plugin', err, {stack: true})); + return cb(); + } + }); +}; + +/** + * Fix list formatting + */ + +function fixList(str) { + str = str.replace(/([ ]{1,4}[+-] \[?[^)]+\)?)\n\n(\s*)([*-]) /gm, '$1\n$2$3 '); + str = str.split('__{_}_*').join('**{*}**'); + return str; +} + +/** + * Instantiate `Remarkable` and use the `prettify` plugin + * on the given `str`. + * + * @param {String} `str` + * @param {Object} `options` + * @return {String} + */ + +function pretty(str, options) { + return new Remarkable(options) + .use(prettify) + .render(str); +} + +/** + * Push the `file` through if the user has specfied + * not to format it. + */ + +function noformat(app, file, locals, argv) { + return app.isTrue('noformat') || app.isFalse('format') + || file.noformat === true || file.format === false + || locals.noformat === true || locals.format === false + || argv.noformat === true || argv.format === false; +} diff --git a/lib/pipeline/index.js b/lib/pipeline/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/pipeline/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/plugins/collections.js b/lib/plugins/collections.js new file mode 100644 index 00000000..da1bf0fa --- /dev/null +++ b/lib/plugins/collections.js @@ -0,0 +1,93 @@ +'use strict'; + +/** + * Plugin for creating default view collections + * | includes + * | layouts + * | pages + * | files + */ + +module.exports = function (options) { + var includes = require('readme-includes'); + var badges = require('readme-badges'); + var loader = require('assemble-loader'); + var getView = require('./get-view'); + var utils = require('../utils'); + + return function(app) { + app.use(loader()); + app.use(getView()); + + app.data({ + author: {name: 'Jon Schlinkert'}, + twitter: {username: 'jonschlinkert'}, + github: {username: 'jonschlinkert'}, + runner: { + name: app.get('cache.data.name'), + url: app.get('cache.data.repository'), + } + }); + + app.create('includes', { + viewType: ['partial'], + renameKey: utils.rename, + cwd: includes, + engine: 'md', + }); + + app.create('badges', { + viewType: ['partial'], + renameKey: utils.rename, + cwd: badges, + engine: 'md' + }); + + app.create('docs', { + viewType: ['partial'], + renameKey: utils.basename, + engine: 'md' + }); + + app.create('layouts', { + viewType: ['layout'], + renameKey: utils.basename, + engine: 'md' + }); + + app.include('npm', { + content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); + + app.include('copyright', { + content: 'Copyright © 2015, {%= author.name %}.' + }); + + app.include('license', { + content: 'Released under the {%= ask("lslsl") %} license.' + }); + + app.include('author', { + locals: { + author: {name: 'Brian Woodward'}, + username: 'doowb' + }, + content: [ + '**{%= author.name %}**', + '', + '+ [github/{%= username %}](https://github.com/{%= username %})', + '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', + ].join('\n') + }); + + app.badge('travis', { + content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' + }); + + app.badge('fury', { + content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' + }); + + app.create('files'); + }; +}; diff --git a/lib/plugins/get-view.js b/lib/plugins/get-view.js new file mode 100644 index 00000000..515ce496 --- /dev/null +++ b/lib/plugins/get-view.js @@ -0,0 +1,30 @@ +'use strict'; + +/** + * Collection plugin for overriding the native `getView` method + * from `templates` with a custom method that calls `loadView` + * if the view isn't already cached and has a `path` property + * indicating that the view is on the file system. + */ + +module.exports = function(options) { + return function (app) { + if (this.isCollection) { + this.getView = getView(app, app.getView); + } + return function (views) { + if (this.isCollection) { + this.getView = getView(views, views.getView); + } + return this; + }; + }; +}; + +function getView(app, fn) { + return function (name) { + return fn.apply(this, arguments) || this.loadView(name, { + ext: '.md' + }); + }.bind(app); +} diff --git a/lib/plugins/index.js b/lib/plugins/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/plugins/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/plugins/questions.js b/lib/plugins/questions.js new file mode 100644 index 00000000..5810b123 --- /dev/null +++ b/lib/plugins/questions.js @@ -0,0 +1,45 @@ +'use strict'; + +var lazy = require('lazy-cache')(require); +lazy('mixin-deep', 'merge'); +lazy('question-cache', 'questions'); +lazy('ask-once', 'ask'); + +module.exports = function (options) { + return function (app) { + + /** + * add a `questions` property to the Verb instance + * @type {Object} + */ + + this.questions = lazy.questions(options); + + /** + * Ask a question, or use a pre-existing value + * to populate the answer. + */ + + app.mixin('ask', function (locals) { + var ctx = lazy.merge({}, this.cache.data, locals || {}); + var ask = lazy.ask({ + questions: this.questions, + store: this.store, + data: ctx + }); + + return function () { + return ask.apply(ask, arguments); + }; + }); + + /** + * Set a question to ask at a later point. + */ + + app.mixin('question', function () { + this.questions.set.apply(this.questions, arguments); + return this; + }); + }; +}; diff --git a/lib/utils.js b/lib/utils.js index 07894c73..0bf3f01e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -17,13 +17,10 @@ lazy('stream-combiner', 'combine'); lazy('through2', 'through'); lazy('vinyl-fs', 'vfs'); lazy('vinyl', 'Vinyl'); -lazy('to-vinyl'); // engine/template utiles lazy('parser-front-matter', 'matter'); lazy('engine-handlebars', 'engine'); -lazy('question-cache', 'questions'); -lazy('ask-once', 'ask'); // task utils lazy('composer-runtimes'); @@ -100,28 +97,6 @@ utils.reloadViews = function reloadViews(app, key) { } }; -/** - * Override the native `getView` method from `templates` - * with a custom method that calls `loadView` if the - * view isn't already cached. - */ - -utils.getView = function (options) { - return function (app) { - return function (collection) { - if (!collection.isCollection) return collection; - var fn = this.getView; - - this.getView = function (name) { - return fn.apply(this, arguments) || this.loadView(name, { - ext: '.md' - }); - }; - return this; - }; - }; -}; - /** * Expose utils */ diff --git a/lib/views.js b/lib/views.js deleted file mode 100644 index c01cd88e..00000000 --- a/lib/views.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; - -var includes = require('readme-includes'); -var badges = require('readme-badges'); -var loader = require('assemble-loader'); -var utils = require('./utils'); - -/** - * Default `viewTypes` - * | includes - * | layouts - * | pages - * | files - */ - -module.exports = function (app) { - app.use(loader()); - app.use(utils.getView()); - - app.data({ - author: {name: 'Jon Schlinkert'}, - twitter: {username: 'jonschlinkert'}, - github: {username: 'jonschlinkert'}, - runner: { - name: app.get('cache.data.name'), - url: app.get('cache.data.repository'), - } - }); - - app.create('includes', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: includes, - engine: 'md', - }); - - app.create('badges', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: badges, - engine: 'md' - }); - - app.create('docs', { - viewType: ['partial'], - renameKey: utils.basename, - engine: 'md' - }); - - app.create('layouts', { - viewType: ['layout'], - renameKey: utils.basename, - engine: 'md' - }); - - app.include('npm', { - content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); - - app.include('copyright', { - content: 'Copyright © 2015, {%= author.name %}.' - }); - - app.include('license', { - content: 'Released under the {%= ask("lslsl") %} license.' - }); - - app.include('author', { - locals: { - author: {name: 'Brian Woodward'}, - username: 'doowb' - }, - content: [ - '**{%= author.name %}**', - '', - '+ [github/{%= username %}](https://github.com/{%= username %})', - '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', - ].join('\n') - }); - - app.badge('travis', { - content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' - }); - - app.badge('fury', { - content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' - }); - - app.create('files'); -}; diff --git a/package.json b/package.json index ee9a8dc4..db1ab53e 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,11 @@ "question-cache": "^0.3.0", "readme-badges": "^0.3.3", "readme-includes": "^0.2.9", + "set-value": "^0.2.0", "src-stream": "^0.1.1", "stream-combiner": "^0.2.2", - "templates": "^0.1.26", + "templates": "^0.2.0", "through2": "^2.0.0", - "to-vinyl": "^0.2.0", "vinyl": "wearefractal/vinyl", "vinyl-fs": "wearefractal/vinyl-fs" }, From bc23791b3017be559bd370a5bea26448649da6e7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 14 Oct 2015 14:19:22 -0400 Subject: [PATCH 014/282] update/add unit tests --- test/app.applyLayout.js | 25 +- test/app.collection.compile.js | 50 + test/app.collection.js | 51 +- test/app.collection.render.js | 155 +++ test/app.compile.js | 11 +- test/app.copy.js | 28 +- test/app.create.js | 7 +- test/app.data.js | 3 +- test/app.dest.js | 1175 +++++++++++++++-- test/app.engines.js | 123 +- test/app.events.js | 4 +- test/app.file.js | 20 + test/app.files.js | 22 + test/app.get-set.js | 3 +- test/app.handle.js | 3 +- test/app.handlers.js | 7 +- test/app.js | 38 +- test/app.layout.js | 6 +- test/app.layouts.js | 6 +- test/app.list.compile.js | 44 + test/app.list.render.js | 157 +++ test/app.lookups.js | 28 +- test/app.middleware.js | 5 +- test/app.option.js | 3 +- test/app.page.js | 20 + test/app.pages.js | 22 + test/app.partial.js | 20 + test/app.partials.js | 22 + test/app.render.js | 3 +- test/app.route.js | 3 +- test/app.src.js | 471 ++++--- test/app.task.js | 29 +- test/app.use.js | 3 +- test/app.view.compile.js | 21 +- test/app.view.render.js | 22 +- test/app.watch.js | 43 +- test/collection.engines.js | 177 +++ test/collection.events.js | 3 +- test/collection.js | 552 +++++++- test/collection.options.js | 3 +- test/collection.render.js | 138 ++ test/collection.use.js | 3 +- test/fixtures/bom-utf16be.txt | Bin 0 -> 244 bytes test/fixtures/bom-utf16le.txt | Bin 0 -> 244 bytes test/fixtures/bom-utf8.txt | 1 + test/fixtures/copy/example.txt | 2 +- .../front-matter/autodetect-no-lang.md | 5 + test/fixtures/front-matter/autodetect-yaml.md | 5 + test/fixtures/front-matter/lang-yaml.md | 5 + test/fixtures/noext/license | 21 + test/fixtures/posts/a.txt | 4 + test/fixtures/posts/b.txt | 4 + test/fixtures/posts/c.txt | 4 + test/fixtures/test-symlink | 1 + test/fixtures/test-symlink-dir/suchempty | 1 + test/fixtures/test.coffee | 2 +- test/fixtures/wow/suchempty | 1 + test/group.js | 12 +- test/handlers.js | 46 + test/helpers.js | 133 +- test/item.js | 1067 +++++++++++++++ test/layouts.js | 17 +- test/list.js | 336 +++-- test/list.render.js | 137 ++ test/mergePartials.js | 30 +- test/out-fixtures/fixtures/vinyl/test.coffee | 1 - test/partials.js | 202 +++ test/renameKey.js | 3 +- test/render.js | 5 +- test/routes.js | 3 +- test/store.js | 2 +- test/support/ignore.js | 6 + test/support/index.js | 66 +- test/view.content.js | 3 +- test/view.events.js | 3 +- test/view.js | 34 +- test/view.methods.js | 3 +- test/view.option.js | 3 +- test/view.render.js | 34 +- test/view.set.js | 5 +- test/view.use.js | 3 +- test/viewTypes.js | 4 +- test/views.js | 139 +- 83 files changed, 5076 insertions(+), 806 deletions(-) create mode 100644 test/app.collection.compile.js create mode 100644 test/app.collection.render.js create mode 100644 test/app.file.js create mode 100644 test/app.files.js create mode 100644 test/app.list.compile.js create mode 100644 test/app.list.render.js create mode 100644 test/app.page.js create mode 100644 test/app.pages.js create mode 100644 test/app.partial.js create mode 100644 test/app.partials.js create mode 100644 test/collection.engines.js create mode 100644 test/collection.render.js create mode 100644 test/fixtures/bom-utf16be.txt create mode 100644 test/fixtures/bom-utf16le.txt create mode 100644 test/fixtures/bom-utf8.txt create mode 100644 test/fixtures/front-matter/autodetect-no-lang.md create mode 100644 test/fixtures/front-matter/autodetect-yaml.md create mode 100644 test/fixtures/front-matter/lang-yaml.md create mode 100644 test/fixtures/noext/license create mode 100644 test/fixtures/posts/a.txt create mode 100644 test/fixtures/posts/b.txt create mode 100644 test/fixtures/posts/c.txt create mode 120000 test/fixtures/test-symlink create mode 100644 test/fixtures/test-symlink-dir/suchempty create mode 100644 test/fixtures/wow/suchempty create mode 100644 test/handlers.js create mode 100644 test/item.js create mode 100644 test/list.render.js delete mode 100644 test/out-fixtures/fixtures/vinyl/test.coffee create mode 100644 test/partials.js create mode 100644 test/support/ignore.js diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js index 0a8d94c1..6fdf3902 100644 --- a/test/app.applyLayout.js +++ b/test/app.applyLayout.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; var page = { content: '<%= name %>', @@ -79,28 +80,6 @@ describe('helpers', function () { done(); }); }); - - it('should throw an error when a defined layout is not applied:', function (done) { - app.layout('no_body_tag.tmpl', {content: 'who? me?'}); - page.layout = 'no_body_tag.tmpl'; - app.page('a.tmpl', page) - .render(function (err) { - assert(err.message === 'cannot find layout tag "body" in "no_body_tag.tmpl"'); - done(); - }); - }); - - it('should emit an error when a defined layout is not applied:', function (done) { - app.on('error', function (err) { - assert(err.message === 'cannot find layout tag "body" in "no_body_tag.tmpl"'); - done(); - }); - page.layout = 'no_body_tag.tmpl'; - app.layout('no_body_tag.tmpl', {content: 'who? me?'}); - app.page('a.tmpl', page) - .render(function () { - }); - }); }); }); diff --git a/test/app.collection.compile.js b/test/app.collection.compile.js new file mode 100644 index 00000000..6bd595b7 --- /dev/null +++ b/test/app.collection.compile.js @@ -0,0 +1,50 @@ +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var Views = App.Views; +var views; + +describe('compile', function () { + beforeEach(function () { + views = new Views(); + }); + + it('should throw an error when an engine cannot be found:', function () { + views.addView('foo.bar', {content: '<%= name %>'}); + var page = views.getView('foo.bar'); + (function() { + views.compile(page); + }).should.throw('Views#compile cannot find an engine for: .bar'); + }); + + it('should compile a template:', function () { + views.engine('tmpl', require('engine-base')); + views.addView('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); + + var page = views.getView('a.tmpl'); + var view = views.compile(page); + assert.equal(typeof view.fn, 'function'); + }); + + it('should compile a template by name:', function () { + views.engine('tmpl', require('engine-base')); + views.addView('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); + + var view = views.compile('a.tmpl'); + assert.equal(typeof view.fn, 'function'); + }); + + it('should throw an error when a callback is given:', function () { + views.engine('md', require('engine-base')); + views.addView('foo.md', {content: '<%= name %>'}); + var page = views.getView('foo.md'); + (function() { + views.compile(page, function () {}); + }).should.throw('Views#compile is sync and does not take a callback function'); + + (function() { + views.compile(page, {}, function () {}); + }).should.throw('Views#compile is sync and does not take a callback function'); + }); +}); diff --git a/test/app.collection.js b/test/app.collection.js index ef1fe4e4..e8660287 100644 --- a/test/app.collection.js +++ b/test/app.collection.js @@ -2,7 +2,10 @@ require('mocha'); require('should'); var fs = require('fs'); var assert = require('assert'); -var App = require('../'); +var define = require('define-property'); +var support = require('./support'); +var App = support.resolve(); +var Collection = App.Collection; var app; describe('collection', function () { @@ -28,7 +31,20 @@ describe('collection', function () { describe('adding views', function () { beforeEach(function () { - app = new App(); + app = new App() + .use(function () { + return function () { + define(this, 'count', { + get: function() { + return Object.keys(this.views).length; + }, + set: function () { + throw new Error('count is a read-only getter and cannot be defined.'); + } + }); + }; + }); + app.engine('tmpl', require('engine-base')); app.create('pages'); }); @@ -71,7 +87,36 @@ describe('collection', function () { .pages('test/fixtures/pages/b.hbs') .pages('test/fixtures/pages/c.hbs'); - assert(Object.keys(app.views.pages).length === 3); + assert(app.pages.count === 3); + }); + }); + + describe('addItem', function () { + beforeEach(function () { + app = new App(); + }); + + it('should add items to a collection', function () { + var pages = app.collection({Collection: Collection}); + pages.addItem('foo'); + pages.addItem('bar'); + pages.addItem('baz'); + + pages.items.hasOwnProperty('foo'); + pages.items.hasOwnProperty('bar'); + pages.items.hasOwnProperty('baz'); + }); + + it('should create a collection from an existing collection:', function () { + var pages = app.collection({Collection: Collection}); + pages.addItem('foo'); + pages.addItem('bar'); + pages.addItem('baz'); + + var posts = app.collection(pages); + posts.items.hasOwnProperty('foo'); + posts.items.hasOwnProperty('bar'); + posts.items.hasOwnProperty('baz'); }); }); diff --git a/test/app.collection.render.js b/test/app.collection.render.js new file mode 100644 index 00000000..6cc96a35 --- /dev/null +++ b/test/app.collection.render.js @@ -0,0 +1,155 @@ +require('mocha'); +require('should'); +var async = require('async'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var pages, app; + +describe('render', function () { + describe('rendering', function () { + beforeEach(function () { + app = App(); + pages = app.create('pages'); + app.engine('tmpl', require('engine-base')); + pages.engine('tmpl', require('engine-base')); + }); + + it('should throw an error when no callback is given:', function () { + (function() { + app.pages.render({}); + }).should.throw('Views#render is async and expects a callback function'); + }); + + it('should throw an error when an engine is not defined:', function (done) { + pages.addView('foo.bar', {content: '<%= name %>'}); + var page = pages.getView('foo.bar'); + + app.pages.render(page, function(err) { + assert(err.message === 'Views#render cannot find an engine for: .bar'); + done(); + }); + }); + + it('should use helpers defined on app to render a view:', function (done) { + var locals = {name: 'Halle'}; + app.helper('upper', function (str) { + return str.toUpperCase(str) + 'app'; + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + app.render(page, function (err, res) { + if (err) return done(err); + + assert(res.content === 'a HALLEapp b'); + done(); + }); + }); + + it('should use helpers defined on app to render a view with collection.render:', function (done) { + var locals = {name: 'Halle'}; + app.helper('upper', function (str) { + return str.toUpperCase(str) + 'app'; + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + pages.helper('upper', app._.helpers.sync.upper); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + + assert(res.content === 'a HALLEapp b'); + done(); + }); + }); + + it('should use helpers when rendering a view:', function (done) { + var locals = {name: 'Halle'}; + pages.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE b'); + done(); + }); + }); + + it('should render a template when contents is a buffer:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a template when content is a string:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a view from its path:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + + pages.render('a.tmpl', function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use a plugin for rendering:', function (done) { + pages.engine('tmpl', require('engine-base')); + pages.option('engine', 'tmpl'); + + pages.addViews({ + 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, + 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, + 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, + 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, + 'e': {content: '<%= title %>', locals: {title: 'eee'}}, + 'f': {content: '<%= title %>', locals: {title: 'fff'}}, + 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, + 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, + 'i': {content: '<%= title %>', locals: {title: 'iii'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + }); + + pages.use(function (collection) { + collection.option('pager', false); + + collection.renderEach = function (cb) { + var list = new List(collection); + async.map(list.items, function (item, next) { + collection.render(item, next); + }, cb); + }; + }); + + pages.renderEach(function (err, items) { + if (err) return done(err); + assert(items[0].content === 'aaa'); + assert(items[9].content === 'jjj'); + assert(items.length === 10); + done(); + }); + }); + }); +}); diff --git a/test/app.compile.js b/test/app.compile.js index 0703d3c3..423c9f99 100644 --- a/test/app.compile.js +++ b/test/app.compile.js @@ -1,6 +1,7 @@ require('should'); var assert = require('assert'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('compile', function () { @@ -26,6 +27,14 @@ describe('compile', function () { assert.equal(typeof view.fn, 'function'); }); + it('should compile a template by name:', function () { + app.engine('tmpl', require('engine-base')); + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); + + var view = app.compile('a.tmpl'); + assert.equal(typeof view.fn, 'function'); + }); + it('should throw an error when a callback is given:', function () { app.engine('md', require('engine-base')); app.page('foo.md', {content: '<%= name %>'}); diff --git a/test/app.copy.js b/test/app.copy.js index bf77b0e3..0e979b79 100644 --- a/test/app.copy.js +++ b/test/app.copy.js @@ -1,12 +1,30 @@ require('mocha'); -require('should'); +var path = require('path'); var assert = require('assert'); -var App = require('../'); +var rimraf = require('rimraf'); +var App = require('..'); var app; -describe.skip('app', function () { - describe('', function () { - it('should:', function () { +var fixtures = path.join(__dirname, 'fixtures/copy/*.txt'); +var outpath = path.join(__dirname, 'out-fixtures'); + +describe('copy()', function() { + beforeEach(function (done) { + rimraf(outpath, done); + app = new App(); + }); + + afterEach(function (done) { + rimraf(outpath, done); + }); + + describe('streams', function () { + it('should copy files', function (done) { + app.copy(fixtures, path.join(__dirname, 'actual')) + .on('data', function (file) { + assert.equal(typeof file, 'object'); + }) + .on('end', done); }); }); }); diff --git a/test/app.create.js b/test/app.create.js index ec023d1c..99a15da5 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -3,7 +3,8 @@ require('should'); var fs = require('fs'); var path = require('path'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('create', function () { @@ -19,11 +20,13 @@ describe('create', function () { it('should add a collection to `views`', function () { app.create('pages'); assert(typeof app.views.pages === 'object'); + assert(typeof app.pages === 'function'); }); it('should add a pluralized collection to `views`', function () { app.create('page'); assert(typeof app.views.pages === 'object'); + assert(typeof app.page === 'function'); }); }); @@ -51,7 +54,7 @@ describe('create', function () { describe('custom instances', function () { it('should create views from custom `View` and `Views` instance/ctor:', function () { var Vinyl = require('vinyl'); - Vinyl.prototype.read = function () { + Vinyl.prototype.read = function (file) { return fs.readFileSync(file.path); }; diff --git a/test/app.data.js b/test/app.data.js index 86ced3bc..9cf2cd3d 100644 --- a/test/app.data.js +++ b/test/app.data.js @@ -2,7 +2,8 @@ require('mocha'); require('should'); var path = require('path'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('app.data', function () { diff --git a/test/app.dest.js b/test/app.dest.js index 50501b6d..52e55840 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -1,23 +1,925 @@ +var spies = require('./support/spy'); +var chmodSpy = spies.chmodSpy; +var statSpy = spies.statSpy; + require('mocha'); -var path = require('path'); -var fs = require('graceful-fs'); var should = require('should'); -var rimraf = require('rimraf'); +var assert = require('assert'); var App = require('..'); var app; -var outpath = path.join(__dirname, './out-fixtures'); +var path = require('path'); +var fs = require('graceful-fs'); +var rimraf = require('rimraf'); -describe('app output stream', function() { - describe('dest()', function() { - beforeEach(function (done) { - app = new App(); - rimraf(outpath, done); +var bufferStream; +var bufEqual = require('buffer-equal'); +var through = require('through2'); +var File = require('vinyl'); + +var outpath = path.join(__dirname, 'out-fixtures'); + + +var wipeOut = function(cb) { + app = new App(); + rimraf(path.join(__dirname, 'out-fixtures/'), cb); + spies.setError('false'); + statSpy.reset(); + chmodSpy.reset(); +}; + +var dataWrap = function(fn) { + return function(data, enc, cb) { + fn(data); + cb(); + }; +}; + +var realMode = function(n) { + return n & 07777; +}; + +describe('dest stream', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should explode on invalid folder (empty)', function(done) { + var stream; + try { + stream = app.dest(); + } catch (err) { + assert(err && typeof err === 'object'); + should.not.exist(stream); + done(); + } + }); + + it('should explode on invalid folder (empty string)', function(done) { + var stream; + try { + stream = app.dest(''); + } catch (err) { + assert(err && typeof err === 'object'); + should.not.exist(stream); + done(); + } + }); + + it('should pass through writes with cwd', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null }); - afterEach(function (done) { - rimraf(outpath, done); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should pass through writes with default cwd', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = app.dest(path.join(__dirname, 'out-fixtures/')); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not write null files', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, 'out-fixtures'); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null }); + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(false); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with relative cwd', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: path.relative(process.cwd(), __dirname)}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with function and relative cwd', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = app.dest(function(file){ + should.exist(file); + file.should.equal(expectedFile); + return './out-fixtures'; + }, {cwd: path.relative(process.cwd(), __dirname)}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0655; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write streaming files to the right folder', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0655; + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + setTimeout(function(){ + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should write directories to the right folder', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0655; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function(){ + return true; + }, + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + fs.lstatSync(expectedPath).isDirectory().should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should allow piping multiple dests in streaming mode', function(done) { + var inputPath1 = path.join(__dirname, 'out-fixtures/multiple-first'); + var inputPath2 = path.join(__dirname, 'out-fixtures/multiple-second'); + var inputBase = path.join(__dirname, 'out-fixtures/'); + var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var stream1 = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream2 = app.dest('./out-fixtures/', {cwd: __dirname}); + var content = fs.readFileSync(srcPath); + var rename = through.obj(function(file, _, next) { + file.path = inputPath2; + this.push(file); + next(); + }); + + stream1.on('data', function(file) { + file.path.should.equal(inputPath1); + }); + + stream1.pipe(rename).pipe(stream2); + stream2.on('data', function(file) { + file.path.should.equal(inputPath2); + }).once('end', function() { + fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); + fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); + done(); + }); + + var file = new File({ + base: inputBase, + path: inputPath1, + cwd: __dirname, + contents: content + }); + + stream1.write(file); + stream1.end(); + }); + + it('should write new files with the default user mode', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = 0666 & (~process.umask()); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write new files with the specified mode', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = 0744; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname, mode:expectedMode}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should update file mode to match the vinyl mode', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var startMode = 0655; + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + assert(chmodSpy.called); + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, startMode); + + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should use different modes for files and directories', function(done) { + var inputBase = path.join(__dirname, 'fixtures/vinyl'); + var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); + var expectedBase = path.join(__dirname, 'out-fixtures/wow'); + var expectedDirMode = 0755; + var expectedFileMode = 0655; + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function(){ + realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); + realMode(buffered[0].stat.mode).should.equal(expectedFileMode); + done(); + }; + + var stream = app.dest('./out-fixtures/', { + cwd: __dirname, + mode: expectedFileMode, + dirMode: expectedDirMode + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should change to the specified base as string', function(done) { + var inputBase = path.join(__dirname, 'fixtures/vinyl'); + var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); + + var firstFile = new File({ + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function(){ + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = app.dest('./out-fixtures/', { + cwd: __dirname, + base: inputBase + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should change to the specified base as function', function(done) { + var inputBase = path.join(__dirname, 'fixtures/vinyl'); + var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); + + var firstFile = new File({ + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function() { + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = app.dest('./out-fixtures/', { + cwd: __dirname, + base: function(file){ + should.exist(file); + file.path.should.equal(inputPath); + return inputBase; + } + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should report IO errors', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, 0); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + stream.on('error', function(err) { + err.code.should.equal('EACCES'); + done(); + }); + stream.write(expectedFile); + }); + + it('should report stat errors', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + spies.setError(function(mod, fn) { + if (fn === 'stat' && arguments[2] === expectedPath) { + return new Error('stat error'); + } + }); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + stream.on('error', function(err) { + err.message.should.equal('stat error'); + done(); + }); + stream.write(expectedFile); + }); + + it('should report chmod errors', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + spies.setError(function(mod, fn) { + if (fn === 'chmod' && arguments[2] === expectedPath) { + return new Error('chmod error'); + } + }); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + stream.on('error', function(err) { + err.message.should.equal('chmod error'); + done(); + }); + stream.write(expectedFile); + }); + + it('should not chmod a matching file', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var expectedCount = 0; + spies.setError(function(mod, fn) { + if (fn === 'stat' && arguments[2] === expectedPath) { + expectedCount++; + } + }); + + var onEnd = function(){ + expectedCount.should.equal(1); + assert(!chmodSpy.called); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, expectedMode); + + statSpy.reset(); + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedMode = 03722; + var normalMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: normalMode + } + }); + + var expectedCount = 0; + spies.setError(function(mod, fn) { + if (fn === 'stat' && arguments[2] === expectedPath) { + expectedCount++; + } + }); + + var onEnd = function(){ + expectedCount.should.equal(1); + assert(!chmodSpy.called); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, expectedMode); + + statSpy.reset(); + chmodSpy.reset(); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not overwrite files with overwrite option set to false', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); + done(); + }; + + // Write expected file which should not be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: false}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should overwrite files with overwrite option set to true', function(done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, 'out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); + done(); + }; + + // This should be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: true}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should create symlinks when the `symlink` attribute is set on the file', function (done) { + var inputPath = path.join(__dirname, 'fixtures/vinyl/test-create-dir-symlink'); + var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + var inputRelativeSymlinkPath = 'wow'; + + var expectedPath = path.join(__dirname, 'out-fixtures/test-create-dir-symlink'); + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, //'' + }); + + // `src()` adds this side-effect with `keepSymlinks` option set to false + inputFile.symlink = inputRelativeSymlinkPath; + + var onEnd = function(){ + fs.readlink(buffered[0].path, function () { + buffered[0].symlink.should.equal(inputFile.symlink); + buffered[0].path.should.equal(expectedPath); + done(); + }); + }; + + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should emit finish event', function(done) { + var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); + var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + + stream.once('finish', function() { + done(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: new Buffer("1234567890") + }); + + stream.write(file); + stream.end(); + }); +}); + +describe('dest', function() { + beforeEach(function (done) { + rimraf(outpath, done); + app = new App(); + }); + + afterEach(function (done) { + rimraf(outpath, done); + }); + + describe('streams', function () { it('should return a stream', function (done) { var stream = app.dest(path.join(__dirname, 'fixtures/')); should.exist(stream); @@ -37,181 +939,166 @@ describe('app output stream', function() { should.exist(file.path); should.exist(file.contents); path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); - String(file.contents).should.equal('this is a test'); + String(file.contents).should.equal('Hello world!'); }); outstream.on('end', function () { fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { should.not.exist(err); should.exist(contents); - String(contents).should.equal('this is a test'); + String(contents).should.equal('Hello world!'); done(); }); }); + }); - it('should return an output stream that does not write non-read files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {read: false}); - var outstream = app.dest(outpath); - instream.pipe(outstream); - - outstream.on('error', done); - outstream.on('data', function (file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); - }); + it('should return an output stream that does not write non-read files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {read: false}); + var outstream = app.dest(outpath); + instream.pipe(outstream); - outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { - should.exist(err); - should.not.exist(contents); - done(); - }); - }); + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); }); - it('should return an output stream that writes streaming files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {buffer: false}); - var outstream = instream.pipe(app.dest(outpath)); - - outstream.on('error', done); - outstream.on('data', function (file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); - }); - outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { - should.not.exist(err); - should.exist(contents); - String(contents).should.equal('this is a test'); - done(); - }); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.exist(err); + should.not.exist(contents); + done(); }); }); + }); - it('should return an output stream that writes streaming files to new directories', function (done) { - testWriteDir({}, done); - }); - - it('should return an output stream that writes streaming files to new directories (buffer: false)', function (done) { - testWriteDir({buffer: false}, done); - }); + it('should return an output stream that writes streaming files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {buffer: false}); + var outstream = instream.pipe(app.dest(outpath)); - it('should return an output stream that writes streaming files to new directories (read: false)', function (done) { - testWriteDir({read: false}, done); + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); }); - - it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function (done) { - testWriteDir({buffer: false, read: false}, done); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.not.exist(err); + should.exist(contents); + String(contents).should.equal('Hello world!'); + done(); + }); }); + }); + it('should return an output stream that writes streaming files to new directories', function (done) { + testWriteDir({}, done); }); - describe('exts', function () { - beforeEach(function () { - app = new App(); - app.set('ext', '.txt'); - }); + it('should return an output stream that writes streaming files to new directories (buffer: false)', function (done) { + testWriteDir({buffer: false}, done); + }); - afterEach(function () { - app.set('ext', '.html'); - }); + it('should return an output stream that writes streaming files to new directories (read: false)', function (done) { + testWriteDir({read: false}, done); + }); - it('should return a stream', function (done) { - var stream = app.dest(path.join(__dirname, 'fixtures/')); - should.exist(stream); - should.exist(stream.on); - done(); - }); + it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function (done) { + testWriteDir({buffer: false, read: false}, done); + }); - it('should return an output stream that writes files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); - var outstream = app.dest(outpath); - instream.pipe(outstream); - - outstream.on('error', done); - outstream.on('data', function (file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); - String(file.contents).should.equal('this is a test'); - }); - outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { - should.not.exist(err); - should.exist(contents); - String(contents).should.equal('this is a test'); - done(); - }); - }); - }); + }); - it('should return an output stream that does not write non-read files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {read: false}); - var outstream = app.dest(outpath); - instream.pipe(outstream); - - outstream.on('error', done); - outstream.on('data', function (file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); - }); + describe('ext', function () { + beforeEach(function () { + app = new App(); + app.set('ext', '.txt'); + }); - outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { - should.exist(err); - should.not.exist(contents); - done(); - }); - }); - }); + afterEach(function () { + app.set('ext', '.html'); + }); - it('should return an output stream that writes streaming files to new directories', function (done) { - testWriteDir({}, done); - }); + it('should return a stream', function (done) { + var stream = app.dest(path.join(__dirname, 'fixtures/')); + should.exist(stream); + should.exist(stream.on); + done(); + }); - it('should return an output stream that writes streaming files to new directories (buffer: false)', function (done) { - testWriteDir({buffer: false}, done); - }); + it('should return an output stream that writes files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); + var outstream = app.dest(outpath); + instream.pipe(outstream); - it('should return an output stream that writes streaming files to new directories (read: false)', function (done) { - testWriteDir({read: false}, done); + outstream.on('error', done); + outstream.on('data', function (file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + String(file.contents).should.equal('Hello world!'); }); - - it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function (done) { - testWriteDir({buffer: false, read: false}, done); + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.not.exist(err); + should.exist(contents); + String(contents).should.equal('Hello world!'); + done(); + }); }); }); - function testWriteDir(srcOptions, done) { - var instream = app.src(path.join(__dirname, 'fixtures/generic'), srcOptions); - var outstream = instream.pipe(app.dest(outpath)); + it('should return an output stream that does not write non-read files', function (done) { + var instream = app.src(path.join(__dirname, 'fixtures/dest/*.txt'), {read: false}); + var outstream = app.dest(outpath); + instream.pipe(outstream); outstream.on('error', done); - outstream.on('data', function(file) { + outstream.on('data', function (file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); - path.join(file.path,'').should.equal(path.join(outpath, './generic')); + should.not.exist(file.contents); + path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); }); - outstream.on('end', function() { - fs.exists(path.join(outpath, 'generic'), function(exists) { - /* jshint expr: true */ - should(exists).be.ok; - /* jshint expr: false */ + outstream.on('end', function () { + fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + should.exist(err); + should.not.exist(contents); done(); }); }); - } + }); }); + + function testWriteDir(srcOptions, done) { + var instream = app.src(path.join(__dirname, 'fixtures/generic'), srcOptions); + var outstream = instream.pipe(app.dest(outpath)); + + outstream.on('error', done); + outstream.on('data', function(file) { + // data should be re-emitted correctly + should.exist(file); + should.exist(file.path); + path.join(file.path,'').should.equal(path.join(outpath, 'generic')); + }); + + outstream.on('end', function() { + fs.exists(path.join(outpath, 'generic'), function(exists) { + /* jshint expr: true */ + should(exists).be.ok; + /* jshint expr: false */ + done(); + }); + }); + } }); + diff --git a/test/app.engines.js b/test/app.engines.js index 98205d09..17c06699 100644 --- a/test/app.engines.js +++ b/test/app.engines.js @@ -1,100 +1,53 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; -describe('engines', function () { - describe('constructor', function () { - it('should create an instance of App:', function () { - app = new App(); - assert(app instanceof App); - }); +describe('engine support', function() { + beforeEach(function() { + app = new App(); }); - describe('static methods', function () { - it('should expose `extend`:', function () { - assert(typeof App.extend ==='function'); - }); + it('should throw an error when engine name is invalid:', function () { + (function () { + app.engine(null, {}); + }).should.throw('expected engine ext to be a string or array.'); }); - describe('prototype methods', function () { - beforeEach(function() { - app = new App(); - }); - - it('should expose `set`', function () { - assert(typeof app.set ==='function'); - }); - it('should expose `get`', function () { - assert(typeof app.get ==='function'); - }); - it('should expose `visit`', function () { - assert(typeof app.visit ==='function'); - }); - it('should expose `define`', function () { - assert(typeof app.define ==='function'); - }); - it('should expose `engine`', function () { - assert(typeof app.engine ==='function'); - }); + it('should register an engine to the given extension', function () { + app.engine('hbs', function () {}); + assert(typeof app.engines['.hbs'] === 'object'); }); - describe('instance', function () { - beforeEach(function() { - app = new App(); - }); - - it('should set an arbitrary value on the instance:', function () { - app.set('a', 'b'); - assert(app.a ==='b'); - }); - - it('should get an arbitrary value from the instance:', function () { - app.set('a', 'b'); - assert(app.get('a') ==='b'); - }); + it('should set an engine with the given extension', function () { + var hbs = function() {}; + hbs.render = function() {}; + hbs.renderFile = function() {}; + app.engine('hbs', hbs); + assert(app.engines['.hbs']); + assert(app.engines['.hbs'].renderFile); + assert(app.engines['.hbs'].render); }); - describe('engines', function() { - beforeEach(function() { - app = new App(); - }); - - it('should throw an error when engine name is invalid:', function () { - (function () { - app.engine(null, {}); - }).should.throw('expected engine ext to be a string or array.'); - }); - - it('should register an engine to the given extension', function () { - app.engine('hbs', function () {}); - assert(typeof app.engines['.hbs'] === 'object'); - }); - - it('should set an engine with the given extension', function () { - var hbs = function() {}; - hbs.render = function() {}; - hbs.renderFile = function() {}; - app.engine('hbs', hbs); - assert(app.engines['.hbs']); - assert(app.engines['.hbs'].renderFile); - assert(app.engines['.hbs'].render); - }); + it('should get an engine:', function () { + app.engine('hbs', function () {}); + var hbs = app.engine('hbs'); + assert(typeof hbs === 'object'); + assert(hbs.hasOwnProperty('render')); + assert(hbs.hasOwnProperty('compile')); + }); - it('should get an engine:', function () { - app.engine('hbs', function () {}); - var hbs = app.engine('hbs'); - assert(typeof hbs === 'object'); - assert(hbs.hasOwnProperty('render')); - assert(hbs.hasOwnProperty('compile')); - }); + it('should return undefined if no engine is found:', function () { + var hbs = app.getEngine(); + assert.equal(typeof hbs, 'undefined'); + }); - it('should register multiple engines to the given extension', function () { - app.engine(['hbs', 'md'], function () {}); - assert(typeof app.engines['.hbs'] === 'object'); - assert(typeof app.engines['.md'] === 'object'); - }); + it('should register multiple engines to the given extension', function () { + app.engine(['hbs', 'md'], function () {}); + assert(typeof app.engines['.hbs'] === 'object'); + assert(typeof app.engines['.md'] === 'object'); }); }); @@ -145,12 +98,13 @@ describe('engines', function () { }); -describe('engine selection:', function (done) { - beforeEach(function () { +describe('engine selection:', function () { + beforeEach(function (done) { app = new App(); app.engine('tmpl', require('engine-base')); app.engine('hbs', require('engine-handlebars')); app.create('pages'); + done(); }); it('should get the engine from file extension:', function (done) { @@ -164,6 +118,7 @@ describe('engine selection:', function (done) { it('should use the engine defined on the collection:', function (done) { app.create('posts', {engine: 'hbs'}); + app.post('a', {content: '{{a}}', locals: {a: 'b'}}) .render(function (err, view) { if (err) return done(err); diff --git a/test/app.events.js b/test/app.events.js index 9bed9ade..3505b9c8 100644 --- a/test/app.events.js +++ b/test/app.events.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('events', function () { @@ -38,7 +39,6 @@ describe('events', function () { it('should listen for `view` events:', function () { var app = new App(); - app.initialize(); app.on('view', function (view) { view.foo = 'bar'; diff --git a/test/app.file.js b/test/app.file.js new file mode 100644 index 00000000..fbed4525 --- /dev/null +++ b/test/app.file.js @@ -0,0 +1,20 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.file()', function () { + beforeEach(function() { + app = assemble(); + }); + + describe('add file', function () { + it('should add files to `app.views.files`:', function () { + app.file('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); + app.file('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); + app.file('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); + assert(Object.keys(app.views.files).length === 3); + }); + }); +}); diff --git a/test/app.files.js b/test/app.files.js new file mode 100644 index 00000000..0732afc6 --- /dev/null +++ b/test/app.files.js @@ -0,0 +1,22 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.files()', function () { + beforeEach(function() { + app = assemble(); + }); + + describe('add files', function () { + it('should add files to `app.views.files`:', function () { + app.files({ + 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, + 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + }); + assert(Object.keys(app.views.files).length === 3); + }); + }); +}); diff --git a/test/app.get-set.js b/test/app.get-set.js index cdc6353d..8bee52c7 100644 --- a/test/app.get-set.js +++ b/test/app.get-set.js @@ -1,6 +1,7 @@ require('should'); var assert = require('assert'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('app.set()', function () { diff --git a/test/app.handle.js b/test/app.handle.js index 795ff842..f968d693 100644 --- a/test/app.handle.js +++ b/test/app.handle.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('handler', function () { diff --git a/test/app.handlers.js b/test/app.handlers.js index 27924f4d..24093b15 100644 --- a/test/app.handlers.js +++ b/test/app.handlers.js @@ -2,8 +2,9 @@ require('should'); var fs = require('fs'); var path = require('path'); var assert = require('assert'); -var utils = require('../lib/utils'); -var App = require('..'); +var resolve = require('resolve-glob'); +var support = require('./support'); +var App = support.resolve(); var app; function decorateViews(views) { @@ -18,7 +19,7 @@ function decorateViews(views) { return view; }; views.loader = function (pattern) { - var files = utils.resolveGlob(pattern); + var files = resolve.sync(pattern); return files.reduce(function (acc, fp) { acc[fp] = {path: fp}; return acc; diff --git a/test/app.js b/test/app.js index 07c347cd..ef8e4638 100644 --- a/test/app.js +++ b/test/app.js @@ -1,7 +1,10 @@ +/* deps: coveralls istanbul */ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); +var Base = App.Base; var app; describe('app', function () { @@ -81,6 +84,27 @@ describe('app', function () { assert(typeof app.foo ==='function'); }); + it('should expose constructors from `lib`:', function () { + app = new App(); + app.expose('Collection'); + assert(typeof app.Collection ==='function'); + }); + + it('should update constructors after init:', function () { + var Group = App.Group; + function MyGroup() { + Base.call(this); + } + Base.extend(MyGroup); + + app = new App(); + assert.equal(app.Group, Group); + assert.equal(app.get('Group'), Group); + app.option('Group', MyGroup); + assert.equal(app.Group, MyGroup); + assert.equal(app.get('Group'), MyGroup); + }); + it('should mixin prototype methods defined on options:', function () { app = new App({ mixins: { @@ -90,5 +114,17 @@ describe('app', function () { assert(typeof app.foo ==='function'); delete App.prototype.foo; }); + + it('should expose `_` on app:', function () { + app = new App(); + assert(typeof app._ ==='object'); + }); + + it('should not re-add `_` in init:', function () { + app = new App(); + app._.foo = 'bar'; + app.defaultConfig(); + assert(app._.foo ==='bar'); + }); }); }); diff --git a/test/app.layout.js b/test/app.layout.js index 036efa18..264d9a1f 100644 --- a/test/app.layout.js +++ b/test/app.layout.js @@ -1,12 +1,12 @@ require('mocha'); require('should'); var assert = require('assert'); -var verb = require('..'); +var assemble = require('..'); var app; -describe('app', function () { +describe('.layout()', function () { beforeEach(function() { - app = verb(); + app = assemble(); }); describe('add layout', function () { diff --git a/test/app.layouts.js b/test/app.layouts.js index 03a47bba..12c99c55 100644 --- a/test/app.layouts.js +++ b/test/app.layouts.js @@ -1,12 +1,12 @@ require('mocha'); require('should'); var assert = require('assert'); -var verb = require('..'); +var assemble = require('..'); var app; -describe('app', function () { +describe('.layouts()', function () { beforeEach(function() { - app = verb(); + app = assemble(); }); describe('add layouts', function () { diff --git a/test/app.list.compile.js b/test/app.list.compile.js new file mode 100644 index 00000000..a49f81ca --- /dev/null +++ b/test/app.list.compile.js @@ -0,0 +1,44 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var list; + +describe('app.list.compile', function () { + beforeEach(function () { + list = new List(); + list.engine('tmpl', require('engine-base')); + }); + + it('should compile an item:', function () { + var buffer = new Buffer('a b c'); + var item = list.addItem('a.tmpl', {contents: buffer}) + .compile(); + + assert(typeof item.fn === 'function'); + }); + + it('should use the compiled function to render:', function () { + var buffer = new Buffer('a <%= title %> c'); + var item = list.addItem('a.tmpl', {contents: buffer}) + .compile(); + + assert(item.fn({title: 'z'})); + assert(typeof item.fn({title: 'z'}) === 'string'); + assert(item.fn({title: 'z'}) === 'a z c'); + }); + + it('should compile a view by name:', function () { + var buffer = new Buffer('a <%= title %> c'); + list.addItem('a.tmpl', {contents: buffer}); + + var item = list.compile('a.tmpl'); + + assert(item.fn({title: 'z'})); + assert(typeof item.fn({title: 'z'}) === 'string'); + assert(item.fn({title: 'z'}) === 'a z c'); + }); +}); + diff --git a/test/app.list.render.js b/test/app.list.render.js new file mode 100644 index 00000000..f4a9e0b9 --- /dev/null +++ b/test/app.list.render.js @@ -0,0 +1,157 @@ +'use strict'; + +require('mocha'); +require('should'); +var async = require('async'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var pages, app; + +describe('render', function () { + describe('rendering', function () { + beforeEach(function () { + app = App(); + pages = app.create('pages'); + app.engine('tmpl', require('engine-base')); + pages.engine('tmpl', require('engine-base')); + }); + + it('should throw an error when no callback is given:', function () { + (function() { + app.pages.render({}); + }).should.throw('Views#render is async and expects a callback function'); + }); + + it('should throw an error when an engine is not defined:', function (done) { + pages.addView('foo.bar', {content: '<%= name %>'}); + var page = pages.getView('foo.bar'); + + app.pages.render(page, function(err) { + assert(err.message === 'Views#render cannot find an engine for: .bar'); + done(); + }); + }); + + it('should use helpers defined on app to render a view:', function (done) { + var locals = {name: 'Halle'}; + app.helper('upper', function (str) { + return str.toUpperCase(str) + 'app'; + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + app.render(page, function (err, res) { + if (err) return done(err); + + assert(res.content === 'a HALLEapp b'); + done(); + }); + }); + + it('should use helpers defined on app to render a view with collection.render:', function (done) { + var locals = {name: 'Halle'}; + app.helper('upper', function (str) { + return str.toUpperCase(str) + 'app'; + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + pages.helper('upper', app._.helpers.sync.upper); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + + assert(res.content === 'a HALLEapp b'); + done(); + }); + }); + + it('should use helpers when rendering a view:', function (done) { + var locals = {name: 'Halle'}; + pages.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE b'); + done(); + }); + }); + + it('should render a template when contents is a buffer:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a template when content is a string:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a view from its path:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + + pages.render('a.tmpl', function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use a plugin for rendering:', function (done) { + pages.engine('tmpl', require('engine-base')); + pages.option('engine', 'tmpl'); + + pages.addViews({ + 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, + 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, + 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, + 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, + 'e': {content: '<%= title %>', locals: {title: 'eee'}}, + 'f': {content: '<%= title %>', locals: {title: 'fff'}}, + 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, + 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, + 'i': {content: '<%= title %>', locals: {title: 'iii'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + }); + + pages.use(function (collection) { + collection.option('pager', false); + + collection.renderEach = function (cb) { + var list = new List(collection); + async.map(list.items, function (item, next) { + collection.render(item, next); + }, cb); + }; + }); + + pages.renderEach(function (err, items) { + if (err) return done(err); + assert(items[0].content === 'aaa'); + assert(items[9].content === 'jjj'); + assert(items.length === 10); + done(); + }); + }); + }); +}); diff --git a/test/app.lookups.js b/test/app.lookups.js index b49a921d..27ecface 100644 --- a/test/app.lookups.js +++ b/test/app.lookups.js @@ -2,29 +2,22 @@ require('mocha'); require('should'); var fs = require('fs'); var path = require('path'); -var globby = require('globby'); var assert = require('assert'); -var Templates = require('../'); -var utils = require('../lib/utils'); +var resolve = require('resolve-glob'); +var support = require('./support'); +var App = support.resolve(); var app; -function resolveGlob(patterns, options) { - var opts = utils.merge({cwd: process.cwd()}, options); - return globby.sync(patterns, opts).map(function (fp) { - return path.resolve(opts.cwd, fp); - }); -} - describe('lookups', function () { beforeEach(function () { - app = new Templates(); + app = new App(); app.option('renameKey', function (key) { return path.basename(key); }); app.create('pages') .use(function (pages) { pages.on('addViews', function (glob) { - var files = resolveGlob(glob); + var files = resolve.sync(glob); files.forEach(function (fp) { pages.addView(fp, {path: fp}); }); @@ -100,6 +93,17 @@ describe('lookups', function () { assert(typeof view.path === 'string'); }); + it('should find a view by collection name:', function () { + app = new App(); + app.option('renameKey', function (key) { + return path.basename(key); + }); + app.create('pages'); + app.page('a/b/c.md', {content: '...'}); + var view = app.find('a/b/c.md'); + assert(typeof view.path === 'string'); + }); + it('should find a view without a collection name:', function () { var view = app.find('a.tmpl'); assert(typeof view.path === 'string'); diff --git a/test/app.middleware.js b/test/app.middleware.js index 479d6a85..25843ed2 100644 --- a/test/app.middleware.js +++ b/test/app.middleware.js @@ -1,12 +1,13 @@ require('mocha'); require('should'); var assert = require('assert'); -var Templates = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('middleware', function () { beforeEach(function () { - app = new Templates(); + app = new App(); app.engine('tmpl', require('engine-base')); app.create('pages'); }); diff --git a/test/app.option.js b/test/app.option.js index 0a0821be..a13a7aad 100644 --- a/test/app.option.js +++ b/test/app.option.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('app.option', function () { diff --git a/test/app.page.js b/test/app.page.js new file mode 100644 index 00000000..74d8b286 --- /dev/null +++ b/test/app.page.js @@ -0,0 +1,20 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.page()', function () { + beforeEach(function() { + app = assemble(); + }); + + describe('add page', function () { + it('should add pages to `app.views.pages`:', function () { + app.page('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); + app.page('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); + app.page('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); + assert(Object.keys(app.views.pages).length === 3); + }); + }); +}); diff --git a/test/app.pages.js b/test/app.pages.js new file mode 100644 index 00000000..8bd65b07 --- /dev/null +++ b/test/app.pages.js @@ -0,0 +1,22 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.pages()', function () { + beforeEach(function() { + app = assemble(); + }); + + describe('add pages', function () { + it('should add pages to `app.views.pages`:', function () { + app.pages({ + 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, + 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + }); + assert(Object.keys(app.views.pages).length === 3); + }); + }); +}); diff --git a/test/app.partial.js b/test/app.partial.js new file mode 100644 index 00000000..2fb13130 --- /dev/null +++ b/test/app.partial.js @@ -0,0 +1,20 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.partial()', function () { + beforeEach(function() { + app = assemble(); + }); + + describe('add partial', function () { + it('should add partials to `app.views.partials`:', function () { + app.partial('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); + app.partial('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); + app.partial('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); + assert(Object.keys(app.views.partials).length === 3); + }); + }); +}); diff --git a/test/app.partials.js b/test/app.partials.js new file mode 100644 index 00000000..de6fef9e --- /dev/null +++ b/test/app.partials.js @@ -0,0 +1,22 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.partials()', function () { + beforeEach(function() { + app = assemble(); + }); + + describe('add partials', function () { + it('should add partials to `app.views.partials`:', function () { + app.partials({ + 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, + 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + }); + assert(Object.keys(app.views.partials).length === 3); + }); + }); +}); diff --git a/test/app.render.js b/test/app.render.js index c22e0862..e796d953 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('render', function () { diff --git a/test/app.route.js b/test/app.route.js index b68878c4..c7ab2621 100644 --- a/test/app.route.js +++ b/test/app.route.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('routes', function () { diff --git a/test/app.src.js b/test/app.src.js index aae44fde..bd2b0891 100644 --- a/test/app.src.js +++ b/test/app.src.js @@ -1,296 +1,295 @@ 'use strict'; var App = require('..'); +var assert = require('assert'); var should = require('should'); var join = require('path').join; var app; -describe('app input stream', function() { +describe('src()', function() { + beforeEach(function () { + app = new App(); + }); - describe('src()', function() { - beforeEach(function () { - app = new App(); - }); + it('should return a stream', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + assert(stream); + assert.equal(typeof stream.on, 'function'); + assert.equal(typeof stream.pipe, 'function'); + done(); + }); - it('should return a stream', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); - should.exist(stream); - should.exist(stream.on); - done(); + it('should return an input stream from a flat glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); }); - - it('should return an input stream from a flat glob', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - String(file.contents).should.equal('this is a test'); - }); - stream.on('end', function () { - done(); - }); + stream.on('end', function () { + done(); }); + }); - it('should return an input stream for multiple globs', function (done) { - var globArray = [ - join(__dirname, './fixtures/generic/run.dmc'), - join(__dirname, './fixtures/generic/test.dmc') - ]; - var stream = app.src(globArray); + it('should return an input stream for multiple globs', function (done) { + var globArray = [ + join(__dirname, './fixtures/generic/run.dmc'), + join(__dirname, './fixtures/generic/test.dmc') + ]; + var stream = app.src(globArray); - var files = []; - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function () { - files.length.should.equal(2); - files[0].path.should.equal(globArray[0]); - files[1].path.should.equal(globArray[1]); - done(); - }); + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(2); + files[0].path.should.equal(globArray[0]); + files[1].path.should.equal(globArray[1]); + done(); }); + }); - it('should return an input stream for multiple globs with negation', function (done) { - var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); - var globArray = [ - join(__dirname, './fixtures/generic/*.dmc'), - '!' + join(__dirname, './fixtures/generic/test.dmc'), - ]; - var stream = app.src(globArray); + it('should return an input stream for multiple globs with negation', function (done) { + var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); + var globArray = [ + join(__dirname, './fixtures/generic/*.dmc'), + '!' + join(__dirname, './fixtures/generic/test.dmc'), + ]; + var stream = app.src(globArray); - var files = []; - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function () { - files.length.should.equal(1); - files[0].path.should.equal(expectedPath); - done(); - }); + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); }); - - it('should return an input stream with no contents when read is false', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - }); - stream.on('end', function () { - done(); - }); + stream.on('end', function () { + files.length.should.equal(1); + files[0].path.should.equal(expectedPath); + done(); }); + }); - it('should return an input stream with contents as stream when buffer is false', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - var buf = ''; - file.contents.on('data', function (d) { - buf += d; - }); - file.contents.on('end', function () { - buf.should.equal('this is a test'); - done(); - }); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - }); + it('should return an input stream with no contents when read is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); }); + stream.on('end', function () { + done(); + }); + }); - it('should return an input stream from a deep glob', function (done) { - var stream = app.src(join(__dirname, './fixtures/**/*.jade')); - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); - String(file.contents).should.equal('test template'); + it('should return an input stream with contents as stream when buffer is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + var buf = ''; + file.contents.on('data', function (d) { + buf += d; }); - stream.on('end', function () { + file.contents.on('end', function () { + buf.should.equal('Hello world!'); done(); }); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); }); + }); - it('should return an input stream from a deeper glob', function (done) { - var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); - var a = 0; - stream.on('error', done); - stream.on('data', function () { - ++a; - }); - stream.on('end', function () { - a.should.equal(2); - done(); - }); + it('should return an input stream from a deep glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.jade')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); + String(file.contents).should.equal('test template'); + }); + stream.on('end', function () { + done(); }); + }); - it('should return a file stream from a flat path', function (done) { - var a = 0; - var stream = app.src(join(__dirname, './fixtures/test.coffee')); - stream.on('error', done); - stream.on('data', function (file) { - ++a; - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - String(file.contents).should.equal('this is a test'); - }); - stream.on('end', function () { - a.should.equal(1); - done(); - }); + it('should return an input stream from a deeper glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); + var a = 0; + stream.on('error', done); + stream.on('data', function () { + ++a; }); + stream.on('end', function () { + a.should.equal(2); + done(); + }); + }); - it('should return a stream', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); - should.exist(stream); - should.exist(stream.on); + it('should return a file stream from a flat path', function (done) { + var a = 0; + var stream = app.src(join(__dirname, './fixtures/test.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + ++a; + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function () { + a.should.equal(1); done(); }); + }); - it('should return an input stream from a flat glob', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - String(file.contents).should.equal('this is a test'); - }); - stream.on('end', function () { - done(); - }); + it('should return a stream', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + should.exist(stream); + should.exist(stream.on); + done(); + }); + + it('should return an input stream from a flat glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); }); + stream.on('end', function () { + done(); + }); + }); - it('should return an input stream for multiple globs', function (done) { - var globArray = [ - join(__dirname, './fixtures/generic/run.dmc'), - join(__dirname, './fixtures/generic/test.dmc') - ]; - var stream = app.src(globArray); + it('should return an input stream for multiple globs', function (done) { + var globArray = [ + join(__dirname, './fixtures/generic/run.dmc'), + join(__dirname, './fixtures/generic/test.dmc') + ]; + var stream = app.src(globArray); - var files = []; - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function () { - files.length.should.equal(2); - files[0].path.should.equal(globArray[0]); - files[1].path.should.equal(globArray[1]); - done(); - }); + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(2); + files[0].path.should.equal(globArray[0]); + files[1].path.should.equal(globArray[1]); + done(); }); + }); - it('should return an input stream for multiple globs, with negation', function (done) { - var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); - var globArray = [ - join(__dirname, './fixtures/generic/*.dmc'), - '!' + join(__dirname, './fixtures/generic/test.dmc'), - ]; - var stream = app.src(globArray); + it('should return an input stream for multiple globs, with negation', function (done) { + var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); + var globArray = [ + join(__dirname, './fixtures/generic/*.dmc'), + '!' + join(__dirname, './fixtures/generic/test.dmc'), + ]; + var stream = app.src(globArray); - var files = []; - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function () { - files.length.should.equal(1); - files[0].path.should.equal(expectedPath); - done(); - }); + var files = []; + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function () { + files.length.should.equal(1); + files[0].path.should.equal(expectedPath); + done(); }); + }); - it('should return an input stream with no contents when read is false', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); - stream.on('error', done); - stream.on('data', function (file) { - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - }); - stream.on('end', function () { - done(); - }); + it('should return an input stream with no contents when read is false', function (done) { + var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); + stream.on('error', done); + stream.on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + }); + stream.on('end', function () { + done(); }); + }); - it.skip('should return a throw an error when buffer is false', function (done) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); - stream.on('error', function () { + it.skip('should throw an error when buffer is false', function (done) { + app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}) + .on('error', function () { done(); - }); - stream.on('data', function () { + }) + .on('data', function () { done(new Error('should have thrown an error')); }); - }); + }); - it('should return an input stream from a deep glob', function (done) { - var stream = app.src(join(__dirname, './fixtures/**/*.jade')); - stream.on('error', done); - stream.on('data', function (file) { + it('should return an input stream from a deep glob', function (done) { + app.src(join(__dirname, './fixtures/**/*.jade')) + .on('error', done) + .on('data', function (file) { should.exist(file); should.exist(file.path); should.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); String(file.contents).should.equal('test template'); - }); - stream.on('end', function () { + }) + .on('end', function () { done(); }); - }); + }); - it('should return an input stream from a deeper glob', function (done) { - var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); - var a = 0; - stream.on('error', done); - stream.on('data', function () { - ++a; - }); - stream.on('end', function () { - a.should.equal(2); - done(); - }); + it('should return an input stream from a deeper glob', function (done) { + var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); + var a = 0; + stream.on('error', done); + stream.on('data', function () { + ++a; }); + stream.on('end', function () { + a.should.equal(2); + done(); + }); + }); - it('should return a file stream from a flat path', function (done) { - var a = 0; - var stream = app.src(join(__dirname, './fixtures/test.coffee')); - stream.on('error', done); - stream.on('data', function (file) { - ++a; - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - String(file.contents).should.equal('this is a test'); - }); - stream.on('end', function () { - a.should.equal(1); - done(); - }); + it('should return a file stream from a flat path', function (done) { + var a = 0; + var stream = app.src(join(__dirname, './fixtures/test.coffee')); + stream.on('error', done); + stream.on('data', function (file) { + ++a; + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function () { + a.should.equal(1); + done(); }); }); }); diff --git a/test/app.task.js b/test/app.task.js index f7f4f131..c0d38ffb 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -1,8 +1,8 @@ var assert = require('assert'); -var App = require('../'); +var App = require('..'); var app; -describe('app', function () { +describe('task()', function () { beforeEach(function () { app = new App(); }); @@ -39,7 +39,7 @@ describe('app', function () { cb(); }); - app.run('default', function (err) { + app.build('default', function (err) { if (err) return done(err); assert.equal(count, 1); done(); @@ -53,16 +53,16 @@ describe('app', function () { cb(); }); - app.run('default', function (err) { + app.build('default', function (err) { if (!err) return done(new Error('Expected an error to be thrown.')); assert.equal(count, 0); done(); }); }); - it('should throw an error when `.run` is called without a callback function.', function () { + it('should throw an error when `.build` is called without a callback function.', function () { try { - app.run('default'); + app.build('default'); throw new Error('Expected an error to be thrown.'); } catch (err) { } @@ -87,9 +87,16 @@ describe('app', function () { cb(); }); app.task('default', ['bar']); - app.run('default', function (err) { + app.build('default', function (err) { if (err) return done(err); - assert.deepEqual(events, ['starting.foo','finished.foo','starting.bar','finished.bar','starting.default','finished.default']); + assert.deepEqual(events, [ + 'starting.default', + 'starting.bar', + 'starting.foo', + 'finished.foo', + 'finished.bar', + 'finished.default' + ]); done(); }); }); @@ -102,7 +109,7 @@ describe('app', function () { app.task('default', function (cb) { return cb(new Error('This is an error')); }); - app.run('default', function (err) { + app.build('default', function (err) { if (err) return done(); done(new Error('Expected an error')); }); @@ -118,7 +125,7 @@ describe('app', function () { app.task('default', function (cb) { cb(new Error('This is an error')); }); - app.run('default', function (err) { + app.build('default', function (err) { assert.equal(errors, 1); if (err) return done(); done(new Error('Expected an error')); @@ -140,7 +147,7 @@ describe('app', function () { cb(); }); - app.run('default', function (err) { + app.build('default', function (err) { if (err) return done(err); assert.deepEqual(seq, ['foo', 'bar', 'default']); done(); diff --git a/test/app.use.js b/test/app.use.js index bb059506..875e8587 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var Views = App.Views; var View = App.View; var app; diff --git a/test/app.view.compile.js b/test/app.view.compile.js index a2172584..e325c925 100644 --- a/test/app.view.compile.js +++ b/test/app.view.compile.js @@ -1,10 +1,11 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; -describe('app view', function () { +describe('app.view.compile', function () { describe('compile method', function () { beforeEach(function () { app = new App(); @@ -12,12 +13,26 @@ describe('app view', function () { app.create('page'); }); - it('should use helpers to render a view:', function () { + it('should compile a view:', function () { var buffer = new Buffer('a b c'); var view = app.page('a.tmpl', {contents: buffer}) .compile(); assert(typeof view.fn === 'function'); }); + + it('should compile a view with settings:', function () { + var buffer = new Buffer('a b c'); + var view = app.page('a.tmpl', {contents: buffer}) + .compile({foo: 'bar'}); + assert(typeof view.fn === 'function'); + }); + + it('should compile a view with isAsync flag:', function () { + var buffer = new Buffer('a b c'); + var view = app.page('a.tmpl', {contents: buffer}) + .compile(true); + assert(typeof view.fn === 'function'); + }); }); }); diff --git a/test/app.view.render.js b/test/app.view.render.js index 694ca868..fcf30877 100644 --- a/test/app.view.render.js +++ b/test/app.view.render.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('helpers', function () { @@ -67,6 +68,25 @@ describe('helpers', function () { done(); }); }); + + it('should use the engine defined on view options:', function (done) { + app.engine('hbs', require('engine-handlebars')); + var locals = {name: 'Halle'}; + + app.helpers({ + prepend: function (prefix, str) { + return prefix + str; + } + }); + + var buffer = new Buffer('a {{prepend "foo " name}} b'); + app.page('a.tmpl', {contents: buffer, locals: locals, options: {engine: 'hbs'}}) + .render(function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a foo Halle b'); + done(); + }); + }); }); }); diff --git a/test/app.watch.js b/test/app.watch.js index 33f4ec9c..698b768f 100644 --- a/test/app.watch.js +++ b/test/app.watch.js @@ -1,23 +1,42 @@ -var path = require('path'); -var fs = require('fs'); +'use strict'; -var fixture = path.join(__dirname, 'fixtures/watch'); -var App = require('../'); +var assert = require('assert'); +var fs = require('fs'); +var App = require('..'); var app; -describe('app', function () { +describe.skip('watch()', function () { beforeEach(function () { app = new App({runtimes: false}); }); - it('should run a task when a file changes', function (done) { - var fn = function () { + it('should watch files and run a task when files change', function (done) { + this.timeout(750); + + var count = 0, watch; + app.task('default', function (cb) { + count++; + cb(); + }); + + app.task('close', function (cb) { + watch.close(); + app.emit('close'); + cb(); + }); + + app.task('watch', function (cb) { + watch = app.watch('test/fixtures/watch/*.txt', ['default', 'close']); + fs.writeFile('test/fixtures/watch/test.txt', 'test', function (err) { + if (err) return cb(err); + app.on('close', cb); + }); + }); + + app.build(['watch'], function (err) { + if (err) return done(err); + assert.equal(count, 1); done(); - }; - app.task('watch-test', fn); - app.watch(fixture + '/**/*.*', ['watch-test']); - setImmediate(function () { - fs.writeFileSync(path.join(fixture, 'test.txt'), 'test'); }); }); }); diff --git a/test/collection.engines.js b/test/collection.engines.js new file mode 100644 index 00000000..4c2a6192 --- /dev/null +++ b/test/collection.engines.js @@ -0,0 +1,177 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var Views = App.Views; +var collection, pages; + +describe('collection engines', function() { + beforeEach(function() { + pages = new Views(); + }); + + it('should throw an error when engine name is invalid:', function () { + (function () { + pages.engine(null, {}); + }).should.throw('expected engine ext to be a string or array.'); + }); + + it('should register an engine to the given extension', function () { + pages.engine('hbs', function () {}); + assert(typeof pages.engines['.hbs'] === 'object'); + }); + + it('should set an engine with the given extension', function () { + var hbs = function() {}; + hbs.render = function() {}; + hbs.renderFile = function() {}; + pages.engine('hbs', hbs); + assert(pages.engines['.hbs']); + assert(pages.engines['.hbs'].renderFile); + assert(pages.engines['.hbs'].render); + }); + + it('should get an engine:', function () { + pages.engine('hbs', function () {}); + var hbs = pages.engine('hbs'); + assert(typeof hbs === 'object'); + assert(hbs.hasOwnProperty('render')); + assert(hbs.hasOwnProperty('compile')); + }); + + it('should register multiple engines to the given extension', function () { + pages.engine(['hbs', 'md'], function () {}); + assert(typeof pages.engines['.hbs'] === 'object'); + assert(typeof pages.engines['.md'] === 'object'); + }); +}); + +describe('engines', function () { + beforeEach(function () { + pages = new Views(); + pages.addView('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); + pages.addView('bar.tmpl', {content: 'A <%= letter %> {{ letter }} C'}); + }); + + it('should register an engine:', function () { + pages.engine('a', {render: function () {}}); + pages.engines.should.have.property('.a'); + }); + + it('should use custom delimiters:', function (done) { + pages.engine('tmpl', require('engine-base'), { + delims: ['{{', '}}'] + }); + + pages.render('foo.tmpl', {letter: 'B'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('A <%= letter %> B C'); + done(); + }); + }); + + it('should override individual delims values:', function (done) { + pages.engine('tmpl', require('engine-base'), { + interpolate: /\{{([^}]+)}}/g, + evaluate: /\{{([^}]+)}}/g, + escape: /\{{-([^}]+)}}/g + }); + pages.render('bar.tmpl', {letter: 'B'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('A <%= letter %> B C'); + done(); + }); + }); + + it('should get an engine:', function () { + pages.engine('a', { + render: function () {} + }); + var a = pages.engine('a'); + a.should.have.property('render'); + }); +}); + + +describe('engine selection:', function () { + beforeEach(function (done) { + collection = new Views(); + collection.engine('tmpl', require('engine-base')); + collection.engine('hbs', require('engine-handlebars')); + done(); + }); + + it('should get the engine from file extension:', function (done) { + var pages = new Views(); + pages.engine('tmpl', require('engine-base')); + pages.engine('hbs', require('engine-handlebars')); + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on the collection:', function (done) { + var posts = new Views({engine: 'hbs'}); + posts.engine('tmpl', require('engine-base')); + posts.engine('hbs', require('engine-handlebars')); + + posts.addView('a', {content: '{{a}}', locals: {a: 'b'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on the view:', function (done) { + var posts = new Views(); + posts.engine('tmpl', require('engine-base')); + posts.engine('hbs', require('engine-handlebars')); + posts.addView('a', {content: '{{a}}', engine: 'hbs', locals: {a: 'b'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on view.options:', function (done) { + var posts = new Views(); + posts.engine('tmpl', require('engine-base')); + posts.engine('hbs', require('engine-handlebars')); + posts.addView('a', {content: '{{a}}', data: {a: 'b'}, options: {engine: 'hbs'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on view.data:', function (done) { + var posts = new Views(); + posts.engine('tmpl', require('engine-base')); + posts.engine('hbs', require('engine-handlebars')); + posts.addView('a', {content: '{{a}}', locals: {a: 'b'}, data: {engine: 'hbs'}}) + .render(function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use the engine defined on render locals:', function (done) { + var posts = new Views(); + posts.engine('tmpl', require('engine-base')); + posts.engine('hbs', require('engine-handlebars')); + posts.addView('a', {content: '{{a}}', locals: {a: 'b'}}) + .render({engine: 'hbs'}, function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); +}); diff --git a/test/collection.events.js b/test/collection.events.js index bec1e5ac..e9198359 100644 --- a/test/collection.events.js +++ b/test/collection.events.js @@ -1,5 +1,6 @@ require('should'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('collection events', function () { diff --git a/test/collection.js b/test/collection.js index f8a7fc47..2ddee92d 100644 --- a/test/collection.js +++ b/test/collection.js @@ -1,123 +1,537 @@ require('mocha'); require('should'); +var path = require('path'); var assert = require('assert'); -var App = require('..'); -var Views = App.Views; +var typeOf = require('kind-of'); +var isBuffer = require('is-buffer'); + +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var Item = App.Item; +var Collection = App.Collection; var collection; describe('collection', function () { - describe('method', function () { - beforeEach(function () { - collection = new Views(); - }); - - it('should expose the collection method', function () { - assert(typeof Views === 'function'); + describe('constructor', function () { + it('should create an instance of Collection', function () { + var collection = new Collection(); + assert(collection instanceof Collection); + assert(typeof collection === 'object'); }); - it('should return a new collection', function () { - var collection = new Views(); + it('should instantiate without new', function () { + var collection = Collection(); + assert(collection instanceof Collection); assert(typeof collection === 'object'); }); + }); - it('should have isCollection property', function () { - var collection = new Views(); - assert(collection.isCollection === true); + describe('static methods', function () { + it('should expose `extend`', function () { + assert(typeof Collection.extend ==='function'); }); }); - describe('adding views', function () { - beforeEach(function () { - collection = new Views(); + describe('prototype methods', function () { + beforeEach(function() { + collection = new Collection(); + }); + + var methods = [ + 'use', + 'setItem', + 'addItem', + 'addItems', + 'addList', + 'getItem', + 'constructor', + 'set', + 'get', + 'del', + 'define', + 'visit', + 'on', + 'once', + 'off', + 'emit', + 'listeners', + 'hasListeners' + ]; + + methods.forEach(function (method) { + it('should expose ' + method + ' method', function () { + assert(typeof collection[method] === 'function'); + }); + }); + + it('should expose isCollection property', function () { + assert(typeof collection.isCollection === 'boolean'); + }); + + it('should expose queue property', function () { + assert(Array.isArray(collection.queue)); }); - it('should load a view onto the respective collection:', function () { - collection.addView('a.hbs'); - collection.views.should.have.property('a.hbs'); + it('should expose items property', function () { + assert(typeOf(collection.items) === 'object'); }); - it('should allow collection methods to be chained:', function () { + it('should expose options property', function () { + assert(typeOf(collection.options) === 'object'); + }); + }); +}); + +describe('methods', function () { + beforeEach(function() { + collection = new Collection(); + }); + + describe('chaining', function () { + it('should allow collection methods to be chained', function () { collection - .addViews({'a.hbs': {path: 'a.hbs'}}) - .addViews({'b.hbs': {path: 'b.hbs'}}) - .addViews({'c.hbs': {path: 'c.hbs'}}); + .addItems({'a.hbs': {path: 'a.hbs'}}) + .addItems({'b.hbs': {path: 'b.hbs'}}) + .addItems({'c.hbs': {path: 'c.hbs'}}); - collection.views.should.have.properties([ + collection.items.should.have.properties([ 'a.hbs', 'b.hbs', 'c.hbs' ]); }); + }); - it('should expose the `option` method:', function () { - collection.option('foo', 'bar') - .addViews('a.hbs') - .addViews('b.hbs') - .addViews('c.hbs'); + describe('use', function () { + it('should expose the instance to plugins', function () { + collection + .use(function (inst) { + inst.foo = 'bar'; + }); - collection.options.should.have.property('foo', 'bar'); - collection.views.should.have.properties([ - 'a.hbs', - 'b.hbs', - 'c.hbs' - ]); + assert(collection.foo === 'bar'); + }); + + it('should expose `item` when the plugin returns a function', function () { + collection + .use(function () { + return function (item) { + item.foo = 'bar'; + }; + }); + + collection.addItem('aaa'); + collection.addItem('bbb'); + collection.addItem('ccc'); + + assert(collection.items.aaa.foo === 'bar'); + assert(collection.items.bbb.foo === 'bar'); + assert(collection.items.ccc.foo === 'bar'); + }); + }); + + describe('get / set', function () { + it('should set a value on the instance', function () { + collection.set('a', 'b'); + assert(collection.a ==='b'); + }); + + it('should get a value from the instance', function () { + collection.set('a', 'b'); + assert(collection.get('a') ==='b'); }); }); - describe('queue', function () { + describe('adding items', function () { beforeEach(function () { - collection = new Views(); + collection = new Collection(); + }); + + it('should load a item onto the respective collection', function () { + collection.addItem('a.hbs'); + collection.items.should.have.property('a.hbs'); + }); + }); + + describe('item', function() { + beforeEach(function() { + collection = new Collection(); + }); + + it('should return a single collection item from a key-value pair', function () { + var one = collection.item('one', {content: 'foo'}); + var two = collection.item('two', {content: 'bar'}); + + assert(one instanceof Item); + assert(one instanceof collection.Item); + assert(one.path === 'one'); + assert(two instanceof Item); + assert(two instanceof collection.Item); + assert(two.path === 'two'); + }); + + it('should return a single collection item from an object', function () { + var one = collection.item({path: 'one', content: 'foo'}); + var two = collection.item({path: 'two', content: 'bar'}); + + assert(one instanceof Item); + assert(one.path === 'one'); + assert(two instanceof Item); + assert(two.path === 'two'); + }); + }); + + describe('addItem', function() { + beforeEach(function() { + collection = new Collection(); + }); + + it('should throw an error when args are invalid', function () { + (function () { + collection.addItem(function() {}); + }).should.throw('expected value to be an object.'); + }); + + it('should add a item to `items`', function () { + collection.addItem('foo'); + collection.items.should.have.property('foo'); + + collection.addItem('one', {content: '...'}); + assert(typeof collection.items.one === 'object'); + assert(isBuffer(collection.items.one.contents)); + }); + + it('should create an instance of `Item`', function () { + collection.addItem('one', {content: '...'}); + assert(collection.items.one instanceof collection.Item); + }); + + it('should allow an `Item` constructor to be passed', function () { + Item.prototype.foo = function(key, value) { + this[key] = value; + }; + collection = new Collection({Item: Item}); + collection.addItem('one', {content: '...'}); + collection.items.one.foo('bar', 'baz'); + assert(collection.items.one.bar === 'baz'); + }); + + it('should allow an instance of `Item` to be passed', function () { + var collection = new Collection({Item: Item}); + var item = new Item({content: '...'}); + collection.addItem('one', item); + item.set('abc', 'xyz'); + assert(collection.items.one instanceof collection.Item); + assert(isBuffer(collection.items.one.contents)); + assert(collection.items.one.abc === 'xyz'); + }); + }); + + describe('addItems', function () { + it('should add multiple items', function () { + collection.addItems({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + assert(isBuffer(collection.items.one.contents)); + assert(isBuffer(collection.items.two.contents)); + }); + + it('should create items from an instance of Collection', function () { + collection.addItems({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + var pages = new Collection(collection); + assert(isBuffer(pages.items.one.contents)); + assert(isBuffer(pages.items.two.contents)); + }); + + it('should add an array of plain-objects', function () { + collection.addItems([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + assert(isBuffer(collection.items.one.contents)); + assert(isBuffer(collection.items.two.contents)); + }); + + it('should add an array of items', function () { + var list = new List([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + + collection.addItems(list.items); + assert(isBuffer(collection.items.one.contents)); + assert(isBuffer(collection.items.two.contents)); + }); + }); + + describe('addList', function() { + beforeEach(function() { + collection = new Collection(); + }); + + it('should add a list of items', function () { + collection.addList([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + assert(isBuffer(collection.items.one.contents)); + assert(isBuffer(collection.items.two.contents)); + }); + + it('should add a list of items from the constructor', function () { + var list = new List([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + + collection = new Collection(list); + assert(isBuffer(collection.items.one.contents)); + assert(isBuffer(collection.items.two.contents)); + }); + + it('should throw an error when list is not an array', function () { + var items = new Collection(); + (function () { + items.addList(); + }).should.throw('expected list to be an array.'); + + (function () { + items.addList({}); + }).should.throw('expected list to be an array.'); + + (function () { + items.addList('foo'); + }).should.throw('expected list to be an array.'); }); - it('should emit arguments on addView:', function (done) { - collection.on('addView', function (a, b, c, d, e) { - assert(a === 'a'); - assert(b === 'b'); - assert(c === 'c'); - assert(d === 'd'); - assert(e === 'e'); - done(); + it('should load an array of items from an event', function () { + var collection = new Collection(); + + collection.on('addList', function (list) { + while (list.length) { + collection.addItem({path: list.pop()}); + } }); - collection.addView('a', 'b', 'c', 'd', 'e'); + collection.addList(['a.txt', 'b.txt', 'c.txt']); + assert(collection.items.hasOwnProperty('a.txt')); + assert(collection.items['a.txt'].path === 'a.txt'); }); - it('should expose the `queue` property for loading views:', function () { - collection.queue.push(collection.view('b', {path: 'b'})); + it('should load an array of items from the addList callback:', function () { + var collection = new Collection(); - collection.addView('a', {path: 'a'}); - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); + collection.addList(['a.txt', 'b.txt', 'c.txt'], function (fp) { + return {path: fp}; + }); + assert(collection.items.hasOwnProperty('a.txt')); + assert(collection.items['a.txt'].path === 'a.txt'); }); - it('should load all views on the queue when addView is called:', function () { - collection.on('addView', function (key, value) { - collection.queue.push(collection.view(key, {content: value})); + it('should load an object of items from an event', function () { + var collection = new Collection(); + + collection.on('addItems', function (items) { + for (var key in items) { + collection.addItem('foo/' + key, items[key]); + delete items[key]; + } + }); + + collection.addItems({ + a: {path: 'a.txt'}, + b: {path: 'b.txt'}, + c: {path: 'c.txt'} }); - collection.addView('a.html', 'aaa'); - collection.addView('b.html', 'bbb'); - collection.addView('c.html', 'ccc'); - assert(collection.views.hasOwnProperty('a.html')); - assert(collection.getView('a.html').content === 'aaa'); - assert(collection.views.hasOwnProperty('b.html')); - assert(collection.getView('b.html').content === 'bbb'); - assert(collection.views.hasOwnProperty('c.html')); - assert(collection.getView('c.html').content === 'ccc'); + assert(collection.items.hasOwnProperty('foo/a')); + assert(collection.items['foo/a'].path === 'a.txt'); }); - it('should expose the `option` method:', function () { + it('should signal `loaded` when finished (addItems)', function () { + var collection = new Collection(); + + collection.on('addItems', function (items) { + for (var key in items) { + if (key === 'c') { + collection.loaded = true; + break; + } + collection.addItem('foo/' + key, items[key]); + } + }); + + collection.addItems({ + a: {path: 'a.txt'}, + b: {path: 'b.txt'}, + c: {path: 'c.txt'} + }); + + assert(collection.items.hasOwnProperty('foo/a')); + assert(!collection.items.hasOwnProperty('foo/c')); + assert(collection.items['foo/a'].path === 'a.txt'); + }); + + it('should signal `loaded` when finished (addList)', function () { + var collection = new Collection(); + + collection.on('addList', function (items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.key === 'c') { + collection.loaded = true; + break; + } + item.key = 'foo/' + item.key; + collection.addItem(item.key, item); + } + }); + + collection.addList([ + {key: 'a', path: 'a.txt'}, + {key: 'b', path: 'b.txt'}, + {key: 'c', path: 'c.txt'} + ]); + + assert(collection.items.hasOwnProperty('foo/a')); + assert(collection.items['foo/a'].path === 'a.txt'); + assert(!collection.items.hasOwnProperty('foo/c')); + }); + }); + + describe('getItem', function() { + beforeEach(function() { + collection = new Collection(); + }); + it('should get a item from `items`', function () { + collection.addItem('one', {content: 'aaa'}); + collection.addItem('two', {content: 'zzz'}); + assert(isBuffer(collection.items.one.contents)); + assert(isBuffer(collection.getItem('one').contents)); + assert(collection.getItem('one').contents.toString() === 'aaa'); + assert(collection.getItem('two').contents.toString() === 'zzz'); + }); + }); +}); + +describe('queue', function () { + beforeEach(function () { + collection = new Collection(); + }); + + it('should emit arguments on addItem', function (done) { + collection.on('addItem', function (args) { + assert(args[0] === 'a'); + assert(args[1] === 'b'); + assert(args[2] === 'c'); + assert(args[3] === 'd'); + assert(args[4] === 'e'); + done(); + }); + + collection.addItem('a', 'b', 'c', 'd', 'e'); + }); + + it('should expose the `queue` property for loading items', function () { + collection.queue.push(collection.item('b', {path: 'b'})); + + collection.addItem('a', {path: 'a'}); + assert(collection.items.hasOwnProperty('a')); + assert(collection.items.hasOwnProperty('b')); + }); + + it('should load all items on the queue when addItem is called', function () { + collection.on('addItem', function (args) { + var len = args.length; + var last = args[len - 1]; + if (typeof last === 'string') { + args[len - 1] = { content: last }; + } + }); + + collection.addItem('a.html', 'aaa'); + collection.addItem('b.html', 'bbb'); + collection.addItem('c.html', 'ccc'); + + assert(collection.items.hasOwnProperty('a.html')); + assert(collection.getItem('a.html').content === 'aaa'); + assert(collection.items.hasOwnProperty('b.html')); + assert(collection.getItem('b.html').content === 'bbb'); + assert(collection.items.hasOwnProperty('c.html')); + assert(collection.getItem('c.html').content === 'ccc'); + }); +}); + +describe('options', function() { + describe('option', function() { + beforeEach(function() { + collection = new Collection(); + }); + + it('should expose the `option` method', function () { + collection.option('foo', 'bar'); + collection.options.should.have.property('foo', 'bar'); + }); + + it('should be chainable', function () { collection.option('foo', 'bar') - .addViews('a.hbs') - .addViews('b.hbs') - .addViews('c.hbs'); + .addItems('a.hbs') + .addItems('b.hbs') + .addItems('c.hbs'); collection.options.should.have.property('foo', 'bar'); - collection.views.should.have.properties([ + collection.items.should.have.properties([ 'a.hbs', 'b.hbs', 'c.hbs' ]); }); + + it('should set a key/value pair on options', function () { + collection.option('a', 'b'); + assert(collection.options.a === 'b'); + }); + + it('should set an object on options', function () { + collection.option({c: 'd'}); + assert(collection.options.c === 'd'); + }); + + it('should get an option', function () { + collection.option({c: 'd'}); + var c = collection.option('c'); + assert(c === 'd'); + }); + }); + + describe('options.renameKey', function() { + beforeEach(function() { + collection = new Collection({ + renameKey: function (key) { + return path.basename(key); + } + }); + }); + + it('should use a custom rename key function on item keys', function() { + collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); + assert(collection.items['d.hbs'].contents.toString() === 'foo bar baz'); + }); + + it('should get a item with the renamed key', function () { + collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); + assert(collection.getItem('d.hbs').contents.toString() === 'foo bar baz'); + }); + + it('should get a item with the original key', function () { + collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); + assert(collection.getItem('a/b/c/d.hbs').contents.toString() === 'foo bar baz'); + }); }); }); + diff --git a/test/collection.options.js b/test/collection.options.js index e58cf617..4994fd41 100644 --- a/test/collection.options.js +++ b/test/collection.options.js @@ -1,5 +1,6 @@ require('should'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('collection.option()', function () { diff --git a/test/collection.render.js b/test/collection.render.js new file mode 100644 index 00000000..d7c1579d --- /dev/null +++ b/test/collection.render.js @@ -0,0 +1,138 @@ +require('mocha'); +require('should'); +var async = require('async'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var Views = App.Views; +var pages; + +describe('render', function () { + describe('rendering', function () { + beforeEach(function () { + pages = new Views(); + pages.engine('tmpl', require('engine-base')); + }); + + it('should throw an error when no callback is given:', function () { + (function() { + pages.render({}); + }).should.throw('Views#render is async and expects a callback function'); + }); + + it('should throw an error when an engine is not defined:', function (done) { + pages.addView('foo.bar', {content: '<%= name %>'}); + var page = pages.getView('foo.bar'); + + pages.render(page, function(err) { + assert(err.message === 'Views#render cannot find an engine for: .bar'); + done(); + }); + }); + + it('should use helpers to render a view:', function (done) { + var locals = {name: 'Halle'}; + + pages.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + + assert(res.content === 'a HALLE b'); + done(); + }); + }); + + it('should use helpers when rendering a view:', function (done) { + var locals = {name: 'Halle'}; + pages.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE b'); + done(); + }); + }); + + it('should render a template when contents is a buffer:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a template when content is a string:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function (err, view) { + if (err) return done(err); + assert(view.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a view from its path:', function (done) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + + pages.render('a.tmpl', function (err, view) { + if (err) return done(err); + assert(view.content === 'b'); + done(); + }); + }); + + it('should use a plugin for rendering:', function (done) { + pages.engine('tmpl', require('engine-base')); + pages.option('engine', 'tmpl'); + + pages.addViews({ + 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, + 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, + 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, + 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, + 'e': {content: '<%= title %>', locals: {title: 'eee'}}, + 'f': {content: '<%= title %>', locals: {title: 'fff'}}, + 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, + 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, + 'i': {content: '<%= title %>', locals: {title: 'iii'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + }); + + pages.use(function (collection) { + collection.option('pager', false); + + collection.renderEach = function (cb) { + var list = new List(collection); + + async.map(list.items, function (item, next) { + collection.render(item, next); + }, cb); + }; + }); + + pages.renderEach(function (err, items) { + if (err) return done(err); + assert(items[0].content === 'aaa'); + assert(items[9].content === 'jjj'); + assert(items.length === 10); + done(); + }); + }); + }); +}); diff --git a/test/collection.use.js b/test/collection.use.js index 05f49331..d7f7cbca 100644 --- a/test/collection.use.js +++ b/test/collection.use.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var Views = App.Views; var View = App.View; var collection; diff --git a/test/fixtures/bom-utf16be.txt b/test/fixtures/bom-utf16be.txt new file mode 100644 index 0000000000000000000000000000000000000000..b9dce78a5d31af4803acd1a0f0dfc14f064a5de1 GIT binary patch literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 literal 0 HcmV?d00001 diff --git a/test/fixtures/bom-utf16le.txt b/test/fixtures/bom-utf16le.txt new file mode 100644 index 0000000000000000000000000000000000000000..07cc600c98675d221bb56d10af38e650538734c9 GIT binary patch literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15l \ No newline at end of file diff --git a/test/fixtures/posts/b.txt b/test/fixtures/posts/b.txt new file mode 100644 index 00000000..1e128c70 --- /dev/null +++ b/test/fixtures/posts/b.txt @@ -0,0 +1,4 @@ +--- +title: BBB +--- +This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/c.txt b/test/fixtures/posts/c.txt new file mode 100644 index 00000000..32f91870 --- /dev/null +++ b/test/fixtures/posts/c.txt @@ -0,0 +1,4 @@ +--- +title: CCC +--- +This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/test-symlink b/test/fixtures/test-symlink new file mode 120000 index 00000000..3fcfe6c7 --- /dev/null +++ b/test/fixtures/test-symlink @@ -0,0 +1 @@ +test.coffee \ No newline at end of file diff --git a/test/fixtures/test-symlink-dir/suchempty b/test/fixtures/test-symlink-dir/suchempty new file mode 100644 index 00000000..65bbcaab --- /dev/null +++ b/test/fixtures/test-symlink-dir/suchempty @@ -0,0 +1 @@ +suchempty \ No newline at end of file diff --git a/test/fixtures/test.coffee b/test/fixtures/test.coffee index a8a94062..6769dd60 100644 --- a/test/fixtures/test.coffee +++ b/test/fixtures/test.coffee @@ -1 +1 @@ -this is a test \ No newline at end of file +Hello world! \ No newline at end of file diff --git a/test/fixtures/wow/suchempty b/test/fixtures/wow/suchempty new file mode 100644 index 00000000..65bbcaab --- /dev/null +++ b/test/fixtures/wow/suchempty @@ -0,0 +1 @@ +suchempty \ No newline at end of file diff --git a/test/group.js b/test/group.js index 6df21072..e846a4f8 100644 --- a/test/group.js +++ b/test/group.js @@ -1,8 +1,14 @@ require('mocha'); require('should'); -var List = require('..').List; -var Group = require('..').Group; -var assert = require('./support/'); + +var assert = require('assert'); +var support = require('./support/'); +assert.containEql = support.containEql; + +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var Group = App.Group; var group; describe('group', function () { diff --git a/test/handlers.js b/test/handlers.js new file mode 100644 index 00000000..4c07276a --- /dev/null +++ b/test/handlers.js @@ -0,0 +1,46 @@ +'use strict'; + +require('should'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('handlers', function () { + describe('custom handlers', function () { + beforeEach(function () { + app = new App(); + app.create('page'); + }); + + it('should add custom middleware handlers:', function () { + app.handler('foo'); + app.handler('bar'); + + app.pages.use(function () { + return function (view) { + app.handle('foo', view); + app.handle('bar', view); + }; + }); + + app.foo(/a/, function (view, next) { + view.one = 'aaa'; + next(); + }); + + app.bar(/z/, function (view, next) { + view.two = 'zzz'; + next(); + }); + + app.pages('a.txt', {content: 'aaa'}); + app.pages('z.txt', {content: 'zzz'}); + + app.pages.getView('a.txt').should.have.property('one'); + app.pages.getView('a.txt').should.not.have.property('two'); + + app.pages.getView('z.txt').should.not.have.property('one'); + app.pages.getView('z.txt').should.have.property('two'); + }); + }); +}); diff --git a/test/helpers.js b/test/helpers.js index b03321fe..4f5be2ab 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,23 +1,17 @@ require('mocha'); require('should'); -var fs = require('fs'); var path = require('path'); +var Base = require('base-methods'); var assert = require('assert'); -var forOwn = require('for-own'); var consolidate = require('consolidate'); var handlebars = require('engine-handlebars'); var matter = require('parser-front-matter'); -var rimraf = require('rimraf'); +var support = require('./support'); var swig = consolidate.swig; require('swig'); -function load(fp) { - fp = path.join(__dirname, 'fixtures', fp); - var str = fs.readFileSync(fp, 'utf8'); - return str; -} - -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('helpers', function () { @@ -52,9 +46,10 @@ describe('helpers', function () { it('should load a glob of sync helper functions:', function () { app.helpers('test/fixtures/helpers/[a-c].js'); - assert(typeof app._.helpers.sync.a === 'function'); - assert(typeof app._.helpers.sync.b === 'function'); + assert(typeof app._.helpers.sync.c === 'function'); + assert(typeof app._.helpers.sync.b === 'function'); + assert(typeof app._.helpers.sync.a === 'function'); }); it('should fail gracefully on bad globs:', function (done) { @@ -94,6 +89,18 @@ describe('helpers', function () { assert(typeof app._.helpers.sync.y === 'function'); assert(typeof app._.helpers.sync.z === 'function'); }); + + it('should add a helper "group":', function () { + app.helperGroup('foo', { + x: function () {}, + y: function () {}, + z: function () {} + }); + + assert(typeof app._.helpers.sync.foo.x === 'function'); + assert(typeof app._.helpers.sync.foo.y === 'function'); + assert(typeof app._.helpers.sync.foo.z === 'function'); + }); }); describe('async helpers', function() { @@ -150,6 +157,18 @@ describe('helpers', function () { assert(typeof app._.helpers.async.y === 'function'); assert(typeof app._.helpers.async.z === 'function'); }); + + it('should add an async helper "group":', function () { + app.helperGroup('foo', { + x: function () {}, + y: function () {}, + z: function () {} + }, true); + + assert(typeof app._.helpers.async.foo.x === 'function'); + assert(typeof app._.helpers.async.foo.y === 'function'); + assert(typeof app._.helpers.async.foo.z === 'function'); + }); }); }); @@ -173,6 +192,28 @@ describe('sync helpers', function () { return str.toUpperCase(); }); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + + assert.equal(typeof view.contents.toString(), 'string'); + assert.equal(view.contents.toString(), 'BBB'); + done(); + }); + }); + + it.skip('should use a namespaced helper:', function (done) { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= foo.upper(a) %>', locals: {a: 'bbb'}}); + + app.helperGroup('foo', { + upper: function (str) { + return str.toUpperCase(); + } + }); + + // console.log(app._.helpers) + var page = app.pages.getView('a.tmpl'); app.render(page, function (err, view) { if (err) return done(err); @@ -321,7 +362,7 @@ describe('built-in helpers:', function () { }); }); - it.skip('should prefer helper locals over view locals.', function (done) { + it('should prefer helper locals over view locals.', function (done) { app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); @@ -450,6 +491,7 @@ describe('helpers integration', function () { return path.join(this.options.cwd, fp); }); + app.option('one', 'two'); app.option('cwd', 'foo/bar'); app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) .render(function (err, res) { @@ -465,6 +507,7 @@ describe('helpers integration', function () { }); app.option('helper.cwd', 'foo/bar'); + app.option('helper.whatever', '...'); app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) .render(function (err, res) { @@ -559,17 +602,73 @@ describe('collection helpers', function () { var one = app.page('one', {content: '{{view "a.hbs"}}'}) .compile() - .fn() + .fn(); var two = app.page('two', {content: '{{view "b.hbs"}}'}) .compile() - .fn() + .fn(); assert(one === 'post-a'); assert(two === 'post-b'); done(); }); + it('should return an empty string if not found', function (done) { + var one = app.page('one', {content: '{{view "foo.hbs"}}'}) + .compile() + .fn(); + assert(one === ''); + done(); + }); + + it('should handle engine errors', function (done) { + app.page('one', {content: '{{posts "foo.hbs"}}'}) + .render(function (err) { + assert(err); + assert(typeof err === 'object'); + assert(typeof err.message === 'string'); + assert(/is not a function/.test(err.message)); + done(); + }); + }); + + it('should handle engine errors', function (done) { + app.engine('tmpl', require('engine-base')); + app.create('foo', {engine: 'tmpl'}); + app.create('bar', {engine: 'tmpl'}); + + app.create('foo', {viewType: 'partial'}); + app.foo('foo.tmpl', {path: 'foo.tmpl', content: '<%= blah.bar %>'}); + app.bar('one.tmpl', {content: '<%= foo("foo.tmpl") %>'}) + .render(function (err) { + assert(err); + assert(typeof err === 'object'); + assert(/blah is not defined/.test(err.message)); + done(); + }); + }); + + it('should work with non-handlebars engine', function (done) { + app.engine('tmpl', require('engine-base')); + app.create('foo', {engine: 'tmpl'}); + app.create('bar', {engine: 'tmpl'}); + + app.foo('a.tmpl', {content: 'foo-a'}); + app.foo('b.tmpl', {content: 'foo-b'}); + + var one = app.bar('one', {content: '<%= view("a.tmpl") %>'}) + .compile() + .fn(); + + var two = app.bar('two', {content: '<%= view("b.tmpl") %>'}) + .compile() + .fn(); + + assert(one === 'foo-a'); + assert(two === 'foo-b'); + done(); + }); + it('should get a specific view from the given collection', function (done) { app.post('a.hbs', {content: 'post-a'}); app.post('b.hbs', {content: 'post-b'}); @@ -580,11 +679,11 @@ describe('collection helpers', function () { var one = app.page('one', {content: '{{view "a.hbs" "posts"}}'}) .compile() - .fn() + .fn(); var two = app.page('two', {content: '{{view "b.hbs" "pages"}}'}) .compile() - .fn() + .fn(); assert(one === 'post-a'); assert(two === 'page-b'); diff --git a/test/item.js b/test/item.js new file mode 100644 index 00000000..23ab488a --- /dev/null +++ b/test/item.js @@ -0,0 +1,1067 @@ +require('mocha'); +var should = require('should'); +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var assert = require('assert'); +var es = require('event-stream'); +var Stream = require('stream'); +var support = require('./support'); +var App = support.resolve(); +var Item = App.Item; +var item; + +describe('Item', function () { + describe('instance', function () { + it('should create an instance of Item:', function () { + item = new Item(); + assert(item instanceof Item); + }); + + it('should instantiate without new:', function () { + item = Item(); + assert(item instanceof Item); + }); + + it('inspect should not double name `Stream` when ctor is `Stream`', function(done) { + var val = new Stream(); + var item = new Item({contents: val}); + console.log(util.inspect(item).name); + done(); + }); + }); + + describe('static methods', function () { + it('should expose `extend`:', function () { + assert(typeof Item.extend === 'function'); + }); + }); + + describe('prototype methods', function () { + beforeEach(function () { + item = new Item(); + }); + + it('should expose `set`:', function () { + assert(typeof item.set === 'function'); + }); + it('should expose `get`:', function () { + assert(typeof item.get === 'function'); + }); + it('should expose `del`:', function () { + assert(typeof item.del === 'function'); + }); + it('should expose `define`:', function () { + assert(typeof item.define === 'function'); + }); + it('should expose `visit`:', function () { + assert(typeof item.visit === 'function'); + }); + }); + + describe('properties', function () { + it('should expose an `options` property', function () { + item = new Item({}); + assert.deepEqual(item.options, {}); + assert(item.hasOwnProperty('options')); + }); + + it('should add `options` when passed on the constructor', function () { + item = new Item({options: {foo: 'bar'}}); + assert(item.options.foo === 'bar'); + }); + + it('should expose a `data` property', function () { + item = new Item({app: {}}); + assert.deepEqual(item.data, {}); + assert(item.hasOwnProperty('data')); + }); + + it('should add `data` when passed on the constructor', function () { + item = new Item({data: {foo: 'bar'}}); + assert(item.data.foo === 'bar'); + }); + + it('should add `locals` when passed on the constructor', function () { + item = new Item({locals: {foo: 'bar'}}); + assert(item.locals.foo === 'bar'); + }); + }); + + describe('set', function () { + it('should set properties on the object', function () { + item = new Item(); + item.set('foo', 'bar'); + assert.equal(item.foo, 'bar'); + }); + }); + + describe('get', function () { + it('should get properties from the object', function () { + item = new Item(); + item.set('foo', 'bar'); + assert.equal(item.get('foo'), 'bar'); + }); + }); + + describe('cwd', function () { + it('should get properties from the object', function () { + item = new Item({cwd: 'test/fixtures'}); + assert(item.cwd === 'test/fixtures'); + }); + }); + + describe('clone', function () { + it('should clone the item:', function () { + item = new Item({content: 'foo'}); + item.set({path: 'foo/bar'}); + item.set('options.one', 'two'); + var clone = item.clone(); + assert(clone.contents); + clone.set('baz', 'quux'); + clone.set('options.three', 'four'); + assert.equal(clone.get('foo'), item.get('foo')); + assert(clone.get('baz') === 'quux'); + assert(!item.get('baz')); + // not deep cloned + assert(clone.get('options.three') === 'four'); + assert(item.get('options.three') === 'four'); + }); + + it('should deep clone the entire object', function () { + item = new Item({content: 'foo'}); + item.set({path: 'foo/bar'}); + item.set('options.one', 'two'); + var clone = item.clone({deep: true}); + clone.set('options.three', 'four'); + assert(item.get('options.one') === 'two'); + assert(clone.get('options.one') === 'two'); + assert(clone.get('options.three') === 'four'); + assert(!item.get('options.three')); + }); + }); + + describe('visit', function () { + it('should visit all properties on an object and call the specified method', function () { + item = new Item(); + var obj = { + foo: 'bar', + bar: 'baz', + baz: 'bang' + }; + item.visit('set', obj); + assert.equal(item.get('foo'), 'bar'); + assert.equal(item.get('bar'), 'baz'); + assert.equal(item.get('baz'), 'bang'); + }); + + it('should visit all properties on all objects in an array and call the specified method', function () { + item = new Item(); + var arr = [{foo: 'bar', bar: 'baz', baz: 'bang'}]; + item.visit('set', arr); + assert.equal(item.get('foo'), 'bar'); + assert.equal(item.get('bar'), 'baz'); + assert.equal(item.get('baz'), 'bang'); + }); + }); +}); + +/** + * The following unit tests are from Vinyl + * Since we inherit vinyl in Item, we need + * to ensure that these still pass. + */ + +describe('Item', function() { + describe('isVinyl()', function() { + it('should return true on a vinyl object', function(done) { + var item = new Item(); + assert(Item.isVinyl(item) === true); + done(); + }); + it('should return false on a normal object', function(done) { + assert(Item.isVinyl({}) === false); + done(); + }); + it('should return false on a null object', function(done) { + assert(Item.isVinyl({}) === false); + done(); + }); + }); + + describe('constructor()', function() { + it('should default cwd to process.cwd', function(done) { + var item = new Item(); + item.cwd.should.equal(process.cwd()); + done(); + }); + + it('should default base to cwd', function(done) { + var cwd = '/'; + var item = new Item({cwd: cwd}); + item.base.should.equal(cwd); + done(); + }); + + it('should default base to cwd even when none is given', function(done) { + var item = new Item(); + item.base.should.equal(process.cwd()); + done(); + }); + + it('should default path to null', function(done) { + var item = new Item(); + should.not.exist(item.path); + done(); + }); + + it('should default history to []', function(done) { + var item = new Item(); + item.history.should.eql([]); + done(); + }); + + it('should default stat to null', function(done) { + var item = new Item(); + should.not.exist(item.stat); + done(); + }); + + it('should default contents to null', function(done) { + var item = new Item(); + should.not.exist(item.contents); + done(); + }); + + it('should set base to given value', function(done) { + var val = '/'; + var item = new Item({base: val}); + item.base.should.equal(val); + done(); + }); + + it('should set cwd to given value', function(done) { + var val = '/'; + var item = new Item({cwd: val}); + item.cwd.should.equal(val); + done(); + }); + + it('should set path to given value', function(done) { + var val = '/test.coffee'; + var item = new Item({path: val}); + item.path.should.equal(val); + item.history.should.eql([val]); + done(); + }); + + it('should set history to given value', function(done) { + var val = '/test.coffee'; + var item = new Item({history: [val]}); + item.path.should.equal(val); + item.history.should.eql([val]); + done(); + }); + + it('should set stat to given value', function(done) { + var val = {}; + var item = new Item({stat: val}); + item.stat.should.equal(val); + done(); + }); + + it('should set contents to given value', function(done) { + var val = new Buffer('test'); + var item = new Item({contents: val}); + item.contents.should.equal(val); + done(); + }); + }); + + describe('isBuffer()', function() { + it('should return true when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var item = new Item({contents: val}); + item.isBuffer().should.equal(true); + done(); + }); + + it('should return false when the contents are a Stream', function(done) { + var val = new Stream(); + var item = new Item({contents: val}); + item.isBuffer().should.equal(false); + done(); + }); + + it('should return false when the contents are a null', function(done) { + var item = new Item({contents: null}); + item.isBuffer().should.equal(false); + done(); + }); + }); + + describe('isStream()', function() { + it('should return false when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var item = new Item({contents: val}); + item.isStream().should.equal(false); + done(); + }); + + it('should return true when the contents are a Stream', function(done) { + var val = new Stream(); + var item = new Item({contents: val}); + item.isStream().should.equal(true); + done(); + }); + + it('should return false when the contents are a null', function(done) { + var item = new Item({contents: null}); + item.isStream().should.equal(false); + done(); + }); + }); + + describe('isNull()', function() { + it('should return false when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var item = new Item({contents: val}); + item.isNull().should.equal(false); + done(); + }); + + it('should return false when the contents are a Stream', function(done) { + var val = new Stream(); + var item = new Item({contents: val}); + item.isNull().should.equal(false); + done(); + }); + + it('should return true when the contents are a null', function(done) { + var item = new Item({contents: null}); + item.isNull().should.equal(true); + done(); + }); + }); + + describe('isDirectory()', function() { + var fakeStat = { + isDirectory: function() { + return true; + } + }; + + it('should return false when the contents are a Buffer', function(done) { + var val = new Buffer('test'); + var item = new Item({contents: val, stat: fakeStat}); + item.isDirectory().should.equal(false); + done(); + }); + + it('should return false when the contents are a Stream', function(done) { + var val = new Stream(); + var item = new Item({contents: val, stat: fakeStat}); + item.isDirectory().should.equal(false); + done(); + }); + + it('should return true when the contents are a null', function(done) { + var item = new Item({contents: null, stat: fakeStat}); + item.isDirectory().should.equal(true); + done(); + }); + }); + + describe('clone()', function() { + it('should copy all attributes over with Buffer', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Buffer('test') + }; + var item = new Item(options); + var item2 = item.clone(); + + item2.should.not.equal(item, 'refs should be different'); + item2.cwd.should.equal(item.cwd); + item2.base.should.equal(item.base); + item2.path.should.equal(item.path); + item2.contents.should.not.equal(item.contents, 'buffer ref should be different'); + item2.contents.toString('utf8').should.equal(item.contents.toString('utf8')); + done(); + }); + + it('should copy buffer\'s reference with option contents: false', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.js', + contents: new Buffer('test') + }; + + var item = new Item(options); + + var copy1 = item.clone({ contents: false }); + copy1.contents.should.equal(item.contents); + + var copy2 = item.clone({}); + copy2.contents.should.not.equal(item.contents); + + var copy3 = item.clone({ contents: 'any string' }); + copy3.contents.should.not.equal(item.contents); + + done(); + }); + + it('should copy all attributes over with Stream', function(done) { + var contents = new Stream.PassThrough(); + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: contents + }; + var item = new Item(options); + var item2 = item.clone(); + + contents.write(new Buffer('wa')); + + process.nextTick(function() { + contents.write(new Buffer('dup')); + contents.end(); + }); + + item2.should.not.equal(item, 'refs should be different'); + item2.cwd.should.equal(item.cwd); + item2.base.should.equal(item.base); + item2.path.should.equal(item.path); + item2.contents.should.not.equal(item.contents, 'stream ref should not be the same'); + item.contents.pipe(es.wait(function(err, data) { + item2.contents.pipe(es.wait(function(err, data2) { + data2.should.not.equal(data, 'stream contents ref should not be the same'); + data2.should.eql(data, 'stream contents should be the same'); + })); + })); + done(); + }); + + it('should copy all attributes over with null', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + var item = new Item(options); + var item2 = item.clone(); + + item2.should.not.equal(item, 'refs should be different'); + item2.cwd.should.equal(item.cwd); + item2.base.should.equal(item.base); + item2.path.should.equal(item.path); + should.not.exist(item2.contents); + done(); + }); + + it('should properly clone the `stat` property', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.js', + contents: new Buffer('test'), + stat: fs.statSync(__filename) + }; + + var item = new Item(options); + var copy = item.clone(); + + assert(copy.stat.isFile()); + assert(!copy.stat.isDirectory()); + assert(item.stat instanceof fs.Stats); + assert(copy.stat instanceof fs.Stats); + done(); + }); + + it('should properly clone the `history` property', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.js', + contents: new Buffer('test'), + stat: fs.statSync(__filename) + }; + + var item = new Item(options); + var copy = item.clone(); + + copy.history[0].should.equal(options.path); + copy.path = 'lol'; + item.path.should.not.equal(copy.path); + done(); + }); + + it('should copy custom properties', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + + var item = new Item(options); + item.custom = { a: 'custom property' }; + var item2 = item.clone(); + + item2.should.not.equal(item, 'refs should be different'); + item2.cwd.should.equal(item.cwd); + item2.base.should.equal(item.base); + item2.path.should.equal(item.path); + item2.custom.should.equal(item.custom); + item2.custom.a.should.equal(item.custom.a); + + done(); + }); + + it('should copy history', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + + var item = new Item(options); + item.path = '/test/test.js'; + item.path = '/test/test-938di2s.js'; + var item2 = item.clone(); + + item2.history.should.eql([ + '/test/test.coffee', + '/test/test.js', + '/test/test-938di2s.js' + ]); + item2.history.should.not.equal([ + '/test/test.coffee', + '/test/test.js', + '/test/test-938di2s.js' + ]); + item2.path.should.eql('/test/test-938di2s.js'); + + done(); + }); + + it('should copy all attributes deeply', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + + var item = new Item(options); + item.custom = { a: 'custom property' }; + + var item2 = item.clone(true); + item2.custom.should.eql(item.custom); + item2.custom.should.not.equal(item.custom); + item2.custom.a.should.equal(item.custom.a); + + var item3 = item.clone({ deep: true }); + item3.custom.should.eql(item.custom); + item3.custom.should.not.equal(item.custom); + item3.custom.a.should.equal(item.custom.a); + + var item4 = item.clone(false); + item4.custom.should.eql(item.custom); + item4.custom.should.equal(item.custom); + item4.custom.a.should.equal(item.custom.a); + + var item5 = item.clone({ deep: false }); + item5.custom.should.eql(item.custom); + item5.custom.should.equal(item.custom); + item5.custom.a.should.equal(item.custom.a); + + done(); + }); + }); + + describe('pipe()', function() { + it('should write to stream with Buffer', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Buffer('test') + }; + var item = new Item(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(options.contents.toString('utf8')); + }); + stream.on('end', function() { + done(); + }); + var ret = item.pipe(stream); + ret.should.equal(stream, 'should return the stream'); + }); + + it('should pipe to stream with Stream', function(done) { + var testChunk = new Buffer('test'); + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Stream.PassThrough() + }; + var item = new Item(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(testChunk.toString('utf8')); + done(); + }); + var ret = item.pipe(stream); + ret.should.equal(stream, 'should return the stream'); + + item.contents.write(testChunk); + }); + + it('should do nothing with null', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + var item = new Item(options); + var stream = new Stream.PassThrough(); + stream.on('data', function() { + throw new Error('should not write'); + }); + stream.on('end', function() { + done(); + }); + var ret = item.pipe(stream); + ret.should.equal(stream, 'should return the stream'); + }); + + it('should write to stream with Buffer', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Buffer('test') + }; + var item = new Item(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(options.contents.toString('utf8')); + done(); + }); + stream.on('end', function() { + throw new Error('should not end'); + }); + var ret = item.pipe(stream, {end: false}); + ret.should.equal(stream, 'should return the stream'); + }); + + it('should pipe to stream with Stream', function(done) { + var testChunk = new Buffer('test'); + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Stream.PassThrough() + }; + var item = new Item(options); + var stream = new Stream.PassThrough(); + stream.on('data', function(chunk) { + should.exist(chunk); + (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); + chunk.toString('utf8').should.equal(testChunk.toString('utf8')); + done(); + }); + stream.on('end', function() { + throw new Error('should not end'); + }); + var ret = item.pipe(stream, {end: false}); + ret.should.equal(stream, 'should return the stream'); + + item.contents.write(testChunk); + }); + + it('should do nothing with null', function(done) { + var options = { + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }; + var item = new Item(options); + var stream = new Stream.PassThrough(); + stream.on('data', function() { + throw new Error('should not write'); + }); + stream.on('end', function() { + throw new Error('should not end'); + }); + var ret = item.pipe(stream, {end: false}); + ret.should.equal(stream, 'should return the stream'); + process.nextTick(done); + }); + }); + + describe('inspect()', function() { + it('should return correct format when no contents and no path', function(done) { + var item = new Item(); + item.inspect().should.equal(''); + done(); + }); + + it('should return correct format when Buffer and no path', function(done) { + var val = new Buffer('test'); + var item = new Item({ + contents: val + }); + item.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when Buffer and relative path', function(done) { + var val = new Buffer('test'); + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: val + }); + item.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when Buffer and only path and no base', function(done) { + var val = new Buffer('test'); + var item = new Item({ + cwd: '/', + path: '/test/test.coffee', + contents: val + }); + delete item.base; + item.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when Stream and relative path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: new Stream.PassThrough() + }); + item.inspect().should.equal('>'); + done(); + }); + + it('should return correct format when null and relative path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: null + }); + item.inspect().should.equal(''); + done(); + }); + }); + + describe('contents get/set', function() { + it('should work with Buffer', function(done) { + var val = new Buffer('test'); + var item = new Item(); + item.contents = val; + item.contents.should.equal(val); + done(); + }); + + it('should work with Stream', function(done) { + var val = new Stream.PassThrough(); + var item = new Item(); + item.contents = val; + item.contents.should.equal(val); + done(); + }); + + it('should work with null', function(done) { + var val = null; + var item = new Item(); + item.contents = val; + (item.contents === null).should.equal(true); + done(); + }); + + it('should work with string', function(done) { + var val = 'test'; + var item = new Item(); + item.contents = val; + item.contents.should.deepEqual(new Buffer(val)); + done(); + }); + }); + + describe('relative get/set', function() { + it('should error on set', function(done) { + var item = new Item(); + try { + item.relative = 'test'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should error on get when no base', function(done) { + var a; + var item = new Item(); + delete item.base; + try { + a = item.relative; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should error on get when no path', function(done) { + var a; + var item = new Item(); + try { + a = item.relative; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return a relative path from base', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.relative.should.equal('test.coffee'); + done(); + }); + + it('should return a relative path from cwd', function(done) { + var item = new Item({ + cwd: '/', + path: '/test/test.coffee' + }); + item.relative.should.equal(path.join('test','test.coffee')); + done(); + }); + }); + + describe('dirname get/set', function() { + it('should error on get when no path', function(done) { + var a; + var item = new Item(); + try { + a = item.dirname; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return the dirname of the path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.dirname.should.equal('/test'); + done(); + }); + + it('should error on set when no path', function(done) { + var item = new Item(); + try { + item.dirname = '/test'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should set the dirname of the path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.dirname = '/test/foo'; + item.path.should.equal('/test/foo/test.coffee'); + done(); + }); + }); + + describe('basename get/set', function() { + it('should error on get when no path', function(done) { + var a; + var item = new Item(); + try { + a = item.basename; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return the basename of the path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.basename.should.equal('test.coffee'); + done(); + }); + + it('should error on set when no path', function(done) { + var item = new Item(); + try { + item.basename = 'test.coffee'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should set the basename of the path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.basename = 'foo.png'; + item.path.should.equal('/test/foo.png'); + done(); + }); + }); + + describe('extname get/set', function() { + it('should error on get when no path', function(done) { + var a; + var item = new Item(); + try { + a = item.extname; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should return the extname of the path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.extname.should.equal('.coffee'); + done(); + }); + + it('should error on set when no path', function(done) { + var item = new Item(); + try { + item.extname = '.coffee'; + } catch (err) { + should.exist(err); + done(); + } + }); + + it('should set the extname of the path', function(done) { + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.extname = '.png'; + item.path.should.equal('/test/test.png'); + done(); + }); + }); + + describe('path get/set', function() { + + it('should record history when instantiation', function() { + var item = new Item({ + cwd: '/', + path: '/test/test.coffee' + }); + + item.path.should.eql('/test/test.coffee'); + item.history.should.eql(['/test/test.coffee']); + }); + + it('should record history when path change', function() { + var item = new Item({ + cwd: '/', + path: '/test/test.coffee' + }); + + item.path = '/test/test.js'; + item.path.should.eql('/test/test.js'); + item.history.should.eql(['/test/test.coffee', '/test/test.js']); + + item.path = '/test/test.coffee'; + item.path.should.eql('/test/test.coffee'); + item.history.should.eql(['/test/test.coffee', '/test/test.js', '/test/test.coffee']); + }); + + it('should not record history when set the same path', function() { + var item = new Item({ + cwd: '/', + path: '/test/test.coffee' + }); + + item.path = '/test/test.coffee'; + item.path = '/test/test.coffee'; + item.path.should.eql('/test/test.coffee'); + item.history.should.eql(['/test/test.coffee']); + + // ignore when set empty string + item.path = ''; + item.path.should.eql('/test/test.coffee'); + item.history.should.eql(['/test/test.coffee']); + }); + + it('should throw when set path null in constructor', function() { + (function() { + new Item({ + cwd: '/', + path: null + }); + }).should.throw('path should be string'); + }); + + it('should throw when set path null', function() { + var item = new Item({ + cwd: '/', + path: 'foo' + }); + + (function() { + item.path = null; + }).should.throw('path should be string'); + }); + }); +}); diff --git a/test/layouts.js b/test/layouts.js index 31381842..f079d32f 100644 --- a/test/layouts.js +++ b/test/layouts.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('layouts', function () { @@ -25,6 +26,20 @@ describe('layouts', function () { }); }); + it('should not apply a layout when `layoutApplied` is set:', function (done) { + app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); + app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); + var page = app.pages.getView('a.tmpl'); + page.option('layoutApplied', true); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'b'); + done(); + }); + }); + it('should not apply a layout to itself:', function (done) { app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c', layout: 'base'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); diff --git a/test/list.js b/test/list.js index 068b9d83..e4edbca8 100644 --- a/test/list.js +++ b/test/list.js @@ -2,22 +2,33 @@ require('mocha'); require('should'); var path = require('path'); var get = require('get-value'); -var List = require('../').List; -var Views = require('../').Views; -var View = require('../').View; -var assert = require('./support/'); +var isBuffer = require('is-buffer'); +var assert = require('assert'); +var typeOf = require('kind-of'); +var support = require('./support/'); +assert.containEql = support.containEql; + +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var Views = App.Views; var list, views; describe('list', function () { describe('constructor', function () { - it('should create an instance of List:', function () { + it('should create an instance of List', function () { var list = new List(); assert(list instanceof List); }); + + it('should instaniate without `new`', function () { + var list = List(); + assert(list instanceof List); + }); }); describe('static methods', function () { - it('should expose `extend`:', function () { + it('should expose `extend`', function () { assert(typeof List.extend ==='function'); }); }); @@ -27,27 +38,51 @@ describe('list', function () { list = new List(); }); - it('should expose `set`', function () { - assert(typeof list.set ==='function'); - }); - it('should expose `get`', function () { - assert(typeof list.get ==='function'); + var methods = [ + 'use', + 'setItem', + 'addItem', + 'addItems', + 'addList', + 'getItem', + 'constructor', + 'set', + 'get', + 'del', + 'define', + 'visit', + 'on', + 'once', + 'off', + 'emit', + 'listeners', + 'hasListeners' + ]; + + methods.forEach(function (method) { + it('should expose the ' + method + ' method', function () { + assert(typeof list[method] === 'function'); + }); }); - it('should expose `visit`', function () { - assert(typeof list.visit ==='function'); + + it('should expose the isList property', function () { + assert(typeof list.isList === 'boolean'); }); - it('should expose `define`', function () { - assert(typeof list.define ==='function'); + + it('should expose the keys property', function () { + assert(Array.isArray(list.keys)); }); - it('should expose `addItem`', function () { - assert(typeof list.addItem ==='function'); + + it('should expose the queue property', function () { + assert(Array.isArray(list.queue)); }); - it('should expose `items`', function () { + it('should expose the items property', function () { assert(Array.isArray(list.items)); }); - it('should expose `keys`', function () { - assert(Array.isArray(list.keys)); + + it('should expose the options property', function () { + assert(typeOf(list.options) === 'object'); }); }); @@ -56,52 +91,79 @@ describe('list', function () { list = new List(); }); - it('should set a value on the instance:', function () { + it('should set a value on the instance', function () { list.set('a', 'b'); assert(list.a ==='b'); }); - it('should get a value from the instance:', function () { + it('should get a value from the instance', function () { list.set('a', 'b'); assert(list.get('a') ==='b'); }); }); - describe('addItem', function() { + describe('use', function () { beforeEach(function() { list = new List(); }); - it('should add an item to `items`:', function () { - list.addItem('one', {content: '...'}); - assert(list.items.length === 1); - assert(Buffer.isBuffer(list.items[0].contents)); + it('should expose the instance to plugins', function () { + list + .use(function (inst) { + inst.foo = 'bar'; + }); + + assert(list.foo === 'bar'); }); - it('should create an instance of `Item`:', function () { - list.addItem('one', {content: '...'}); - assert(list.items[0] instanceof list.Item); + it('should expose `item` when the plugin returns a function', function () { + list + .use(function () { + return function (item) { + item.foo = 'bar'; + }; + }); + + list.addItem('aaa'); + list.addItem('bbb'); + list.addItem('ccc'); + + assert(list.items[0].foo === 'bar'); + assert(list.items[1].foo === 'bar'); + assert(list.items[2].foo === 'bar'); }); + }); - it('should allow an `Item` constructor to be passed:', function () { - var Vinyl = require('vinyl'); - Vinyl.prototype.foo = function(key, value) { - this[key] = value; - }; - list = new List({Item: Vinyl}); - list.addItem('one', {content: '...'}); - list.items[0].foo('bar', 'baz'); - assert(list.items[0].bar === 'baz'); + describe('addItem', function() { + }); + + describe('removeItem', function() { + beforeEach(function() { + list = new List(); }); - it('should allow an instance of `Item` to be passed:', function () { - var list = new List({Item: View}); - var view = new View({content: '...'}); - list.addItem('one', view); - view.set('abc', 'xyz'); - assert(list.items[0] instanceof list.Item); - assert(Buffer.isBuffer(list.items[0].contents)); - assert(list.items[0].abc === 'xyz'); + it('should remove an item from `items`', function () { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + var a = list.getItem('a'); + list.removeItem(a); + assert(list.items.length === 2); + var c = list.getItem(c); + list.removeItem(c); + assert(list.items[0].key === 'b'); + }); + + it('should remove an item from `items` by key', function () { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + list.removeItem('c'); + assert(list.items.length === 2); + list.removeItem('b'); + assert(list.items[0].key === 'a'); }); }); @@ -110,31 +172,53 @@ describe('list', function () { list = new List(); }); - it('should add an object with multiple items:', function () { + it('should add an object with multiple items', function () { list.addItems({ one: {content: 'foo'}, two: {content: 'bar'} }); - assert(Buffer.isBuffer(list.items[0].contents)); - assert(Buffer.isBuffer(list.items[1].contents)); + assert(isBuffer(list.items[0].contents)); + assert(isBuffer(list.items[1].contents)); + }); + + it('should signal `loaded` when finished (addItems)', function () { + list.on('addItems', function (items) { + for (var key in items) { + if (key === 'c') { + list.loaded = true; + break; + } + list.addItem('foo/' + key, items[key]); + } + }); + + list.addItems({ + a: {path: 'a.txt'}, + b: {path: 'b.txt'}, + c: {path: 'c.txt'} + }); + + assert.equal(list.items.length, 2); + assert.equal(list.items[0].key, 'foo/a'); + assert.equal(list.items[0].path, 'a.txt'); }); }); - describe('addItems', function() { + describe('addList', function () { beforeEach(function() { list = new List(); }); - it('should add an array with multiple items:', function () { + it('should add an array with multiple items', function () { list.addList([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} ]); - assert(Buffer.isBuffer(list.items[0].contents)); - assert(Buffer.isBuffer(list.items[1].contents)); + assert(isBuffer(list.items[0].contents)); + assert(isBuffer(list.items[1].contents)); }); - it('should take a sync callback on `addList`:', function () { + it('should take a callback on `addList`', function () { function addContents(item) { item.contents = new Buffer(item.path.charAt(0)); } @@ -145,9 +229,97 @@ describe('list', function () { { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, ], addContents); - assert(Buffer.isBuffer(list.items[0].contents)); - assert(Buffer.isBuffer(list.items[1].contents)); - assert(Buffer.isBuffer(list.items[2].contents)); + assert(isBuffer(list.items[0].contents)); + assert(isBuffer(list.items[1].contents)); + assert(isBuffer(list.items[2].contents)); + }); + + it('should throw an error when the list is not an array', function () { + function addContents(item) { + item.contents = new Buffer(item.path.charAt(0)); + } + + (function () { + list.addList({ + 'a.md': {locals: { date: '2014-01-01', foo: 'zzz', bar: 1 }}, + 'f.md': {locals: { date: '2014-01-01', foo: 'mmm', bar: 2 }}, + 'd.md': {locals: { date: '2014-01-01', foo: 'xxx', bar: 3 }}, + }, addContents); + + assert(isBuffer(list.items[0].contents)); + assert(isBuffer(list.items[1].contents)); + assert(isBuffer(list.items[2].contents)); + }).should.throw('expected list to be an array.'); + }); + + it('should signal `loaded` when finished (addList)', function () { + list.on('addList', function (items) { + var len = items.length, i = -1; + while (++i < len) { + if (items[i].path === 'd.md') { + list.loaded = true; + break; + } + list.addItem('foo/' + items[i].path, items[i]); + } + }); + + list.addList([ + { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, + { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + ]); + + assert.equal(list.items.length, 2); + assert.equal(list.keys.indexOf('d.md'), -1); + }); + }); + + describe('queue', function () { + beforeEach(function () { + list = new List(); + }); + + it('should emit arguments on addItem', function (done) { + list.on('addItem', function (args) { + assert(args[0] === 'a'); + assert(args[1] === 'b'); + assert(args[2] === 'c'); + assert(args[3] === 'd'); + assert(args[4] === 'e'); + done(); + }); + + list.addItem('a', 'b', 'c', 'd', 'e'); + }); + + it('should expose the `queue` property for loading items', function () { + list.queue.push(list.item('b', {path: 'b'})); + + list.addItem('a', {path: 'a'}); + assert(list.items[0].key === 'a'); + assert(list.items[1].key === 'b'); + }); + + it('should load all items on the queue when addItem is called', function () { + list.on('addItem', function (args) { + var len = args.length; + var last = args[len - 1]; + if (typeof last === 'string') { + args[len - 1] = { content: last }; + } + }); + + list.addItem('a.html', 'aaa'); + list.addItem('b.html', 'bbb'); + list.addItem('c.html', 'ccc'); + + assert(list.items[0].path === 'a.html'); + assert(list.getItem('a.html').content === 'aaa'); + assert(list.items[1].path === 'b.html'); + assert(list.getItem('b.html').content === 'bbb'); + assert(list.items[2].path === 'c.html'); + assert(list.getItem('c.html').content === 'ccc'); }); }); @@ -168,7 +340,7 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should sort a list:', function () { + it('should sort a list', function () { list = new List(); list.addList(items); @@ -202,7 +374,7 @@ describe('list', function () { ]); }); - it('should not sort the (original) instance list `items`:', function () { + it('should not sort the (original) instance list `items`', function () { list = new List(); list.addList(items); @@ -240,7 +412,7 @@ describe('list', function () { ]); }); - it('should pass options to array-sort from the constructor:', function () { + it('should pass options to array-sort from the constructor', function () { list = new List({sort: {reverse: true}}); list.addList(items); @@ -274,7 +446,7 @@ describe('list', function () { ]); }); - it('should pass options to array-sort from the sortBy method:', function () { + it('should pass options to array-sort from the sortBy method', function () { list = new List(); list.addList(items); @@ -326,7 +498,7 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should group a list by a property:', function () { + it('should group a list by a property', function () { list = new List(); list.addList(items); @@ -357,7 +529,7 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should group a list by a property:', function () { + it('should group a list by a property', function () { list = new List(items); var context = list @@ -395,7 +567,7 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should paginate a list:', function () { + it('should paginate a list', function () { list = new List(items); var res = list.paginate(); @@ -404,7 +576,15 @@ describe('list', function () { assert.containEql(res[1].items, items.slice(10)); }); - it('should paginate a list with given options:', function () { + it('should add pager properties', function () { + list = new List({pager: true}); + list.addList(items); + list.items.forEach(function (item, i) { + assert.equal(item.data.pager.index, i); + }); + }); + + it('should paginate a list with given options', function () { list = new List(items); var res = list.paginate({limit: 5}); @@ -420,15 +600,15 @@ describe('list', function () { views = new Views(); }); - it('should add views from an instance of Views:', function () { + it('should add views from an instance of Views', function () { views.addViews({ one: {content: 'foo'}, two: {content: 'bar'} }); list = new List(views); - assert(Buffer.isBuffer(list.items[0].contents)); - assert(Buffer.isBuffer(list.items[1].contents)); + assert(isBuffer(list.items[0].contents)); + assert(isBuffer(list.items[1].contents)); }); }); @@ -436,14 +616,14 @@ describe('list', function () { beforeEach(function() { list = new List(); }); - it('should get the index of a key when key is not renamed:', function () { + it('should get the index of a key when key is not renamed', function () { list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); list.addItem('a/b/c/eee.hbs', {content: 'eee'}); assert(list.getIndex('a/b/c/ddd.hbs') === 0); assert(list.getIndex('a/b/c/eee.hbs') === 1); }); - it('should get the index of a key when key is renamed:', function () { + it('should get the index of a key when key is renamed', function () { list = new List({ renameKey: function (key) { return path.basename(key); @@ -463,12 +643,12 @@ describe('list', function () { list = new List(); }); - it('should get an view from `views`:', function () { + it('should get an view from `views`', function () { list.addItem('one', {content: 'aaa'}); list.addItem('two', {content: 'zzz'}); assert(list.items.length === 2); - assert(Buffer.isBuffer(list.items[0].contents)); - assert(Buffer.isBuffer(list.getItem('one').contents)); + assert(isBuffer(list.items[0].contents)); + assert(isBuffer(list.getItem('one').contents)); assert(list.getItem('one').contents.toString() === 'aaa'); assert(list.getItem('two').contents.toString() === 'zzz'); }); @@ -479,20 +659,20 @@ describe('list', function () { list = new List(); }); - it('should use middleware on a list:', function () { + it('should use middleware on a list', function () { list.addItem('one', {content: 'aaa'}); list.addItem('two', {content: 'zzz'}); list - .use(function (list, options) { - options.foo = 'bar'; + .use(function () { + this.set('foo', 'bar'); }) .use(function () { this.set('one', 'two'); }); assert(list.one === 'two'); - assert(list.options.foo === 'bar'); + assert(list.foo === 'bar'); }); }); }); diff --git a/test/list.render.js b/test/list.render.js new file mode 100644 index 00000000..b660836c --- /dev/null +++ b/test/list.render.js @@ -0,0 +1,137 @@ +require('mocha'); +require('should'); +var async = require('async'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var pages; + +describe('render', function () { + describe('rendering', function () { + beforeEach(function () { + pages = new List(); + pages.engine('tmpl', require('engine-base')); + }); + + it('should throw an error when no callback is given:', function () { + (function() { + pages.render({}); + }).should.throw('List#render is async and expects a callback function'); + }); + + it('should throw an error when an engine is not defined:', function (done) { + pages.addItem('foo.bar', {content: '<%= name %>'}); + var page = pages.getItem('foo.bar'); + + pages.render(page, function(err) { + assert(err.message === 'List#render cannot find an engine for: .bar'); + done(); + }); + }); + + it('should use helpers to render a item:', function (done) { + var locals = {name: 'Halle'}; + + pages.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + pages.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getItem('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + + assert(res.content === 'a HALLE b'); + done(); + }); + }); + + it('should use helpers when rendering a item:', function (done) { + var locals = {name: 'Halle'}; + pages.helper('upper', function (str) { + return str.toUpperCase(str); + }); + + pages.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getItem('a.tmpl'); + + pages.render(page, function (err, res) { + if (err) return done(err); + assert(res.content === 'a HALLE b'); + done(); + }); + }); + + it('should render a template when contents is a buffer:', function (done) { + pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var item = pages.getItem('a.tmpl'); + + pages.render(item, function (err, item) { + if (err) return done(err); + assert(item.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a template when content is a string:', function (done) { + pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var item = pages.getItem('a.tmpl'); + + pages.render(item, function (err, item) { + if (err) return done(err); + assert(item.contents.toString() === 'b'); + done(); + }); + }); + + it('should render a item from its path:', function (done) { + pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + + pages.render('a.tmpl', function (err, item) { + if (err) return done(err); + assert(item.content === 'b'); + done(); + }); + }); + + it('should use a plugin for rendering:', function (done) { + pages.engine('tmpl', require('engine-base')); + pages.option('engine', 'tmpl'); + + pages.addItems({ + 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, + 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, + 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, + 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, + 'e': {content: '<%= title %>', locals: {title: 'eee'}}, + 'f': {content: '<%= title %>', locals: {title: 'fff'}}, + 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, + 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, + 'i': {content: '<%= title %>', locals: {title: 'iii'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + }); + + pages.use(function (collection) { + collection.option('pager', false); + + collection.renderEach = function (cb) { + var list = new List(collection); + + async.map(list.items, function (item, next) { + collection.render(item, next); + }, cb); + }; + }); + + pages.renderEach(function (err, items) { + if (err) return done(err); + assert(items[0].content === 'aaa'); + assert(items[9].content === 'jjj'); + assert(items.length === 10); + done(); + }); + }); + }); +}); diff --git a/test/mergePartials.js b/test/mergePartials.js index 463c86c5..92e2f77b 100644 --- a/test/mergePartials.js +++ b/test/mergePartials.js @@ -1,11 +1,13 @@ require('should'); -var omit = require('object.omit'); -var App = require('..'); -var app, keys; +var support = require('./support'); +var App = support.resolve(); +var app; describe('mergePartials', function () { beforeEach(function () { app = new App(); + // reset views + app.views = {}; }); it('should merge multiple partials collections onto one collection:', function () { @@ -23,7 +25,7 @@ describe('mergePartials', function () { actual.partials.should.have.properties(['a', 'b', 'c']); }); - it('should keep partials collections on seperate collections:', function () { + it('should keep partials collections on separate collections:', function () { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); @@ -33,11 +35,7 @@ describe('mergePartials', function () { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({ - mergePartials: false, - mergeTypes: ['foos', 'bars', 'bazs'] - }); - + var actual = app.mergePartials({mergePartials: false}); actual.should.not.have.property('partials'); actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); }); @@ -57,11 +55,7 @@ describe('mergePartials', function () { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({ - mergePartials: false, - mergeTypes: ['foos', 'bars', 'bazs'] - }); - + var actual = app.mergePartials({mergePartials: false}); actual.should.not.have.property('partials'); actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); arr.should.eql(['aaa', 'bbb', 'ccc']); @@ -82,11 +76,7 @@ describe('mergePartials', function () { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({ - mergePartials: false, - mergeTypes: ['foos', 'bars', 'bazs'] - }); - + var actual = app.mergePartials({mergePartials: false}); actual.should.eql({ foos: {a: 'aaa onMerge'}, bars: {b: 'bbb onMerge'}, @@ -111,7 +101,7 @@ describe('mergePartials', function () { app.baz('c', {path: 'c', content: 'ccc'}); var actual = app.mergePartials({mergePartials: false}); - actual.should.have.property('bazs', { c: 'ccc' }); + actual.should.eql({ bazs: { c: 'ccc' } }); }); }); diff --git a/test/out-fixtures/fixtures/vinyl/test.coffee b/test/out-fixtures/fixtures/vinyl/test.coffee deleted file mode 100644 index 6a537b5b..00000000 --- a/test/out-fixtures/fixtures/vinyl/test.coffee +++ /dev/null @@ -1 +0,0 @@ -1234567890 \ No newline at end of file diff --git a/test/partials.js b/test/partials.js new file mode 100644 index 00000000..575e7b54 --- /dev/null +++ b/test/partials.js @@ -0,0 +1,202 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app, pages; + +describe('partials', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.engine('hbs', require('engine-handlebars')); + + app.create('partials', { viewType: 'partial' }); + app.create('include', { viewType: 'partial' }); + app.create('layouts', { viewType: 'layout' }); + pages = app.create('page'); + }); + + it('should inject a partial with a helper:', function (done) { + app.include('base', {path: 'base.tmpl', content: 'xyz'}); + app.pages('a.tmpl', {path: 'a.tmpl', content: 'a <%= include("base") %> c'}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a xyz c'); + done(); + }); + }); + + it('should inject a partial with a helper on a collection:', function (done) { + app.include('base', {path: 'base.tmpl', content: 'xyz'}); + pages.engine('.tmpl', require('engine-handlebars')); + pages.helpers(app._.helpers.sync); + pages.asyncHelpers(app._.helpers.async); + pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{include "base" }} c'}); + var page = pages.getView('a.tmpl'); + + pages.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a xyz c'); + done(); + }); + }); + + it('should use handlebars partial with a helper on a collection:', function (done) { + app.include('base', {path: 'base.tmpl', content: 'xyz'}); + pages.engine('.tmpl', require('engine-handlebars')); + pages.helpers(app._.helpers.sync); + pages.asyncHelpers(app._.helpers.async); + pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{> base }} c'}); + + var page = pages.getView('a.tmpl'); + var locals = app.mergePartials(this.options); + + pages.render(page, locals, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a xyz c'); + done(); + }); + }); + + it('should use layouts with partials:', function (done) { + app.layout('default', {path: 'a.tmpl', content: 'a {% body %} c'}); + app.include('b', {path: 'b.tmpl', content: 'b', layout: 'default'}); + app.pages('a.tmpl', {path: 'c.tmpl', content: '<%= include("b") %>'}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c'); + done(); + }); + }); + + it('should add `layoutApplied` after layout is applied:', function (done) { + app.layout('default', {path: 'a.tmpl', content: 'a {% body %} c'}); + app.include('b', {path: 'b.tmpl', content: 'b', layout: 'default'}); + app.pages('a.tmpl', {path: 'c.tmpl', content: '<%= include("b") %>'}); + var page = app.pages.getView('a.tmpl'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(app.layouts.getView('default').options.layoutApplied); + assert.equal(view.content, 'a b c'); + done(); + }); + }); + + it('should pass partials to handlebars:', function (done) { + app.onMerge(/\.hbs$/, function (view, next) { + app.applyLayout(view); + next(); + }); + + app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); + app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); + app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); + var page = app.pages.getView('a.hbs'); + + app.render(page, function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a foo c'); + done(); + }); + }); + + it('should only merge in the specified viewTypes:', function (done) { + app.onMerge(/\.hbs$/, function (view, next) { + app.applyLayout(view); + next(); + }); + + app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); + app.option('mergeTypes', ['includes']); + + app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default'}); + app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); + + app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); + app.pages.getView('a.hbs') + .render(function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a foo c'); + done(); + }); + + }); + + it('should merge the specified viewTypes in the order defined:', function (done) { + app.onMerge(/\.hbs$/, function (view, next) { + app.applyLayout(view); + next(); + }); + + app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); + app.option('mergeTypes', ['includes', 'partials']); + + app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default'}); + app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); + + app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); + app.pages.getView('a.hbs') + .render(function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a bar c'); + done(); + }); + }); + + it('should not merge in partials with `options.nomerge` defined:', function (done) { + app.onMerge(/\.hbs$/, function (view, next) { + app.applyLayout(view); + next(); + }); + + app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); + app.option('mergeTypes', ['includes', 'partials']); + + app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default', options: {nomerge: true}}); + app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); + + app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); + app.pages.getView('a.hbs') + .render(function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a foo c'); + done(); + }); + }); + + it('should emit an `onMerge` event:', function (done) { + app.on('onMerge', function (view) { + app.applyLayout(view); + }); + + app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); + app.option('mergeTypes', ['includes', 'partials']); + + app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default'}); + app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); + + app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); + app.pages.getView('a.hbs') + .render(function (err, view) { + if (err) return done(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a bar c'); + done(); + }); + }); +}); diff --git a/test/renameKey.js b/test/renameKey.js index 13a8e080..9662f039 100644 --- a/test/renameKey.js +++ b/test/renameKey.js @@ -1,5 +1,6 @@ var path = require('path'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; function renameKey(key) { diff --git a/test/render.js b/test/render.js index 2883877c..1c0b05e8 100644 --- a/test/render.js +++ b/test/render.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('render', function () { @@ -51,7 +52,7 @@ describe('render', function () { it('should emit a re-thrown error when rethrow is true:', function (done) { delete view.locals.name; - app = new App({rethrow: true, silent: true}); + app = new App({rethrow: true, silent: false}); app.engine('tmpl', require('engine-base')); app.create('page'); diff --git a/test/routes.js b/test/routes.js index e56ba519..fed137f5 100644 --- a/test/routes.js +++ b/test/routes.js @@ -1,6 +1,7 @@ require('should'); var assert = require('assert'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; function append(str) { diff --git a/test/store.js b/test/store.js index 627da3f3..a4dc3281 100644 --- a/test/store.js +++ b/test/store.js @@ -205,7 +205,7 @@ describe('events', function () { it('should emit `del` when a value is delted:', function () { var res; app.store.on('del', function (keys) { - keys.should.eql(['a']); + keys.should.eql('a'); assert(typeof app.store.get('a') === 'undefined'); }); diff --git a/test/support/ignore.js b/test/support/ignore.js new file mode 100644 index 00000000..ef599038 --- /dev/null +++ b/test/support/ignore.js @@ -0,0 +1,6 @@ +module.exports = [ + 'addEventListener', + 'removeEventListener', + 'removeAllListeners', + 'removeListener' +]; \ No newline at end of file diff --git a/test/support/index.js b/test/support/index.js index f0ecd8fa..43491cef 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -1,13 +1,17 @@ 'use strict'; -var assert = module.exports = require('assert'); -require('should'); +var path = require('path'); +var pkg = require('load-pkg'); +var lookup = require('look-up'); +var assert = require('assert'); +var ignore = require('./ignore'); +var cache = {}; -assert.containEql = function containEql(actual, expected) { +exports.containEql = function containEql(actual, expected) { if (Array.isArray(expected)) { var len = expected.length; while (len--) { - assert.containEql(actual[len], expected[len]); + exports.containEql(actual[len], expected[len]); } } else { for (var key in expected) { @@ -15,3 +19,57 @@ assert.containEql = function containEql(actual, expected) { } } }; + +exports.keys = function keys(obj) { + var arr = []; + for (var key in obj) { + if (ignore.indexOf(key) === -1) { + arr.push(key); + } + } + return arr; +}; + +exports.resolve = function(filepath) { + filepath = filepath || ''; + var key = 'app:' + filepath; + if (cache.hasOwnProperty(key)) { + return cache[key]; + } + + var prefix = pkg.name !== 'templates' + ? 'templates' + : ''; + + var base = filepath + ? path.join(prefix, filepath) + : process.cwd(); + + var fp = tryResolve(base); + + if (typeof fp === 'undefined') { + throw new Error('cannot resolve: ' + fp); + } + return (cache[key] = require(fp)); +}; + +function tryResolve(name) { + try { + return require.resolve(name); + } catch(err) {} + + try { + return require.resolve(path.resolve(name)); + } catch(err) {} +} + +function tryRequire(name) { + try { + return require(name); + } catch(err) {} + + try { + return require(path.resolve(name)); + } catch(err) {} +} + diff --git a/test/view.content.js b/test/view.content.js index 86cd43b3..ccd21f75 100644 --- a/test/view.content.js +++ b/test/view.content.js @@ -2,7 +2,8 @@ require('mocha'); require('should'); var fs = require('fs'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('content', function () { diff --git a/test/view.events.js b/test/view.events.js index f2d52bdf..eef1dcdc 100644 --- a/test/view.events.js +++ b/test/view.events.js @@ -1,5 +1,6 @@ require('should'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('view.option()', function () { diff --git a/test/view.js b/test/view.js index 1ce9d88d..c16e48de 100644 --- a/test/view.js +++ b/test/view.js @@ -5,7 +5,9 @@ var path = require('path'); var assert = require('assert'); var Stream = require('stream'); var es = require('event-stream'); -var View = require('../').View; +var support = require('./support'); +var App = support.resolve(); +var View = App.View; var view; describe('View', function () { @@ -173,9 +175,7 @@ describe('View', function () { view = new View({path: 'foo', contents: 'a b c', layout: 'default'}); assert(view.layout === 'default'); }); - }); - describe('compile', function () { it('should add a compiled function to `view.fn`', function () { view = new View({path: 'foo', contents: 'a <%= name %> z'}); view.compile(); @@ -192,16 +192,7 @@ describe('View', function () { }); }); - it('should render a template', function (done) { - view = new View({path: 'foo', contents: 'a <%= name %> z'}); - view.render({name: 'Halle'}, function (err, res) { - if (err) return done(err); - assert(res.contents.toString() === 'a Halle z'); - done(); - }); - }); - - it('should render fn using data passed on the constructor', function (done) { + it('should render `fn` using data passed on the constructor', function (done) { view = new View({ path: 'foo', contents: 'a <%= name %> z', @@ -217,6 +208,17 @@ describe('View', function () { done(); }); }); + }); + + describe('render', function () { + it('should render a template', function (done) { + view = new View({path: 'foo', contents: 'a <%= name %> z'}); + view.render({name: 'Halle'}, function (err, res) { + if (err) return done(err); + assert(res.contents.toString() === 'a Halle z'); + done(); + }); + }); it('should render fn using data passed on the constructor', function (done) { view = new View({ @@ -258,15 +260,15 @@ describe('View', function() { describe('isVinyl()', function() { it('should return true on a vinyl object', function(done) { var view = new View(); - View.isVinyl(view).should.equal(true); + assert(View.isVinyl(view) === true); done(); }); it('should return false on a normal object', function(done) { - View.isVinyl({}).should.equal(false); + assert(View.isVinyl({}) === false); done(); }); it('should return false on a null object', function(done) { - View.isVinyl({}).should.equal(false); + assert(View.isVinyl({}) === false); done(); }); }); diff --git a/test/view.methods.js b/test/view.methods.js index 635d9922..bb120c69 100644 --- a/test/view.methods.js +++ b/test/view.methods.js @@ -1,5 +1,6 @@ require('should'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('view.option()', function () { diff --git a/test/view.option.js b/test/view.option.js index ffc185ea..4190be74 100644 --- a/test/view.option.js +++ b/test/view.option.js @@ -1,5 +1,6 @@ require('should'); -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); var app; describe('view.option()', function () { diff --git a/test/view.render.js b/test/view.render.js index d3a84d56..5fe5c7dd 100644 --- a/test/view.render.js +++ b/test/view.render.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); -var View = require('../').View; -var App = require('..'); +var support = require('./support'); +var App = support.resolve(); +var View = App.View; var view, app; describe('helpers', function () { @@ -10,17 +11,44 @@ describe('helpers', function () { app = new App(); view = new View(); app.engine('tmpl', require('engine-base')); + app.create('layouts', {viewType: 'layout'}); app.create('pages'); }); it('should expose `.render` for rendering a view:', function (done) { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) + app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) .render({a: 'bbb'}, function (err, res) { if (err) return done(err); res.content.should.equal('bbb'); done(); }); }); + + it('should render a view with a layout', function (done) { + app.layout('default.tmpl', {content: 'a {% body %} b'}); + app.page('a.tmpl', {content: '<%= title %>', layout: 'default.tmpl'}) + .render({title: 'zzz'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('a zzz b'); + done(); + }); + }); + + it('should render a view with a layout', function (done) { + app.layout('foo.tmpl', {content: 'a {% body %} a'}); + app.layout('bar.tmpl', {content: 'b {% body %} b'}); + app.pages('a.tmpl', {content: '<%= title %>'}); + + app.pages.getView('a.tmpl') + .option('resolveLayout', function () { + return 'bar.tmpl'; + }) + .render({title: 'zzz'}, function (err, res) { + if (err) return done(err); + res.content.should.equal('b zzz b'); + done(); + }); + }); }); }); diff --git a/test/view.set.js b/test/view.set.js index 9828c518..60eb98a8 100644 --- a/test/view.set.js +++ b/test/view.set.js @@ -2,7 +2,8 @@ require('mocha'); require('should'); var fs = require('fs'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; describe('set', function () { @@ -26,7 +27,7 @@ describe('set', function () { .render(function (err, res) { if (err) return done(err); - assert(res.contents.toString() === 'Brooke'); + assert(res.content === 'Brooke'); done(); }); }); diff --git a/test/view.use.js b/test/view.use.js index a17efff0..3706c0b0 100644 --- a/test/view.use.js +++ b/test/view.use.js @@ -1,7 +1,8 @@ require('mocha'); require('should'); var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var View = App.View; var view; diff --git a/test/viewTypes.js b/test/viewTypes.js index a6c89e9f..47da0a9f 100644 --- a/test/viewTypes.js +++ b/test/viewTypes.js @@ -1,8 +1,8 @@ var assert = require('assert'); -var App = require('../'); +var support = require('./support'); +var App = support.resolve(); var app; - describe('viewType', function () { describe('view types', function () { beforeEach(function () { diff --git a/test/views.js b/test/views.js index ce516bee..2a88024a 100644 --- a/test/views.js +++ b/test/views.js @@ -2,10 +2,13 @@ require('mocha'); require('should'); var path = require('path'); var assert = require('assert'); +var typeOf = require('kind-of'); var isBuffer = require('is-buffer'); -var List = require('..').List; -var View = require('..').View; -var Views = require('..').Views; +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var View = App.View; +var Views = App.Views; var collection; describe('views', function () { @@ -14,6 +17,11 @@ describe('views', function () { var collection = new Views(); assert(collection instanceof Views); }); + + it('should instantiate without `new`:', function () { + var collection = Views(); + assert(collection instanceof Views); + }); }); describe('static methods', function () { @@ -27,20 +35,47 @@ describe('views', function () { collection = new Views(); }); - it('should expose `set`', function () { - assert(typeof collection.set ==='function'); + var methods = [ + 'use', + 'setView', + 'addView', + 'addViews', + 'addList', + 'getView', + 'constructor', + 'set', + 'get', + 'del', + 'define', + 'visit', + 'on', + 'once', + 'off', + 'emit', + 'listeners', + 'hasListeners' + ]; + + methods.forEach(function (method) { + it('should expose ' + method + ' method', function () { + assert(typeof collection[method] === 'function'); + }); }); - it('should expose `get`', function () { - assert(typeof collection.get ==='function'); + + it('should expose isCollection property', function () { + assert(typeof collection.isCollection === 'boolean'); }); - it('should expose `visit`', function () { - assert(typeof collection.visit ==='function'); + + it('should expose queue property', function () { + assert(Array.isArray(collection.queue)); }); - it('should expose `define`', function () { - assert(typeof collection.define ==='function'); + + it('should expose views property', function () { + assert(typeOf(collection.views) === 'object'); }); - it('should expose `addView`', function () { - assert(typeof collection.addView ==='function'); + + it('should expose options property', function () { + assert(typeOf(collection.options) === 'object'); }); }); @@ -171,9 +206,9 @@ describe('views', function () { var one = collection.view('one', {content: 'foo'}); var two = collection.view('two', {content: 'bar'}); - assert(one instanceof View); + assert(one.isView); assert(one.path === 'one'); - assert(two instanceof View); + assert(two.isView); assert(two.path === 'two'); }); @@ -181,9 +216,9 @@ describe('views', function () { var one = collection.view({path: 'one', content: 'foo'}); var two = collection.view({path: 'two', content: 'bar'}); - assert(one instanceof View); + assert(one.isView); assert(one.path === 'one'); - assert(two instanceof View); + assert(two.isView); assert(two.path === 'two'); }); }); @@ -235,6 +270,7 @@ describe('views', function () { while (list.length) { collection.addView({path: list.pop()}); } + this.loaded = true; }); collection.addList(['a.txt', 'b.txt', 'c.txt']); @@ -242,6 +278,27 @@ describe('views', function () { assert(collection.views['a.txt'].path === 'a.txt'); }); + it('should load an array of items from the addList callback:', function () { + var collection = new Views(); + + collection.addList(['a.txt', 'b.txt', 'c.txt'], function (fp) { + return {path: fp}; + }); + assert(collection.views.hasOwnProperty('a.txt')); + assert(collection.views['a.txt'].path === 'a.txt'); + }); + + // it('should not blow up on', function () { + // var collection = new Views(); + // var list = [{path: 'a.txt'}, {path: 'b.txt'}, {path: 'c.txt'}]; + // collection.addList(list, function (item) { + // item.content = path.basename(item.path, path.extname(item.path)); + // }); + // assert(collection.views.hasOwnProperty('a.txt')); + // assert(collection.views['a.txt'].path === 'a.txt'); + // assert(collection.views['a.txt'].content === 'a'); + // }); + it('should load an object of views from an event:', function () { var collection = new Views(); @@ -338,3 +395,51 @@ describe('options', function() { }); }); + +describe('queue', function () { + beforeEach(function () { + collection = new Views(); + }); + + it('should emit arguments on addView', function (done) { + collection.on('addView', function (args) { + assert(args[0] === 'a'); + assert(args[1] === 'b'); + assert(args[2] === 'c'); + assert(args[3] === 'd'); + assert(args[4] === 'e'); + done(); + }); + + collection.addView('a', 'b', 'c', 'd', 'e'); + }); + + it('should expose the `queue` property for loading views', function () { + collection.queue.push(collection.view('b', {path: 'b'})); + + collection.addView('a', {path: 'a'}); + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + }); + + it('should load all views on the queue when addView is called', function () { + collection.on('addView', function (args) { + var len = args.length; + var last = args[len - 1]; + if (typeof last === 'string') { + args[len - 1] = { content: last }; + } + }); + + collection.addView('a.html', 'aaa'); + collection.addView('b.html', 'bbb'); + collection.addView('c.html', 'ccc'); + + assert(collection.views.hasOwnProperty('a.html')); + assert(collection.getView('a.html').content === 'aaa'); + assert(collection.views.hasOwnProperty('b.html')); + assert(collection.getView('b.html').content === 'bbb'); + assert(collection.views.hasOwnProperty('c.html')); + assert(collection.getView('c.html').content === 'ccc'); + }); +}); \ No newline at end of file From 6cec9e7351527cb4281f236d64a85bf9819aed44 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 14 Oct 2015 14:19:54 -0400 Subject: [PATCH 015/282] add tasks for pulling down assemble tests --- gulpfile.js | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index cdbcc986..46f6ce12 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,10 +1,18 @@ +'use strict'; + var gulp = require('gulp'); -var mocha = require('gulp-mocha'); +var stylish = require('jshint-stylish'); var istanbul = require('gulp-istanbul'); var jshint = require('gulp-jshint'); -require('jshint-stylish'); +var mocha = require('gulp-mocha'); +var git = require('gulp-git'); +var del = require('rimraf'); -var lint = ['index.js', 'lib/*.js']; +var lint = ['index.js', 'lib/{*,plugins/*}.js']; + +function url(repo) { + return 'https://github.com/' + repo; +} gulp.task('coverage', function () { return gulp.src(lint) @@ -12,19 +20,37 @@ gulp.task('coverage', function () { .pipe(istanbul.hookRequire()); }); -gulp.task('mocha', ['coverage'], function () { +gulp.task('test', ['clone', 'coverage'], function () { return gulp.src('test/*.js') .pipe(mocha({reporter: 'spec'})) + .pipe(istanbul.writeReports()) .pipe(istanbul.writeReports({ reporters: [ 'text' ], reportOpts: {dir: 'coverage', file: 'summary.txt'} - })) + })); }); -gulp.task('jshint', function () { - return gulp.src(lint) +gulp.task('lint', function () { + return gulp.src(lint.concat('test/*.js')) .pipe(jshint()) - .pipe(jshint.reporter('jshint-stylish')) + .pipe(jshint.reporter(stylish)); +}); + +gulp.task('spec', ['clone'], function (cb) { + gulp.src('test/_spec/test/*.js') + .pipe(mocha({reporter: 'spec'})) + .on('end', function () { + del('test/_spec', cb); + }); +}); + +gulp.task('clone', function(cb) { + del('test/_spec', function (err) { + if (err) return cb(err); + git.clone(url('assemble/assemble'), { + args: 'test/_spec' + }, cb); + }); }); -gulp.task('default', ['mocha', 'jshint']); +gulp.task('default', ['test', 'lint']); From afd54283820f0c87b939b2fbb4c0ed84ae623116 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 14 Oct 2015 14:20:07 -0400 Subject: [PATCH 016/282] inherit assemble --- index.js | 278 +++++-------------------------------------------------- 1 file changed, 25 insertions(+), 253 deletions(-) diff --git a/index.js b/index.js index 72954e59..792fa3bb 100644 --- a/index.js +++ b/index.js @@ -5,15 +5,12 @@ */ var path = require('path'); -var store = require('data-store'); -// var Assemble = require('assemble'); -var Templates = require('templates'); -var Composer = require('composer'); -var proto = Composer.prototype; -var plugin = require('./lib/plugins'); +var Assemble = require('assemble'); +var utils = require('./lib/utils'); var lib = require('./lib'); -var Locals = lib.locals; -var utils = lib.utils; +var datastore = lib.store; +var config = lib.config; +var locals = lib.locals; /** * Create an `verb` application. This is the main function exported @@ -27,263 +24,38 @@ var utils = lib.utils; * @api public */ +var defaults = require('./lib/plugins/collections'); + function Verb(options) { if (!(this instanceof Verb)) { return new Verb(options); } - Templates.apply(this, arguments); - Composer.apply(this, arguments); - this.options = options || {}; - this.initVerb(this.options); -} - -Templates.inherit(Verb, Composer); - -/** - * `Verb` prototype methods - */ - -Templates.extend(Verb, { - constructor: Verb, - - /** - * Initialize Verb defaults - */ - - initVerb: function(opts) { - this.use(plugin.collections()); - this.use(plugin.questions()); - - this.initEngines(this); - this.initMiddleware(this); - this.initListeners(this); - lib.helpers(this); - - this.store = store('verb', opts.store); - this.locals = new Locals('verb', this); - lib.config(this); - }, - - initListeners: function (app) { - this.on('option', function (key) { - utils.reloadViews(app, key); - }); - - this.on('use', function () { - utils.reloadViews(app); - }); - }, - - /** - * Default template engines - */ - - initEngines: function () { - this.option('rethrow', { regex: /\{%=?([^%]*)%}/ }); - this.engine('md', require('engine-base'), { - delims: ['{%', '%}'] - }); - }, - - /** - * Default middleware for parsing front matter - */ - - initMiddleware: function (app) { - this.onLoad(/\.md$/, function (view, next) { - utils.matter.parse(view, next); - }); - }, - - /** - * Glob patterns or filepaths to source files. - * - * ```js - * app.src('src/*.hbs', {layout: 'default'}); - * ``` - * @name .src - * @param {String|Array} `glob` Glob patterns or file paths to source files. - * @param {Object} `options` Options or locals to merge into the context and/or pass to `src` plugins - * @api public - */ - src: function () { - return utils.vfs.src.apply(utils.vfs, arguments); - }, + options = utils.defaults({}, options, { + reload: false + }); - /** - * Glob patterns or paths for symlinks. - * - * ```js - * app.symlink('src/**'); - * ``` - * @name .symlink - * @param {String|Array} `glob` - * @api public - */ + Assemble.call(this, options); + this.define('isVerb', true); - symlink: function () { - return utils.vfs.symlink.apply(utils.vfs, arguments); - }, - /** - * Specify a destination for processed files. - * - * ```js - * app.dest('dist/'); - * ``` - * @name .dest - * @param {String|Function} `dest` File path or rename function. - * @param {Object} `options` Options and locals to pass to `dest` plugins - * @api public - */ - - dest: function (dest) { - if (!dest) throw new Error('expected dest to be a string or function.'); - return utils.vfs.dest.apply(utils.vfs, arguments); - }, - - /** - * Copy files with the given glob `patterns` to the specified `dest`. - * - * ```js - * app.task('assets', function() { - * app.copy('assets/**', 'dist/'); - * }); - * ``` - * @name .copy - * @param {String|Array} `patterns` Glob patterns of files to copy. - * @param {String|Function} `dest` Desination directory. - * @return {Stream} Stream, to continue processing if necessary. - * @api public - */ - - copy: function(patterns, dest, options) { - return utils.vfs.src(patterns, options) - .pipe(utils.vfs.dest(dest, options)); - }, - - /** - * Push a view collection into a vinyl stream. - * - * ```js - * app.toStream('posts', function(file) { - * return file.path !== 'index.hbs'; - * }) - * ``` - * @name .toStream - * @param {String} `collection` The name of the view collection to push into the stream. - * @param {Function} Optionally pass a filter function to use for filtering views. - * @return {Stream} - * @api public - */ - - toStream: function (name) { - var views = this.getViews(name) || {}; - var stream = utils.through.obj(); - setImmediate(function () { - Object.keys(views).forEach(function (key) { - stream.write(views[key]); - }); - stream.end(); - }); - return utils.srcStream(stream); - }, - - /** - * Render a vinyl file. - * - * ```js - * app.src('*.hbs') - * .pipe(app.renderFile()); - * ``` - * - * @name .renderFile - * @param {Object} `locals` Optionally locals to pass to the template engine for rendering. - * @return {Object} - * @api public - */ - - renderFile: function (locals) { - var app = this; - var collection = this.collection(); - return utils.through.obj(function (file, enc, cb) { - if (typeof locals === 'function') { - cb = locals; - locals = {}; - } - - var view = collection.setView(file); - app.handleView('onLoad', view); - - var ctx = utils.merge({}, app.cache.data, locals, view.data); - app.render(view, ctx, function (err, res) { - if (err) return cb(err); - file = new utils.Vinyl(res); - cb(null, file); - }); - }); - }, - - /** - * Define a task to be run when the task is called. - * - * ```js - * app.task('default', function() { - * app.src('templates/*.hbs') - * .pipe(app.dest('dist/')); - * }); - * ``` - * @name .task - * @param {String} `name` Task name - * @param {Function} `fn` function that is called when the task is run. - * @api public - */ - - task: function (/*name*/) { - utils.runtimes(this); - return proto.task.apply(this, arguments); - }, - - /** - * Run one or more tasks. - * - * ```js - * app.run(['foo', 'bar'], function(err) { - * if (err) console.error('ERROR:', err); - * }); - * ``` - * @name .run - * @param {Array|String} `tasks` Task name or array of task names. - * @param {Function} `cb` callback function that exposes `err` - * @api public - */ - - run: function (/*tasks, cb*/) { - return proto.run.apply(this, arguments); - }, + this.use(datastore); + this.use(locals); + this.use(config); + // lib.helpers(this); + this.use(lib.helpers); + this.use(defaults); + // this.use(ask()); +} - /** - * Re-run the specified task(s) when a file changes. - * - * ```js - * app.task('watch', function() { - * app.watch('docs/*.md', ['docs']); - * }); - * ``` - * - * @param {String|Array} `glob` Filepaths or glob patterns. - * @param {Array} `tasks` Task(s) to watch. - * @api public - */ +/** + * Inherit `Assemble` + */ - watch: function (/*glob, tasks*/) { - return proto.watch.apply(this, arguments); - } -}); +Assemble.extend(Verb); /** - * Expose the `Verb` constructor + * Expose `Verb` */ module.exports = Verb; From 2c20006c33e68c0d13794767f71b3b277243db2e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 14 Oct 2015 14:20:18 -0400 Subject: [PATCH 017/282] reorganize plugins --- lib/config.js | 125 +++++++++++++++++---------------- lib/helpers.js | 5 -- lib/locals.js | 13 ++-- lib/plugins/collections.js | 139 ++++++++++++++++++------------------- lib/plugins/questions.js | 45 ------------ lib/store.js | 11 +++ lib/utils.js | 60 +++++----------- 7 files changed, 172 insertions(+), 226 deletions(-) delete mode 100644 lib/plugins/questions.js create mode 100644 lib/store.js diff --git a/lib/config.js b/lib/config.js index 12c64b15..c4b5fe77 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,7 +1,12 @@ +'use strict'; -var Config = require('map-config'); +var utils = require('./utils'); -module.exports = function (app, config) { +module.exports = function (app) { + if (!app.isApp) return; + + // console.log(app) + var Config = utils.Config; var config = app.locals.cache; var keys = Object.keys(app.views); var views = {}; @@ -31,63 +36,63 @@ module.exports = function (app, config) { }, app); configMap.process(config); +}; - // configMap.process({ - // "ignore": [ - // ".git" - // ], - // "deps": { - // "include": [ - // "readme-includes", - // "readme-badges" - // ], - // "ignore": [ - // "support" - // ] - // }, - // "collections": { - // "docs": { - // "options": { - // "cwd": "fixtures/foo" - // }, - // "set": { - // "a.b.c": "d" - // } - // }, - // "includes": { - // "options": { - // "cwd": "fixtures/bar" - // } - // }, - // "badges": { - // "options": { - // "cwd": "fixtures/baz" - // }, - // "addViews": { - // "foo": "this is a badge" - // } - // } - // }, - // "helpers": { - // "whatever": "@/helper-related" - // }, - // "related": { - // "list": [ - // "composer", - // "assemble", - // "template", - // "engine" - // ] - // }, - // "reflinks": [ - // "jsdiff", - // "option-cache", - // "template", - // "plasma", - // "config-cache" - // ] - // }); +// configMap.process({ +// "ignore": [ +// ".git" +// ], +// "deps": { +// "include": [ +// "readme-includes", +// "readme-badges" +// ], +// "ignore": [ +// "support" +// ] +// }, +// "collections": { +// "docs": { +// "options": { +// "cwd": "fixtures/foo" +// }, +// "set": { +// "a.b.c": "d" +// } +// }, +// "includes": { +// "options": { +// "cwd": "fixtures/bar" +// } +// }, +// "badges": { +// "options": { +// "cwd": "fixtures/baz" +// }, +// "addViews": { +// "foo": "this is a badge" +// } +// } +// }, +// "helpers": { +// "whatever": "@/helper-related" +// }, +// "related": { +// "list": [ +// "composer", +// "assemble", +// "template", +// "engine" +// ] +// }, +// "reflinks": [ +// "jsdiff", +// "option-cache", +// "template", +// "plasma", +// "config-cache" +// ] +// }); - // // console.log(verb.docs.get('a.b.c')); - // console.log(verb._.helpers); -}; +// // console.log(verb.docs.get('a.b.c')); +// console.log(verb._.helpers); diff --git a/lib/helpers.js b/lib/helpers.js index cd354739..d64d82c9 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -3,11 +3,6 @@ var utils = require('./utils'); module.exports = function (app) { - app.asyncHelper('ask', function () { - var fn = app.ask(); - return fn.apply(app, arguments); - }); - app.asyncHelper('related', require('helper-related')(app.options)); app.asyncHelper('reflinks', require('helper-reflinks')); diff --git a/lib/locals.js b/lib/locals.js index 76bef6da..072dfa84 100644 --- a/lib/locals.js +++ b/lib/locals.js @@ -1,18 +1,17 @@ 'use strict'; -var get = require('get-value'); -var set = require('set-value'); +var utils = require('./utils'); function Locals(name, app) { this.cache = app.cache.data[name] || (app.cache.data[name] = {}); } Locals.prototype.get = function(key) { - return get(this.cache, key); + return utils.get(this.cache, key); }; Locals.prototype.set = function(key, value) { - set(this.cache, key, value); + utils.set(this.cache, key, value); return this; }; @@ -20,4 +19,8 @@ Locals.prototype.set = function(key, value) { * Expose Locals */ -module.exports = Locals; +module.exports = function (app) { + app.locals = new Locals('verb', app); +}; + +// module.exports = Locals; diff --git a/lib/plugins/collections.js b/lib/plugins/collections.js index da1bf0fa..316f3412 100644 --- a/lib/plugins/collections.js +++ b/lib/plugins/collections.js @@ -8,86 +8,85 @@ * | files */ -module.exports = function (options) { - var includes = require('readme-includes'); - var badges = require('readme-badges'); - var loader = require('assemble-loader'); - var getView = require('./get-view'); - var utils = require('../utils'); +var includes = require('readme-includes'); +var badges = require('readme-badges'); +var utils = require('../utils'); - return function(app) { - app.use(loader()); - app.use(getView()); +module.exports = function (app) { + app.data({ + author: { + name: 'Jon Schlinkert' + }, + twitter: { + username: 'jonschlinkert' + }, + github: { + username: 'jonschlinkert' + }, + runner: { + name: app.get('cache.data.name'), + url: app.get('cache.data.repository'), + } + }); - app.data({ - author: {name: 'Jon Schlinkert'}, - twitter: {username: 'jonschlinkert'}, - github: {username: 'jonschlinkert'}, - runner: { - name: app.get('cache.data.name'), - url: app.get('cache.data.repository'), - } - }); + app.create('includes', { + viewType: ['partial'], + renameKey: utils.rename, + cwd: includes, + engine: 'md', + }); - app.create('includes', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: includes, - engine: 'md', - }); + app.create('badges', { + viewType: ['partial'], + renameKey: utils.rename, + cwd: badges, + engine: 'md' + }); - app.create('badges', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: badges, - engine: 'md' - }); + app.create('docs', { + viewType: ['partial'], + renameKey: utils.basename, + engine: 'md' + }); - app.create('docs', { - viewType: ['partial'], - renameKey: utils.basename, - engine: 'md' - }); + app.create('layouts', { + viewType: ['layout'], + renameKey: utils.basename, + engine: 'md' + }); - app.create('layouts', { - viewType: ['layout'], - renameKey: utils.basename, - engine: 'md' - }); + app.include('npm', { + content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); - app.include('npm', { - content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); + app.include('copyright', { + content: 'Copyright © 2015, {%= author.name %}.' + }); - app.include('copyright', { - content: 'Copyright © 2015, {%= author.name %}.' - }); + app.include('license', { + content: 'Released under the {%= ask("lslsl") %} license.' + }); - app.include('license', { - content: 'Released under the {%= ask("lslsl") %} license.' - }); - - app.include('author', { - locals: { - author: {name: 'Brian Woodward'}, - username: 'doowb' + app.include('author', { + locals: { + author: { + name: 'Brian Woodward' }, - content: [ - '**{%= author.name %}**', - '', - '+ [github/{%= username %}](https://github.com/{%= username %})', - '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', - ].join('\n') - }); - - app.badge('travis', { - content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' - }); + username: 'doowb' + }, + content: [ + '**{%= author.name %}**', + '', + '+ [github/{%= username %}](https://github.com/{%= username %})', + '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', + ].join('\n') + }); - app.badge('fury', { - content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' - }); + app.badge('travis', { + content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' + }); - app.create('files'); - }; + app.badge('fury', { + content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' + }); }; diff --git a/lib/plugins/questions.js b/lib/plugins/questions.js deleted file mode 100644 index 5810b123..00000000 --- a/lib/plugins/questions.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -var lazy = require('lazy-cache')(require); -lazy('mixin-deep', 'merge'); -lazy('question-cache', 'questions'); -lazy('ask-once', 'ask'); - -module.exports = function (options) { - return function (app) { - - /** - * add a `questions` property to the Verb instance - * @type {Object} - */ - - this.questions = lazy.questions(options); - - /** - * Ask a question, or use a pre-existing value - * to populate the answer. - */ - - app.mixin('ask', function (locals) { - var ctx = lazy.merge({}, this.cache.data, locals || {}); - var ask = lazy.ask({ - questions: this.questions, - store: this.store, - data: ctx - }); - - return function () { - return ask.apply(ask, arguments); - }; - }); - - /** - * Set a question to ask at a later point. - */ - - app.mixin('question', function () { - this.questions.set.apply(this.questions, arguments); - return this; - }); - }; -}; diff --git a/lib/store.js b/lib/store.js new file mode 100644 index 00000000..87989fe9 --- /dev/null +++ b/lib/store.js @@ -0,0 +1,11 @@ +'use strict'; + +var utils = require('./utils'); + +/** + * Expose Locals + */ + +module.exports = function (app) { + app.store = utils.store('verb', app.options.store); +}; diff --git a/lib/utils.js b/lib/utils.js index 0bf3f01e..d1293616 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -7,33 +7,20 @@ var path = require('path'); * Lazily required module dependencies */ -var lazy = require('lazy-cache')(require); - -// type utils -lazy('mixin-deep', 'merge'); - -// file/view utils -lazy('stream-combiner', 'combine'); -lazy('through2', 'through'); -lazy('vinyl-fs', 'vfs'); -lazy('vinyl', 'Vinyl'); - -// engine/template utiles -lazy('parser-front-matter', 'matter'); -lazy('engine-handlebars', 'engine'); - -// task utils -lazy('composer-runtimes'); -lazy('src-stream'); -lazy('dest'); - -var utils = lazy; - -utils.runtimes = function(app) { - if (app.options.runtimes !== false) { - utils.composerRuntimes(app); - } -}; +var utils = require('lazy-cache')(require); +var fn = require; + +require = utils; +require('data-store', 'store'); +require('mixin-deep', 'merge'); +require('get-value', 'get'); +require('set-value', 'set'); +require('defaults-shallow', 'defaults'); +require('parser-front-matter', 'matter'); +require('resolve-dir'); +require('engine-base', 'engine'); +require('map-config', 'Config'); +require = fn; utils.basename = function(key) { return path.basename(key, path.extname(key)); @@ -58,7 +45,11 @@ utils.tryRead = function(fp) { }; utils.npm = function(name) { - return utils.tryRequire(name) || utils.tryRequire(path.resolve(name)); + return utils.tryRequire(name) || utils.tryRequire(utils.resolve(name)); +}; + +utils.resolve = function(fp) { + return path.resolve(utils.resolveDir(fp)); }; utils.identity = function(val) { @@ -84,19 +75,6 @@ utils.resolveConfig = function (config, fn) { } }; -utils.reloadViews = function reloadViews(app, key) { - for (var name in app.views) { - if (app.views.hasOwnProperty(name)) { - var views = app.views[name]; - - if (!key || typeof app[name][key] !== 'function') { - app.create(name, app[name].options); - app[name].addViews(views); - } - } - } -}; - /** * Expose utils */ From b405a248eb432f545825550f11ceb7425504754f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 27 Oct 2015 05:49:08 -0400 Subject: [PATCH 018/282] clean out old files --- lib/pipeline/format.js | 85 -------------------------- lib/pipeline/index.js | 1 - lib/plugins/collections.js | 92 ---------------------------- lib/plugins/get-view.js | 30 --------- lib/plugins/index.js | 1 - middleware/pretty.js | 121 +++++++++++++++++++++++++++++++++++++ test/app.file.js | 20 ------ test/app.files.js | 22 ------- test/app.handlers.js | 45 ++++++-------- test/app.page.js | 20 ------ test/app.pages.js | 22 ------- test/app.partial.js | 20 ------ test/app.partials.js | 22 ------- test/item.js | 1 - test/questions.js | 58 ++++++++++++++++++ test/store.js | 7 ++- 16 files changed, 202 insertions(+), 365 deletions(-) delete mode 100644 lib/pipeline/format.js delete mode 100644 lib/pipeline/index.js delete mode 100644 lib/plugins/collections.js delete mode 100644 lib/plugins/get-view.js delete mode 100644 lib/plugins/index.js create mode 100644 middleware/pretty.js delete mode 100644 test/app.file.js delete mode 100644 test/app.files.js delete mode 100644 test/app.page.js delete mode 100644 test/app.pages.js delete mode 100644 test/app.partial.js delete mode 100644 test/app.partials.js create mode 100644 test/questions.js diff --git a/lib/pipeline/format.js b/lib/pipeline/format.js deleted file mode 100644 index 6bd74098..00000000 --- a/lib/pipeline/format.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -var through = require('through2'); -var Remarkable = require('remarkable'); -var PluginError = require('plugin-error'); -var prettify = require('pretty-remarkable'); -var extend = require('extend-shallow'); - - -module.exports = function(locals) { - var argv = this.get('argv'); - var app = this; - - // add a trailing newline to docs? - var newline = argv.newline || this.config.get('newline'); - - return through.obj(function (file, enc, cb) { - if (file.isNull()) { - this.push(file); - return cb(); - } - - try { - if (file.path.indexOf('README.md') === -1 || noformat(app, file, locals, argv)) { - this.push(file); - return cb(); - } - - // pass some extra formatting info to `pretty-remarkable` - var opts = extend({}, locals, file.options); - opts.username = app.get('data.username'); - opts.name = app.get('data.author.name'); - - // prettify - var str = pretty(file.contents.toString(), opts); - str = str.trim() + (newline ? '\n' : ''); - str = fixList(str); - - // rebuffer contents - file.contents = new Buffer(str); - this.push(file); - return cb(); - } catch(err) { - this.emit('error', new PluginError('formatter plugin', err, {stack: true})); - return cb(); - } - }); -}; - -/** - * Fix list formatting - */ - -function fixList(str) { - str = str.replace(/([ ]{1,4}[+-] \[?[^)]+\)?)\n\n(\s*)([*-]) /gm, '$1\n$2$3 '); - str = str.split('__{_}_*').join('**{*}**'); - return str; -} - -/** - * Instantiate `Remarkable` and use the `prettify` plugin - * on the given `str`. - * - * @param {String} `str` - * @param {Object} `options` - * @return {String} - */ - -function pretty(str, options) { - return new Remarkable(options) - .use(prettify) - .render(str); -} - -/** - * Push the `file` through if the user has specfied - * not to format it. - */ - -function noformat(app, file, locals, argv) { - return app.isTrue('noformat') || app.isFalse('format') - || file.noformat === true || file.format === false - || locals.noformat === true || locals.format === false - || argv.noformat === true || argv.format === false; -} diff --git a/lib/pipeline/index.js b/lib/pipeline/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/pipeline/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/lib/plugins/collections.js b/lib/plugins/collections.js deleted file mode 100644 index 316f3412..00000000 --- a/lib/plugins/collections.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict'; - -/** - * Plugin for creating default view collections - * | includes - * | layouts - * | pages - * | files - */ - -var includes = require('readme-includes'); -var badges = require('readme-badges'); -var utils = require('../utils'); - -module.exports = function (app) { - app.data({ - author: { - name: 'Jon Schlinkert' - }, - twitter: { - username: 'jonschlinkert' - }, - github: { - username: 'jonschlinkert' - }, - runner: { - name: app.get('cache.data.name'), - url: app.get('cache.data.repository'), - } - }); - - app.create('includes', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: includes, - engine: 'md', - }); - - app.create('badges', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: badges, - engine: 'md' - }); - - app.create('docs', { - viewType: ['partial'], - renameKey: utils.basename, - engine: 'md' - }); - - app.create('layouts', { - viewType: ['layout'], - renameKey: utils.basename, - engine: 'md' - }); - - app.include('npm', { - content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); - - app.include('copyright', { - content: 'Copyright © 2015, {%= author.name %}.' - }); - - app.include('license', { - content: 'Released under the {%= ask("lslsl") %} license.' - }); - - app.include('author', { - locals: { - author: { - name: 'Brian Woodward' - }, - username: 'doowb' - }, - content: [ - '**{%= author.name %}**', - '', - '+ [github/{%= username %}](https://github.com/{%= username %})', - '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', - ].join('\n') - }); - - app.badge('travis', { - content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' - }); - - app.badge('fury', { - content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' - }); -}; diff --git a/lib/plugins/get-view.js b/lib/plugins/get-view.js deleted file mode 100644 index 515ce496..00000000 --- a/lib/plugins/get-view.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -/** - * Collection plugin for overriding the native `getView` method - * from `templates` with a custom method that calls `loadView` - * if the view isn't already cached and has a `path` property - * indicating that the view is on the file system. - */ - -module.exports = function(options) { - return function (app) { - if (this.isCollection) { - this.getView = getView(app, app.getView); - } - return function (views) { - if (this.isCollection) { - this.getView = getView(views, views.getView); - } - return this; - }; - }; -}; - -function getView(app, fn) { - return function (name) { - return fn.apply(this, arguments) || this.loadView(name, { - ext: '.md' - }); - }.bind(app); -} diff --git a/lib/plugins/index.js b/lib/plugins/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/plugins/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/middleware/pretty.js b/middleware/pretty.js new file mode 100644 index 00000000..8e262de5 --- /dev/null +++ b/middleware/pretty.js @@ -0,0 +1,121 @@ +'use strict'; + +var through = require('through2'); +var Remarkable = require('remarkable'); +var PluginError = require('plugin-error'); +var prettify = require('pretty-remarkable'); +var extend = require('extend-shallow'); + + +module.exports = function(app) { + var argv = this.get('argv'); + var app = this; + + // add a trailing newline to docs? + var newline = argv.newline || this.config.get('newline'); + + return function (view, next) { + // pass some extra formatting info to `pretty-remarkable` + var opts = extend({}, app.options, view.options); + opts.username = app.get('data.username'); + opts.name = app.get('data.author.name'); + + var str = view.content; + var res = extractTables(str); + str = res.str; + + // prettify + str = pretty(str, opts); + str = str.trim() + (newline ? '\n' : ''); + str = fixParam(str); + str = fixList(str); + + res.keys.forEach(function (key) { + var table = res.tables[key].trim(); + str = str.split(key).join(table); + }); + + } +}; + +/** + * Fix list formatting + */ + +function fixList(str) { + return str.replace(/([ ]{1,4}[+-] \[?[^)]+\)?)\n\n\* /gm, '$1\n* '); +} + +/** + * Fix params + */ + +function fixParam(str) { + return str.split('__{_}_*').join('**{any}**'); +} + +/** + * Instantiate `Remarkable` and use the `prettify` plugin + * on the given `str`. + * + * @param {String} `str` + * @param {Object} `options` + * @return {String} + */ + +function pretty(str, options) { + return new Remarkable(options) + .use(prettify) + .render(str); +} + +/** + * Push the `view` through if the user has specfied + * not to format it. + */ + +function noformat(app, view, locals, argv) { + return app.isTrue('noformat') || app.isFalse('format') + || view.noformat === true || view.format === false + || locals.noformat === true || locals.format === false + || argv.noformat === true || argv.format === false; +} + + +function extractTables(str) { + var re = /^\s*[|](?=.*[|])(.*)$/; + var lines = str.split(/\r\n|\r|\n/); + var len = lines.length, i = -1; + var tables = {}; + var inside = false; + var prev = false; + var num = 0; + var content = ''; + var keys = [], key = '__TABLE_0_'; + + while (++i < len) { + var line = lines[i]; + + if (re.test(line)) { + if (prev) { + keys.push(key); + content += key; + key = '__TABLE_' + (num++) + '_'; + } + prev = false; + inside = true; + tables[key] = tables[key] || ''; + tables[key] += line + '\n'; + } else { + content += line + '\n'; + inside = false; + prev = true; + } + } + + var res = {}; + res.keys = keys; + res.str = content; + res.tables = tables; + return res; +} diff --git a/test/app.file.js b/test/app.file.js deleted file mode 100644 index fbed4525..00000000 --- a/test/app.file.js +++ /dev/null @@ -1,20 +0,0 @@ -require('mocha'); -require('should'); -var assert = require('assert'); -var assemble = require('..'); -var app; - -describe('.file()', function () { - beforeEach(function() { - app = assemble(); - }); - - describe('add file', function () { - it('should add files to `app.views.files`:', function () { - app.file('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); - app.file('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); - app.file('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); - assert(Object.keys(app.views.files).length === 3); - }); - }); -}); diff --git a/test/app.files.js b/test/app.files.js deleted file mode 100644 index 0732afc6..00000000 --- a/test/app.files.js +++ /dev/null @@ -1,22 +0,0 @@ -require('mocha'); -require('should'); -var assert = require('assert'); -var assemble = require('..'); -var app; - -describe('.files()', function () { - beforeEach(function() { - app = assemble(); - }); - - describe('add files', function () { - it('should add files to `app.views.files`:', function () { - app.files({ - 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, - 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, - 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, - }); - assert(Object.keys(app.views.files).length === 3); - }); - }); -}); diff --git a/test/app.handlers.js b/test/app.handlers.js index 24093b15..e29f1845 100644 --- a/test/app.handlers.js +++ b/test/app.handlers.js @@ -7,33 +7,23 @@ var support = require('./support'); var App = support.resolve(); var app; -function decorateViews(views) { - var fn = views.decorateView; - views.decorateView = function () { - var view = fn.apply(fn, arguments); +function read(views) { + return function (view) { view.read = function () { if (!this.contents) { this.contents = fs.readFileSync(this.path); } }; return view; - }; - views.loader = function (pattern) { - var files = resolve.sync(pattern); - return files.reduce(function (acc, fp) { - acc[fp] = {path: fp}; - return acc; - }, {}); - }; - return views; + } } -describe.skip('handlers', function () { +describe('handlers', function () { describe('custom handlers', function () { beforeEach(function () { app = new App(); app.create('pages') - .use(decorateViews) + .use(read) .option('renameKey', function (key) { return path.basename(key); }); @@ -46,17 +36,14 @@ describe.skip('handlers', function () { }); it('should add custom middleware handlers:', function () { - app.pages.on('view', function (key, val) { - val.read(); + app.pages.on('view', function (view) { + console.log(view.read) + // view.read(); }); app.handler('foo'); app.handler('bar'); - // app.on('foo', function () { - // console.log(arguments) - // }) - app.foo(/./, function (view, next) { view.one = 'aaa'; next(); @@ -67,10 +54,14 @@ describe.skip('handlers', function () { next(); }); - var pages = app.pages('test/fixtures/templates/*.tmpl') + app + .pages('a', {contents: '...'}) + .pages('b', {contents: '...'}) + .pages('c', {contents: '...'}) .use(function (pages) { - var fn = pages.decorateView; - pages.decorateView = function (view) { + // console.log(pages) + var fn = pages.extendView; + pages.extendView = function (view) { view = fn(view); app.handleView('foo', view); return view; @@ -79,8 +70,8 @@ describe.skip('handlers', function () { }); // .pages('test/fixtures/pages/*.hbs') // .use(function (pages) { - // var fn = pages.decorateView; - // pages.decorateView = function (view) { + // var fn = pages.extendView; + // pages.extendView = function (view) { // view = fn(view); // app.handleView('bar', view); // return view; @@ -88,7 +79,7 @@ describe.skip('handlers', function () { // return pages; // }) - console.log(pages.getView('a.tmpl').one); + // console.log(pages.getView('a.tmpl').one); // console.log(app.pages.getView('a.tmpl').one) // console.log(app.pages.getView('a.hbs').two) diff --git a/test/app.page.js b/test/app.page.js deleted file mode 100644 index 74d8b286..00000000 --- a/test/app.page.js +++ /dev/null @@ -1,20 +0,0 @@ -require('mocha'); -require('should'); -var assert = require('assert'); -var assemble = require('..'); -var app; - -describe('.page()', function () { - beforeEach(function() { - app = assemble(); - }); - - describe('add page', function () { - it('should add pages to `app.views.pages`:', function () { - app.page('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); - app.page('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); - app.page('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); - assert(Object.keys(app.views.pages).length === 3); - }); - }); -}); diff --git a/test/app.pages.js b/test/app.pages.js deleted file mode 100644 index 8bd65b07..00000000 --- a/test/app.pages.js +++ /dev/null @@ -1,22 +0,0 @@ -require('mocha'); -require('should'); -var assert = require('assert'); -var assemble = require('..'); -var app; - -describe('.pages()', function () { - beforeEach(function() { - app = assemble(); - }); - - describe('add pages', function () { - it('should add pages to `app.views.pages`:', function () { - app.pages({ - 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, - 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, - 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, - }); - assert(Object.keys(app.views.pages).length === 3); - }); - }); -}); diff --git a/test/app.partial.js b/test/app.partial.js deleted file mode 100644 index 2fb13130..00000000 --- a/test/app.partial.js +++ /dev/null @@ -1,20 +0,0 @@ -require('mocha'); -require('should'); -var assert = require('assert'); -var assemble = require('..'); -var app; - -describe('.partial()', function () { - beforeEach(function() { - app = assemble(); - }); - - describe('add partial', function () { - it('should add partials to `app.views.partials`:', function () { - app.partial('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); - app.partial('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); - app.partial('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); - assert(Object.keys(app.views.partials).length === 3); - }); - }); -}); diff --git a/test/app.partials.js b/test/app.partials.js deleted file mode 100644 index de6fef9e..00000000 --- a/test/app.partials.js +++ /dev/null @@ -1,22 +0,0 @@ -require('mocha'); -require('should'); -var assert = require('assert'); -var assemble = require('..'); -var app; - -describe('.partials()', function () { - beforeEach(function() { - app = assemble(); - }); - - describe('add partials', function () { - it('should add partials to `app.views.partials`:', function () { - app.partials({ - 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, - 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, - 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, - }); - assert(Object.keys(app.views.partials).length === 3); - }); - }); -}); diff --git a/test/item.js b/test/item.js index 23ab488a..2c3becda 100644 --- a/test/item.js +++ b/test/item.js @@ -26,7 +26,6 @@ describe('Item', function () { it('inspect should not double name `Stream` when ctor is `Stream`', function(done) { var val = new Stream(); var item = new Item({contents: val}); - console.log(util.inspect(item).name); done(); }); }); diff --git a/test/questions.js b/test/questions.js new file mode 100644 index 00000000..5ae3bad5 --- /dev/null +++ b/test/questions.js @@ -0,0 +1,58 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('content', function () { + beforeEach(function () { + app = new App(); + }); + + it('should store a question:', function () { + app.question('a', 'b'); + assert(app.questions); + assert(app.questions.cache); + assert(app.questions.cache.a); + assert(app.questions.cache.a.name === 'a'); + assert(app.questions.cache.a.message === 'b'); + }); + + it('should ask a question and use data value to answer:', function (done) { + app.question('a', 'b'); + app.data('a', 'b'); + + app.ask('a', function (err, answer) { + assert(!err); + assert(answer); + assert(answer === 'b'); + done(); + }) + }); + + it('should ask a question and use store value to answer:', function (done) { + app.question('a', 'b'); + app.store.set('a', 'c'); + + app.ask('a', function (err, answer) { + assert(!err); + assert(answer); + assert(answer === 'c'); + done(); + }) + }); + + it('should ask a question and use config value to answer:', function (done) { + app.question('a', 'b'); + app.store.set('a', 'c'); + + app.ask('a', function (err, answer) { + assert(!err); + assert(answer); + assert(answer === 'c'); + done(); + }) + }); +}); diff --git a/test/store.js b/test/store.js index a4dc3281..861d6a93 100644 --- a/test/store.js +++ b/test/store.js @@ -14,8 +14,11 @@ describe('store', function () { app = new App(); }); - afterEach(function () { - app.store.del({force: true}); + afterEach(function (cb) { + app.store.del({force: true}, function (err) { + if (err) return cb(err); + cb(); + }); }); it('should create an instance of Store', function () { From f2456b3cbf1c530c0c6acc0138a66ea53e5ac781 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 27 Oct 2015 05:50:09 -0400 Subject: [PATCH 019/282] setup core modules --- index.js | 55 ++++++++++++--------- lib/config.js | 1 - lib/defaults.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/helpers.js | 20 +++++--- lib/locals.js | 2 +- lib/utils.js | 61 +++++++++++++++++++---- package.json | 73 ++++++++++++++------------- 7 files changed, 262 insertions(+), 79 deletions(-) create mode 100644 lib/defaults.js diff --git a/index.js b/index.js index 792fa3bb..dd284659 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,10 @@ +/*! + * verb + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + 'use strict'; /** @@ -5,15 +12,14 @@ */ var path = require('path'); -var Assemble = require('assemble'); +var Core = require('assemble-core'); +var ask = require('assemble-ask'); +var config = require('./defaults'); var utils = require('./lib/utils'); var lib = require('./lib'); -var datastore = lib.store; -var config = lib.config; -var locals = lib.locals; /** - * Create an `verb` application. This is the main function exported + * Create a `verb` application. This is the main function exported * by the verb module. * * ```js @@ -24,38 +30,43 @@ var locals = lib.locals; * @api public */ -var defaults = require('./lib/plugins/collections'); - function Verb(options) { if (!(this instanceof Verb)) { return new Verb(options); } - options = utils.defaults({}, options, { - reload: false - }); + this.options = utils.extend({reload: false}, options); + Core.call(this, this.options); - Assemble.call(this, options); - this.define('isVerb', true); - - - this.use(datastore); - this.use(locals); - this.use(config); - // lib.helpers(this); + this.use(lib.store); + this.use(lib.locals); + this.use(ask()); + // this.use(lib.config); this.use(lib.helpers); - this.use(defaults); - // this.use(ask()); + this.use(lib.defaults); + this.use(config); + + this.define('isVerb', true); + this.option('rethrow', { regex: /\{%=?([^%]*)%}/ }); + this.engine('md', require('engine-base'), { + delims: ['{%', '%}'] + }); } /** - * Inherit `Assemble` + * Inherit `Core` */ -Assemble.extend(Verb); +Core.extend(Verb); /** * Expose `Verb` */ module.exports = Verb; + +/** + * Expose `Verb` + */ + +module.exports.utils = utils; diff --git a/lib/config.js b/lib/config.js index c4b5fe77..cd82aeff 100644 --- a/lib/config.js +++ b/lib/config.js @@ -5,7 +5,6 @@ var utils = require('./utils'); module.exports = function (app) { if (!app.isApp) return; - // console.log(app) var Config = utils.Config; var config = app.locals.cache; var keys = Object.keys(app.views); diff --git a/lib/defaults.js b/lib/defaults.js new file mode 100644 index 00000000..23bef2ed --- /dev/null +++ b/lib/defaults.js @@ -0,0 +1,129 @@ +'use strict'; + +/** + * Plugin for creating default view collections + * | includes + * | layouts + * | pages + * | files + */ + +module.exports = function (app) { + var includes = require('readme-includes'); + // var badges = require('readme-badges'); + var utils = require('./utils'); + + app.create('includes', { + viewType: ['partial'], + renameKey: utils.rename, + cwd: includes, + engine: 'md', + }); + + app.create('badges', { + viewType: ['partial'], + renameKey: utils.rename, + // cwd: badges, + engine: 'md' + }); + + app.create('shields', { + viewType: ['partial'], + renameKey: utils.rename, + engine: 'md' + }); + + app.create('docs', { + viewType: ['partial'], + renameKey: utils.basename, + engine: 'md' + }); + + app.create('layouts', { + viewType: ['layout'], + renameKey: utils.basename, + engine: 'md' + }); + + app.question('foo', 'this is foo, yo!'); + + app.shield('dt', { + content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); + app.shield('dm', { + content: '[![monthly downloads](https://img.shields.io/npm/dm/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); + + app.include('npm', { + content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' + }); + + app.include('copyright', { + content: 'Copyright © 2015, {%= author.name %}.' + }); + + app.include('license', { + content: 'Released under the {%= ask("License") %} license.' + }); + + app.include('footer', { + content: '_(TODO)_' + }); + + app.include('contributing', { + content: '_(TODO)_' + }); + + app.include('tests', { + content: '_(TODO)_' + }); + + app.include('install-global', { + content: [ + 'Install with [npm](https://www.npmjs.com/)', + '', + '```sh', + '$ npm i -g {%= name %} --save', + '```' + ].join('\n') + }); + + app.include('author', { + locals: { + author: { + name: 'Brian Woodward' + }, + username: 'doowb' + }, + content: [ + '**{%= author.name %}**', + '', + '+ [github/{%= username %}](https://github.com/{%= username %})', + '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', + ].join('\n') + }); + + app.badge('travis', { + content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' + }); + + app.badge('fury', { + content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' + }); + + app.data({ + author: { + name: 'Jon Schlinkert' + }, + twitter: { + username: 'jonschlinkert' + }, + github: { + username: 'jonschlinkert' + }, + runner: { + name: app.get('cache.data.name'), + url: app.get('cache.data.repository'), + } + }); +}; diff --git a/lib/helpers.js b/lib/helpers.js index d64d82c9..17386650 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -2,23 +2,27 @@ var utils = require('./utils'); -module.exports = function (app) { - app.asyncHelper('related', require('helper-related')(app.options)); - app.asyncHelper('reflinks', require('helper-reflinks')); +module.exports = function () { + this.asyncHelper('related', require('helper-related')(this.options)); + this.asyncHelper('reflinks', require('helper-reflinks')(this.options)); - app.helper('date', function () { + this.helper('split', function (val) { + return val.split(','); + }); + + this.helper('date', function () { return new Date(); }); - app.helper('log', function (msg) { + this.helper('log', function (msg) { console.log.apply(console, arguments); }); - app.helper('trim', function (str) { + this.helper('trim', function (str) { return str.trim(); }); - app.helper('apidocs', function () { - // app.ask(locals) + this.helper('apidocs', function () { + // this.ask(locals) }); }; diff --git a/lib/locals.js b/lib/locals.js index 072dfa84..7f9d2247 100644 --- a/lib/locals.js +++ b/lib/locals.js @@ -19,7 +19,7 @@ Locals.prototype.set = function(key, value) { * Expose Locals */ -module.exports = function (app) { +module.exports = function(app) { app.locals = new Locals('verb', app); }; diff --git a/lib/utils.js b/lib/utils.js index d1293616..f63913c5 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -11,15 +11,18 @@ var utils = require('lazy-cache')(require); var fn = require; require = utils; +require('async'); +require('resolve-dir'); require('data-store', 'store'); require('mixin-deep', 'merge'); require('get-value', 'get'); require('set-value', 'set'); -require('defaults-shallow', 'defaults'); +require('extend-shallow', 'extend'); require('parser-front-matter', 'matter'); -require('resolve-dir'); +require('middleware-utils', 'mu'); require('engine-base', 'engine'); require('map-config', 'Config'); + require = fn; utils.basename = function(key) { @@ -65,14 +68,52 @@ utils.rename = function(key) { return key.slice(len); }; -utils.resolveConfig = function (config, fn) { - fn = fn || utils.identity; - for (var key in config) { - if (config.hasOwnProperty(key)) { - var val = utils.npm(resolveDir(config[key])); - fn(key, val); - } - } +/** + * Run middleware in series + * + * ```js + * var fns = require('./fns/'); + * + * app.onLoad(/\.js$/, utils.series([ + * fns.foo, + * fns.bar, + * fns.baz, + * ])); + * ``` + * @param {Array} `fns` Array of middleware functions + * @api public + */ + +exports.series = function(fns) { + return function (file, cb) { + utils.async.eachSeries(fns, function (fn, next) { + fn(file, next); + }, cb); + }; +}; + +/** + * Run middleware in parallel. + * + * ```js + * var fns = require('./fns/'); + * + * app.onLoad(/\.js$/, utils.parallel([ + * fns.foo, + * fns.bar, + * fns.baz, + * ])); + * ``` + * @param {Array} `fns` Array of middleware functions + * @api public + */ + +exports.parallel = function(fns) { + return function (file, cb) { + utils.async.each(fns, function (fn, next) { + fn(file, next); + }, cb); + }; }; /** diff --git a/package.json b/package.json index db1ab53e..b9b930eb 100644 --- a/package.json +++ b/package.json @@ -21,52 +21,53 @@ "test": "mocha" }, "dependencies": { - "ask-once": "^0.4.3", - "assemble-loader": "^0.1.3", - "composer": "^0.4.1", - "composer-runtimes": "^0.3.1", - "data-store": "^0.9.0", - "dest": "^0.2.1", - "engine-handlebars": "^0.8.0", + "assemble-ask": "^0.1.2", + "assemble-core": "^0.1.1", + "data-store": "^0.10.1", + "engine-base": "^0.1.2", "export-files": "^2.1.0", - "helper-reflinks": "^1.4.1", - "helper-related": "^0.10.0", + "extend-shallow": "^2.0.1", + "get-value": "^1.2.1", + "helper-reflinks": "^2.0.0", + "helper-related": "^0.11.1", "lazy-cache": "^0.2.3", - "map-config": "^0.1.0", + "map-config": "^0.1.1", + "middleware-utils": "^0.1.4", "mixin-deep": "^1.1.3", "parser-front-matter": "^1.2.5", - "question-cache": "^0.3.0", - "readme-badges": "^0.3.3", "readme-includes": "^0.2.9", - "set-value": "^0.2.0", - "src-stream": "^0.1.1", - "stream-combiner": "^0.2.2", - "templates": "^0.2.0", - "through2": "^2.0.0", - "vinyl": "wearefractal/vinyl", - "vinyl-fs": "wearefractal/vinyl-fs" + "resolve-dir": "^0.1.0", + "set-value": "^0.2.0" }, "devDependencies": { + "async": "^1.4.2", + "base-methods": "^0.2.14", "buffer-equal": "0.0.1", "consolidate": "^0.13.1", - "engine-base": "^0.1.2", - "event-stream": "^3.3.1", - "for-own": "^0.1.3", - "get-value": "^1.2.1", - "globby": "^3.0.1", + "coveralls": "^2.11.4", + "define-property": "^0.2.5", + "engine-handlebars": "^0.8.0", + "event-stream": "^3.3.2", "graceful-fs": "^4.1.2", "gulp": "^3.9.0", - "gulp-istanbul": "^0.10.0", + "gulp-git": "^1.5.0", + "gulp-istanbul": "^0.10.1", "gulp-jshint": "^1.11.2", "gulp-mocha": "^2.1.3", - "is-buffer": "^1.0.2", + "is-buffer": "^1.1.0", + "istanbul": "^0.4.0", "jshint-stylish": "^2.0.1", - "minimist": "^1.2.0", - "mocha": "*", - "object.omit": "^2.0.0", + "kind-of": "^2.0.1", + "load-pkg": "^1.3.0", + "look-up": "^0.8.1", + "mocha": "^2.3.3", + "resolve-glob": "^0.1.2", "rimraf": "^2.4.3", - "should": "*", - "swig": "^1.4.2" + "should": "^7.1.0", + "sinon": "^1.17.1", + "swig": "^1.4.2", + "through2": "^2.0.0", + "vinyl": "^1.0.0" }, "keywords": [ "comment", @@ -104,12 +105,10 @@ ] }, "helpers": { - "helper": { - "whatever": "@/helper-related" - }, - "asyncHelper": { - "whatever": "@/helper-related" - } + "whatever": "@/helper-related" + }, + "asyncHelpers": { + "whatever": "@/helper-related" }, "related": { "list": [ From a651abd7b7f0e93a2c1067e2f6e87ec29d59e691 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 27 Oct 2015 07:21:38 -0400 Subject: [PATCH 020/282] update tests to latest --- test/app.collection.js | 19 +- test/app.collection.render.js | 2 + test/app.copy.js | 6 +- test/app.create.js | 24 +- test/app.dest.js | 173 ++++--- test/app.events.js | 13 +- test/app.handle.js | 10 + test/app.handlers.js | 96 +--- test/app.list.js | 105 ++++ test/app.list.render.js | 157 ------ test/app.option.js | 6 - test/app.renderFile.js | 135 +++++ test/app.symlink.js | 396 +++++++++++++++ test/app.toStream.js | 64 +++ test/collection.use.js | 138 +++--- test/dest.js | 908 ---------------------------------- test/fixtures/pages/a.hbs | 3 +- test/fixtures/pages/b.hbs | 3 +- test/fixtures/pages/c.hbs | 3 +- test/group.js | 4 + test/helpers.js | 20 +- test/list.js | 10 + test/list.use.js | 156 ++++++ test/mergePartials.js | 3 +- test/store.js | 35 +- test/views.js | 96 +++- test/views.use.js | 156 ++++++ 27 files changed, 1368 insertions(+), 1373 deletions(-) create mode 100644 test/app.list.js delete mode 100644 test/app.list.render.js create mode 100644 test/app.renderFile.js create mode 100644 test/app.symlink.js create mode 100644 test/app.toStream.js delete mode 100644 test/dest.js create mode 100644 test/list.use.js create mode 100644 test/views.use.js diff --git a/test/app.collection.js b/test/app.collection.js index e8660287..fde0c24d 100644 --- a/test/app.collection.js +++ b/test/app.collection.js @@ -1,6 +1,7 @@ require('mocha'); require('should'); var fs = require('fs'); +var path = require('path'); var assert = require('assert'); var define = require('define-property'); var support = require('./support'); @@ -51,7 +52,7 @@ describe('collection', function () { it('should load a view onto the respective collection:', function () { app.pages('test/fixtures/pages/a.hbs'); - app.views.pages.should.have.property('test/fixtures/pages/a.hbs'); + app.views.pages.should.have.property(path.resolve('test/fixtures/pages/a.hbs')); }); it('should allow collection methods to be chained:', function () { @@ -61,9 +62,9 @@ describe('collection', function () { .pages('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' + path.resolve('test/fixtures/pages/a.hbs'), + path.resolve('test/fixtures/pages/b.hbs'), + path.resolve('test/fixtures/pages/c.hbs') ]); }); @@ -75,9 +76,9 @@ describe('collection', function () { app.pages.options.should.have.property('foo', 'bar'); app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' + path.resolve('test/fixtures/pages/a.hbs'), + path.resolve('test/fixtures/pages/b.hbs'), + path.resolve('test/fixtures/pages/c.hbs') ]); }); @@ -137,7 +138,7 @@ describe('collection', function () { .set('data.name', 'Brian') .render(function (err, res) { if (err) return done(err); - assert(res.contents.toString() === 'Brian'); + assert(res.content === 'Brian'); done(); }); }); @@ -165,7 +166,7 @@ describe('collection singular method', function () { it('should add a view to the created collection:', function () { app.page('test/fixtures/pages/a.hbs'); - assert(typeof app.views.pages['test/fixtures/pages/a.hbs'] === 'object'); + assert(typeof app.views.pages[path.resolve('test/fixtures/pages/a.hbs')] === 'object'); }); it('should expose the `option` method:', function () { diff --git a/test/app.collection.render.js b/test/app.collection.render.js index 6cc96a35..135b1347 100644 --- a/test/app.collection.render.js +++ b/test/app.collection.render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var async = require('async'); diff --git a/test/app.copy.js b/test/app.copy.js index 0e979b79..4a0d510b 100644 --- a/test/app.copy.js +++ b/test/app.copy.js @@ -6,16 +6,16 @@ var App = require('..'); var app; var fixtures = path.join(__dirname, 'fixtures/copy/*.txt'); -var outpath = path.join(__dirname, 'out-fixtures'); +var actual = path.join(__dirname, 'actual'); describe('copy()', function() { beforeEach(function (done) { - rimraf(outpath, done); + rimraf(actual, done); app = new App(); }); afterEach(function (done) { - rimraf(outpath, done); + rimraf(actual, done); }); describe('streams', function () { diff --git a/test/app.create.js b/test/app.create.js index 99a15da5..9178245f 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -92,7 +92,7 @@ describe('create', function () { app.page('b.hbs', {content: 'b'}); app.page('c.hbs', {content: 'c'}); app.views.pages.should.have.properties(['a.hbs', 'b.hbs', 'c.hbs']); - assert(app.views.pages['a.hbs'].contents.toString() === 'a'); + assert(app.views.pages['a.hbs'].content === 'a'); }); it('should create views from file paths:', function () { @@ -101,9 +101,9 @@ describe('create', function () { app.page('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' + path.resolve('test/fixtures/pages/a.hbs'), + path.resolve('test/fixtures/pages/b.hbs'), + path.resolve('test/fixtures/pages/c.hbs') ]); }); }); @@ -134,7 +134,7 @@ describe('create', function () { collection.addView('test/fixtures/templates/a.tmpl'); collection.read('a.tmpl'); - assert(collection.getView('a.tmpl').contents.toString() === '<%= name %>'); + assert(collection.getView('a.tmpl').content === '<%= name %>'); }); }); @@ -173,4 +173,18 @@ describe('create', function () { assert(app.layouts.options.foo === 'bar'); }); }); + + describe('collection instantiation', function () { + it('should expose collection instance methods that are created after instantiation on the app collection loader', function () { + app.create('pages'); + app.pages.use(function (collection) { + collection.define('foo', function (msg) { + return 'foo ' + msg; + }); + }); + + assert(app.pages.foo); + assert(typeof app.pages.foo === 'function'); + }); + }); }); diff --git a/test/app.dest.js b/test/app.dest.js index 52e55840..1e3d67e8 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -17,12 +17,11 @@ var bufEqual = require('buffer-equal'); var through = require('through2'); var File = require('vinyl'); -var outpath = path.join(__dirname, 'out-fixtures'); - +var actual = path.join(__dirname, 'actual'); var wipeOut = function(cb) { app = new App(); - rimraf(path.join(__dirname, 'out-fixtures/'), cb); + rimraf(path.join(__dirname, 'actual/'), cb); spies.setError('false'); statSpy.reset(); chmodSpy.reset(); @@ -81,7 +80,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -106,7 +105,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest(path.join(__dirname, 'out-fixtures/')); + var stream = app.dest(path.join(__dirname, 'actual/')); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -118,9 +117,9 @@ describe('dest stream', function() { it('should not write null files', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedFile = new File({ base: inputBase, @@ -139,7 +138,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -151,9 +150,9 @@ describe('dest stream', function() { it('should write buffer files to the right folder with relative cwd', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedContents = fs.readFileSync(inputPath); var expectedFile = new File({ @@ -174,7 +173,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', {cwd: path.relative(process.cwd(), __dirname)}); + var stream = app.dest('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -186,9 +185,9 @@ describe('dest stream', function() { it('should write buffer files to the right folder with function and relative cwd', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedContents = fs.readFileSync(inputPath); var expectedFile = new File({ @@ -212,7 +211,7 @@ describe('dest stream', function() { var stream = app.dest(function(file){ should.exist(file); file.should.equal(expectedFile); - return './out-fixtures'; + return './actual'; }, {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; @@ -225,10 +224,10 @@ describe('dest stream', function() { it('should write buffer files to the right folder', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0655; var expectedFile = new File({ @@ -253,7 +252,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -265,10 +264,10 @@ describe('dest stream', function() { it('should write streaming files to the right folder', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0655; var contentStream = through.obj(); @@ -294,7 +293,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -310,9 +309,9 @@ describe('dest stream', function() { it('should write directories to the right folder', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test'); + var expectedPath = path.join(__dirname, 'actual/test'); var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0655; var expectedFile = new File({ @@ -340,7 +339,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -350,12 +349,12 @@ describe('dest stream', function() { }); it('should allow piping multiple dests in streaming mode', function(done) { - var inputPath1 = path.join(__dirname, 'out-fixtures/multiple-first'); - var inputPath2 = path.join(__dirname, 'out-fixtures/multiple-second'); - var inputBase = path.join(__dirname, 'out-fixtures/'); + var inputPath1 = path.join(__dirname, 'actual/multiple-first'); + var inputPath2 = path.join(__dirname, 'actual/multiple-second'); + var inputBase = path.join(__dirname, 'actual/'); var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var stream1 = app.dest('./out-fixtures/', {cwd: __dirname}); - var stream2 = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream1 = app.dest('./actual/', {cwd: __dirname}); + var stream2 = app.dest('./actual/', {cwd: __dirname}); var content = fs.readFileSync(srcPath); var rename = through.obj(function(file, _, next) { file.path = inputPath2; @@ -390,7 +389,7 @@ describe('dest stream', function() { it('should write new files with the default user mode', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedMode = 0666 & (~process.umask()); @@ -410,7 +409,7 @@ describe('dest stream', function() { }; chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -423,7 +422,7 @@ describe('dest stream', function() { it('should write new files with the specified mode', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedMode = 0744; @@ -443,7 +442,7 @@ describe('dest stream', function() { }; chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname, mode:expectedMode}); + var stream = app.dest('./actual/', {cwd: __dirname, mode:expectedMode}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -456,9 +455,9 @@ describe('dest stream', function() { it('should update file mode to match the vinyl mode', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var startMode = 0655; var expectedMode = 0722; @@ -486,7 +485,7 @@ describe('dest stream', function() { fs.chmodSync(expectedPath, startMode); chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -499,7 +498,7 @@ describe('dest stream', function() { it('should use different modes for files and directories', function(done) { var inputBase = path.join(__dirname, 'fixtures/vinyl'); var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); - var expectedBase = path.join(__dirname, 'out-fixtures/wow'); + var expectedBase = path.join(__dirname, 'actual/wow'); var expectedDirMode = 0755; var expectedFileMode = 0655; @@ -516,7 +515,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', { + var stream = app.dest('./actual/', { cwd: __dirname, mode: expectedFileMode, dirMode: expectedDirMode @@ -545,7 +544,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', { + var stream = app.dest('./actual/', { cwd: __dirname, base: inputBase }); @@ -573,7 +572,7 @@ describe('dest stream', function() { done(); }; - var stream = app.dest('./out-fixtures/', { + var stream = app.dest('./actual/', { cwd: __dirname, base: function(file){ should.exist(file); @@ -593,9 +592,9 @@ describe('dest stream', function() { it('should report IO errors', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0722; var expectedFile = new File({ @@ -612,7 +611,7 @@ describe('dest stream', function() { fs.closeSync(fs.openSync(expectedPath, 'w')); fs.chmodSync(expectedPath, 0); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.code.should.equal('EACCES'); done(); @@ -623,9 +622,9 @@ describe('dest stream', function() { it('should report stat errors', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0722; var expectedFile = new File({ @@ -647,7 +646,7 @@ describe('dest stream', function() { } }); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.message.should.equal('stat error'); done(); @@ -658,9 +657,9 @@ describe('dest stream', function() { it('should report chmod errors', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0722; var expectedFile = new File({ @@ -682,7 +681,7 @@ describe('dest stream', function() { } }); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.message.should.equal('chmod error'); done(); @@ -693,9 +692,9 @@ describe('dest stream', function() { it('should not chmod a matching file', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 0722; var expectedFile = new File({ @@ -728,7 +727,7 @@ describe('dest stream', function() { statSpy.reset(); chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -741,9 +740,9 @@ describe('dest stream', function() { it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedBase = path.join(__dirname, 'actual'); var expectedMode = 03722; var normalMode = 0722; @@ -776,7 +775,7 @@ describe('dest stream', function() { statSpy.reset(); chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -791,8 +790,8 @@ describe('dest stream', function() { var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var inputContents = fs.readFileSync(inputPath); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var expectedBase = path.join(__dirname, 'actual'); var existingContents = 'Lorem Ipsum'; var inputFile = new File({ @@ -812,7 +811,7 @@ describe('dest stream', function() { fs.mkdirSync(expectedBase); fs.writeFileSync(expectedPath, existingContents); - var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: false}); + var stream = app.dest('./actual/', {cwd: __dirname, overwrite: false}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -826,8 +825,8 @@ describe('dest stream', function() { var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var inputContents = fs.readFileSync(inputPath); - var expectedPath = path.join(__dirname, 'out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, 'out-fixtures'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var expectedBase = path.join(__dirname, 'actual'); var existingContents = 'Lorem Ipsum'; var inputFile = new File({ @@ -847,7 +846,7 @@ describe('dest stream', function() { fs.mkdirSync(expectedBase); fs.writeFileSync(expectedPath, existingContents); - var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: true}); + var stream = app.dest('./actual/', {cwd: __dirname, overwrite: true}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -861,7 +860,7 @@ describe('dest stream', function() { var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var inputRelativeSymlinkPath = 'wow'; - var expectedPath = path.join(__dirname, 'out-fixtures/test-create-dir-symlink'); + var expectedPath = path.join(__dirname, 'actual/test-create-dir-symlink'); var inputFile = new File({ base: inputBase, @@ -881,7 +880,7 @@ describe('dest stream', function() { }); }; - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -892,7 +891,7 @@ describe('dest stream', function() { it('should emit finish event', function(done) { var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); + var stream = app.dest('./actual/', {cwd: __dirname}); stream.once('finish', function() { done(); @@ -911,12 +910,12 @@ describe('dest stream', function() { describe('dest', function() { beforeEach(function (done) { - rimraf(outpath, done); + rimraf(actual, done); app = new App(); }); afterEach(function (done) { - rimraf(outpath, done); + rimraf(actual, done); }); describe('streams', function () { @@ -928,8 +927,8 @@ describe('dest', function() { }); it('should return an output stream that writes files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); - var outstream = app.dest(outpath); + var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt')); + var outstream = app.dest(actual); instream.pipe(outstream); outstream.on('error', done); @@ -938,11 +937,11 @@ describe('dest', function() { should.exist(file); should.exist(file.path); should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); String(file.contents).should.equal('Hello world!'); }); outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { should.not.exist(err); should.exist(contents); String(contents).should.equal('Hello world!'); @@ -952,8 +951,8 @@ describe('dest', function() { }); it('should return an output stream that does not write non-read files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {read: false}); - var outstream = app.dest(outpath); + var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt'), {read: false}); + var outstream = app.dest(actual); instream.pipe(outstream); outstream.on('error', done); @@ -962,11 +961,11 @@ describe('dest', function() { should.exist(file); should.exist(file.path); should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); }); outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { should.exist(err); should.not.exist(contents); done(); @@ -975,8 +974,8 @@ describe('dest', function() { }); it('should return an output stream that writes streaming files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt'), {buffer: false}); - var outstream = instream.pipe(app.dest(outpath)); + var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt'), {buffer: false}); + var outstream = instream.pipe(app.dest(actual)); outstream.on('error', done); outstream.on('data', function (file) { @@ -984,10 +983,10 @@ describe('dest', function() { should.exist(file); should.exist(file.path); should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); }); outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { should.not.exist(err); should.exist(contents); String(contents).should.equal('Hello world!'); @@ -1032,8 +1031,8 @@ describe('dest', function() { }); it('should return an output stream that writes files', function (done) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/*.txt')); - var outstream = app.dest(outpath); + var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt')); + var outstream = app.dest(actual); instream.pipe(outstream); outstream.on('error', done); @@ -1042,11 +1041,11 @@ describe('dest', function() { should.exist(file); should.exist(file.path); should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); String(file.contents).should.equal('Hello world!'); }); outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { should.not.exist(err); should.exist(contents); String(contents).should.equal('Hello world!'); @@ -1057,7 +1056,7 @@ describe('dest', function() { it('should return an output stream that does not write non-read files', function (done) { var instream = app.src(path.join(__dirname, 'fixtures/dest/*.txt'), {read: false}); - var outstream = app.dest(outpath); + var outstream = app.dest(actual); instream.pipe(outstream); outstream.on('error', done); @@ -1066,11 +1065,11 @@ describe('dest', function() { should.exist(file); should.exist(file.path); should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(outpath, 'example.txt')); + path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); }); outstream.on('end', function () { - fs.readFile(path.join(outpath, 'example.txt'), function (err, contents) { + fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { should.exist(err); should.not.exist(contents); done(); @@ -1081,18 +1080,18 @@ describe('dest', function() { function testWriteDir(srcOptions, done) { var instream = app.src(path.join(__dirname, 'fixtures/generic'), srcOptions); - var outstream = instream.pipe(app.dest(outpath)); + var outstream = instream.pipe(app.dest(actual)); outstream.on('error', done); outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); - path.join(file.path,'').should.equal(path.join(outpath, 'generic')); + path.join(file.path,'').should.equal(path.join(actual, 'generic')); }); outstream.on('end', function() { - fs.exists(path.join(outpath, 'generic'), function(exists) { + fs.exists(path.join(actual, 'generic'), function(exists) { /* jshint expr: true */ should(exists).be.ok; /* jshint expr: false */ diff --git a/test/app.events.js b/test/app.events.js index 3505b9c8..3bf001e1 100644 --- a/test/app.events.js +++ b/test/app.events.js @@ -12,8 +12,7 @@ describe('events', function () { it('should listen for an event:', function () { var app = new App(); - app.on('foo', function () { - }); + app.on('foo', function () {}); assert(Array.isArray(app._callbacks['$foo'])); }); @@ -27,16 +26,6 @@ describe('events', function () { app.emit('foo', 'bar'); }); - it('should listen for error events:', function (done) { - var app = new App(); - app.on('foo', function (val) { - assert(val === 'bar'); - done(); - }); - assert(Array.isArray(app._callbacks['$foo'])); - app.emit('foo', 'bar'); - }); - it('should listen for `view` events:', function () { var app = new App(); diff --git a/test/app.handle.js b/test/app.handle.js index f968d693..8ce9902e 100644 --- a/test/app.handle.js +++ b/test/app.handle.js @@ -20,4 +20,14 @@ describe('handler', function () { done(); }); }); + + it('should not blow up if `options.handled` does not exist:', function (done) { + var page = app.page('foo', {contents: null}); + delete page.options.handled; + + app.handle('foo', page, function (err, view) { + assert(typeof view.path === 'string'); + done(); + }); + }); }); diff --git a/test/app.handlers.js b/test/app.handlers.js index e29f1845..71712f67 100644 --- a/test/app.handlers.js +++ b/test/app.handlers.js @@ -7,15 +7,25 @@ var support = require('./support'); var App = support.resolve(); var app; -function read(views) { - return function (view) { +function decorateViews(views) { + var fn = views.decorateView; + views.decorateView = function () { + var view = fn.apply(fn, arguments); view.read = function () { if (!this.contents) { this.contents = fs.readFileSync(this.path); } }; return view; - } + }; + views.loader = function (pattern) { + var files = resolve.sync(pattern); + return files.reduce(function (acc, fp) { + acc[fp] = {path: fp}; + return acc; + }, {}); + }; + return views; } describe('handlers', function () { @@ -23,7 +33,7 @@ describe('handlers', function () { beforeEach(function () { app = new App(); app.create('pages') - .use(read) + .use(decorateViews) .option('renameKey', function (key) { return path.basename(key); }); @@ -36,11 +46,6 @@ describe('handlers', function () { }); it('should add custom middleware handlers:', function () { - app.pages.on('view', function (view) { - console.log(view.read) - // view.read(); - }); - app.handler('foo'); app.handler('bar'); @@ -54,75 +59,14 @@ describe('handlers', function () { next(); }); - app - .pages('a', {contents: '...'}) - .pages('b', {contents: '...'}) - .pages('c', {contents: '...'}) - .use(function (pages) { - // console.log(pages) - var fn = pages.extendView; - pages.extendView = function (view) { - view = fn(view); - app.handleView('foo', view); - return view; - }; - return pages; + app.page('abc', {content: '...'}) + .use(function (view) { + app.handleView('foo', view); + app.handleView('bar', view); }); - // .pages('test/fixtures/pages/*.hbs') - // .use(function (pages) { - // var fn = pages.extendView; - // pages.extendView = function (view) { - // view = fn(view); - // app.handleView('bar', view); - // return view; - // }; - // return pages; - // }) - - // console.log(pages.getView('a.tmpl').one); - // console.log(app.pages.getView('a.tmpl').one) - // console.log(app.pages.getView('a.hbs').two) - - // app.pages.getView('a.txt').should.have.property('one'); - // app.pages.getView('a.txt').should.have.property('two'); - // app.pages.getView('a.md').should.not.have.property('one'); - // app.pages.getView('a.md').should.have.property('two'); + app.views.pages.abc.should.have.property('one', 'aaa'); + app.views.pages.abc.should.have.property('two', 'zzz'); }); - - // it('should add custom middleware handlers:', function () { - // app.handler('foo'); - // app.handler('bar'); - - // function handle(method) { - // return function (view) { - // return app.handle(method, view); - // } - // } - - // app.foo(/./, function (view, next) { - // view.one = 'aaa'; - // next(); - // }); - - // app.bar(/./, function (view, next) { - // view.two = 'zzz'; - // next(); - // }); - - // app.pages('test/fixtures/*.txt') - // .use(handle('foo')) - - // .pages('test/fixtures/*.md') - // .use(handle('bar')) - - // .use(utils.rename); - - // app.pages.getView('a.txt').should.have.property('one'); - // app.pages.getView('a.txt').should.have.property('two'); - - // app.pages.getView('a.md').should.not.have.property('one'); - // app.pages.getView('a.md').should.have.property('two'); - // }); }); }); diff --git a/test/app.list.js b/test/app.list.js new file mode 100644 index 00000000..9c9c3ea3 --- /dev/null +++ b/test/app.list.js @@ -0,0 +1,105 @@ +require('mocha'); +require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var app; + +describe('list', function () { + describe('method', function () { + beforeEach(function () { + app = new App(); + }); + + it('should expose the list method', function () { + assert(typeof app.list === 'function'); + }); + + it('should return a new list', function () { + var list = app.list(); + assert(typeof list === 'object'); + }); + + it('should have isList property', function () { + var list = app.list(); + assert(list.isList === true); + }); + }); + + describe('adding items', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + }); + + it('should add an item to a list:', function () { + app.pages('test/fixtures/pages/a.hbs'); + var list = app.list(); + list.addItem(app.pages.getView('test/fixtures/pages/a.hbs')); + assert(list.hasItem(path.resolve('test/fixtures/pages/a.hbs'))); + }); + + it('should expose the `option` method from a list:', function () { + var list = app.list(); + list.option('a', 'b'); + assert(list.options); + assert(list.options.a === 'b'); + }); + }); + + describe('addItem', function () { + beforeEach(function () { + app = new App(); + }); + + it('should add items to a list', function () { + var pages = app.list({List: List}); + pages.addItem('foo'); + pages.addItem('bar'); + pages.addItem('baz'); + + pages.items.hasOwnProperty('foo'); + pages.items.hasOwnProperty('bar'); + pages.items.hasOwnProperty('baz'); + }); + + it('should create a list from an existing list:', function () { + var pages = app.list({List: List}); + pages.addItem('foo'); + pages.addItem('bar'); + pages.addItem('baz'); + + var posts = app.list(pages); + posts.items.hasOwnProperty('foo'); + posts.items.hasOwnProperty('bar'); + posts.items.hasOwnProperty('baz'); + }); + }); + + describe('rendering items', function () { + beforeEach(function () { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('pages'); + }); + + it('should render a item with inherited app.render', function (done) { + app.page('test/fixtures/templates/a.tmpl') + .use(function (item) { + if (!item.contents) { + item.contents = fs.readFileSync(item.path); + } + }) + .set('data.name', 'Brian') + .render(function (err, res) { + if (err) return done(err); + assert(res.content === 'Brian'); + done(); + }); + }); + }); +}); diff --git a/test/app.list.render.js b/test/app.list.render.js deleted file mode 100644 index f4a9e0b9..00000000 --- a/test/app.list.render.js +++ /dev/null @@ -1,157 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var async = require('async'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var pages, app; - -describe('render', function () { - describe('rendering', function () { - beforeEach(function () { - app = App(); - pages = app.create('pages'); - app.engine('tmpl', require('engine-base')); - pages.engine('tmpl', require('engine-base')); - }); - - it('should throw an error when no callback is given:', function () { - (function() { - app.pages.render({}); - }).should.throw('Views#render is async and expects a callback function'); - }); - - it('should throw an error when an engine is not defined:', function (done) { - pages.addView('foo.bar', {content: '<%= name %>'}); - var page = pages.getView('foo.bar'); - - app.pages.render(page, function(err) { - assert(err.message === 'Views#render cannot find an engine for: .bar'); - done(); - }); - }); - - it('should use helpers defined on app to render a view:', function (done) { - var locals = {name: 'Halle'}; - app.helper('upper', function (str) { - return str.toUpperCase(str) + 'app'; - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - app.render(page, function (err, res) { - if (err) return done(err); - - assert(res.content === 'a HALLEapp b'); - done(); - }); - }); - - it('should use helpers defined on app to render a view with collection.render:', function (done) { - var locals = {name: 'Halle'}; - app.helper('upper', function (str) { - return str.toUpperCase(str) + 'app'; - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - pages.helper('upper', app._.helpers.sync.upper); - var page = pages.getView('a.tmpl'); - - pages.render(page, function (err, res) { - if (err) return done(err); - - assert(res.content === 'a HALLEapp b'); - done(); - }); - }); - - it('should use helpers when rendering a view:', function (done) { - var locals = {name: 'Halle'}; - pages.helper('upper', function (str) { - return str.toUpperCase(str); - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function (err, res) { - if (err) return done(err); - assert(res.content === 'a HALLE b'); - done(); - }); - }); - - it('should render a template when contents is a buffer:', function (done) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function (err, view) { - if (err) return done(err); - assert(view.contents.toString() === 'b'); - done(); - }); - }); - - it('should render a template when content is a string:', function (done) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function (err, view) { - if (err) return done(err); - assert(view.contents.toString() === 'b'); - done(); - }); - }); - - it('should render a view from its path:', function (done) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - - pages.render('a.tmpl', function (err, view) { - if (err) return done(err); - assert(view.content === 'b'); - done(); - }); - }); - - it('should use a plugin for rendering:', function (done) { - pages.engine('tmpl', require('engine-base')); - pages.option('engine', 'tmpl'); - - pages.addViews({ - 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, - 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, - 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, - 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, - 'e': {content: '<%= title %>', locals: {title: 'eee'}}, - 'f': {content: '<%= title %>', locals: {title: 'fff'}}, - 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, - 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, - 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, - }); - - pages.use(function (collection) { - collection.option('pager', false); - - collection.renderEach = function (cb) { - var list = new List(collection); - async.map(list.items, function (item, next) { - collection.render(item, next); - }, cb); - }; - }); - - pages.renderEach(function (err, items) { - if (err) return done(err); - assert(items[0].content === 'aaa'); - assert(items[9].content === 'jjj'); - assert(items.length === 10); - done(); - }); - }); - }); -}); diff --git a/test/app.option.js b/test/app.option.js index a13a7aad..12bdd6f9 100644 --- a/test/app.option.js +++ b/test/app.option.js @@ -20,12 +20,6 @@ describe('app.option', function () { assert(app.options.c === 'd'); }); - it('should throw on invalid args:', function () { - (function () { - app.option(function () {}); - }).should.throw('expected a string or object.'); - }); - it('should set an option.', function() { app.option('a', 'b'); app.options.should.have.property('a'); diff --git a/test/app.renderFile.js b/test/app.renderFile.js new file mode 100644 index 00000000..1b3b478d --- /dev/null +++ b/test/app.renderFile.js @@ -0,0 +1,135 @@ +'use strict'; + +var assemble = require('..'); +var assert = require('assert'); +var should = require('should'); +var path = require('path'); +var app; + +describe('app.renderFile()', function() { + beforeEach(function () { + app = assemble(); + app.engine('hbs', require('engine-handlebars')); + app.engine('*', require('engine-base')); + + app.create('files', {engine: '*'}); + app.file('a', {content: 'this is <%= title() %>'}); + app.file('b', {content: 'this is <%= title() %>'}); + app.file('c', {content: 'this is <%= title() %>'}); + + app.option('renameKey', function (key) { + return path.basename(key, path.extname(key)); + }); + + app.helper('title', function () { + var view = this.context.view; + var key = view.key; + var ctx = this.context[key]; + if (ctx && ctx.title) return ctx.title; + return key; + }); + }); + + it('should render views from src', function (done) { + var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); + var files = []; + + stream.pipe(app.renderFile()) + .on('error', done) + .on('data', function (file) { + files.push(file); + }) + .on('end', function () { + assert.equal(files[0].basename, 'a.hbs'); + assert.equal(files[1].basename, 'b.hbs'); + assert.equal(files[2].basename, 'c.hbs'); + done(); + }); + }); + + it('should render views with the engine that matches the file extension', function (done) { + var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); + var files = []; + + stream.pipe(app.renderFile()) + .on('error', done) + .on('data', function (file) { + files.push(file); + }) + .on('end', function () { + assert(/

a<\/h1>/.test(files[0].content)); + assert(/

b<\/h1>/.test(files[1].content)); + assert(/

c<\/h1>/.test(files[2].content)); + done(); + }); + }); + + it('should render views from src with the engine passed on the opts', function (done) { + var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); + var files = []; + + stream.pipe(app.renderFile({engine: '*'})) + .on('error', done) + .on('data', function (file) { + files.push(file); + }) + .on('end', function () { + assert(/

a<\/h2>/.test(files[0].content)); + assert(/

b<\/h2>/.test(files[1].content)); + assert(/

c<\/h2>/.test(files[2].content)); + done(); + }); + }); + + it('should use the context passed on the opts', function (done) { + var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); + var files = []; + + stream.pipe(app.renderFile({a: {title: 'foo'}})) + .on('error', done) + .on('data', function (file) { + files.push(file); + }) + .on('end', function () { + assert(/

foo<\/h1>/.test(files[0].content)); + assert(/

b<\/h1>/.test(files[1].content)); + assert(/

c<\/h1>/.test(files[2].content)); + done(); + }); + }); + + it('should render the files in a collection', function (cb) { + var files = []; + app.toStream('files') + .pipe(app.renderFile()) + .on('error', cb) + .on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + files.push(file); + }) + .on('end', function () { + assert(/this is a/.test(files[0].content)); + assert(/this is b/.test(files[1].content)); + assert(/this is c/.test(files[2].content)); + assert.equal(files.length, 3); + cb(); + }); + }); + + it('should handle engine errors', function (cb) { + app.create('notdefined', {engine: '*'}); + app.notdefined('foo', {content: '<%= bar %>'}); + app.toStream('notdefined') + .pipe(app.renderFile()) + .on('error', function (err) { + assert.equal(typeof err, 'object'); + assert.equal(err.message, 'bar is not defined'); + cb(); + }) + .on('end', function () { + cb(new Error('expected renderFile to handle the error.')); + }); + }); +}); diff --git a/test/app.symlink.js b/test/app.symlink.js new file mode 100644 index 00000000..69e8d459 --- /dev/null +++ b/test/app.symlink.js @@ -0,0 +1,396 @@ +require('mocha'); +var should = require('should'); +var fs = require('graceful-fs'); +var path = require('path'); +var rimraf = require('rimraf'); +var bufEqual = require('buffer-equal'); +var through = require('through2'); +var File = require('vinyl'); +var assemble = require('..'); +var spies = require('./support/spy'); +var chmodSpy = spies.chmodSpy; +var statSpy = spies.statSpy; +var app, bufferStream; + +var wipeOut = function(cb) { + rimraf(path.join(__dirname, './actual/'), cb); + spies.setError('false'); + statSpy.reset(); + chmodSpy.reset(); + app = assemble(); +}; + +var dataWrap = function(fn) { + return function(data, enc, cb) { + fn(data); + cb(); + }; +}; + +var realMode = function(n) { + return n & 07777; +}; + +describe('symlink stream', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should pass through writes with cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = app.symlink('./actual/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should pass through writes with default cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = app.symlink(path.join(__dirname, './actual/')); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should make link to the right folder with relative cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedBase = path.join(__dirname, './actual'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + fs.readlinkSync(expectedPath).should.equal(inputPath); + done(); + }; + + var stream = app.symlink('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with function and relative cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedBase = path.join(__dirname, './actual'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + fs.readlinkSync(expectedPath).should.equal(inputPath); + done(); + }; + + var stream = app.symlink(function(file){ + should.exist(file); + file.should.equal(expectedFile); + return './actual'; + }, {cwd: path.relative(process.cwd(), __dirname)}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = 0655; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + fs.readlinkSync(expectedPath).should.equal(inputPath); + done(); + }; + + var stream = app.symlink('./actual/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write streaming files to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = 0655; + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + stat: { + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + fs.readlinkSync(expectedPath).should.equal(inputPath); + done(); + }; + + var stream = app.symlink('./actual/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + setTimeout(function(){ + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should write directories to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/wow'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/wow'); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = 0655; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function(){ + return true; + }, + mode: expectedMode + } + }); + + var onEnd = function(){ + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.readlinkSync(expectedPath).should.equal(inputPath); + fs.lstatSync(expectedPath).isDirectory().should.equal(false); + fs.statSync(expectedPath).isDirectory().should.equal(true); + done(); + }; + + var stream = app.symlink('./actual/', {cwd: __dirname}); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should use different modes for files and directories', function(done) { + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + var expectedBase = path.join(__dirname, './actual/wow'); + var expectedDirMode = 0755; + var expectedFileMode = 0655; + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function(){ + realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); + realMode(buffered[0].stat.mode).should.equal(expectedFileMode); + done(); + }; + + var stream = app.symlink('./actual/', { + cwd: __dirname, + mode: expectedFileMode, + dirMode: expectedDirMode + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should change to the specified base', function(done) { + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function(){ + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = app.symlink('./actual/', { + cwd: __dirname, + base: inputBase + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should report IO errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = 0722; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode + } + }); + + fs.mkdirSync(expectedBase); + fs.chmodSync(expectedBase, 0); + + var stream = app.symlink('./actual/', {cwd: __dirname}); + stream.on('error', function(err) { + err.code.should.equal('EACCES'); + done(); + }); + stream.write(expectedFile); + }); + + ['end', 'finish'].forEach(function(eventName) { + it('should emit ' + eventName + ' event', function(done) { + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var stream = app.symlink('./actual/', {cwd: __dirname}); + + stream.on(eventName, function() { + done(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: new Buffer("1234567890") + }); + + stream.write(file); + stream.end(); + }); + }); +}); diff --git a/test/app.toStream.js b/test/app.toStream.js new file mode 100644 index 00000000..97bd30ec --- /dev/null +++ b/test/app.toStream.js @@ -0,0 +1,64 @@ +'use strict'; + +var assemble = require('..'); +var assert = require('assert'); +var should = require('should'); +var app; + +describe('toStream()', function() { + beforeEach(function () { + app = assemble(); + app.create('pages'); + app.page('a', {content: 'this is A'}); + app.page('b', {content: 'this is B'}); + app.page('c', {content: 'this is C'}); + + app.create('posts'); + app.post('x', {content: 'this is X'}); + app.post('y', {content: 'this is Y'}); + app.post('z', {content: 'this is Z'}); + }); + + it('should return a stream', function (cb) { + var stream = app.toStream(); + should.exist(stream); + should.exist(stream.on); + cb(); + }); + + it('should return a stream for a collection', function (cb) { + var stream = app.toStream('pages'); + should.exist(stream); + should.exist(stream.on); + cb(); + }); + + it('should stack handle multiple collections', function (cb) { + var files = []; + app.toStream('pages') + .pipe(app.toStream('posts')) + .on('data', function(file) { + files.push(file); + }) + .on('end', function () { + assert.equal(files.length, 6); + cb(); + }); + }); + + it('should push each item in the collection into the stream', function (cb) { + var files = []; + app.toStream('pages') + .on('error', cb) + .on('data', function (file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + files.push(file.path); + }) + .on('end', function () { + assert.equal(files.length, 3); + cb(); + }); + }); +}); \ No newline at end of file diff --git a/test/collection.use.js b/test/collection.use.js index d7f7cbca..bc1c045f 100644 --- a/test/collection.use.js +++ b/test/collection.use.js @@ -3,58 +3,58 @@ require('should'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); -var Views = App.Views; -var View = App.View; +var Collection = App.Collection; +var Item = App.Item; var collection; describe('collection.use', function () { beforeEach(function () { - collection = new Views(); + collection = new Collection(); }); it('should expose the instance to `use`:', function (done) { collection.use(function (inst) { - assert(inst instanceof Views); + assert(inst instanceof Collection); done(); }); }); it('should be chainable:', function (done) { collection.use(function (inst) { - assert(inst instanceof Views); + assert(inst instanceof Collection); }) .use(function (inst) { - assert(inst instanceof Views); + assert(inst instanceof Collection); }) .use(function (inst) { - assert(inst instanceof Views); + assert(inst instanceof Collection); done(); }); }); it('should expose the collection to a plugin:', function () { - collection.use(function (views) { - assert(views instanceof Views); - views.foo = views.addView.bind(views); + collection.use(function (items) { + assert(items instanceof Collection); + items.foo = items.addItem.bind(items); }); collection.foo('a', {content: '...'}); - assert(collection.views.hasOwnProperty('a')); + assert(collection.items.hasOwnProperty('a')); }); it('should expose collection when chained:', function () { collection - .use(function (views) { - assert(views instanceof Views); - views.foo = views.addView.bind(views); + .use(function (items) { + assert(items instanceof Collection); + items.foo = items.addItem.bind(items); }) - .use(function (views) { - assert(views instanceof Views); - views.bar = views.addView.bind(views); + .use(function (items) { + assert(items instanceof Collection); + items.bar = items.addItem.bind(items); }) - .use(function (views) { - assert(views instanceof Views); - views.baz = views.addView.bind(views); + .use(function (items) { + assert(items instanceof Collection); + items.baz = items.addItem.bind(items); }); var pages = collection; @@ -63,25 +63,25 @@ describe('collection.use', function () { pages.bar({path: 'b', content: '...'}); pages.baz({path: 'c', content: '...'}); - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); + assert(collection.items.hasOwnProperty('a')); + assert(collection.items.hasOwnProperty('b')); + assert(collection.items.hasOwnProperty('c')); }); - it('should work when a custom `View` constructor is passed:', function () { - collection = new Views({View: require('vinyl')}); + it('should work when a custom `Item` constructor is passed:', function () { + collection = new Collection({Item: require('vinyl')}); collection - .use(function (views) { - assert(views instanceof Views); - views.foo = views.addView.bind(views); + .use(function (items) { + assert(items instanceof Collection); + items.foo = items.addItem.bind(items); }) - .use(function (views) { - assert(views instanceof Views); - views.bar = views.addView.bind(views); + .use(function (items) { + assert(items instanceof Collection); + items.bar = items.addItem.bind(items); }) - .use(function (views) { - assert(views instanceof Views); - views.baz = views.addView.bind(views); + .use(function (items) { + assert(items instanceof Collection); + items.baz = items.addItem.bind(items); }); var pages = collection; @@ -90,67 +90,67 @@ describe('collection.use', function () { pages.bar({path: 'b', content: '...'}); pages.baz({path: 'c', content: '...'}); - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); + assert(collection.items.hasOwnProperty('a')); + assert(collection.items.hasOwnProperty('b')); + assert(collection.items.hasOwnProperty('c')); }); - it('should pass to view `use` if a function is returned:', function () { - collection.use(function (views) { - assert(views instanceof Views); + it('should pass to item `use` if a function is returned:', function () { + collection.use(function (items) { + assert(items instanceof Collection); - return function (view) { - view.foo = views.addView.bind(views); - assert(view instanceof View); + return function (item) { + item.foo = items.addItem.bind(items); + assert(item instanceof Item); }; }); - collection.addView('a', {content: '...'}) + collection.addItem('a', {content: '...'}) .foo({path: 'b', content: '...'}) .foo({path: 'c', content: '...'}) .foo({path: 'd', content: '...'}); - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); - assert(collection.views.hasOwnProperty('d')); + assert(collection.items.hasOwnProperty('a')); + assert(collection.items.hasOwnProperty('b')); + assert(collection.items.hasOwnProperty('c')); + assert(collection.items.hasOwnProperty('d')); }); - it('should be chainable when a view function is returned:', function () { + it('should be chainable when a item function is returned:', function () { collection - .use(function (views) { - assert(views instanceof Views); + .use(function (items) { + assert(items instanceof Collection); - return function (view) { - view.foo = views.addView.bind(views); - assert(view instanceof View); + return function (item) { + item.foo = items.addItem.bind(items); + assert(item instanceof Item); }; }) - .use(function (views) { - assert(views instanceof Views); + .use(function (items) { + assert(items instanceof Collection); - return function (view) { - view.bar = views.addView.bind(views); - assert(view instanceof View); + return function (item) { + item.bar = items.addItem.bind(items); + assert(item instanceof Item); }; }) - .use(function (views) { - assert(views instanceof Views); + .use(function (items) { + assert(items instanceof Collection); - return function (view) { - view.baz = views.addView.bind(views); - assert(view instanceof View); + return function (item) { + item.baz = items.addItem.bind(items); + assert(item instanceof Item); }; }); - collection.addView('a', {content: '...'}) + collection.addItem('a', {content: '...'}) .foo({path: 'b', content: '...'}) .bar({path: 'c', content: '...'}) .baz({path: 'd', content: '...'}); - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); - assert(collection.views.hasOwnProperty('d')); + assert(collection.items.hasOwnProperty('a')); + assert(collection.items.hasOwnProperty('b')); + assert(collection.items.hasOwnProperty('c')); + assert(collection.items.hasOwnProperty('d')); }); }); diff --git a/test/dest.js b/test/dest.js deleted file mode 100644 index 3f077c06..00000000 --- a/test/dest.js +++ /dev/null @@ -1,908 +0,0 @@ -var spies = require('./support/spy'); -var chmodSpy = spies.chmodSpy; -var statSpy = spies.statSpy; - -var assert = require('assert'); -var App = require('../'); -var app; - -var path = require('path'); -var fs = require('graceful-fs'); -var rimraf = require('rimraf'); - -var bufferStream; -var bufEqual = require('buffer-equal'); -var through = require('through2'); -var File = require('vinyl'); - -var should = require('should'); -require('mocha'); - -var wipeOut = function(cb) { - app = new App(); - rimraf(path.join(__dirname, './out-fixtures/'), cb); - spies.setError('false'); - statSpy.reset(); - chmodSpy.reset(); -}; - -var dataWrap = function(fn) { - return function(data, enc, cb) { - fn(data); - cb(); - }; -}; - -var realMode = function(n) { - return n & 07777; -}; - -describe('dest stream', function() { - beforeEach(wipeOut); - afterEach(wipeOut); - - it('should explode on invalid folder (empty)', function(done) { - var stream; - try { - stream = app.dest(); - } catch (err) { - should.exist(err); - should.not.exist(stream); - done(); - } - }); - - it('should explode on invalid folder (empty string)', function(done) { - var stream; - try { - stream = app.dest(''); - } catch (err) { - should.exist(err); - should.not.exist(stream); - done(); - } - }); - - it('should pass through writes with cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should pass through writes with default cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var stream = app.dest(path.join(__dirname, './out-fixtures/')); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not write null files', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(false); - done(); - }; - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with relative cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); - }; - - var stream = app.dest('./out-fixtures/', {cwd: path.relative(process.cwd(), __dirname)}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with function and relative cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); - }; - - var stream = app.dest(function(file){ - should.exist(file); - file.should.equal(expectedFile); - return './out-fixtures'; - }, {cwd: path.relative(process.cwd(), __dirname)}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0655; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write streaming files to the right folder', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0655; - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - stat: { - mode: expectedMode - } - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - setTimeout(function(){ - contentStream.write(expectedContents); - contentStream.end(); - }, 100); - stream.end(); - }); - - it('should write directories to the right folder', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0655; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function(){ - return true; - }, - mode: expectedMode - } - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).isDirectory().should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should allow piping multiple dests in streaming mode', function(done) { - var inputPath1 = path.join(__dirname, './out-fixtures/multiple-first'); - var inputPath2 = path.join(__dirname, './out-fixtures/multiple-second'); - var inputBase = path.join(__dirname, './out-fixtures/'); - var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var stream1 = app.dest('./out-fixtures/', {cwd: __dirname}); - var stream2 = app.dest('./out-fixtures/', {cwd: __dirname}); - var content = fs.readFileSync(srcPath); - var rename = through.obj(function(file, _, next) { - file.path = inputPath2; - this.push(file); - next(); - }); - - stream1.on('data', function(file) { - file.path.should.equal(inputPath1); - }); - - stream1.pipe(rename).pipe(stream2); - stream2.on('data', function(file) { - file.path.should.equal(inputPath2); - }).once('end', function() { - fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); - fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); - done(); - }); - - var file = new File({ - base: inputBase, - path: inputPath1, - cwd: __dirname, - contents: content - }); - - stream1.write(file); - stream1.end(); - }); - - it('should write new files with the default user mode', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = 0666 & (~process.umask()); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write new files with the specified mode', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = 0744; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname, mode:expectedMode}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should update file mode to match the vinyl mode', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var startMode = 0655; - var expectedMode = 0722; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var onEnd = function(){ - assert(chmodSpy.called); - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, startMode); - - chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should use different modes for files and directories', function(done) { - var inputBase = path.join(__dirname, './fixtures/vinyl'); - var inputPath = path.join(__dirname, './fixtures/vinyl/wow/suchempty'); - var expectedBase = path.join(__dirname, './out-fixtures/wow'); - var expectedDirMode = 0755; - var expectedFileMode = 0655; - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function(){ - realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); - realMode(buffered[0].stat.mode).should.equal(expectedFileMode); - done(); - }; - - var stream = app.dest('./out-fixtures/', { - cwd: __dirname, - mode: expectedFileMode, - dirMode: expectedDirMode - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should change to the specified base as string', function(done) { - var inputBase = path.join(__dirname, './fixtures/vinyl'); - var inputPath = path.join(__dirname, './fixtures/vinyl/wow/suchempty'); - - var firstFile = new File({ - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function(){ - buffered[0].base.should.equal(inputBase); - done(); - }; - - var stream = app.dest('./out-fixtures/', { - cwd: __dirname, - base: inputBase - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should change to the specified base as function', function(done) { - var inputBase = path.join(__dirname, './fixtures/vinyl'); - var inputPath = path.join(__dirname, './fixtures/vinyl/wow/suchempty'); - - var firstFile = new File({ - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function() { - buffered[0].base.should.equal(inputBase); - done(); - }; - - var stream = app.dest('./out-fixtures/', { - cwd: __dirname, - base: function(file){ - should.exist(file); - file.path.should.equal(inputPath); - return inputBase; - } - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should report IO errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0722; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, 0); - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - stream.on('error', function(err) { - err.code.should.equal('EACCES'); - done(); - }); - stream.write(expectedFile); - }); - - it('should report stat errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0722; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - spies.setError(function(mod, fn) { - if (fn === 'stat' && arguments[2] === expectedPath) { - return new Error('stat error'); - } - }); - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - stream.on('error', function(err) { - err.message.should.equal('stat error'); - done(); - }); - stream.write(expectedFile); - }); - - it('should report chmod errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0722; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - spies.setError(function(mod, fn) { - if (fn === 'chmod' && arguments[2] === expectedPath) { - return new Error('chmod error'); - } - }); - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - stream.on('error', function(err) { - err.message.should.equal('chmod error'); - done(); - }); - stream.write(expectedFile); - }); - - it('should not chmod a matching file', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 0722; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var expectedCount = 0; - spies.setError(function(mod, fn) { - if (fn === 'stat' && arguments[2] === expectedPath) { - expectedCount++; - } - }); - - var onEnd = function(){ - expectedCount.should.equal(1); - assert(!chmodSpy.called); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - statSpy.reset(); - chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = 03722; - var normalMode = 0722; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: normalMode - } - }); - - var expectedCount = 0; - spies.setError(function(mod, fn) { - if (fn === 'stat' && arguments[2] === expectedPath) { - expectedCount++; - } - }); - - var onEnd = function(){ - expectedCount.should.equal(1); - assert(!chmodSpy.called); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - statSpy.reset(); - chmodSpy.reset(); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not overwrite files with overwrite option set to false', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, './out-fixtures'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); - done(); - }; - - // Write expected file which should not be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: false}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should overwrite files with overwrite option set to true', function(done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, './out-fixtures'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents - }); - - var onEnd = function(){ - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); - done(); - }; - - // This should be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = app.dest('./out-fixtures/', {cwd: __dirname, overwrite: true}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should create symlinks when the `symlink` attribute is set on the file', function (done) { - var inputPath = path.join(__dirname, './fixtures/vinyl/test-create-dir-symlink'); - var inputBase = path.join(__dirname, './fixtures/vinyl/'); - var inputRelativeSymlinkPath = 'wow'; - - var expectedPath = path.join(__dirname, './out-fixtures/test-create-dir-symlink'); - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, //'' - }); - - // `src()` adds this side-effect with `keepSymlinks` option set to false - inputFile.symlink = inputRelativeSymlinkPath; - - var onEnd = function(){ - fs.readlink(buffered[0].path, function () { - buffered[0].symlink.should.equal(inputFile.symlink); - buffered[0].path.should.equal(expectedPath); - done(); - }); - }; - - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should emit finish event', function(done) { - var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var stream = app.dest('./out-fixtures/', {cwd: __dirname}); - - stream.once('finish', function() { - done(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: new Buffer("1234567890") - }); - - stream.write(file); - stream.end(); - }); -}); diff --git a/test/fixtures/pages/a.hbs b/test/fixtures/pages/a.hbs index 0ec5d8d8..51320bdc 100644 --- a/test/fixtures/pages/a.hbs +++ b/test/fixtures/pages/a.hbs @@ -1 +1,2 @@ -

\ No newline at end of file +

{{title}}

+

<%= title() %>

\ No newline at end of file diff --git a/test/fixtures/pages/b.hbs b/test/fixtures/pages/b.hbs index 0ec5d8d8..51320bdc 100644 --- a/test/fixtures/pages/b.hbs +++ b/test/fixtures/pages/b.hbs @@ -1 +1,2 @@ -

\ No newline at end of file +

{{title}}

+

<%= title() %>

\ No newline at end of file diff --git a/test/fixtures/pages/c.hbs b/test/fixtures/pages/c.hbs index 0ec5d8d8..51320bdc 100644 --- a/test/fixtures/pages/c.hbs +++ b/test/fixtures/pages/c.hbs @@ -1 +1,2 @@ -

\ No newline at end of file +

{{title}}

+

<%= title() %>

\ No newline at end of file diff --git a/test/group.js b/test/group.js index e846a4f8..601e91ee 100644 --- a/test/group.js +++ b/test/group.js @@ -66,6 +66,10 @@ describe('group', function () { group = new Group(); }); + it('should expose options:', function () { + assert(typeof group.options === 'object'); + }); + it('should set a value on the instance:', function () { group.set('a', 'b'); assert(group.a ==='b'); diff --git a/test/helpers.js b/test/helpers.js index 4f5be2ab..13981f0b 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -6,12 +6,13 @@ var assert = require('assert'); var consolidate = require('consolidate'); var handlebars = require('engine-handlebars'); var matter = require('parser-front-matter'); -var support = require('./support'); var swig = consolidate.swig; require('swig'); var support = require('./support'); var App = support.resolve(); +var helpers = App._.proto.helpers; +var init = App._.proto.init; var app; describe('helpers', function () { @@ -34,6 +35,19 @@ describe('helpers', function () { }); }); + describe('instance', function () { + it('should prime _', function () { + function Foo() { + Base.call(this); + init(this); + } + Base.extend(Foo); + var foo = new Foo(); + helpers(foo); + assert(typeof foo._ ==='object'); + }); + }); + describe('helpers', function() { beforeEach(function() { app = new App(); @@ -203,7 +217,7 @@ describe('sync helpers', function () { }); }); - it.skip('should use a namespaced helper:', function (done) { + it('should use a namespaced helper:', function (done) { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= foo.upper(a) %>', locals: {a: 'bbb'}}); app.helperGroup('foo', { @@ -315,7 +329,7 @@ describe('built-in helpers:', function () { }); }); - it.skip('should use locals from the `view.render` method:', function (done) { + it('should use locals from the `view.render` method:', function (done) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) diff --git a/test/list.js b/test/list.js index e4edbca8..0dc23ca1 100644 --- a/test/list.js +++ b/test/list.js @@ -135,6 +135,16 @@ describe('list', function () { }); describe('addItem', function() { + beforeEach(function() { + list = new List(); + }); + + it('should add items to a list', function () { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + }); }); describe('removeItem', function() { diff --git a/test/list.use.js b/test/list.use.js new file mode 100644 index 00000000..c192a009 --- /dev/null +++ b/test/list.use.js @@ -0,0 +1,156 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var Item = App.Item; +var list; + +describe('list.use', function () { + beforeEach(function () { + list = new List(); + }); + + it('should expose the instance to `use`:', function (done) { + list.use(function (inst) { + assert(inst instanceof List); + done(); + }); + }); + + it('should be chainable:', function (done) { + list.use(function (inst) { + assert(inst instanceof List); + }) + .use(function (inst) { + assert(inst instanceof List); + }) + .use(function (inst) { + assert(inst instanceof List); + done(); + }); + }); + + it('should expose the list to a plugin:', function () { + list.use(function (items) { + assert(items instanceof List); + items.foo = items.addItem.bind(items); + }); + + list.foo('a', {content: '...'}); + assert(list.hasItem('a')); + }); + + it('should expose list when chained:', function () { + list + .use(function (items) { + assert(items instanceof List); + items.foo = items.addItem.bind(items); + }) + .use(function (items) { + assert(items instanceof List); + items.bar = items.addItem.bind(items); + }) + .use(function (items) { + assert(items instanceof List); + items.baz = items.addItem.bind(items); + }); + + var pages = list; + + pages.foo({path: 'a', content: '...'}); + pages.bar({path: 'b', content: '...'}); + pages.baz({path: 'c', content: '...'}); + + assert(list.hasItem('a')); + assert(list.hasItem('b')); + assert(list.hasItem('c')); + }); + + it('should work when a custom `Item` constructor is passed:', function () { + list = new List({Item: require('vinyl')}); + list + .use(function (items) { + assert(items instanceof List); + items.foo = items.addItem.bind(items); + }) + .use(function (items) { + assert(items instanceof List); + items.bar = items.addItem.bind(items); + }) + .use(function (items) { + assert(items instanceof List); + items.baz = items.addItem.bind(items); + }); + + var pages = list; + + pages.foo({path: 'a', content: '...'}); + pages.bar({path: 'b', content: '...'}); + pages.baz({path: 'c', content: '...'}); + + assert(list.hasItem('a')); + assert(list.hasItem('b')); + assert(list.hasItem('c')); + }); + + it('should pass to item `use` if a function is returned:', function () { + list.use(function (items) { + assert(items instanceof List); + + return function (item) { + item.foo = items.addItem.bind(items); + assert(item.isItem || item.isView); + }; + }); + + list.addItem('a', {content: '...'}) + .foo({path: 'b', content: '...'}) + .foo({path: 'c', content: '...'}) + .foo({path: 'd', content: '...'}); + + assert(list.hasItem('a')); + assert(list.hasItem('b')); + assert(list.hasItem('c')); + assert(list.hasItem('d')); + }); + + it('should be chainable when a item function is returned:', function () { + list + .use(function (items) { + assert(items instanceof List); + + return function (item) { + item.foo = items.addItem.bind(items); + assert(item instanceof Item); + }; + }) + .use(function (items) { + assert(items instanceof List); + + return function (item) { + item.bar = items.addItem.bind(items); + assert(item instanceof Item); + }; + }) + .use(function (items) { + assert(items instanceof List); + + return function (item) { + item.baz = items.addItem.bind(items); + assert(item instanceof Item); + }; + }); + + list.addItem('a', {content: '...'}) + .foo({path: 'b', content: '...'}) + .bar({path: 'c', content: '...'}) + .baz({path: 'd', content: '...'}); + + assert(list.hasItem('a')); + assert(list.hasItem('b')); + assert(list.hasItem('c')); + assert(list.hasItem('d')); + }); +}); diff --git a/test/mergePartials.js b/test/mergePartials.js index 92e2f77b..8a738ea5 100644 --- a/test/mergePartials.js +++ b/test/mergePartials.js @@ -6,7 +6,6 @@ var app; describe('mergePartials', function () { beforeEach(function () { app = new App(); - // reset views app.views = {}; }); @@ -25,7 +24,7 @@ describe('mergePartials', function () { actual.partials.should.have.properties(['a', 'b', 'c']); }); - it('should keep partials collections on separate collections:', function () { + it('should keep partials collections on separaet collections:', function () { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); diff --git a/test/store.js b/test/store.js index 861d6a93..8d3f3c6f 100644 --- a/test/store.js +++ b/test/store.js @@ -4,7 +4,7 @@ require('mocha'); require('should'); var fs = require('fs'); var path = require('path'); -var Store = require('data-store'); +var store = require('base-store'); var assert = require('assert'); var App = require('../'); var app; @@ -12,17 +12,12 @@ var app; describe('store', function () { beforeEach(function () { app = new App(); + app.use(store('verb-tests')) }); afterEach(function (cb) { - app.store.del({force: true}, function (err) { - if (err) return cb(err); - cb(); - }); - }); - - it('should create an instance of Store', function () { - assert(app.store instanceof Store); + app.store.del({force: true}); + cb(); }); it('should create a store at the given `cwd`', function () { @@ -165,6 +160,16 @@ describe('store', function () { }); describe('events', function () { + beforeEach(function () { + app = new App(); + app.use(store('verb-tests')); + }); + + afterEach(function (cb) { + app.store.del({force: true}); + cb(); + }); + it('should emit `set` when an object is set:', function () { var keys = []; app.store.on('set', function (key) { @@ -217,17 +222,17 @@ describe('events', function () { app.store.del('a'); }); - it('should emit deleted keys on `del`:', function () { - var res; - app.store.on('del', function (keys) { - keys.should.eql(['a', 'c', 'e']); - assert(Object.keys(app.store.data).length === 0); + it('should emit deleted keys on `del`:', function (done) { + var keys = []; + app.store.on('del', function (key) { + keys.push(key); }); app.store.set('a', 'b'); app.store.set('c', 'd'); app.store.set('e', 'f'); - app.store.data.should.have.properties(['a', 'c', 'e']); app.store.del({force: true}); + keys.should.eql(['a', 'c', 'e']); + done(); }); }); diff --git a/test/views.js b/test/views.js index 2a88024a..8f99b7d4 100644 --- a/test/views.js +++ b/test/views.js @@ -168,6 +168,30 @@ describe('views', function () { collection = new Views(); }); + it('should emit an error if a string glob pattern is passed', function (done) { + try { + collection.addViews('*.js'); + done(new Error('expected an error')); + } catch(err) { + assert(err); + assert(err.message); + assert(/glob/.test(err.message)); + done(); + } + }); + + it('should emit an error if an array glob pattern is passed', function (done) { + try { + collection.addViews(['*.js']); + done(new Error('expected an error')); + } catch(err) { + assert(err); + assert(err.message); + assert(/glob/.test(err.message)); + done(); + } + }); + it('should add multiple views:', function () { collection.addViews({ one: {content: 'foo'}, @@ -177,6 +201,18 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); + it('should return the collection instance for chaining:', function () { + var views = collection.addViews({ + one: {content: 'foo'}, + two: {content: 'bar'} + }); + + var view = views.getView('one'); + assert(view); + assert(view.content); + assert(view.content === 'foo'); + }); + it('should create views from an instance of Views', function () { collection.addViews({ one: {content: 'foo'}, @@ -228,6 +264,30 @@ describe('views', function () { collection = new Views(); }); + it('should emit an error if a string glob pattern is passed', function (done) { + try { + collection.addList('*.js'); + done(new Error('expected an error')); + } catch(err) { + assert(err); + assert(err.message); + assert(/glob/.test(err.message)); + done(); + } + }); + + it('should emit an error if an array glob pattern is passed', function (done) { + try { + collection.addList(['*.js']); + done(new Error('expected an error')); + } catch(err) { + assert(err); + assert(err.message); + assert(/glob/.test(err.message)); + done(); + } + }); + it('should add a list of views:', function () { collection.addList([ {path: 'one', content: 'foo'}, @@ -237,7 +297,7 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); - it('should add a list of views from the constructor:', function () { + it('should add a list from the constructor:', function () { var list = new List([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -248,6 +308,17 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); + it('should add list items from the constructor:', function () { + var list = new List([ + {path: 'one', content: 'foo'}, + {path: 'two', content: 'bar'} + ]); + + collection = new Views(list.items); + assert(isBuffer(collection.views.one.contents)); + assert(isBuffer(collection.views.two.contents)); + }); + it('should throw an error when list is not an array:', function () { var views = new Views(); (function () { @@ -264,18 +335,18 @@ describe('views', function () { }); it('should load an array of items from an event:', function () { - var collection = new Views(); + var pages = new Views(); - collection.on('addList', function (list) { + pages.on('addList', function (list) { while (list.length) { - collection.addView({path: list.pop()}); + pages.addView({path: list.pop()}); } this.loaded = true; }); - collection.addList(['a.txt', 'b.txt', 'c.txt']); - assert(collection.views.hasOwnProperty('a.txt')); - assert(collection.views['a.txt'].path === 'a.txt'); + pages.addList(['a.txt', 'b.txt', 'c.txt']); + assert(pages.views.hasOwnProperty('a.txt')); + assert(pages.views['a.txt'].path === 'a.txt'); }); it('should load an array of items from the addList callback:', function () { @@ -288,17 +359,6 @@ describe('views', function () { assert(collection.views['a.txt'].path === 'a.txt'); }); - // it('should not blow up on', function () { - // var collection = new Views(); - // var list = [{path: 'a.txt'}, {path: 'b.txt'}, {path: 'c.txt'}]; - // collection.addList(list, function (item) { - // item.content = path.basename(item.path, path.extname(item.path)); - // }); - // assert(collection.views.hasOwnProperty('a.txt')); - // assert(collection.views['a.txt'].path === 'a.txt'); - // assert(collection.views['a.txt'].content === 'a'); - // }); - it('should load an object of views from an event:', function () { var collection = new Views(); diff --git a/test/views.use.js b/test/views.use.js new file mode 100644 index 00000000..09d47dfb --- /dev/null +++ b/test/views.use.js @@ -0,0 +1,156 @@ +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var Views = App.Views; +var View = App.View; +var collection; + +describe('views.use', function () { + beforeEach(function () { + collection = new Views(); + }); + + it('should expose the instance to `use`:', function (done) { + collection.use(function (inst) { + assert(inst instanceof Views); + done(); + }); + }); + + it('should be chainable:', function (done) { + collection.use(function (inst) { + assert(inst instanceof Views); + }) + .use(function (inst) { + assert(inst instanceof Views); + }) + .use(function (inst) { + assert(inst instanceof Views); + done(); + }); + }); + + it('should expose the collection to a plugin:', function () { + collection.use(function (views) { + assert(views instanceof Views); + views.foo = views.addView.bind(views); + }); + + collection.foo('a', {content: '...'}); + assert(collection.views.hasOwnProperty('a')); + }); + + it('should expose collection when chained:', function () { + collection + .use(function (views) { + assert(views instanceof Views); + views.foo = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.bar = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.baz = views.addView.bind(views); + }); + + var pages = collection; + + pages.foo({path: 'a', content: '...'}); + pages.bar({path: 'b', content: '...'}); + pages.baz({path: 'c', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + }); + + it('should work when a custom `View` constructor is passed:', function () { + collection = new Views({View: require('vinyl')}); + collection + .use(function (views) { + assert(views instanceof Views); + views.foo = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.bar = views.addView.bind(views); + }) + .use(function (views) { + assert(views instanceof Views); + views.baz = views.addView.bind(views); + }); + + var pages = collection; + + pages.foo({path: 'a', content: '...'}); + pages.bar({path: 'b', content: '...'}); + pages.baz({path: 'c', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + }); + + it('should pass to view `use` if a function is returned:', function () { + collection.use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.foo = views.addView.bind(views); + assert(view instanceof View); + }; + }); + + collection.addView('a', {content: '...'}) + .foo({path: 'b', content: '...'}) + .foo({path: 'c', content: '...'}) + .foo({path: 'd', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + assert(collection.views.hasOwnProperty('d')); + }); + + it('should be chainable when a view function is returned:', function () { + collection + .use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.foo = views.addView.bind(views); + assert(view instanceof View); + }; + }) + .use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.bar = views.addView.bind(views); + assert(view instanceof View); + }; + }) + .use(function (views) { + assert(views instanceof Views); + + return function (view) { + view.baz = views.addView.bind(views); + assert(view instanceof View); + }; + }); + + collection.addView('a', {content: '...'}) + .foo({path: 'b', content: '...'}) + .bar({path: 'c', content: '...'}) + .baz({path: 'd', content: '...'}); + + assert(collection.views.hasOwnProperty('a')); + assert(collection.views.hasOwnProperty('b')); + assert(collection.views.hasOwnProperty('c')); + assert(collection.views.hasOwnProperty('d')); + }); +}); From 3de502fb86f322b467a67f249bd2ddb4abd6760d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 27 Oct 2015 07:27:17 -0400 Subject: [PATCH 021/282] setup defaults, config --- .travis.yml | 10 +--- example.js | 27 --------- index.js | 46 +++++++++++---- lib/config.js | 114 ++++++++------------------------------ lib/create.js | 45 +++++++++++++++ lib/defaults.js | 129 ------------------------------------------- lib/helpers.js | 4 +- lib/locals.js | 30 +++++----- lib/store.js | 11 ---- lib/utils.js | 7 ++- middleware/pretty.js | 121 ---------------------------------------- package.json | 75 ++++++++++--------------- 12 files changed, 159 insertions(+), 460 deletions(-) delete mode 100644 example.js create mode 100644 lib/create.js delete mode 100644 lib/defaults.js delete mode 100644 lib/store.js delete mode 100644 middleware/pretty.js diff --git a/.travis.yml b/.travis.yml index 0fc9381e..d7a07159 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,5 @@ -sudo: false language: node_js node_js: - - "0.10" + - "stable" - "0.12" - - "0.13" - - "iojs" -matrix: - fast_finish: true - allow_failures: - - node_js: "0.13" + - "0.10" diff --git a/example.js b/example.js deleted file mode 100644 index 5bb23d87..00000000 --- a/example.js +++ /dev/null @@ -1,27 +0,0 @@ -var argv = require('minimist')(process.argv.slice(2)); -var lint = require('lint-templates'); -var verb = require('./'); -var app = verb() - .use(lint()) - - -// app.includes.load('*.md'); -app.badges.load('*.md'); - -app.on('error', function (err) { - if (err) { - console.log(err.stack); - process.exit(0); - } -}); - -app.badges.getView('bower') - // .set('data.name', 'NAME') - // .set('data.github.repopath', 'verbiagesss') - .lint(/\{%=?([^%]+)%}/g) - .lint(/\{{([^}]+)}}/g) - // .render(function (err, res) { - // if (err) return console.error('Error:', err); - // if (res) console.log('Done:', res); - // }); - diff --git a/index.js b/index.js index dd284659..99d80b84 100644 --- a/index.js +++ b/index.js @@ -11,12 +11,18 @@ * module dependencies */ -var path = require('path'); +var cli = require('base-cli'); +var store = require('base-store'); +var config = require('base-config'); +var loader = require('assemble-loader'); var Core = require('assemble-core'); var ask = require('assemble-ask'); -var config = require('./defaults'); +var minimist = require('minimist'); +var expand = require('expand-args'); +var rimraf = require('rimraf'); +var create = require('./lib/create'); +var locals = require('./lib/locals'); var utils = require('./lib/utils'); -var lib = require('./lib'); /** * Create a `verb` application. This is the main function exported @@ -37,16 +43,34 @@ function Verb(options) { this.options = utils.extend({reload: false}, options); Core.call(this, this.options); + this.define('isVerb', true); + this.name = 'verb'; + + var argv = minimist(process.argv.slice(2)); + if (process.argv.length > 3) { + argv = expand(argv); + } - this.use(lib.store); - this.use(lib.locals); - this.use(ask()); - // this.use(lib.config); - this.use(lib.helpers); - this.use(lib.defaults); - this.use(config); + this.set('argv', this.argv || argv); + this.set('pkg', require('load-pkg')()); + this.set('updaters', {}); + + create(this); + this.use(utils.runtimes()) + .use(locals({name: this.name})) + .use(store(this.name)) + .use(config()) + .use(loader()) + .use(ask()) + .use(cli()); + + var verb = this.get('pkg.verb'); + this.config.process(verb); + + this.onLoad(/\.md$/, function (view, next) { + utils.matter.parse(view, next); + }); - this.define('isVerb', true); this.option('rethrow', { regex: /\{%=?([^%]*)%}/ }); this.engine('md', require('engine-base'), { delims: ['{%', '%}'] diff --git a/lib/config.js b/lib/config.js index cd82aeff..61f26864 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,97 +1,31 @@ 'use strict'; -var utils = require('./utils'); module.exports = function (app) { if (!app.isApp) return; - var Config = utils.Config; - var config = app.locals.cache; - var keys = Object.keys(app.views); - var views = {}; - - keys.forEach(function (key) { - var instance = app[key]; - - views[key] = function (config) { - var mapper = new Config({ - options: 'option', - set: 'set', - addViews: 'addViews' - }, instance); - - console.log('loading templates from config: "' + key + '"'); - return mapper.process(config); - }; - }); - - var collections = new Config(views); - var configMap = new Config({ - collections: function (config) { - return collections.process(config); - }, - helpers: 'helpers', - asyncHelpers: 'asyncHelpers' - }, app); - - configMap.process(config); + var utils = require('./utils'); + var config = require('base-config'); + app.use(config()); + + app.config + .map('collections', function(val) { + app.visit('create', val); + }) + .map('addViews') + .map('addView') + .map('helpers') + .map('asyncHelpers') + .map('ignore', function(val) { + app.option('ignore', val); + }) + .map('files', function(val) { + app.files(val); + }) + .map('reflinks', function(val) { + app.data({reflinks: val}); + }) + .map('related', function(val) { + app.data({related: val}); + }); }; - -// configMap.process({ -// "ignore": [ -// ".git" -// ], -// "deps": { -// "include": [ -// "readme-includes", -// "readme-badges" -// ], -// "ignore": [ -// "support" -// ] -// }, -// "collections": { -// "docs": { -// "options": { -// "cwd": "fixtures/foo" -// }, -// "set": { -// "a.b.c": "d" -// } -// }, -// "includes": { -// "options": { -// "cwd": "fixtures/bar" -// } -// }, -// "badges": { -// "options": { -// "cwd": "fixtures/baz" -// }, -// "addViews": { -// "foo": "this is a badge" -// } -// } -// }, -// "helpers": { -// "whatever": "@/helper-related" -// }, -// "related": { -// "list": [ -// "composer", -// "assemble", -// "template", -// "engine" -// ] -// }, -// "reflinks": [ -// "jsdiff", -// "option-cache", -// "template", -// "plasma", -// "config-cache" -// ] -// }); - -// // console.log(verb.docs.get('a.b.c')); -// console.log(verb._.helpers); diff --git a/lib/create.js b/lib/create.js new file mode 100644 index 00000000..cac7d758 --- /dev/null +++ b/lib/create.js @@ -0,0 +1,45 @@ +'use strict'; + +/** + * Plugin for creating default view collections + * | docs + * | layouts + * | includes + * | shields + * | badges + */ + +var utils = require('./utils'); + +module.exports = function (app) { + app.create('badges', { + viewType: ['partial'], + renameKey: utils.rename, + engine: 'md' + }); + + app.create('shields', { + viewType: ['partial'], + renameKey: utils.rename, + engine: 'md' + }); + + app.create('includes', { + viewType: ['partial'], + renameKey: utils.rename, + cwd: utils.includes, + engine: 'md', + }); + + app.create('docs', { + viewType: ['partial'], + renameKey: utils.basename, + engine: 'md' + }); + + app.create('layouts', { + viewType: ['layout'], + renameKey: utils.basename, + engine: 'md' + }); +}; diff --git a/lib/defaults.js b/lib/defaults.js deleted file mode 100644 index 23bef2ed..00000000 --- a/lib/defaults.js +++ /dev/null @@ -1,129 +0,0 @@ -'use strict'; - -/** - * Plugin for creating default view collections - * | includes - * | layouts - * | pages - * | files - */ - -module.exports = function (app) { - var includes = require('readme-includes'); - // var badges = require('readme-badges'); - var utils = require('./utils'); - - app.create('includes', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: includes, - engine: 'md', - }); - - app.create('badges', { - viewType: ['partial'], - renameKey: utils.rename, - // cwd: badges, - engine: 'md' - }); - - app.create('shields', { - viewType: ['partial'], - renameKey: utils.rename, - engine: 'md' - }); - - app.create('docs', { - viewType: ['partial'], - renameKey: utils.basename, - engine: 'md' - }); - - app.create('layouts', { - viewType: ['layout'], - renameKey: utils.basename, - engine: 'md' - }); - - app.question('foo', 'this is foo, yo!'); - - app.shield('dt', { - content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); - app.shield('dm', { - content: '[![monthly downloads](https://img.shields.io/npm/dm/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); - - app.include('npm', { - content: '[![total downloads](https://img.shields.io/npm/dt/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})' - }); - - app.include('copyright', { - content: 'Copyright © 2015, {%= author.name %}.' - }); - - app.include('license', { - content: 'Released under the {%= ask("License") %} license.' - }); - - app.include('footer', { - content: '_(TODO)_' - }); - - app.include('contributing', { - content: '_(TODO)_' - }); - - app.include('tests', { - content: '_(TODO)_' - }); - - app.include('install-global', { - content: [ - 'Install with [npm](https://www.npmjs.com/)', - '', - '```sh', - '$ npm i -g {%= name %} --save', - '```' - ].join('\n') - }); - - app.include('author', { - locals: { - author: { - name: 'Brian Woodward' - }, - username: 'doowb' - }, - content: [ - '**{%= author.name %}**', - '', - '+ [github/{%= username %}](https://github.com/{%= username %})', - '+ [twitter/{%= username %}](http://twitter.com/{%= username %})', - ].join('\n') - }); - - app.badge('travis', { - content: '[![Build Status](https://travis-ci.org/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})' - }); - - app.badge('fury', { - content: '[![NPM version](https://badge.fury.io/js/{%= name %}.svg)](http://badge.fury.io/js/{%= name %})' - }); - - app.data({ - author: { - name: 'Jon Schlinkert' - }, - twitter: { - username: 'jonschlinkert' - }, - github: { - username: 'jonschlinkert' - }, - runner: { - name: app.get('cache.data.name'), - url: app.get('cache.data.repository'), - } - }); -}; diff --git a/lib/helpers.js b/lib/helpers.js index 17386650..880f9fd6 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -3,8 +3,8 @@ var utils = require('./utils'); module.exports = function () { - this.asyncHelper('related', require('helper-related')(this.options)); - this.asyncHelper('reflinks', require('helper-reflinks')(this.options)); + this.asyncHelper('related', utils.related(this.options)); + this.asyncHelper('reflinks', utils.reflinks(this.options)); this.helper('split', function (val) { return val.split(','); diff --git a/lib/locals.js b/lib/locals.js index 7f9d2247..7fd049cd 100644 --- a/lib/locals.js +++ b/lib/locals.js @@ -1,26 +1,30 @@ 'use strict'; +var get = require('get-value'); +var set = require('set-value'); var utils = require('./utils'); +module.exports = function (config) { + if (!config || !config.name) { + throw new Error('expected config.name to be a string.'); + } + + return function (app) { + var opts = utils.extend({}, config, app.option('update')); + this.locals = new Locals(config.name, this); + return this; + }; +}; + function Locals(name, app) { - this.cache = app.cache.data[name] || (app.cache.data[name] = {}); + this.cache = get(app.cache.data, name) || (app.cache.data[name] = {}); } Locals.prototype.get = function(key) { - return utils.get(this.cache, key); + return get(this.cache, key); }; Locals.prototype.set = function(key, value) { - utils.set(this.cache, key, value); + set(this.cache, key, value); return this; }; - -/** - * Expose Locals - */ - -module.exports = function(app) { - app.locals = new Locals('verb', app); -}; - -// module.exports = Locals; diff --git a/lib/store.js b/lib/store.js deleted file mode 100644 index 87989fe9..00000000 --- a/lib/store.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -/** - * Expose Locals - */ - -module.exports = function (app) { - app.store = utils.store('verb', app.options.store); -}; diff --git a/lib/utils.js b/lib/utils.js index f63913c5..70dbc6f3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -13,16 +13,17 @@ var fn = require; require = utils; require('async'); require('resolve-dir'); -require('data-store', 'store'); -require('mixin-deep', 'merge'); +require('helper-related', 'related'); +require('helper-reflinks', 'reflinks'); +require('readme-includes', 'includes'); require('get-value', 'get'); require('set-value', 'set'); +require('composer-runtimes', 'runtimes'); require('extend-shallow', 'extend'); require('parser-front-matter', 'matter'); require('middleware-utils', 'mu'); require('engine-base', 'engine'); require('map-config', 'Config'); - require = fn; utils.basename = function(key) { diff --git a/middleware/pretty.js b/middleware/pretty.js deleted file mode 100644 index 8e262de5..00000000 --- a/middleware/pretty.js +++ /dev/null @@ -1,121 +0,0 @@ -'use strict'; - -var through = require('through2'); -var Remarkable = require('remarkable'); -var PluginError = require('plugin-error'); -var prettify = require('pretty-remarkable'); -var extend = require('extend-shallow'); - - -module.exports = function(app) { - var argv = this.get('argv'); - var app = this; - - // add a trailing newline to docs? - var newline = argv.newline || this.config.get('newline'); - - return function (view, next) { - // pass some extra formatting info to `pretty-remarkable` - var opts = extend({}, app.options, view.options); - opts.username = app.get('data.username'); - opts.name = app.get('data.author.name'); - - var str = view.content; - var res = extractTables(str); - str = res.str; - - // prettify - str = pretty(str, opts); - str = str.trim() + (newline ? '\n' : ''); - str = fixParam(str); - str = fixList(str); - - res.keys.forEach(function (key) { - var table = res.tables[key].trim(); - str = str.split(key).join(table); - }); - - } -}; - -/** - * Fix list formatting - */ - -function fixList(str) { - return str.replace(/([ ]{1,4}[+-] \[?[^)]+\)?)\n\n\* /gm, '$1\n* '); -} - -/** - * Fix params - */ - -function fixParam(str) { - return str.split('__{_}_*').join('**{any}**'); -} - -/** - * Instantiate `Remarkable` and use the `prettify` plugin - * on the given `str`. - * - * @param {String} `str` - * @param {Object} `options` - * @return {String} - */ - -function pretty(str, options) { - return new Remarkable(options) - .use(prettify) - .render(str); -} - -/** - * Push the `view` through if the user has specfied - * not to format it. - */ - -function noformat(app, view, locals, argv) { - return app.isTrue('noformat') || app.isFalse('format') - || view.noformat === true || view.format === false - || locals.noformat === true || locals.format === false - || argv.noformat === true || argv.format === false; -} - - -function extractTables(str) { - var re = /^\s*[|](?=.*[|])(.*)$/; - var lines = str.split(/\r\n|\r|\n/); - var len = lines.length, i = -1; - var tables = {}; - var inside = false; - var prev = false; - var num = 0; - var content = ''; - var keys = [], key = '__TABLE_0_'; - - while (++i < len) { - var line = lines[i]; - - if (re.test(line)) { - if (prev) { - keys.push(key); - content += key; - key = '__TABLE_' + (num++) + '_'; - } - prev = false; - inside = true; - tables[key] = tables[key] || ''; - tables[key] += line + '\n'; - } else { - content += line + '\n'; - inside = false; - prev = true; - } - } - - var res = {}; - res.keys = keys; - res.str = content; - res.tables = tables; - return res; -} diff --git a/package.json b/package.json index b9b930eb..865aa0cf 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,9 @@ }, "license": "MIT", "files": [ + "defaults.js", "index.js", - "lib/" + "lib" ], "main": "index.js", "engines": { @@ -21,27 +22,34 @@ "test": "mocha" }, "dependencies": { - "assemble-ask": "^0.1.2", - "assemble-core": "^0.1.1", - "data-store": "^0.10.1", + "assemble-ask": "^0.1.4", + "assemble-core": "^0.1.7", + "assemble-loader": "^0.2.4", + "async": "^1.5.0", + "base-cli": "^0.1.2", + "base-config": "^0.1.2", + "base-store": "^0.1.1", + "composer-runtimes": "^0.5.1", "engine-base": "^0.1.2", + "expand-args": "^0.2.1", "export-files": "^2.1.0", "extend-shallow": "^2.0.1", "get-value": "^1.2.1", - "helper-reflinks": "^2.0.0", + "helper-reflinks": "^2.0.1", "helper-related": "^0.11.1", - "lazy-cache": "^0.2.3", - "map-config": "^0.1.1", + "lazy-cache": "^0.2.4", + "load-pkg": "^2.0.1", + "map-config": "^0.2.1", "middleware-utils": "^0.1.4", - "mixin-deep": "^1.1.3", - "parser-front-matter": "^1.2.5", + "minimist": "^1.2.0", + "parser-front-matter": "^1.3.0", "readme-includes": "^0.2.9", "resolve-dir": "^0.1.0", + "rimraf": "^2.4.3", "set-value": "^0.2.0" }, "devDependencies": { - "async": "^1.4.2", - "base-methods": "^0.2.14", + "base-methods": "^0.3.1", "buffer-equal": "0.0.1", "consolidate": "^0.13.1", "coveralls": "^2.11.4", @@ -50,24 +58,22 @@ "event-stream": "^3.3.2", "graceful-fs": "^4.1.2", "gulp": "^3.9.0", - "gulp-git": "^1.5.0", - "gulp-istanbul": "^0.10.1", + "gulp-git": "^1.6.0", + "gulp-istanbul": "^0.10.2", "gulp-jshint": "^1.11.2", "gulp-mocha": "^2.1.3", "is-buffer": "^1.1.0", "istanbul": "^0.4.0", "jshint-stylish": "^2.0.1", "kind-of": "^2.0.1", - "load-pkg": "^1.3.0", "look-up": "^0.8.1", "mocha": "^2.3.3", - "resolve-glob": "^0.1.2", - "rimraf": "^2.4.3", - "should": "^7.1.0", - "sinon": "^1.17.1", + "resolve-glob": "^0.1.3", + "should": "^7.1.1", + "sinon": "^1.17.2", "swig": "^1.4.2", "through2": "^2.0.0", - "vinyl": "^1.0.0" + "vinyl": "^1.1.0" }, "keywords": [ "comment", @@ -92,38 +98,17 @@ "verbiage" ], "verb": { - "ignore": [ - ".git" - ], - "deps": { - "include": [ - "readme-includes", - "readme-badges" - ], - "ignore": [ - "support" - ] - }, "helpers": { "whatever": "@/helper-related" }, - "asyncHelpers": { - "whatever": "@/helper-related" - }, "related": { "list": [ - "composer", "assemble", - "template", - "engine" + "assemble-core", + "composer", + "templates", + "update" ] - }, - "reflinks": [ - "jsdiff", - "option-cache", - "template", - "plasma", - "config-cache" - ] + } } } From 48100a7ef09646b5e3eb3591ae1d669d57fb4319 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 27 Oct 2015 07:50:19 -0400 Subject: [PATCH 022/282] tests for `config` method --- examples/config.js | 2 + index.js | 5 +- lib/config.js | 8 +- package.json | 3 + test/app.config.js | 579 +++++++++++++++++++++++++++++++++++++ test/fixtures/plugins/a.js | 4 + test/fixtures/plugins/b.js | 4 + test/fixtures/plugins/c.js | 4 + 8 files changed, 602 insertions(+), 7 deletions(-) create mode 100644 test/app.config.js create mode 100644 test/fixtures/plugins/a.js create mode 100644 test/fixtures/plugins/b.js create mode 100644 test/fixtures/plugins/c.js diff --git a/examples/config.js b/examples/config.js index 0c3afd63..cfaa9c46 100644 --- a/examples/config.js +++ b/examples/config.js @@ -3,3 +3,5 @@ var app = verb(); app.config('helpers', app.helper.bind(app)); console.log(app._.helpers) + +console.log(app.cache.data); diff --git a/index.js b/index.js index 99d80b84..612547ee 100644 --- a/index.js +++ b/index.js @@ -13,13 +13,13 @@ var cli = require('base-cli'); var store = require('base-store'); -var config = require('base-config'); var loader = require('assemble-loader'); var Core = require('assemble-core'); var ask = require('assemble-ask'); var minimist = require('minimist'); var expand = require('expand-args'); var rimraf = require('rimraf'); +var config = require('./lib/config'); var create = require('./lib/create'); var locals = require('./lib/locals'); var utils = require('./lib/utils'); @@ -55,11 +55,12 @@ function Verb(options) { this.set('pkg', require('load-pkg')()); this.set('updaters', {}); + config(this); create(this); + this.use(utils.runtimes()) .use(locals({name: this.name})) .use(store(this.name)) - .use(config()) .use(loader()) .use(ask()) .use(cli()); diff --git a/lib/config.js b/lib/config.js index 61f26864..428f1b96 100644 --- a/lib/config.js +++ b/lib/config.js @@ -16,11 +16,8 @@ module.exports = function (app) { .map('addView') .map('helpers') .map('asyncHelpers') - .map('ignore', function(val) { - app.option('ignore', val); - }) - .map('files', function(val) { - app.files(val); + .map('data', function(val) { + app.visit('data', val); }) .map('reflinks', function(val) { app.data({reflinks: val}); @@ -28,4 +25,5 @@ module.exports = function (app) { .map('related', function(val) { app.data({related: val}); }); + }; diff --git a/package.json b/package.json index 865aa0cf..d2356486 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,9 @@ "verbiage" ], "verb": { + "data": { + "zzz.one": "yyy" + }, "helpers": { "whatever": "@/helper-related" }, diff --git a/test/app.config.js b/test/app.config.js new file mode 100644 index 00000000..9ec83919 --- /dev/null +++ b/test/app.config.js @@ -0,0 +1,579 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var minimist = require('minimist'); +var expandArgs = require('expand-args'); +var config = require('base-config'); +var verb = require('..'); +var app; + +function expand(argv) { + return expandArgs(minimist(argv)); +} + +describe('config', function () { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + describe('methods', function () { + it('should expose a "config" function on app:', function () { + assert(app.config); + assert(typeof app.config === 'function'); + }); + + it('should expose a "process" method on app.config:', function () { + assert(typeof app.config.process === 'function'); + }); + + it('should expose a "map" method on app.config:', function () { + assert(typeof app.config.map === 'function'); + }); + }); + + describe('config mapping', function () { + it('should expose the config object from app.config', function () { + assert(app.config.config); + assert(typeof app.config.config === 'object'); + }); + + it('should add a set method to config', function () { + assert(typeof app.config.config.set === 'function'); + }); + it('should add a get method to config', function () { + assert(typeof app.config.config.get === 'function'); + }); + it('should add a del method to config', function () { + assert(typeof app.config.config.del === 'function'); + }); + }); + + describe('use', function() { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + it('should use a plugin', function(cb) { + app.once('use', function() { + cb(); + }); + + app.config.process({use: 'test/fixtures/plugins/a'}); + }); + + it('should use a plugin from a cwd', function(cb) { + app.once('use', function() { + cb(); + }); + + app.config.process({ + cwd: 'test/fixtures/plugins', + use: 'a' + }); + }); + + it('should throw an error when plugin is not found', function(cb) { + try { + app.config.process({ + cwd: 'test/fixtures/plugins', + use: 'd' + }); + assert(new Error('expected an error')); + } catch(err) { + assert(err); + assert(err.message); + assert(err.message === 'cannot find plugin: d'); + cb(); + } + }); + + it('should use an array of plugins from a cwd', function(cb) { + var fns = 0; + app.on('use', function() { + fns++; + }); + + app.config.process({ + cwd: 'test/fixtures/plugins', + use: 'a,b,c' + }); + + assert(fns === 3); + cb(); + }); + }); + + describe('map', function() { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + it('should process an object of flags', function(cb) { + app.on('option', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process({option: {a: 'b'}}); + }); + + it('should process an object passed to config', function(cb) { + app = verb(); + app.use(config({option: {a: 'b'}})); + + app.on('option', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(); + }); + + it('should process an array passed to config', function(cb) { + app = verb(); + app.use(config([{option: {a: 'b'}}])); + + app.on('option', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(); + }); + + it('should be chainable', function(cb) { + app.config.alias('a', 'b') + .alias('b', 'c') + .alias('c', 'set') + .map('set') + + app.on('set', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process({c: {a: 'b'}}); + }); + + it('should add properties to app.config.config', function (cb) { + app.config.map('foo', 'set'); + app.config.map('bar', 'get'); + var called = 0; + + app.on('set', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + called++; + }); + + app.on('get', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + called++; + }); + + app.config.process({set: {a: 'b'}, get: 'a'}); + assert(called === 2); + cb(); + }); + }); + + describe('store.map', function() { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + it('should expose `store.config', function () { + assert(app.store.config); + assert(typeof app.store.config === 'function'); + }); + + it('should not blow up if store plugin is not used', function () { + var foo = verb(); + delete foo.store; + foo.use(config()); + assert(typeof foo.store === 'undefined'); + }); + + it('should add properties to app.store.config.config', function (cb) { + app.store.config.alias('foo', 'set'); + app.store.config.alias('bar', 'get'); + var called = 0; + + app.store.on('set', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + called++; + }); + + app.store.on('get', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + called++; + }); + + app.store.config.process({foo: {a: 'b'}, bar: 'a'}); + assert(called === 2); + cb(); + }); + + it('should work as a function', function (cb) { + app.store.config({ + foo: 'set', + bar: 'get' + }); + + var called = 0; + + app.store.on('set', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + called++; + }); + + app.store.on('get', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + called++; + }); + + app.store.config.process({foo: {a: 'b'}, bar: 'a'}); + assert(called === 2); + cb(); + }); + }); + + describe('process', function() { + it('should process an object of flags', function(cb) { + app.on('option', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process({option: {a: 'b'}}); + }); + }); +}); + +describe('should handle methods added by other plugins', function () { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + afterEach(function() { + app.store.del({force: true}); + }); + + describe('store', function () { + it('should add a store method to config', function () { + assert(typeof app.config.config.store === 'function'); + }); + }); + + describe('option', function () { + it('should add an option method to config', function () { + assert(typeof app.config.config.option === 'function'); + }); + }); + + describe('data', function () { + it('should add a data method to config', function () { + assert(typeof app.config.config.data === 'function'); + }); + }); +}); + +describe('events', function () { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + afterEach(function() { + app.store.del({force: true}); + }); + + describe('set', function () { + it('should emit a set event', function (cb) { + var argv = expand(['--set=a:b']); + + app.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + }); + + describe('get', function () { + it('should emit a get event', function (cb) { + var argv = expand(['--get=a']); + app.set('a', 'b'); + + app.on('get', function(key, val) { + assert(key); + assert(val); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + }); + + describe('del', function () { + it('should emit a del event', function (cb) { + var argv = expand(['--del=a']); + app.set('a', 'b'); + + app.on('del', function(key) { + assert(key); + assert(key === 'a'); + assert(typeof app.a === 'undefined'); + cb(); + }); + + app.config.process(argv); + }); + }); + + describe('option', function () { + it('should emit an option event', function (cb) { + var argv = expand(['--option=a:b']); + + app.on('option', function(key, val) { + assert(key); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + }); + + describe('data', function () { + it('should emit a data event', function (cb) { + var argv = expand(['--data=a:b']); + + app.on('data', function(args) { + assert(Array.isArray(args)); + assert(args.length === 1); + assert(args[0].a === 'b'); + cb(); + }); + + app.config.process(argv); + }); + }); + + describe('store', function () { + it('should emit a store.set event', function (cb) { + var argv = expand(['--store.set=a:b']); + app.store.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.store.data.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should emit a store.get event', function (cb) { + var argv = expand(['--store.get=a']); + app.store.set('a', 'b'); + + app.store.on('get', function(key, val) { + assert(key); + assert(val); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should emit a store.del event', function (cb) { + var argv = expand(['--store.del=a,b']); + app.store.set('a', 'aaa'); + app.store.set('b', 'bbb'); + var keys = []; + + app.store.on('del', function(key) { + keys.push(key); + }); + + app.config.process(argv); + assert(keys.length === 2); + process.nextTick(function () { + assert(Object.keys(app.store.data).length === 2); + }); + cb(); + }); + + it('should delete the entire store', function (cb) { + var argv = expand(['--store.del=force:true']); + app.store.set('a', 'aaa'); + app.store.set('b', 'bbb'); + var keys = []; + + app.store.on('del', function(key) { + keys.push(key); + }); + + app.config.process(argv); + assert(keys.length === 2); + process.nextTick(function () { + assert(Object.keys(app.store.data).length === 0); + }); + cb(); + }); + }); +}); + +describe('aliases', function () { + beforeEach(function() { + app = verb(); + app.use(config()); + }); + + afterEach(function() { + app.store.del({force: true}); + }); + + describe('config', function () { + it('should map an object to methods', function (cb) { + var argv = expand(['--set=a:b']); + app.config({ + set: 'set' + }); + + app.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should use custom functions', function (cb) { + var argv = expand(['--foo=a:b']); + app.config({ + set: 'set', + foo: function (key, val) { + app.set(key, val); + } + }); + + app.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should use alias mappings', function (cb) { + var argv = expand(['--foo=a:b']); + app.config({ + set: 'set', + foo: 'set' + }); + + app.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should expose config.map', function (cb) { + var argv = expand(['--set=a:b']); + app.config.map('set'); + + app.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should expose config.alias', function (cb) { + var argv = expand(['--set=a:b']); + app.config.alias('foo', 'set'); + + app.on('set', function(key, val) { + assert(key); + assert(val); + assert(app.a === 'b'); + assert(key === 'a'); + assert(val === 'b'); + cb(); + }); + + app.config.process(argv); + }); + + it('should throw if args are invalid', function (cb) { + try { + app.config([]); + cb(new Error('expected an error')); + } catch(err) { + assert(err); + assert(err.message); + assert(err.message === 'expected key to be a string or object'); + cb(); + } + }); + }); +}); diff --git a/test/fixtures/plugins/a.js b/test/fixtures/plugins/a.js new file mode 100644 index 00000000..74fc33fd --- /dev/null +++ b/test/fixtures/plugins/a.js @@ -0,0 +1,4 @@ +module.exports = function (app) { + console.log('plugin A'); + app.set('a', 'AAA'); +}; diff --git a/test/fixtures/plugins/b.js b/test/fixtures/plugins/b.js new file mode 100644 index 00000000..0b909750 --- /dev/null +++ b/test/fixtures/plugins/b.js @@ -0,0 +1,4 @@ +module.exports = function (app) { + console.log('plugin B'); + app.set('b', 'BBB'); +}; diff --git a/test/fixtures/plugins/c.js b/test/fixtures/plugins/c.js new file mode 100644 index 00000000..0002206f --- /dev/null +++ b/test/fixtures/plugins/c.js @@ -0,0 +1,4 @@ +module.exports = function (app) { + console.log('plugin C'); + app.set('c', 'CCC'); +}; From e405fc6a992724c4f46abb376f9f1f2d963eefb8 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Dec 2015 10:20:33 -0500 Subject: [PATCH 023/282] use loadPkg --- test/support/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/support/index.js b/test/support/index.js index 43491cef..fc91929e 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -1,8 +1,7 @@ 'use strict'; var path = require('path'); -var pkg = require('load-pkg'); -var lookup = require('look-up'); +var loadPkg = require('load-pkg'); var assert = require('assert'); var ignore = require('./ignore'); var cache = {}; @@ -37,6 +36,7 @@ exports.resolve = function(filepath) { return cache[key]; } + var pkg = loadPkg.sync(process.cwd()); var prefix = pkg.name !== 'templates' ? 'templates' : ''; From 21e41dcb285c0797b4a9caee77769206f467f2b4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Dec 2015 10:22:54 -0500 Subject: [PATCH 024/282] major updates: - verb can now run "verb apps", from the command line or programmatically - verb apps can be nested - verb apps can run other verb apps, and tasks, and tasks from other verb apps --- .eslintrc | 125 ++++ .jshintrc | 19 - .travis.yml | 5 + README.md | 3 - cli.js | 57 ++ docs/recipes/extend-a-generator.md | 33 + docs/recipes/generate.js | 37 ++ docs/src/argv.md | 6 + docs/src/readme/layouts/default.md | 31 + examples/config.js | 7 - examples/fixtures/test.md | 1 + examples/fixtures/test.txt | 1 + examples/scaffold.js | 37 ++ gulpfile.js | 47 +- index.js | 184 ++++-- lib/config.js | 29 - lib/create.js | 45 -- lib/defaults.js | 49 ++ lib/docs.js | 63 ++ lib/helpers.js | 28 - lib/index.js | 1 - lib/locals.js | 30 - lib/utils.js | 150 ++--- package.json | 126 ++-- test/app.collection.js | 25 +- test/app.config.js | 579 ------------------ test/app.copy.js | 1 + test/app.create.js | 30 +- test/app.list.js | 17 +- test/app.task.js | 6 +- test/collection.engines.js | 1 - test/collection.js | 4 + test/fixtures/example.txt | 1 + test/fixtures/one/generator.js | 19 + test/fixtures/one/package.json | 30 + test/fixtures/one/templates/a.txt | 1 + test/fixtures/one/templates/x.txt | 1 + test/fixtures/one/templates/y.txt | 1 + test/fixtures/one/templates/z.txt | 1 + test/fixtures/plugins/a.js | 4 - test/fixtures/plugins/b.js | 4 - test/fixtures/plugins/c.js | 4 - test/fixtures/templates/a.tmpl | 2 +- test/fixtures/templates/b.tmpl | 2 +- test/fixtures/templates/c.tmpl | 2 +- test/fixtures/three/four/five/generator.js | 14 + test/fixtures/three/four/five/package.json | 30 + test/fixtures/three/four/five/templates/a.txt | 0 test/fixtures/three/four/five/templates/b.txt | 0 test/fixtures/three/four/five/templates/c.txt | 0 test/fixtures/three/four/generator.js | 14 + test/fixtures/three/four/package.json | 30 + test/fixtures/three/four/templates/a.txt | 0 test/fixtures/three/four/templates/b.txt | 0 test/fixtures/three/four/templates/c.txt | 0 test/fixtures/three/generator.js | 14 + test/fixtures/three/package.json | 30 + test/fixtures/three/templates/a.txt | 0 test/fixtures/three/templates/b.txt | 0 test/fixtures/three/templates/c.txt | 0 test/fixtures/two/generate.js | 21 + test/fixtures/two/package.json | 30 + test/fixtures/two/templates/a.txt | 0 test/fixtures/two/templates/b.txt | 0 test/fixtures/two/templates/c.txt | 0 test/fixtures/updaters/a.txt | 1 + test/fixtures/updaters/b.txt | 1 + test/fixtures/updaters/c.txt | 1 + test/helpers.js | 167 ++--- test/mergePartials.js | 3 +- test/questions.js | 2 +- test/render.js | 9 +- test/store.js | 30 +- test/view.set.js | 4 +- verbfile.js | 141 +++++ 75 files changed, 1240 insertions(+), 1151 deletions(-) create mode 100644 .eslintrc delete mode 100644 .jshintrc delete mode 100644 README.md create mode 100755 cli.js create mode 100644 docs/recipes/extend-a-generator.md create mode 100644 docs/recipes/generate.js create mode 100644 docs/src/argv.md create mode 100644 docs/src/readme/layouts/default.md delete mode 100644 examples/config.js create mode 100644 examples/fixtures/test.md create mode 100644 examples/fixtures/test.txt create mode 100644 examples/scaffold.js delete mode 100644 lib/config.js delete mode 100644 lib/create.js create mode 100644 lib/defaults.js create mode 100644 lib/docs.js delete mode 100644 lib/helpers.js delete mode 100644 lib/index.js delete mode 100644 lib/locals.js delete mode 100644 test/app.config.js create mode 100644 test/fixtures/example.txt create mode 100644 test/fixtures/one/generator.js create mode 100644 test/fixtures/one/package.json create mode 100644 test/fixtures/one/templates/a.txt create mode 100644 test/fixtures/one/templates/x.txt create mode 100644 test/fixtures/one/templates/y.txt create mode 100644 test/fixtures/one/templates/z.txt delete mode 100644 test/fixtures/plugins/a.js delete mode 100644 test/fixtures/plugins/b.js delete mode 100644 test/fixtures/plugins/c.js create mode 100644 test/fixtures/three/four/five/generator.js create mode 100644 test/fixtures/three/four/five/package.json create mode 100644 test/fixtures/three/four/five/templates/a.txt create mode 100644 test/fixtures/three/four/five/templates/b.txt create mode 100644 test/fixtures/three/four/five/templates/c.txt create mode 100644 test/fixtures/three/four/generator.js create mode 100644 test/fixtures/three/four/package.json create mode 100644 test/fixtures/three/four/templates/a.txt create mode 100644 test/fixtures/three/four/templates/b.txt create mode 100644 test/fixtures/three/four/templates/c.txt create mode 100644 test/fixtures/three/generator.js create mode 100644 test/fixtures/three/package.json create mode 100644 test/fixtures/three/templates/a.txt create mode 100644 test/fixtures/three/templates/b.txt create mode 100644 test/fixtures/three/templates/c.txt create mode 100644 test/fixtures/two/generate.js create mode 100644 test/fixtures/two/package.json create mode 100644 test/fixtures/two/templates/a.txt create mode 100644 test/fixtures/two/templates/b.txt create mode 100644 test/fixtures/two/templates/c.txt create mode 100644 test/fixtures/updaters/a.txt create mode 100644 test/fixtures/updaters/b.txt create mode 100644 test/fixtures/updaters/c.txt create mode 100644 verbfile.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..7d80e461 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,125 @@ +{ + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true + }, + + "env": { + "browser": false, + "es6": true, + "node": true, + "mocha": true + }, + + "globals": { + "document": false, + "navigator": false, + "window": false + }, + + "rules": { + "accessor-pairs": 2, + "arrow-spacing": [2, { "before": true, "after": true }], + "block-spacing": [2, "always"], + "brace-style": [2, "1tbs", { "allowSingleLine": true }], + "comma-dangle": [2, "never"], + "comma-spacing": [2, { "before": false, "after": true }], + "comma-style": [2, "last"], + "constructor-super": 2, + "curly": [2, "multi-line"], + "dot-location": [2, "property"], + "eol-last": 2, + "eqeqeq": [2, "allow-null"], + "verbApp-star-spacing": [2, { "before": true, "after": true }], + "handle-callback-err": [2, "^(err|error)$" ], + "indent": [2, 2, { "SwitchCase": 1 }], + "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "new-cap": [2, { "newIsCap": true, "capIsNew": false }], + "new-parens": 2, + "no-array-constructor": 2, + "no-caller": 2, + "no-class-assign": 2, + "no-cond-assign": 2, + "no-const-assign": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-dupe-args": 2, + "no-dupe-class-members": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty-character-class": 2, + "no-empty-label": 2, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": [2, "functions"], + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implied-eval": 2, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [2, { "max": 1 }], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-proto": 0, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-return-assign": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 0, + "no-undef": 2, + "no-undef-init": 2, + "no-unexpected-multiline": 2, + "no-unneeded-ternary": [2, { "defaultAssignment": false }], + "no-unreachable": 2, + "no-unused-vars": [2, { "vars": "all", "args": "none" }], + "no-useless-call": 0, + "no-with": 2, + "one-var": [0, { "initialized": "never" }], + "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], + "padded-blocks": [0, "never"], + "quotes": [2, "single", "avoid-escape"], + "radix": 2, + "semi": [2, "always"], + "semi-spacing": [2, { "before": false, "after": true }], + "space-after-keywords": [2, "always"], + "space-before-blocks": [2, "always"], + "space-before-function-paren": [2, "never"], + "space-before-keywords": [2, "always"], + "space-in-parens": [2, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], + "use-isnan": 2, + "valid-typeof": 2, + "wrap-iife": [2, "any"], + "yoda": [2, "never"] + } +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 1d9d5929..00000000 --- a/.jshintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "asi": false, - "boss": true, - "curly": false, - "eqeqeq": true, - "eqnull": true, - "esnext": true, - "immed": true, - "latedef": false, - "laxbreak": true, - "laxcomma": false, - "mocha": true, - "newcap": true, - "noarg": true, - "node": true, - "sub": true, - "undef": true, - "unused": true -} diff --git a/.travis.yml b/.travis.yml index d7a07159..d6e658ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,10 @@ +sudo: false language: node_js node_js: - "stable" - "0.12" - "0.10" +matrix: + fast_finish: true + allow_failures: + - node_js: "0.10" diff --git a/README.md b/README.md deleted file mode 100644 index b89dfbc7..00000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# verb [![NPM version](https://badge.fury.io/js/verb2.svg)](http://badge.fury.io/js/verb2) - -wip \ No newline at end of file diff --git a/cli.js b/cli.js new file mode 100755 index 00000000..0ce772f0 --- /dev/null +++ b/cli.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node + +var path = require('path'); +var runner = require('base-runner'); +var minimist = require('minimist'); +var utils = require('./lib/utils'); +var Verb = require('./'); + +// parse argv +var args = minimist(process.argv.slice(2), { + alias: {verbose: 'v'} +}); + +// register `runner` as a mixin +Verb.mixin(runner('verb', 'verbApp')); + +/** + * Get the `base` instance of verb to use for + * registering all other instances. This will either + * be local to the user (e.g. `node_modules/verb`) + * or a globally installed module + */ + +var base = Verb.getConfig('verbfile.js', __dirname); + +/** + * Resolve config files (`verbfile.js`) + */ + +base.resolve({pattern: 'verb-*/verbfile.js', cwd: '@/'}); + +/** + * Run verbApps and tasks + */ + +base.cli.map('verbApps', function(verbApps) { + if (!verbApps.length) { + if (base.tasks.hasOwnProperty('default')) { + verbApps = [{base: ['default']}]; + } else { + console.log(' no default task is defined.'); + utils.timestamp('done'); + return; + } + } + + base.runVerbApps(verbApps, function(err) { + // if (err) return console.error(err); + utils.timestamp('done'); + }); +}); + +/** + * Process args + */ + +base.cli.process(args); diff --git a/docs/recipes/extend-a-generator.md b/docs/recipes/extend-a-generator.md new file mode 100644 index 00000000..a2a53733 --- /dev/null +++ b/docs/recipes/extend-a-generator.md @@ -0,0 +1,33 @@ +# Extending verbApps + +It's easy to extend a verbApp with the functionality, tasks and sub-verbApps of another verbApp. + +Let's say you want to extend verbApp `xyz` with verbApp `abc`. + +```js +verb.register('abc', function(app, base, env) { +}); + +verb.register('xyz', function(app, base, env) { + base.on('preBuild', function() { + var abc = base.getVerbApp('abc'); + abc.extendVerbApp(app); + }); +}); +``` + +**Pro tip** + +Run _both_ verbApps in different contexts: + +```js +verb.build('templates', function(err) { + if (err) return console.error(err); + + verb.verbApp('one') + .build('templates', function(err) { + if (err) return console.error(err); + console.log(verb.views.templates); + }); +}); +``` \ No newline at end of file diff --git a/docs/recipes/generate.js b/docs/recipes/generate.js new file mode 100644 index 00000000..c7f4c91b --- /dev/null +++ b/docs/recipes/generate.js @@ -0,0 +1,37 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var glob = require('matched'); + +module.exports = function(verb, base, env) { + verb.task('default', function() {}); + verb.task('readme', function() {}); + + verb.task('templates', function(cb) { + var opts = {cwd: env.cwd, dot: true}; + if (!verb.templates) verb.create('templates'); + + glob('templates/*', opts, function(err, files) { + if (err) return cb(err); + + files.forEach(function(name) { + var fp = path.join(env.cwd, name); + verb.template(name, {path: fp, content: fs.readFileSync(fp)}); + }); + cb(); + }); + }); + + verb.register('docs', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + + app.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); + }); +}; diff --git a/docs/src/argv.md b/docs/src/argv.md new file mode 100644 index 00000000..c5b0272d --- /dev/null +++ b/docs/src/argv.md @@ -0,0 +1,6 @@ +# argv + + +```js +var argv = base.get('env.argv'); +``` \ No newline at end of file diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md new file mode 100644 index 00000000..e22136fb --- /dev/null +++ b/docs/src/readme/layouts/default.md @@ -0,0 +1,31 @@ +# {%= name %} {%= badge.npm %} {%= badge.travis %} + +> {%= description %} + +## Install +{%= include("install-npm", {save: true}) %} + +## Usage +{% body %} + +## Related projects +{%= related((verb.related && verb.related.list) || []) %} + +## Running tests +{%= include("tests") %} + +## Contributing +{%= include("contributing") %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} + +{%= reflinks(verb.reflinks || []) %} diff --git a/examples/config.js b/examples/config.js deleted file mode 100644 index cfaa9c46..00000000 --- a/examples/config.js +++ /dev/null @@ -1,7 +0,0 @@ -var verb = require('../'); -var app = verb(); - -app.config('helpers', app.helper.bind(app)); -console.log(app._.helpers) - -console.log(app.cache.data); diff --git a/examples/fixtures/test.md b/examples/fixtures/test.md new file mode 100644 index 00000000..36f1f1b5 --- /dev/null +++ b/examples/fixtures/test.md @@ -0,0 +1 @@ +<%= name %> \ No newline at end of file diff --git a/examples/fixtures/test.txt b/examples/fixtures/test.txt new file mode 100644 index 00000000..5b88ae15 --- /dev/null +++ b/examples/fixtures/test.txt @@ -0,0 +1 @@ +test file! \ No newline at end of file diff --git a/examples/scaffold.js b/examples/scaffold.js new file mode 100644 index 00000000..5c24dc85 --- /dev/null +++ b/examples/scaffold.js @@ -0,0 +1,37 @@ +'use strict'; + +var Verb = require('..'); +var verb = new Verb(); + +var Scaffold = require('scaffold'); +var scaffold = new Scaffold({ + a: { + options: { + cwd: 'examples/fixtures', + destBase: 'two', + }, + data: {name: 'Jon'}, + files: [ + {src: '*.txt', dest: 'a'}, + {src: '*.txt', dest: 'b'}, + {src: '*.txt', dest: 'c'}, + {src: '*.md', dest: 'md', data: {name: 'Jon'}}, + ] + }, + b: { + cwd: 'examples/fixtures', + destBase: 'one', + data: {name: 'Brian'}, + files: [ + {src: '*.txt', dest: 'a'}, + {src: '*.txt', dest: 'b'}, + {src: '*.txt', dest: 'c'}, + {src: '*.md', dest: 'md', data: {name: 'Brian'}}, + ] + } +}); + +verb.scaffold(scaffold, function(err) { + if (err) return console.log(err); + console.log('done!'); +}); diff --git a/gulpfile.js b/gulpfile.js index 46f6ce12..7d0c54ab 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,18 +1,11 @@ 'use strict'; var gulp = require('gulp'); -var stylish = require('jshint-stylish'); -var istanbul = require('gulp-istanbul'); -var jshint = require('gulp-jshint'); var mocha = require('gulp-mocha'); -var git = require('gulp-git'); -var del = require('rimraf'); - -var lint = ['index.js', 'lib/{*,plugins/*}.js']; +var istanbul = require('gulp-istanbul'); +var eslint = require('gulp-eslint'); -function url(repo) { - return 'https://github.com/' + repo; -} +var lint = ['index.js', 'lib/*.js', 'test/*.js']; gulp.task('coverage', function () { return gulp.src(lint) @@ -20,37 +13,15 @@ gulp.task('coverage', function () { .pipe(istanbul.hookRequire()); }); -gulp.task('test', ['clone', 'coverage'], function () { +gulp.task('mocha', ['coverage'], function () { return gulp.src('test/*.js') .pipe(mocha({reporter: 'spec'})) - .pipe(istanbul.writeReports()) - .pipe(istanbul.writeReports({ - reporters: [ 'text' ], - reportOpts: {dir: 'coverage', file: 'summary.txt'} - })); -}); - -gulp.task('lint', function () { - return gulp.src(lint.concat('test/*.js')) - .pipe(jshint()) - .pipe(jshint.reporter(stylish)); + .pipe(istanbul.writeReports()); }); -gulp.task('spec', ['clone'], function (cb) { - gulp.src('test/_spec/test/*.js') - .pipe(mocha({reporter: 'spec'})) - .on('end', function () { - del('test/_spec', cb); - }); -}); - -gulp.task('clone', function(cb) { - del('test/_spec', function (err) { - if (err) return cb(err); - git.clone(url('assemble/assemble'), { - args: 'test/_spec' - }, cb); - }); +gulp.task('eslint', function () { + return gulp.src(lint) + .pipe(eslint()) }); -gulp.task('default', ['test', 'lint']); +gulp.task('default', ['mocha', 'eslint']); diff --git a/index.js b/index.js index 612547ee..18279907 100644 --- a/index.js +++ b/index.js @@ -1,38 +1,19 @@ -/*! - * verb - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - 'use strict'; -/** - * module dependencies - */ - -var cli = require('base-cli'); -var store = require('base-store'); -var loader = require('assemble-loader'); -var Core = require('assemble-core'); -var ask = require('assemble-ask'); -var minimist = require('minimist'); -var expand = require('expand-args'); -var rimraf = require('rimraf'); -var config = require('./lib/config'); -var create = require('./lib/create'); -var locals = require('./lib/locals'); +var async = require('async'); +var Assemble = require('assemble-core'); +var defaults = require('./lib/defaults'); var utils = require('./lib/utils'); +var docs = require('./lib/docs'); /** - * Create a `verb` application. This is the main function exported - * by the verb module. + * Create an instance of `Verb` with the given `options` * * ```js - * var verb = require('verb'); - * var app = verb(); + * var Verb = require('verb'); + * var verb = new Verb(options); * ``` - * @param {Object} `options` Optionally pass default options to use. + * @param {Object} `options` Configuration options to initialize with. * @api public */ @@ -41,57 +22,138 @@ function Verb(options) { return new Verb(options); } - this.options = utils.extend({reload: false}, options); - Core.call(this, this.options); - this.define('isVerb', true); - this.name = 'verb'; + Assemble.apply(this, arguments); + this.name = this.options.name || 'base'; + this.isVerb = true; + this.verbApps = {}; - var argv = minimist(process.argv.slice(2)); - if (process.argv.length > 3) { - argv = expand(argv); - } + this.use(utils.middleware()) + .use(utils.loader()) + .use(utils.store()) + .use(docs()) + .use(utils.ask()); - this.set('argv', this.argv || argv); - this.set('pkg', require('load-pkg')()); - this.set('updaters', {}); + this.engine(['md', 'text'], require('engine-base'), { + delims: ['{%', '%}'] + }); - config(this); - create(this); + this.on('register', function(name, app) { + // bubble up errors to `base` instance + app.on('error', app.base.emit.bind(app.base, 'error')); + app.use(docs()); + }); - this.use(utils.runtimes()) - .use(locals({name: this.name})) - .use(store(this.name)) - .use(loader()) - .use(ask()) - .use(cli()); + defaults(this, this.base, this.env); +} - var verb = this.get('pkg.verb'); - this.config.process(verb); +/** + * Inherit assemble-core + */ - this.onLoad(/\.md$/, function (view, next) { - utils.matter.parse(view, next); - }); +Assemble.extend(Verb); - this.option('rethrow', { regex: /\{%=?([^%]*)%}/ }); - this.engine('md', require('engine-base'), { - delims: ['{%', '%}'] - }); -} +/** + * Similar to [copy](#copy) but calls a plugin `pipeline` if passed + * on the `options`. This allows plugin pipelines to be programmatically + * built-up and dynamically changed on-the-fly. + * + * ```js + * verb.process({src: ['a.txt', 'b.txt']}, options); + * ``` + * + * @param {Object} `files` + * @param {Object} `options` + * @param {Function} `cb` + * @return {Stream} Returns a [vinyl][] src stream + * @api public + */ + +Verb.prototype.process = function(files, options) { + options = options || {}; + files.options = files.options || {}; + var pipeline = files.options.pipeline || options.pipeline; + var opts = utils.extend({}, this.options, files.options, options); + + return this.src(files.src, opts) + .pipe(this.pipeline(pipeline, opts)) + .pipe(this.dest(files.dest, opts)); +}; /** - * Inherit `Core` + * Verb `files` configurations in parallel. + * + * ```js + * verb.each(files, function(err) { + * if (err) console.log(err); + * }); + * ``` + * @param {Object} `config` + * @param {Function} `cb` + * @api public */ -Core.extend(Verb); +Verb.prototype.each = function(config, cb) { + async.each(config.files, function(files, next) { + this.process(files, files.options) + .on('error', next) + .on('end', next); + }.bind(this), cb); + return this; +}; /** - * Expose `Verb` + * Verb `files` configurations in series. + * + * ```js + * verb.eachSeries(files, function(err) { + * if (err) console.log(err); + * }); + * ``` + * @param {Object} `config` + * @param {Function} `cb` + * @api public */ -module.exports = Verb; +Verb.prototype.eachSeries = function(config, cb) { + async.eachSeries(config.files, function(files, next) { + this.process(files, files.options) + .on('error', next) + .on('end', next); + }.bind(this), cb); +}; + +/** + * Verb files from a declarative [scaffold][] configuration. + * + * ```js + * var Scaffold = require('scaffold'); + * var scaffold = new Scaffold({ + * options: {cwd: 'source'}, + * posts: { + * src: ['content/*.md'] + * }, + * pages: { + * src: ['templates/*.hbs'] + * } + * }); + * + * verb.scaffold(scaffold, function(err) { + * if (err) console.log(err); + * }); + * ``` + * @param {Object} `scaffold` Scaffold configuration + * @param {Function} `cb` Callback function + * @api public + */ + +Verb.prototype.scaffold = function(scaffold, cb) { + async.eachOf(scaffold, function(target, name, next) { + this.each(target, next); + }.bind(this), cb); +}; /** * Expose `Verb` */ -module.exports.utils = utils; +module.exports = Verb; diff --git a/lib/config.js b/lib/config.js deleted file mode 100644 index 428f1b96..00000000 --- a/lib/config.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - - -module.exports = function (app) { - if (!app.isApp) return; - - var utils = require('./utils'); - var config = require('base-config'); - app.use(config()); - - app.config - .map('collections', function(val) { - app.visit('create', val); - }) - .map('addViews') - .map('addView') - .map('helpers') - .map('asyncHelpers') - .map('data', function(val) { - app.visit('data', val); - }) - .map('reflinks', function(val) { - app.data({reflinks: val}); - }) - .map('related', function(val) { - app.data({related: val}); - }); - -}; diff --git a/lib/create.js b/lib/create.js deleted file mode 100644 index cac7d758..00000000 --- a/lib/create.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -/** - * Plugin for creating default view collections - * | docs - * | layouts - * | includes - * | shields - * | badges - */ - -var utils = require('./utils'); - -module.exports = function (app) { - app.create('badges', { - viewType: ['partial'], - renameKey: utils.rename, - engine: 'md' - }); - - app.create('shields', { - viewType: ['partial'], - renameKey: utils.rename, - engine: 'md' - }); - - app.create('includes', { - viewType: ['partial'], - renameKey: utils.rename, - cwd: utils.includes, - engine: 'md', - }); - - app.create('docs', { - viewType: ['partial'], - renameKey: utils.basename, - engine: 'md' - }); - - app.create('layouts', { - viewType: ['layout'], - renameKey: utils.basename, - engine: 'md' - }); -}; diff --git a/lib/defaults.js b/lib/defaults.js new file mode 100644 index 00000000..dd0312d0 --- /dev/null +++ b/lib/defaults.js @@ -0,0 +1,49 @@ +'use strict'; + +var utils = require('./utils'); + +module.exports = function(app, base, env) { + var plugins = base.get('env.argv.plugins'); + + app.data({ + runner: { + name: 'verb', + url: 'https://github.com/verbose/verb' + } + }); + + // app.asyncHelper('apidocs', function(name, cb) { + // console.log(arguments) + // cb(null, ''); + // }); + + app.asyncHelper('related', utils.related({verbose: true})); + app.asyncHelper('reflinks', utils.reflinks({verbose: true})); + app.helper('copyright', require('helper-copyright')({ + linkify: true + })); + + app.helper('date', function () { + return new Date(); + }); + + // function handle(stage) { + // return utils.through.obj(function(file, enc, next) { + // if (file.isNull()) return next(); + // app.handle(stage, file, next); + // }); + // } + + return function(cb) { + cb(); + // app.toStream('files') + // .on('error', cb) + // .pipe(handle('onStream')) + // .pipe(app.pipeline(plugins)) + // .pipe(handle('preWrite')) + // .pipe(app.dest('.')) + // .pipe(utils.exhaust(handle('postWrite'))) + // .on('error', cb) + // .on('end', cb); + }; +}; diff --git a/lib/docs.js b/lib/docs.js new file mode 100644 index 00000000..06d6075e --- /dev/null +++ b/lib/docs.js @@ -0,0 +1,63 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var utils = require('./utils'); +var loaded; +var views = {}; + +module.exports = function(options) { + return function(app) { + create(app, 'docs', 'renderable'); + create(app, 'includes', 'partial'); + create(app, 'layouts', 'layout'); + + var userDocs = path.join.bind(path, process.cwd()); + + if (fs.existsSync(userDocs())) { + app.docs.loadViews('.verb.md', {cwd: userDocs()}); + + app.includes.loadViews('*.md', {cwd: userDocs('docs/includes')}); + app.layouts.loadViews('*.md', {cwd: userDocs('docs/layouts')}); + app.docs.loadViews('*.md', {cwd: userDocs('docs')}); + } + + var collections = loadDefaults(app); + if (collections) { + for (var key in collections) { + var collection = collections[key]; + for (var name in collection) { + if (!app.views[key][name]) { + app.views[key][name] = collection[name]; + } + } + } + } + }; +}; + +function loadDefaults(app) { + if (loaded) return views; + loaded = true; + + var docs = path.join.bind(path, __dirname, '../docs/src/readme'); + app.includes.loadViews('*.md', {cwd: docs('includes')}); + app.layouts.loadViews('*.md', {cwd: docs('layouts')}); + app.docs.loadViews('*.md', {cwd: docs()}); + + // cache views + views.includes = app.views.includes; + views.layouts = app.views.layouts; + views.docs = app.views.docs; + return views; +} + +function create(app, name, type) { + if (app[name]) return; + return app.create(name, { + viewType: [type], + renameKey: function(key) { + return path.basename(key, path.extname(key)); + } + }); +} diff --git a/lib/helpers.js b/lib/helpers.js deleted file mode 100644 index 880f9fd6..00000000 --- a/lib/helpers.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -module.exports = function () { - this.asyncHelper('related', utils.related(this.options)); - this.asyncHelper('reflinks', utils.reflinks(this.options)); - - this.helper('split', function (val) { - return val.split(','); - }); - - this.helper('date', function () { - return new Date(); - }); - - this.helper('log', function (msg) { - console.log.apply(console, arguments); - }); - - this.helper('trim', function (str) { - return str.trim(); - }); - - this.helper('apidocs', function () { - // this.ask(locals) - }); -}; diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/lib/locals.js b/lib/locals.js deleted file mode 100644 index 7fd049cd..00000000 --- a/lib/locals.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var get = require('get-value'); -var set = require('set-value'); -var utils = require('./utils'); - -module.exports = function (config) { - if (!config || !config.name) { - throw new Error('expected config.name to be a string.'); - } - - return function (app) { - var opts = utils.extend({}, config, app.option('update')); - this.locals = new Locals(config.name, this); - return this; - }; -}; - -function Locals(name, app) { - this.cache = get(app.cache.data, name) || (app.cache.data[name] = {}); -} - -Locals.prototype.get = function(key) { - return get(this.cache, key); -}; - -Locals.prototype.set = function(key, value) { - set(this.cache, key, value); - return this; -}; diff --git a/lib/utils.js b/lib/utils.js index 70dbc6f3..4807f599 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,124 +1,98 @@ 'use strict'; var fs = require('fs'); -var path = require('path'); /** - * Lazily required module dependencies + * Module dependencies */ var utils = require('lazy-cache')(require); var fn = require; - require = utils; -require('async'); -require('resolve-dir'); -require('helper-related', 'related'); + +/** + * Lazily required module dependencies + */ + +// helpers require('helper-reflinks', 'reflinks'); -require('readme-includes', 'includes'); -require('get-value', 'get'); -require('set-value', 'set'); -require('composer-runtimes', 'runtimes'); -require('extend-shallow', 'extend'); -require('parser-front-matter', 'matter'); -require('middleware-utils', 'mu'); -require('engine-base', 'engine'); -require('map-config', 'Config'); -require = fn; +require('helper-related', 'related'); -utils.basename = function(key) { - return path.basename(key, path.extname(key)); -}; +// plugins and extensions +require('base-runner'); +require('base-store', 'store'); +require('base-questions', 'ask'); +require('assemble-loader', 'loader'); +require('common-middleware', 'middleware'); -utils.arrayify = function(val) { - return Array.isArray(val) ? val : [val]; -}; +require('extend-shallow', 'extend'); +require('ansi-colors', 'colors'); +require('time-stamp', 'stamp'); +require('matched', 'glob'); +require('success-symbol'); +require('assemble-core'); +require('is-valid-glob'); +require('has-glob'); +require('async'); -utils.tryRequire = function(name) { - try { - return require(name); - } catch(err) {} - return null; -}; +/** + * Restore `require` + */ -utils.tryRead = function(fp) { - try { - return fs.readFileSync(fp); - } catch(err) {} - return null; -}; +require = fn; -utils.npm = function(name) { - return utils.tryRequire(name) || utils.tryRequire(utils.resolve(name)); -}; +/** + * Convenience method for loading files. + */ -utils.resolve = function(fp) { - return path.resolve(utils.resolveDir(fp)); +utils.globFiles = function(patterns, options) { + var opts = utils.extend({dot: true}, options); + opts.cwd = opts.cwd || process.cwd(); + opts.ignore = ['**/.DS_Store', '**/.git']; + opts.realpath = true; + return utils.glob.sync(patterns, opts); }; -utils.identity = function(val) { - return val; -}; +/** + * Create a formatted timestamp + * + * @param {String} msg + * @return {String} + */ -utils.rename = function(key) { - var cwd = this.options.cwd; - if (!cwd || key.indexOf(cwd) === -1) { - return key; - } - var len = cwd.length + 1; - return key.slice(len); +utils.timestamp = function(msg) { + var time = ' ' + utils.colors.gray(utils.stamp('HH:mm:ss.ms', new Date())); + return console.log(time, msg, utils.colors.green(utils.successSymbol)); }; /** - * Run middleware in series - * - * ```js - * var fns = require('./fns/'); - * - * app.onLoad(/\.js$/, utils.series([ - * fns.foo, - * fns.bar, - * fns.baz, - * ])); - * ``` - * @param {Array} `fns` Array of middleware functions - * @api public + * Try to read a directory */ -exports.series = function(fns) { - return function (file, cb) { - utils.async.eachSeries(fns, function (fn, next) { - fn(file, next); - }, cb); - }; +utils.tryReaddir = function(dir) { + try { + return fs.readdirSync(dir); + } catch (err) {} + return []; }; /** - * Run middleware in parallel. - * - * ```js - * var fns = require('./fns/'); - * - * app.onLoad(/\.js$/, utils.parallel([ - * fns.foo, - * fns.bar, - * fns.baz, - * ])); - * ``` - * @param {Array} `fns` Array of middleware functions - * @api public + * Try to require a file */ -exports.parallel = function(fns) { - return function (file, cb) { - utils.async.each(fns, function (fn, next) { - fn(file, next); - }, cb); - }; +utils.tryRequire = function(name) { + try { + return require(name); + } catch (err) {} + + try { + return require(path.resolve(name)); + } catch (err) {} + return {}; }; /** - * Expose utils + * Expose `utils` modules */ module.exports = utils; diff --git a/package.json b/package.json index d2356486..57ba99a9 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,17 @@ { - "name": "verb2", - "description": "Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes.", - "version": "0.8.8", - "homepage": "https://github.com/verbose/verb2", + "name": "verb", + "description": "Documentation generator", + "version": "0.9.0", + "homepage": "https://github.com/jonschlinkert/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "repository": "verbose/verb2", + "repository": "jonschlinkert/verb", "bugs": { - "url": "https://github.com/verbose/verb/issues" + "url": "https://github.com/jonschlinkert/verb/issues" }, "license": "MIT", "files": [ - "defaults.js", - "index.js", - "lib" + "lib/", + "index.js" ], "main": "index.js", "engines": { @@ -21,96 +20,85 @@ "scripts": { "test": "mocha" }, + "preferGlobal": true, + "bin": { + "verb": "cli.js" + }, "dependencies": { - "assemble-ask": "^0.1.4", - "assemble-core": "^0.1.7", + "ansi-colors": "^0.1.0", + "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", - "async": "^1.5.0", - "base-cli": "^0.1.2", - "base-config": "^0.1.2", - "base-store": "^0.1.1", - "composer-runtimes": "^0.5.1", + "base-questions": "^0.1.2", + "base-runner": "^0.4.1", + "base-store": "^0.2.0", + "common-middleware": "^0.1.3", "engine-base": "^0.1.2", - "expand-args": "^0.2.1", - "export-files": "^2.1.0", "extend-shallow": "^2.0.1", - "get-value": "^1.2.1", - "helper-reflinks": "^2.0.1", - "helper-related": "^0.11.1", + "has-glob": "^0.1.1", + "helper-copyright": "^2.0.0", + "helper-reflinks": "^2.2.1", + "helper-related": "^0.12.1", + "is-valid-glob": "^0.3.0", "lazy-cache": "^0.2.4", - "load-pkg": "^2.0.1", - "map-config": "^0.2.1", - "middleware-utils": "^0.1.4", + "matched": "^0.3.2", "minimist": "^1.2.0", - "parser-front-matter": "^1.3.0", - "readme-includes": "^0.2.9", - "resolve-dir": "^0.1.0", - "rimraf": "^2.4.3", - "set-value": "^0.2.0" + "success-symbol": "^0.1.0", + "time-stamp": "^0.1.3" }, "devDependencies": { - "base-methods": "^0.3.1", + "async": "^1.5.0", + "base-methods": "^0.6.1", "buffer-equal": "0.0.1", "consolidate": "^0.13.1", "coveralls": "^2.11.4", + "data-store": "^0.12.0", "define-property": "^0.2.5", "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", + "get-value": "^2.0.0", "graceful-fs": "^4.1.2", "gulp": "^3.9.0", - "gulp-git": "^1.6.0", - "gulp-istanbul": "^0.10.2", - "gulp-jshint": "^1.11.2", - "gulp-mocha": "^2.1.3", + "gulp-eslint": "^1.1.1", + "gulp-istanbul": "^0.10.3", + "gulp-mocha": "^2.2.0", "is-buffer": "^1.1.0", - "istanbul": "^0.4.0", - "jshint-stylish": "^2.0.1", - "kind-of": "^2.0.1", - "look-up": "^0.8.1", - "mocha": "^2.3.3", - "resolve-glob": "^0.1.3", - "should": "^7.1.1", + "istanbul": "^0.4.1", + "kind-of": "^3.0.2", + "load-pkg": "^3.0.0", + "mocha": "*", + "parser-front-matter": "^1.3.0", + "resolve-glob": "^0.1.7", + "rimraf": "^2.4.4", + "scaffold": "^0.2.1", + "should": "*", "sinon": "^1.17.2", "swig": "^1.4.2", "through2": "^2.0.0", "vinyl": "^1.1.0" }, "keywords": [ - "comment", - "comments", - "doc", - "docs", - "document", - "documentation", - "generate", - "generator", - "gh", - "gh-pages", - "markdown", - "md", - "pages", - "readme", - "repo", - "repository", + "app", + "boilerplate", + "create", + "verb", + "verbApp", + "init", + "initialize", + "project", + "scaffold", "template", "templates", - "verb", - "verbiage" + "webapp", + "yeoman" ], "verb": { - "data": { - "zzz.one": "yyy" - }, - "helpers": { - "whatever": "@/helper-related" - }, "related": { "list": [ - "assemble", + "base-methods", "assemble-core", - "composer", - "templates", - "update" + "resolve-modules", + "base-runner", + "base-resolver" ] } } diff --git a/test/app.collection.js b/test/app.collection.js index fde0c24d..b96cbaf2 100644 --- a/test/app.collection.js +++ b/test/app.collection.js @@ -1,7 +1,6 @@ require('mocha'); require('should'); var fs = require('fs'); -var path = require('path'); var assert = require('assert'); var define = require('define-property'); var support = require('./support'); @@ -52,7 +51,7 @@ describe('collection', function () { it('should load a view onto the respective collection:', function () { app.pages('test/fixtures/pages/a.hbs'); - app.views.pages.should.have.property(path.resolve('test/fixtures/pages/a.hbs')); + app.views.pages.should.have.property('test/fixtures/pages/a.hbs'); }); it('should allow collection methods to be chained:', function () { @@ -62,9 +61,9 @@ describe('collection', function () { .pages('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - path.resolve('test/fixtures/pages/a.hbs'), - path.resolve('test/fixtures/pages/b.hbs'), - path.resolve('test/fixtures/pages/c.hbs') + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' ]); }); @@ -76,9 +75,9 @@ describe('collection', function () { app.pages.options.should.have.property('foo', 'bar'); app.views.pages.should.have.properties([ - path.resolve('test/fixtures/pages/a.hbs'), - path.resolve('test/fixtures/pages/b.hbs'), - path.resolve('test/fixtures/pages/c.hbs') + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' ]); }); @@ -124,16 +123,16 @@ describe('collection', function () { describe('rendering views', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); app.create('pages'); }); it('should render a view with inherited app.render', function (done) { app.page('test/fixtures/templates/a.tmpl') .use(function (view) { - if (!view.contents) { - view.contents = fs.readFileSync(view.path); - } + view.content = fs.readFileSync(view.path); }) .set('data.name', 'Brian') .render(function (err, res) { @@ -166,7 +165,7 @@ describe('collection singular method', function () { it('should add a view to the created collection:', function () { app.page('test/fixtures/pages/a.hbs'); - assert(typeof app.views.pages[path.resolve('test/fixtures/pages/a.hbs')] === 'object'); + assert(typeof app.views.pages['test/fixtures/pages/a.hbs'] === 'object'); }); it('should expose the `option` method:', function () { diff --git a/test/app.config.js b/test/app.config.js deleted file mode 100644 index 9ec83919..00000000 --- a/test/app.config.js +++ /dev/null @@ -1,579 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var minimist = require('minimist'); -var expandArgs = require('expand-args'); -var config = require('base-config'); -var verb = require('..'); -var app; - -function expand(argv) { - return expandArgs(minimist(argv)); -} - -describe('config', function () { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - describe('methods', function () { - it('should expose a "config" function on app:', function () { - assert(app.config); - assert(typeof app.config === 'function'); - }); - - it('should expose a "process" method on app.config:', function () { - assert(typeof app.config.process === 'function'); - }); - - it('should expose a "map" method on app.config:', function () { - assert(typeof app.config.map === 'function'); - }); - }); - - describe('config mapping', function () { - it('should expose the config object from app.config', function () { - assert(app.config.config); - assert(typeof app.config.config === 'object'); - }); - - it('should add a set method to config', function () { - assert(typeof app.config.config.set === 'function'); - }); - it('should add a get method to config', function () { - assert(typeof app.config.config.get === 'function'); - }); - it('should add a del method to config', function () { - assert(typeof app.config.config.del === 'function'); - }); - }); - - describe('use', function() { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - it('should use a plugin', function(cb) { - app.once('use', function() { - cb(); - }); - - app.config.process({use: 'test/fixtures/plugins/a'}); - }); - - it('should use a plugin from a cwd', function(cb) { - app.once('use', function() { - cb(); - }); - - app.config.process({ - cwd: 'test/fixtures/plugins', - use: 'a' - }); - }); - - it('should throw an error when plugin is not found', function(cb) { - try { - app.config.process({ - cwd: 'test/fixtures/plugins', - use: 'd' - }); - assert(new Error('expected an error')); - } catch(err) { - assert(err); - assert(err.message); - assert(err.message === 'cannot find plugin: d'); - cb(); - } - }); - - it('should use an array of plugins from a cwd', function(cb) { - var fns = 0; - app.on('use', function() { - fns++; - }); - - app.config.process({ - cwd: 'test/fixtures/plugins', - use: 'a,b,c' - }); - - assert(fns === 3); - cb(); - }); - }); - - describe('map', function() { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - it('should process an object of flags', function(cb) { - app.on('option', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process({option: {a: 'b'}}); - }); - - it('should process an object passed to config', function(cb) { - app = verb(); - app.use(config({option: {a: 'b'}})); - - app.on('option', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(); - }); - - it('should process an array passed to config', function(cb) { - app = verb(); - app.use(config([{option: {a: 'b'}}])); - - app.on('option', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(); - }); - - it('should be chainable', function(cb) { - app.config.alias('a', 'b') - .alias('b', 'c') - .alias('c', 'set') - .map('set') - - app.on('set', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process({c: {a: 'b'}}); - }); - - it('should add properties to app.config.config', function (cb) { - app.config.map('foo', 'set'); - app.config.map('bar', 'get'); - var called = 0; - - app.on('set', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - called++; - }); - - app.on('get', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - called++; - }); - - app.config.process({set: {a: 'b'}, get: 'a'}); - assert(called === 2); - cb(); - }); - }); - - describe('store.map', function() { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - it('should expose `store.config', function () { - assert(app.store.config); - assert(typeof app.store.config === 'function'); - }); - - it('should not blow up if store plugin is not used', function () { - var foo = verb(); - delete foo.store; - foo.use(config()); - assert(typeof foo.store === 'undefined'); - }); - - it('should add properties to app.store.config.config', function (cb) { - app.store.config.alias('foo', 'set'); - app.store.config.alias('bar', 'get'); - var called = 0; - - app.store.on('set', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - called++; - }); - - app.store.on('get', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - called++; - }); - - app.store.config.process({foo: {a: 'b'}, bar: 'a'}); - assert(called === 2); - cb(); - }); - - it('should work as a function', function (cb) { - app.store.config({ - foo: 'set', - bar: 'get' - }); - - var called = 0; - - app.store.on('set', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - called++; - }); - - app.store.on('get', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - called++; - }); - - app.store.config.process({foo: {a: 'b'}, bar: 'a'}); - assert(called === 2); - cb(); - }); - }); - - describe('process', function() { - it('should process an object of flags', function(cb) { - app.on('option', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process({option: {a: 'b'}}); - }); - }); -}); - -describe('should handle methods added by other plugins', function () { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - afterEach(function() { - app.store.del({force: true}); - }); - - describe('store', function () { - it('should add a store method to config', function () { - assert(typeof app.config.config.store === 'function'); - }); - }); - - describe('option', function () { - it('should add an option method to config', function () { - assert(typeof app.config.config.option === 'function'); - }); - }); - - describe('data', function () { - it('should add a data method to config', function () { - assert(typeof app.config.config.data === 'function'); - }); - }); -}); - -describe('events', function () { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - afterEach(function() { - app.store.del({force: true}); - }); - - describe('set', function () { - it('should emit a set event', function (cb) { - var argv = expand(['--set=a:b']); - - app.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - }); - - describe('get', function () { - it('should emit a get event', function (cb) { - var argv = expand(['--get=a']); - app.set('a', 'b'); - - app.on('get', function(key, val) { - assert(key); - assert(val); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - }); - - describe('del', function () { - it('should emit a del event', function (cb) { - var argv = expand(['--del=a']); - app.set('a', 'b'); - - app.on('del', function(key) { - assert(key); - assert(key === 'a'); - assert(typeof app.a === 'undefined'); - cb(); - }); - - app.config.process(argv); - }); - }); - - describe('option', function () { - it('should emit an option event', function (cb) { - var argv = expand(['--option=a:b']); - - app.on('option', function(key, val) { - assert(key); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - }); - - describe('data', function () { - it('should emit a data event', function (cb) { - var argv = expand(['--data=a:b']); - - app.on('data', function(args) { - assert(Array.isArray(args)); - assert(args.length === 1); - assert(args[0].a === 'b'); - cb(); - }); - - app.config.process(argv); - }); - }); - - describe('store', function () { - it('should emit a store.set event', function (cb) { - var argv = expand(['--store.set=a:b']); - app.store.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.store.data.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should emit a store.get event', function (cb) { - var argv = expand(['--store.get=a']); - app.store.set('a', 'b'); - - app.store.on('get', function(key, val) { - assert(key); - assert(val); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should emit a store.del event', function (cb) { - var argv = expand(['--store.del=a,b']); - app.store.set('a', 'aaa'); - app.store.set('b', 'bbb'); - var keys = []; - - app.store.on('del', function(key) { - keys.push(key); - }); - - app.config.process(argv); - assert(keys.length === 2); - process.nextTick(function () { - assert(Object.keys(app.store.data).length === 2); - }); - cb(); - }); - - it('should delete the entire store', function (cb) { - var argv = expand(['--store.del=force:true']); - app.store.set('a', 'aaa'); - app.store.set('b', 'bbb'); - var keys = []; - - app.store.on('del', function(key) { - keys.push(key); - }); - - app.config.process(argv); - assert(keys.length === 2); - process.nextTick(function () { - assert(Object.keys(app.store.data).length === 0); - }); - cb(); - }); - }); -}); - -describe('aliases', function () { - beforeEach(function() { - app = verb(); - app.use(config()); - }); - - afterEach(function() { - app.store.del({force: true}); - }); - - describe('config', function () { - it('should map an object to methods', function (cb) { - var argv = expand(['--set=a:b']); - app.config({ - set: 'set' - }); - - app.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should use custom functions', function (cb) { - var argv = expand(['--foo=a:b']); - app.config({ - set: 'set', - foo: function (key, val) { - app.set(key, val); - } - }); - - app.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should use alias mappings', function (cb) { - var argv = expand(['--foo=a:b']); - app.config({ - set: 'set', - foo: 'set' - }); - - app.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should expose config.map', function (cb) { - var argv = expand(['--set=a:b']); - app.config.map('set'); - - app.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should expose config.alias', function (cb) { - var argv = expand(['--set=a:b']); - app.config.alias('foo', 'set'); - - app.on('set', function(key, val) { - assert(key); - assert(val); - assert(app.a === 'b'); - assert(key === 'a'); - assert(val === 'b'); - cb(); - }); - - app.config.process(argv); - }); - - it('should throw if args are invalid', function (cb) { - try { - app.config([]); - cb(new Error('expected an error')); - } catch(err) { - assert(err); - assert(err.message); - assert(err.message === 'expected key to be a string or object'); - cb(); - } - }); - }); -}); diff --git a/test/app.copy.js b/test/app.copy.js index 4a0d510b..6df9b1bd 100644 --- a/test/app.copy.js +++ b/test/app.copy.js @@ -21,6 +21,7 @@ describe('copy()', function() { describe('streams', function () { it('should copy files', function (done) { app.copy(fixtures, path.join(__dirname, 'actual')) + .on('error', done) .on('data', function (file) { assert.equal(typeof file, 'object'); }) diff --git a/test/app.create.js b/test/app.create.js index 9178245f..eb096991 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -83,7 +83,9 @@ describe('create', function () { describe('chaining', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); app.create('page'); }); @@ -92,7 +94,7 @@ describe('create', function () { app.page('b.hbs', {content: 'b'}); app.page('c.hbs', {content: 'c'}); app.views.pages.should.have.properties(['a.hbs', 'b.hbs', 'c.hbs']); - assert(app.views.pages['a.hbs'].content === 'a'); + assert(app.views.pages['a.hbs'].contents.toString() === 'a'); }); it('should create views from file paths:', function () { @@ -101,9 +103,9 @@ describe('create', function () { app.page('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - path.resolve('test/fixtures/pages/a.hbs'), - path.resolve('test/fixtures/pages/b.hbs'), - path.resolve('test/fixtures/pages/c.hbs') + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' ]); }); }); @@ -112,7 +114,9 @@ describe('create', function () { describe('instance', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); }); it('should return the collection instance', function () { @@ -126,22 +130,24 @@ describe('create', function () { .use(function (views) { views.read = function (name) { var view = this.getView(name); - if (!view.contents) { - view.contents = fs.readFileSync(view.path); + if (!view.content) { + view.content = fs.readFileSync(view.path); } }; }); collection.addView('test/fixtures/templates/a.tmpl'); collection.read('a.tmpl'); - assert(collection.getView('a.tmpl').content === '<%= name %>'); + assert(collection.getView('a.tmpl').content === '{%= name %}'); }); }); describe('viewType', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); }); it('should add collection to the given viewType', function () { @@ -158,7 +164,9 @@ describe('create', function () { describe('events', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); }); it('should emit `create` when a collection is created:', function () { diff --git a/test/app.list.js b/test/app.list.js index 9c9c3ea3..217d5ddd 100644 --- a/test/app.list.js +++ b/test/app.list.js @@ -1,7 +1,8 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); -var path = require('path'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); @@ -32,7 +33,9 @@ describe('list', function () { describe('adding items', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); app.create('pages'); }); @@ -40,7 +43,7 @@ describe('list', function () { app.pages('test/fixtures/pages/a.hbs'); var list = app.list(); list.addItem(app.pages.getView('test/fixtures/pages/a.hbs')); - assert(list.hasItem(path.resolve('test/fixtures/pages/a.hbs'))); + assert(list.hasItem('test/fixtures/pages/a.hbs')); }); it('should expose the `option` method from a list:', function () { @@ -83,21 +86,23 @@ describe('list', function () { describe('rendering items', function () { beforeEach(function () { app = new App(); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); app.create('pages'); }); it('should render a item with inherited app.render', function (done) { app.page('test/fixtures/templates/a.tmpl') .use(function (item) { - if (!item.contents) { + if (!item.contents.toString()) { item.contents = fs.readFileSync(item.path); } }) .set('data.name', 'Brian') .render(function (err, res) { if (err) return done(err); - assert(res.content === 'Brian'); + assert(res.contents.toString() === 'Brian'); done(); }); }); diff --git a/test/app.task.js b/test/app.task.js index c0d38ffb..40727cab 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -70,13 +70,13 @@ describe('task()', function () { it('should emit task events', function (done) { var events = []; - app.on('starting', function (task) { + app.on('task:starting', function (task) { events.push('starting.' + task.name); }); - app.on('finished', function (task) { + app.on('task:finished', function (task) { events.push('finished.' + task.name); }); - app.on('error', function (err, task) { + app.on('task:error', function (err, task) { events.push('error.' + task.name); }); diff --git a/test/collection.engines.js b/test/collection.engines.js index 4c2a6192..edb94f1e 100644 --- a/test/collection.engines.js +++ b/test/collection.engines.js @@ -93,7 +93,6 @@ describe('engines', function () { }); }); - describe('engine selection:', function () { beforeEach(function (done) { collection = new Views(); diff --git a/test/collection.js b/test/collection.js index 2ddee92d..62484080 100644 --- a/test/collection.js +++ b/test/collection.js @@ -229,6 +229,10 @@ describe('methods', function () { }); describe('addItems', function () { + beforeEach(function() { + collection = new Collection(); + }); + it('should add multiple items', function () { collection.addItems({ one: {content: 'foo'}, diff --git a/test/fixtures/example.txt b/test/fixtures/example.txt new file mode 100644 index 00000000..a8a94062 --- /dev/null +++ b/test/fixtures/example.txt @@ -0,0 +1 @@ +this is a test \ No newline at end of file diff --git a/test/fixtures/one/generator.js b/test/fixtures/one/generator.js new file mode 100644 index 00000000..546d0048 --- /dev/null +++ b/test/fixtures/one/generator.js @@ -0,0 +1,19 @@ +'use strict'; + + +module.exports = function(app, base, env) { + app.task('default', function(cb) { + console.log('one > default'); + cb(); + }); + + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + + app.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/one/package.json b/test/fixtures/one/package.json new file mode 100644 index 00000000..d1d46764 --- /dev/null +++ b/test/fixtures/one/package.json @@ -0,0 +1,30 @@ +{ + "name": "one", + "description": "The most interesting project in the world > Verb", + "version": "0.1.0", + "homepage": "https://github.com/jonschlinkert/one", + "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "repository": "jonschlinkert/one", + "bugs": { + "url": "https://github.com/jonschlinkert/one/issues" + }, + "license": "MIT", + "files": [ + "index.js" + ], + "main": "index.js", + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "dependencies": { + "resolve-modules": "^0.1.2" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "keywords": [] +} diff --git a/test/fixtures/one/templates/a.txt b/test/fixtures/one/templates/a.txt new file mode 100644 index 00000000..2ff82506 --- /dev/null +++ b/test/fixtures/one/templates/a.txt @@ -0,0 +1 @@ +one: aaa \ No newline at end of file diff --git a/test/fixtures/one/templates/x.txt b/test/fixtures/one/templates/x.txt new file mode 100644 index 00000000..139e1d8e --- /dev/null +++ b/test/fixtures/one/templates/x.txt @@ -0,0 +1 @@ +one: xxx \ No newline at end of file diff --git a/test/fixtures/one/templates/y.txt b/test/fixtures/one/templates/y.txt new file mode 100644 index 00000000..7308ea90 --- /dev/null +++ b/test/fixtures/one/templates/y.txt @@ -0,0 +1 @@ +one: yyy \ No newline at end of file diff --git a/test/fixtures/one/templates/z.txt b/test/fixtures/one/templates/z.txt new file mode 100644 index 00000000..04c378ad --- /dev/null +++ b/test/fixtures/one/templates/z.txt @@ -0,0 +1 @@ +one: zzz \ No newline at end of file diff --git a/test/fixtures/plugins/a.js b/test/fixtures/plugins/a.js deleted file mode 100644 index 74fc33fd..00000000 --- a/test/fixtures/plugins/a.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = function (app) { - console.log('plugin A'); - app.set('a', 'AAA'); -}; diff --git a/test/fixtures/plugins/b.js b/test/fixtures/plugins/b.js deleted file mode 100644 index 0b909750..00000000 --- a/test/fixtures/plugins/b.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = function (app) { - console.log('plugin B'); - app.set('b', 'BBB'); -}; diff --git a/test/fixtures/plugins/c.js b/test/fixtures/plugins/c.js deleted file mode 100644 index 0002206f..00000000 --- a/test/fixtures/plugins/c.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = function (app) { - console.log('plugin C'); - app.set('c', 'CCC'); -}; diff --git a/test/fixtures/templates/a.tmpl b/test/fixtures/templates/a.tmpl index 36f1f1b5..4994bb36 100644 --- a/test/fixtures/templates/a.tmpl +++ b/test/fixtures/templates/a.tmpl @@ -1 +1 @@ -<%= name %> \ No newline at end of file +{%= name %} \ No newline at end of file diff --git a/test/fixtures/templates/b.tmpl b/test/fixtures/templates/b.tmpl index 36f1f1b5..4994bb36 100644 --- a/test/fixtures/templates/b.tmpl +++ b/test/fixtures/templates/b.tmpl @@ -1 +1 @@ -<%= name %> \ No newline at end of file +{%= name %} \ No newline at end of file diff --git a/test/fixtures/templates/c.tmpl b/test/fixtures/templates/c.tmpl index 36f1f1b5..4994bb36 100644 --- a/test/fixtures/templates/c.tmpl +++ b/test/fixtures/templates/c.tmpl @@ -1 +1 @@ -<%= name %> \ No newline at end of file +{%= name %} \ No newline at end of file diff --git a/test/fixtures/three/four/five/generator.js b/test/fixtures/three/four/five/generator.js new file mode 100644 index 00000000..607c7ebe --- /dev/null +++ b/test/fixtures/three/four/five/generator.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function(verb, base) { + verb.task('default', function() {}); + verb.task('a', function() {}); + verb.task('b', function() {}); + verb.task('c', function() {}); + + verb.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/three/four/five/package.json b/test/fixtures/three/four/five/package.json new file mode 100644 index 00000000..984f0f6b --- /dev/null +++ b/test/fixtures/three/four/five/package.json @@ -0,0 +1,30 @@ +{ + "name": "five", + "description": "The most interesting project in the world > Verb", + "version": "0.1.0", + "homepage": "https://github.com/jonschlinkert/five", + "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "repository": "jonschlinkert/five", + "bugs": { + "url": "https://github.com/jonschlinkert/five/issues" + }, + "license": "MIT", + "files": [ + "index.js" + ], + "main": "index.js", + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "dependencies": { + "resolve-modules": "^0.1.2" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "keywords": [] +} diff --git a/test/fixtures/three/four/five/templates/a.txt b/test/fixtures/three/four/five/templates/a.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/four/five/templates/b.txt b/test/fixtures/three/four/five/templates/b.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/four/five/templates/c.txt b/test/fixtures/three/four/five/templates/c.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/four/generator.js b/test/fixtures/three/four/generator.js new file mode 100644 index 00000000..607c7ebe --- /dev/null +++ b/test/fixtures/three/four/generator.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function(verb, base) { + verb.task('default', function() {}); + verb.task('a', function() {}); + verb.task('b', function() {}); + verb.task('c', function() {}); + + verb.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/three/four/package.json b/test/fixtures/three/four/package.json new file mode 100644 index 00000000..3f25912f --- /dev/null +++ b/test/fixtures/three/four/package.json @@ -0,0 +1,30 @@ +{ + "name": "four", + "description": "The most interesting project in the world > Verb", + "version": "0.1.0", + "homepage": "https://github.com/jonschlinkert/four", + "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "repository": "jonschlinkert/four", + "bugs": { + "url": "https://github.com/jonschlinkert/four/issues" + }, + "license": "MIT", + "files": [ + "index.js" + ], + "main": "index.js", + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "dependencies": { + "resolve-modules": "^0.1.2" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "keywords": [] +} diff --git a/test/fixtures/three/four/templates/a.txt b/test/fixtures/three/four/templates/a.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/four/templates/b.txt b/test/fixtures/three/four/templates/b.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/four/templates/c.txt b/test/fixtures/three/four/templates/c.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/generator.js b/test/fixtures/three/generator.js new file mode 100644 index 00000000..607c7ebe --- /dev/null +++ b/test/fixtures/three/generator.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function(verb, base) { + verb.task('default', function() {}); + verb.task('a', function() {}); + verb.task('b', function() {}); + verb.task('c', function() {}); + + verb.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/three/package.json b/test/fixtures/three/package.json new file mode 100644 index 00000000..12b5fac3 --- /dev/null +++ b/test/fixtures/three/package.json @@ -0,0 +1,30 @@ +{ + "name": "three", + "description": "The most interesting project in the world > Verb", + "version": "0.1.0", + "homepage": "https://github.com/jonschlinkert/three", + "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "repository": "jonschlinkert/three", + "bugs": { + "url": "https://github.com/jonschlinkert/three/issues" + }, + "license": "MIT", + "files": [ + "index.js" + ], + "main": "index.js", + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "dependencies": { + "resolve-modules": "^0.1.2" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "keywords": [] +} diff --git a/test/fixtures/three/templates/a.txt b/test/fixtures/three/templates/a.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/templates/b.txt b/test/fixtures/three/templates/b.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/three/templates/c.txt b/test/fixtures/three/templates/c.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/two/generate.js b/test/fixtures/two/generate.js new file mode 100644 index 00000000..8e487499 --- /dev/null +++ b/test/fixtures/two/generate.js @@ -0,0 +1,21 @@ +'use strict'; + +var Verb = require('../..'); +var verb = new Verb(); + +verb.task('default', function() {}); +verb.task('a', function() {}); +verb.task('b', function() {}); +verb.task('c', function() {}); + +verb.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); +}); + +/** + * Expose this instance of `Verb` + */ + +module.exports = verb; \ No newline at end of file diff --git a/test/fixtures/two/package.json b/test/fixtures/two/package.json new file mode 100644 index 00000000..8a02df01 --- /dev/null +++ b/test/fixtures/two/package.json @@ -0,0 +1,30 @@ +{ + "name": "two", + "description": "The most interesting project in the world > Verb", + "version": "0.1.0", + "homepage": "https://github.com/jonschlinkert/two", + "author": "Jon Schlinkert (https://github.com/jonschlinkert)", + "repository": "jonschlinkert/two", + "bugs": { + "url": "https://github.com/jonschlinkert/two/issues" + }, + "license": "MIT", + "files": [ + "index.js" + ], + "main": "index.js", + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "dependencies": { + "resolve-modules": "^0.1.2" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "keywords": [] +} diff --git a/test/fixtures/two/templates/a.txt b/test/fixtures/two/templates/a.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/two/templates/b.txt b/test/fixtures/two/templates/b.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/two/templates/c.txt b/test/fixtures/two/templates/c.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/updaters/a.txt b/test/fixtures/updaters/a.txt new file mode 100644 index 00000000..7c4a013e --- /dev/null +++ b/test/fixtures/updaters/a.txt @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/test/fixtures/updaters/b.txt b/test/fixtures/updaters/b.txt new file mode 100644 index 00000000..01f02e32 --- /dev/null +++ b/test/fixtures/updaters/b.txt @@ -0,0 +1 @@ +bbb \ No newline at end of file diff --git a/test/fixtures/updaters/c.txt b/test/fixtures/updaters/c.txt new file mode 100644 index 00000000..2383bd58 --- /dev/null +++ b/test/fixtures/updaters/c.txt @@ -0,0 +1 @@ +ccc \ No newline at end of file diff --git a/test/helpers.js b/test/helpers.js index 13981f0b..b8388fb2 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -66,12 +66,12 @@ describe('helpers', function () { assert(typeof app._.helpers.sync.a === 'function'); }); - it('should fail gracefully on bad globs:', function (done) { + it('should fail gracefully on bad globs:', function (cb) { try { app.helpers('test/fixtures/helpers/*.foo'); - done(); + cb(); } catch(err) { - done(new Error('should not throw an error.')); + cb(new Error('should not throw an error.')); } }); @@ -141,12 +141,12 @@ describe('helpers', function () { assert(typeof app._.helpers.async.three === 'function'); }); - it('should fail gracefully on bad globs:', function (done) { + it('should fail gracefully on bad globs:', function (cb) { try { app.asyncHelpers('test/fixtures/helpers/*.foo'); - done(); + cb(); } catch(err) { - done(new Error('should not throw an error.')); + cb(new Error('should not throw an error.')); } }); @@ -200,7 +200,7 @@ describe('sync helpers', function () { assert(app._.helpers.sync.hasOwnProperty('b')); }); - it('should use a helper:', function (done) { + it('should use a helper:', function (cb) { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= upper(a) %>', locals: {a: 'bbb'}}); app.helper('upper', function (str) { return str.toUpperCase(); @@ -209,15 +209,15 @@ describe('sync helpers', function () { var page = app.pages.getView('a.tmpl'); app.render(page, function (err, view) { - if (err) return done(err); + if (err) return cb(err); assert.equal(typeof view.contents.toString(), 'string'); assert.equal(view.contents.toString(), 'BBB'); - done(); + cb(); }); }); - it('should use a namespaced helper:', function (done) { + it('should use a namespaced helper:', function (cb) { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= foo.upper(a) %>', locals: {a: 'bbb'}}); app.helperGroup('foo', { @@ -230,11 +230,11 @@ describe('sync helpers', function () { var page = app.pages.getView('a.tmpl'); app.render(page, function (err, view) { - if (err) return done(err); + if (err) return cb(err); assert.equal(typeof view.contents.toString(), 'string'); assert.equal(view.contents.toString(), 'BBB'); - done(); + cb(); }); }); }); @@ -253,7 +253,7 @@ describe('async helpers', function () { app._.helpers.async.should.have.property('b'); }); - it('should use an async helper:', function (done) { + it('should use an async helper:', function (cb) { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= lower(a) %>', locals: {a: 'BBB'}}); app.asyncHelper('lower', function (str, next) { if (typeof next !== 'function') return str; @@ -262,16 +262,16 @@ describe('async helpers', function () { var page = app.pages.getView('a.tmpl'); app.render(page, function (err, view) { - if (err) return done(err); + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'bbb'); - done(); + cb(); }); }); }); describe('built-in helpers:', function () { - describe('automatically generated helpers for default view types:', function () { + describe('automatically verbd helpers for default view types:', function () { beforeEach(function () { app = new App({rethrow: false}); app.engine('md', require('engine-base')); @@ -285,80 +285,80 @@ describe('built-in helpers:', function () { }); }); - it('should expose front matter to the `partial` helper.', function (done) { + it('should expose front matter to the `partial` helper.', function (cb) { app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); app.render('b.md', function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo AAA bar'); - done(); + cb(); }); }); - it('should use helper locals.', function (done) { + it('should use helper locals.', function (cb) { app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo CCC bar'); - done(); + cb(); }); }); - it('should use front matter data.', function (done) { + it('should use front matter data.', function (cb) { app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo AAA bar'); - done(); + cb(); }); }); - it('should use partial locals:', function (done) { + it('should use partial locals:', function (cb) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) .render({name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo EEE bar'); - done(); + cb(); }); }); - it('should use locals from the `view.render` method:', function (done) { + it('should use locals from the `view.render` method:', function (cb) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) .render({name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo EEE bar'); - done(); + cb(); }); }); - it('should use locals from the `app.render` method:', function (done) { + it('should use locals from the `app.render` method:', function (cb) { app.partial('abc.md', {content: '<%= name %>'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo DDD bar'); - done(); + cb(); }); }); - it('should return an empty string when the partial is missing.', function (done) { + it('should return an empty string when the partial is missing.', function (cb) { app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("def.md", { name: "CCC" }) %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo bar'); - done(); + cb(); }); }); }); @@ -376,38 +376,38 @@ describe('built-in helpers:', function () { }); }); - it('should prefer helper locals over view locals.', function (done) { + it('should prefer helper locals over view locals.', function (cb) { app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo CCC bar'); - done(); + cb(); }); }); - it('should give preference to view locals over render locals.', function (done) { + it('should give preference to view locals over render locals.', function (cb) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); var page = app.pages.getView('xyz.md'); app.render(page, {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo BBB bar'); - done(); + cb(); }); }); - it('should use render locals when other locals are not defined.', function (done) { + it('should use render locals when other locals are not defined.', function (cb) { app.partial('abc.md', {content: '<%= name %>'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo DDD bar'); - done(); + cb(); }); }); }); @@ -424,7 +424,7 @@ describe('built-in helpers:', function () { }); }); - it('should use the `partial` helper with handlebars.', function (done) { + it('should use the `partial` helper with handlebars.', function (cb) { app.engine(['tmpl', 'md'], require('engine-base')); app.engine('hbs', handlebars); @@ -432,13 +432,13 @@ describe('built-in helpers:', function () { app.page('a.hbs', {path: 'a.hbs', content: 'foo {{{partial "title.hbs" this}}} bar'}); app.render('a.hbs', {name: 'Halle Nicole'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('foo Halle Nicole bar'); - done(); + cb(); }); }); - it('should use the `partial` helper with any engine.', function (done) { + it('should use the `partial` helper with any engine.', function (cb) { app.engine('hbs', handlebars); app.engine('md', handlebars); app.engine('swig', swig); @@ -467,9 +467,9 @@ describe('built-in helpers:', function () { var page = app.pages.getView('g.md'); locals.author = page.data.author || locals.author; page.render(locals, function (err, res) { - if (err) return done(err); + if (err) return cb(err); res.content.should.equal('Brian Woodward'); - done(null, res.content); + cb(null, res.content); }); }); }); @@ -483,7 +483,7 @@ describe('helpers integration', function () { }); describe('.helpers()', function () { - it('should add helpers and use them in templates.', function (done) { + it('should add helpers and use them in templates.', function (cb) { app.helpers({ upper: function (str) { return str.toUpperCase(); @@ -492,15 +492,15 @@ describe('helpers integration', function () { app.page('doc.md', {content: 'a <%= upper(name) %> b'}) .render({name: 'Halle'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); }); describe('helper options:', function () { - it('should expose `this.options` to helpers:', function (done) { + it('should expose `this.options` to helpers:', function (cb) { app.helper('cwd', function (fp) { return path.join(this.options.cwd, fp); }); @@ -509,13 +509,13 @@ describe('helpers integration', function () { app.option('cwd', 'foo/bar'); app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) .render(function (err, res) { - if (err) return done(err); + if (err) return cb(err); assert(res.content === 'a foo/bar/baz b'); - done(); + cb(); }); }); - it('should pass helper options to helpers:', function (done) { + it('should pass helper options to helpers:', function (cb) { app.helper('cwd', function (fp) { return path.join(this.options.cwd, fp); }); @@ -525,15 +525,15 @@ describe('helpers integration', function () { app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) .render(function (err, res) { - if (err) return done(err); + if (err) return cb(err); assert(res.content === 'a foo/bar/baz b'); - done(); + cb(); }); }); }); describe('options.helpers', function () { - it('should register helpers passed on the options:', function (done) { + it('should register helpers passed on the options:', function (cb) { app.option({ helpers: { upper: function (str) { @@ -547,15 +547,15 @@ describe('helpers integration', function () { app.page('doc.md', {content: 'a <%= upper(name) %> <%= foo("bar") %> b'}) .render({name: 'Halle'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); assert(res.content === 'a HALLE foobar b'); - done(); + cb(); }); }); }); describe('options.helpers', function () { - it('should add helpers and use them in templates.', function (done) { + it('should add helpers and use them in templates.', function (cb) { app.options.helpers = { upper: function (str) { return str.toUpperCase(); @@ -567,9 +567,9 @@ describe('helpers integration', function () { app.page('doc.md', {content: 'a <%= upper(name) %> b'}) .render({name: 'Halle'}, function (err, res) { - if (err) return done(err); + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); }); @@ -589,7 +589,7 @@ describe('collection helpers', function () { }); describe('plural', function () { - it('should get the given collection', function (done) { + it('should get the given collection', function (cb) { app.post('a.hbs', {content: 'foo'}); app.post('b.hbs', {content: 'bar'}); app.post('c.hbs', {content: 'baz'}); @@ -602,15 +602,15 @@ describe('collection helpers', function () { content: '{{> list.hbs }}' }) .render(function (err, res) { - if (err) return done(err); + if (err) return cb(err); assert(res.content === 'foobarbaz'); - done(); + cb(); }); }); }); describe('single', function () { - it('should get a view from an unspecified collection', function (done) { + it('should get a view from an unspecified collection', function (cb) { app.post('a.hbs', {content: 'post-a'}); app.post('b.hbs', {content: 'post-b'}); @@ -624,29 +624,30 @@ describe('collection helpers', function () { assert(one === 'post-a'); assert(two === 'post-b'); - done(); + cb(); }); - it('should return an empty string if not found', function (done) { + it('should return an empty string if not found', function (cb) { var one = app.page('one', {content: '{{view "foo.hbs"}}'}) .compile() .fn(); assert(one === ''); - done(); + cb(); }); - it('should handle engine errors', function (done) { + it('should handle engine errors', function(cb) { + app.post('foo.hbs', {content: '{{one "two"}}'}) app.page('one', {content: '{{posts "foo.hbs"}}'}) .render(function (err) { assert(err); assert(typeof err === 'object'); assert(typeof err.message === 'string'); - assert(/is not a function/.test(err.message)); - done(); + assert(/Missing helper: "one"/.test(err.message)); + cb(); }); }); - it('should handle engine errors', function (done) { + it('should handle engine errors2', function (cb) { app.engine('tmpl', require('engine-base')); app.create('foo', {engine: 'tmpl'}); app.create('bar', {engine: 'tmpl'}); @@ -658,11 +659,11 @@ describe('collection helpers', function () { assert(err); assert(typeof err === 'object'); assert(/blah is not defined/.test(err.message)); - done(); + cb(); }); }); - it('should work with non-handlebars engine', function (done) { + it('should work with non-handlebars engine', function (cb) { app.engine('tmpl', require('engine-base')); app.create('foo', {engine: 'tmpl'}); app.create('bar', {engine: 'tmpl'}); @@ -680,10 +681,10 @@ describe('collection helpers', function () { assert(one === 'foo-a'); assert(two === 'foo-b'); - done(); + cb(); }); - it('should get a specific view from the given collection', function (done) { + it('should get a specific view from the given collection', function (cb) { app.post('a.hbs', {content: 'post-a'}); app.post('b.hbs', {content: 'post-b'}); app.post('c.hbs', {content: 'post-c'}); @@ -701,7 +702,7 @@ describe('collection helpers', function () { assert(one === 'post-a'); assert(two === 'page-b'); - done(); + cb(); }); }); }); diff --git a/test/mergePartials.js b/test/mergePartials.js index 8a738ea5..0145e071 100644 --- a/test/mergePartials.js +++ b/test/mergePartials.js @@ -6,7 +6,6 @@ var app; describe('mergePartials', function () { beforeEach(function () { app = new App(); - app.views = {}; }); it('should merge multiple partials collections onto one collection:', function () { @@ -24,7 +23,7 @@ describe('mergePartials', function () { actual.partials.should.have.properties(['a', 'b', 'c']); }); - it('should keep partials collections on separaet collections:', function () { + it('should keep partials collections on separate collections:', function () { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); diff --git a/test/questions.js b/test/questions.js index 5ae3bad5..2441c787 100644 --- a/test/questions.js +++ b/test/questions.js @@ -6,7 +6,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('content', function () { +describe.skip('content', function () { beforeEach(function () { app = new App(); }); diff --git a/test/render.js b/test/render.js index 1c0b05e8..d03eedc3 100644 --- a/test/render.js +++ b/test/render.js @@ -26,6 +26,7 @@ describe('render', function () { }); it('should throw an error when a variable is undefined:', function (done) { + delete app.cache.data.name; delete view.locals.name; app.page('a.tmpl', view) @@ -36,9 +37,11 @@ describe('render', function () { }); it('should re-throw an error when rethrow is true:', function (done) { + app = new App({rethrow: true, silent: true}); + + delete app.cache.data.name; delete view.locals.name; - app = new App({rethrow: true, silent: true}); app.engine('tmpl', require('engine-base')); app.create('page'); @@ -50,9 +53,11 @@ describe('render', function () { }); it('should emit a re-thrown error when rethrow is true:', function (done) { + app = new App({rethrow: true, silent: false}); + + delete app.cache.data.name; delete view.locals.name; - app = new App({rethrow: true, silent: false}); app.engine('tmpl', require('engine-base')); app.create('page'); diff --git a/test/store.js b/test/store.js index 8d3f3c6f..5791f579 100644 --- a/test/store.js +++ b/test/store.js @@ -4,15 +4,14 @@ require('mocha'); require('should'); var fs = require('fs'); var path = require('path'); -var store = require('base-store'); +var Store = require('data-store'); var assert = require('assert'); var App = require('../'); var app; -describe('store', function () { +describe.skip('store', function () { beforeEach(function () { app = new App(); - app.use(store('verb-tests')) }); afterEach(function (cb) { @@ -22,7 +21,6 @@ describe('store', function () { it('should create a store at the given `cwd`', function () { app = new App({store: {cwd: __dirname + '/actual'}}); - app.store.set('foo', 'bar'); assert(path.basename(app.store.path) === 'verb.json'); assert(app.store.data.hasOwnProperty('foo')); @@ -159,10 +157,10 @@ describe('store', function () { }); }); -describe('events', function () { +describe.skip('events', function () { beforeEach(function () { app = new App(); - app.use(store('verb-tests')); + app.store = new Store('verb-tests'); }); afterEach(function (cb) { @@ -210,11 +208,11 @@ describe('events', function () { keys.should.eql(['a', 'c']); }); - it('should emit `del` when a value is delted:', function () { - var res; + it.skip('should emit `del` when a value is deleted:', function () { app.store.on('del', function (keys) { - keys.should.eql('a'); - assert(typeof app.store.get('a') === 'undefined'); + // keys.should.equal('a'); + // assert(typeof app.store.get('a') === 'undefined'); + // cb(); }); app.store.set('a', {b: 'c'}); @@ -222,17 +220,17 @@ describe('events', function () { app.store.del('a'); }); - it('should emit deleted keys on `del`:', function (done) { - var keys = []; - app.store.on('del', function (key) { - keys.push(key); + it('should emit deleted keys on `del`:', function (cb) { + app.store.on('del', function (keys) { + keys.should.eql(['a', 'c', 'e']); + assert(Object.keys(app.store.data).length === 0); + cb(); }); app.store.set('a', 'b'); app.store.set('c', 'd'); app.store.set('e', 'f'); + app.store.data.should.have.properties(['a', 'c', 'e']); app.store.del({force: true}); - keys.should.eql(['a', 'c', 'e']); - done(); }); }); diff --git a/test/view.set.js b/test/view.set.js index 60eb98a8..fd594692 100644 --- a/test/view.set.js +++ b/test/view.set.js @@ -10,7 +10,9 @@ describe('set', function () { beforeEach(function () { app = new App(); app.create('page'); - app.engine('tmpl', require('engine-base')); + app.engine('tmpl', require('engine-base'), { + delims: ['{%', '%}'] + }); }); it('should set a property on a view:', function (done) { diff --git a/verbfile.js b/verbfile.js new file mode 100644 index 00000000..d7a15fe5 --- /dev/null +++ b/verbfile.js @@ -0,0 +1,141 @@ +'use strict'; + +var path = require('path'); +var utils = require('./lib/utils'); +var argv = require('minimist')(process.argv.slice(2), { + alias: {v: 'verbose'} +}); + +module.exports = function(verb, base, env) { + verb.on('error', function(err) { + console.log(err.message); + }); + + /** + * Event listeners + */ + + verb.onLoad(/\.verb\.md/, function(file, next) { + file.path = 'readme.md'; + next(); + }); + + verb.preLayout(/(\.verb|readme)\.md/i, function(file, next) { + file.layout = 'default'; + next(); + }); + + verb.include('install-npm.md', { + content: [ + 'Install with [npm](https://www.npmjs.com/)', + '', + '```sh', + '$ npm i base-argv --save', + '```' + ].join('\n') + }); + + verb.include('tests.md', { + content: [ + 'Install dev dependencies:', + '', + '```sh', + '$ npm i -d && npm test', + '```' + ].join('\n') + }); + + verb.include('contributing.md', { + content: 'Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= author.username %}/{%= name %}/issues/new).' + }); + + verb.include('author.md', { + content: [ + '**{%= author.name %}**', + '', + '+ [github/{%= author.username %}](https://github.com/{%= author.username %})', + '+ [twitter/{%= author.twitter %}](http://twitter.com/{%= author.twitter %})' + ].join('\n') + }); + + verb.include('footer.md', { + content: '_This file was generated by [{%= runner.name %}]({%= runner.url %}) on {%= date() %}._' + }); + + var name = verb.name; + var user = base.store.get('author.username'); + verb.data({repo: user + '/' + name}); + + if (!verb.cache.data.hasOwnProperty('description')) { + verb.data(verb.get('env.user.pkg') || {}); + } + + // verb.helper('shield', function() { + // return 'https://img.shields.io/travis/USER/REPO.svg'; + // }); + + verb.data({ + badge: { + travis: '[![Build Status](https://img.shields.io/travis/{%= repo %}.svg)](https://travis-ci.org/{%= repo %})', + npm: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', + coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= repo %}.svg)](https://coveralls.io/r/{%= repo %})' + }, + }); + + verb.data({license: 'Released under the MIT license.'}); + + verb.questions.set('author.twitter', 'Author\'s twitter username?'); + if (argv.init) { + verb.questions.options.forceAll = true; + } + + verb.task('readme', function(cb) { + verb.ask(function(err, answers) { + if (err) return cb(err); + + verb.toStream('docs', function(key) { + return key === '.verb'; + }) + .pipe(verb.renderFile('text', answers)) + .pipe(verb.dest(process.cwd())) + .on('finish', cb); + }); + }); + + verb.task('docs', function(cb) { + verb.ask(function(err, answers) { + if (err) return cb(err); + + verb.toStream('docs') + .on('error', cb) + .pipe(verb.renderFile('text', answers)) + .on('error', cb) + .pipe(verb.dest(dest('readme.md'))) + .on('finish', cb); + }); + }); + + verb.register('store', function(app, base) { + app.task('del', function() { + verb.store.del({force: true}); + console.log('deleted store.'); + cb(); + }); + }); + + verb.task('default', ['readme']); +}; + +/** + * Rename template files + */ + +function dest(dest) { + return function(file) { + file.base = path.dirname(dest); + file.path = dest; + file.basename = file.basename.replace(/^_/, '.'); + file.basename = file.basename.replace(/^\$/, ''); + return file.base; + }; +} From dcbdf2b1db5829d013f64b1e0b4700a620366f89 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Dec 2015 10:30:20 -0500 Subject: [PATCH 025/282] start updating .verb.md --- .verb.md | 304 +++++++++++++++++++++++++++++++++++++++++++++++++++- verbfile.js | 2 +- 2 files changed, 303 insertions(+), 3 deletions(-) diff --git a/.verb.md b/.verb.md index 95964a45..3a14ae69 100644 --- a/.verb.md +++ b/.verb.md @@ -1,3 +1,303 @@ -# verb {%= badge("fury") %} +## toc -wip \ No newline at end of file + + +**TODO** + +- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API +- [x] support sub-apps (to any level of nesting) +- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] +- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +- [x] support _instance plugins_ that allow you to easily add functionality and features to verb +- [x] support any template engine +- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) +- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +- [x] 820+ unit tests +- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +- [ ] CLI docs (started) +- [ ] User help (e.g. when the user does `verb help` or just `verb`) +- [ ] API docs +- [ ] App guidelines and conventions + +## Install + +To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. + +**Install verb** + +{%= include("install-global") %} + +**Install a app** + +If you aren't familiar with verb, just take the `node` app for a test drive: + +```sh +$ npm i -g verb-node +``` + +**Run a app** + +If everything installed correctly, you should now be able to verb a new project with the following command (make sure you run the command from an empty directory!): + +```sh +$ verb node +``` + +*** + +## Usage + +```sh +$ verb [args] +``` + +## CLI + +_(WIP)_ + +### help + +_(TODO)_ + +Get started with Verb. + +```js +$ verb help +``` + +### init + +_(TODO)_ + +Get started with Verb. + +```js +$ verb init +``` + +Upon running `init`, verb will prompt you for answers to the following questions: + + +### Run apps + +```sh +$ verb [options] +``` + +**Example** + +Run app `abc` + +```sh +$ verb abc +``` + +### Run tasks + +To run a task on the `base` app, just pass the name of the task to run. + +```sh +$ verb [options] +``` + +Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. + +**Example** + +Run task `bar`: + +```sh +$ verb bar +``` + +### Run sub-apps + +> Sub-apps are normal apps that are called from (or registered by) other apps. + +Dot-notation is used for getting and runing sub-apps. + +```sh +$ verb . [options] +``` + +**Examples** + +Run sub-app `b` on app `a`: + +```sh +$ verb a.b [options] +``` + +Run sub-app `c`: + +```sh +$ verb a.b.c [options] +``` + +And so on... + + +### Run a app's tasks + +```sh +$ verb : [options] +``` + +**Example** + +Run task `bar` on app `foo`. + +```sh +$ verb foo:bar +``` + +### Run a sub-app's tasks + +```sh +$ verb .: [options] +``` + +**Example** + +Run task `foo` on sub.app `a.b.c`. + +```sh +$ verb a.b.c:foo +``` + +## API +{%= apidocs("index.js") %} + +### .getConfig + +Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. + +Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. + +**Params** + +* `filename` **{String}**: Then name of the config file to lookup. +* `returns` **{Object}**: Returns the "base" instance. + +**Example** + +```js +var verb = Verb.getConfig('verbfile.js'); +``` + +### .getTask + +Get task `name` from the `verb.tasks` object. + +**Params** + +* `name` **{String}** +* `returns` **{Object}** + +**Example** + +```js +verb.getTask('abc'); + +// get a task from app `foo` +verb.getTask('foo:abc'); + +// get a task from sub-app `foo.bar` +verb.getTask('foo.bar:abc'); +``` + +### .addApp + +Alias for `register`. Adds a `app` with the given `name` to the `verb.apps` object. + +**Params** + +* `name` **{String}**: The name of the config object to register +* `config` **{Object|Function}**: The config object or function + +**Example** + +```js +base.addApp('foo', function(app, base, env) { + // `app` is a `Verb` instance created for the app + // `base` is a "shared" instance that provides access to all loaded apps + // `env` is a configuration/environment object with details about the app, + // user cwd, etc +}); +``` + +### .hasApp + +Return true if app `name` is registered. Dot-notation may be used to check for [sub-apps](#sub-apps). + +**Params** + +* `name` **{String}** +* `returns` **{Boolean}** + +**Example** + +```js +base.hasApp('foo.bar.baz'); +``` + +### .getApp + +Return app `name` is registered. Dot-notation may be used to get [sub-apps](#sub-apps). + +**Params** + +* `name` **{String}** +* `returns` **{Boolean}** + +**Example** + +```js +base.getApp('foo'); +// or +base.getApp('foo.bar.baz'); +``` + +### .extendApp + +Extend an app. + +**Params** + +* `app` **{Object}** +* `returns` **{Object}**: Returns the instance for chaining. + +**Example** + +```js +var foo = base.getApp('foo'); +foo.extendApp(app); +``` + +### .invoke + +Invoke app `fn` with the given `base` instance. + +**Params** + +* `fn` **{Function}**: The app function. +* `app` **{Object}**: The "base" instance to use with the app. +* `returns` **{Object}** + +**Example** + +```js +verb.invoke(app.fn, app); +``` + +## Authoring apps + +_(TODO)_ + +### App naming conventions + +Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. + + +{%= reflinks(['verb', 'assemble', 'base-methods', 'assemble-core', 'gulp', 'vinyl', 'scaffold']) %} \ No newline at end of file diff --git a/verbfile.js b/verbfile.js index d7a15fe5..dc7d5a27 100644 --- a/verbfile.js +++ b/verbfile.js @@ -97,7 +97,7 @@ module.exports = function(verb, base, env) { return key === '.verb'; }) .pipe(verb.renderFile('text', answers)) - .pipe(verb.dest(process.cwd())) + .pipe(verb.dest(dest('readme.md'))) .on('finish', cb); }); }); From d6e54a4d6c3440049e725346018b04d797d000b5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Dec 2015 15:00:59 -0500 Subject: [PATCH 026/282] adds defaults --- .eslintrc | 2 +- .verb.md | 2 +- cli.js | 29 ++- docs/src/readme/layouts/default.md | 4 +- index.js | 18 +- lib/badges.js | 8 + lib/cli.js | 20 ++ lib/config.js | 27 +++ lib/data.js | 26 ++ lib/defaults.js | 48 +--- lib/helpers.js | 20 ++ lib/includes.js | 33 +++ lib/middleware.js | 28 +++ lib/{docs.js => templates.js} | 21 +- lib/utils.js | 24 +- package.json | 7 +- readme.md | 377 +++++++++++++++++++++++++++++ verbfile.js | 108 +++------ 18 files changed, 656 insertions(+), 146 deletions(-) create mode 100644 lib/badges.js create mode 100644 lib/cli.js create mode 100644 lib/config.js create mode 100644 lib/data.js create mode 100644 lib/helpers.js create mode 100644 lib/includes.js create mode 100644 lib/middleware.js rename lib/{docs.js => templates.js} (80%) create mode 100644 readme.md diff --git a/.eslintrc b/.eslintrc index 7d80e461..7b5d047f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -30,7 +30,7 @@ "dot-location": [2, "property"], "eol-last": 2, "eqeqeq": [2, "allow-null"], - "verbApp-star-spacing": [2, { "before": true, "after": true }], + "generator-star-spacing": [2, { "before": true, "after": true }], "handle-callback-err": [2, "^(err|error)$" ], "indent": [2, 2, { "SwitchCase": 1 }], "key-spacing": [2, { "beforeColon": false, "afterColon": true }], diff --git a/.verb.md b/.verb.md index 3a14ae69..823fe567 100644 --- a/.verb.md +++ b/.verb.md @@ -28,7 +28,7 @@ To get started, you'll first need to install `verb` globally using [npm][], alon {%= include("install-global") %} -**Install a app** +**Install a verb app** If you aren't familiar with verb, just take the `node` app for a test drive: diff --git a/cli.js b/cli.js index 0ce772f0..ac0c22ee 100755 --- a/cli.js +++ b/cli.js @@ -1,7 +1,8 @@ #!/usr/bin/env node var path = require('path'); -var runner = require('base-runner'); +var mapCommands = require('map-config'); +var processArgv = require('base-argv').processArgv(); var minimist = require('minimist'); var utils = require('./lib/utils'); var Verb = require('./'); @@ -11,8 +12,12 @@ var args = minimist(process.argv.slice(2), { alias: {verbose: 'v'} }); +Verb.use(function(app) { + app.set('cache.argv', processArgv(args)); +}); + // register `runner` as a mixin -Verb.mixin(runner('verb', 'verbApp')); +Verb.mixin(utils.runner('verb', 'verbApp')); /** * Get the `base` instance of verb to use for @@ -23,6 +28,24 @@ Verb.mixin(runner('verb', 'verbApp')); var base = Verb.getConfig('verbfile.js', __dirname); +/** + * Custom base-cli command mappings. + */ + +var cli = mapCommands(base) + .map('save', function(val) { + console.log(val) + }) + .map('data', function(val) { + app.visit('data', val); + }) + .map('cwd', function(fp) { + app.option('cwd', fp); + }); + +var argv = base.processArgv(args); +cli.process(argv.options); + /** * Resolve config files (`verbfile.js`) */ @@ -45,7 +68,7 @@ base.cli.map('verbApps', function(verbApps) { } base.runVerbApps(verbApps, function(err) { - // if (err) return console.error(err); + if (err) return console.error(err); utils.timestamp('done'); }); }); diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index e22136fb..4bd88fd6 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -7,10 +7,10 @@ ## Usage {% body %} - +{% if (verb && verb.related && verb.related.list) { %} ## Related projects {%= related((verb.related && verb.related.list) || []) %} - +{% } %} ## Running tests {%= include("tests") %} diff --git a/index.js b/index.js index 18279907..a4f6218a 100644 --- a/index.js +++ b/index.js @@ -2,9 +2,11 @@ var async = require('async'); var Assemble = require('assemble-core'); +var templates = require('./lib/templates'); var defaults = require('./lib/defaults'); +var config = require('./lib/config'); var utils = require('./lib/utils'); -var docs = require('./lib/docs'); +var cli = require('./lib/cli'); /** * Create an instance of `Verb` with the given `options` @@ -27,23 +29,27 @@ function Verb(options) { this.isVerb = true; this.verbApps = {}; + config(this); + cli(this); + this.use(utils.middleware()) + .use(utils.pipeline()) .use(utils.loader()) + .use(utils.config()) .use(utils.store()) - .use(docs()) + .use(templates()) .use(utils.ask()); + defaults(this, this.base, this.env); this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] }); this.on('register', function(name, app) { // bubble up errors to `base` instance - app.on('error', app.base.emit.bind(app.base, 'error')); - app.use(docs()); + defaults(app, app.base, app.env); + app.use(templates()); }); - - defaults(this, this.base, this.env); } /** diff --git a/lib/badges.js b/lib/badges.js new file mode 100644 index 00000000..9b12863e --- /dev/null +++ b/lib/badges.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + travis: '[![Build Status](https://img.shields.io/travis/{%= repo %}.svg)](https://travis-ci.org/{%= repo %})', + fury: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', + npm: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', + coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= repo %}.svg)](https://coveralls.io/r/{%= repo %})' +}; diff --git a/lib/cli.js b/lib/cli.js new file mode 100644 index 00000000..e339acd4 --- /dev/null +++ b/lib/cli.js @@ -0,0 +1,20 @@ +'use strict'; + +var utils = require('./utils'); + +module.exports = function(app) { + // console.log(app.cli) + // app.cli + // .map('save', function(key, val) { + // console.log(key, val) + // }) + // .map('data', function(val) { + // app.visit('data', val); + // }) + // .map('cwd', function(fp) { + // app.option('cwd', fp); + // }); + + // app.define('commands', app.cli.keys); +}; + diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 00000000..b78f15f8 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,27 @@ +'use strict'; + +var utils = require('./utils'); + +module.exports = function (app) { + if (!app.isVerb) return; + app.use(utils.config()); + + app.config + .map('collections', function(val) { + app.visit('create', val); + }) + .map('addViews') + .map('plugins') + .map('helpers') + .map('asyncHelpers') + .map('data', function(val) { + app.visit('data', val); + }) + .map('reflinks', function(val) { + app.data({reflinks: val}); + }) + .map('related', function(val) { + app.data({related: val}); + }); + +}; diff --git a/lib/data.js b/lib/data.js new file mode 100644 index 00000000..617285df --- /dev/null +++ b/lib/data.js @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = function(app, base, env) { + app.data({ + runner: { + name: 'verb', + url: 'https://github.com/verbose/verb' + } + }); + + var name = app.name; + var user = base.store.get('author.username'); + app.data({repo: user + '/' + name}); + + if (!app.cache.data.app) app.data({verb: {}}); + if (!app.cache.data.hasOwnProperty('description')) { + app.data(app.get('env.user.pkg') || {}); + } + + // app.helper('shield', function() { + // return 'https://img.shields.io/travis/USER/REPO.svg'; + // }); + + app.data({license: 'Released under the MIT license.'}); + app.questions.set('author.twitter', 'Author\'s twitter username?'); +}; diff --git a/lib/defaults.js b/lib/defaults.js index dd0312d0..c1a60807 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -1,49 +1,13 @@ 'use strict'; +var middleware = require('./middleware'); +var helpers = require('./helpers'); var utils = require('./utils'); +var data = require('./data'); module.exports = function(app, base, env) { var plugins = base.get('env.argv.plugins'); - - app.data({ - runner: { - name: 'verb', - url: 'https://github.com/verbose/verb' - } - }); - - // app.asyncHelper('apidocs', function(name, cb) { - // console.log(arguments) - // cb(null, ''); - // }); - - app.asyncHelper('related', utils.related({verbose: true})); - app.asyncHelper('reflinks', utils.reflinks({verbose: true})); - app.helper('copyright', require('helper-copyright')({ - linkify: true - })); - - app.helper('date', function () { - return new Date(); - }); - - // function handle(stage) { - // return utils.through.obj(function(file, enc, next) { - // if (file.isNull()) return next(); - // app.handle(stage, file, next); - // }); - // } - - return function(cb) { - cb(); - // app.toStream('files') - // .on('error', cb) - // .pipe(handle('onStream')) - // .pipe(app.pipeline(plugins)) - // .pipe(handle('preWrite')) - // .pipe(app.dest('.')) - // .pipe(utils.exhaust(handle('postWrite'))) - // .on('error', cb) - // .on('end', cb); - }; + middleware(app, base, env); + helpers(app, base, env); + data(app, base, env); }; diff --git a/lib/helpers.js b/lib/helpers.js new file mode 100644 index 00000000..c0882dca --- /dev/null +++ b/lib/helpers.js @@ -0,0 +1,20 @@ +'use strict'; + +var utils = require('./utils'); + +module.exports = function(app, base, env) { + app.asyncHelper('apidocs', function(name, cb) { + console.log(arguments) + cb(null, ''); + }); + + app.asyncHelper('related', utils.related({verbose: true})); + app.asyncHelper('reflinks', utils.reflinks({verbose: true})); + app.helper('copyright', require('helper-copyright')({ + linkify: true + })); + + app.helper('date', function() { + return new Date(); + }); +}; diff --git a/lib/includes.js b/lib/includes.js new file mode 100644 index 00000000..61e40ed2 --- /dev/null +++ b/lib/includes.js @@ -0,0 +1,33 @@ +'use strict'; + +module.exports = { + 'install-npm.md': [ + 'Install with [npm](https://www.npmjs.com/)', + '', + '```sh', + '$ npm i {%= name %} --save', + '```' + ].join('\n'), + 'install-global.md': [ + 'Install globally with [npm](https://www.npmjs.com/)', + '', + '```sh', + '$ npm i -g {%= name %}', + '```' + ].join('\n'), + 'tests.md': [ + 'Install dev dependencies:', + '', + '```sh', + '$ npm i -d && npm test', + '```' + ].join('\n'), + 'author.md': [ + '**{%= author.name %}**', + '', + '+ [github/{%= author.username %}](https://github.com/{%= author.username %})', + '+ [twitter/{%= author.twitter %}](http://twitter.com/{%= author.twitter %})' + ].join('\n'), + 'contributing.md': 'Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= author.username %}/{%= name %}/issues/new).', + 'footer.md': '_This file was generated by [{%= runner.name %}]({%= runner.url %}) on {%= date() %}._' +}; diff --git a/lib/middleware.js b/lib/middleware.js new file mode 100644 index 00000000..c64b5962 --- /dev/null +++ b/lib/middleware.js @@ -0,0 +1,28 @@ +'use strict'; + +var utils = require('./utils'); + +module.exports = function(app, base, env) { + app.on('error', function(err) { + console.log(err.stack); + }); + + app.onLoad(/\.verb\.md/, function(file, next) { + file.path = 'readme.md'; + next(); + }); + + app.preLayout(/(\.verb|readme)\.md/i, function(file, next) { + if (needsLayout(file.content)) { + file.layout = 'default'; + } + next(); + }); +}; + +function needsLayout(str) { + if (!/^# /.test(str)) { + return true; + } + return false; +} diff --git a/lib/docs.js b/lib/templates.js similarity index 80% rename from lib/docs.js rename to lib/templates.js index 06d6075e..5dd97614 100644 --- a/lib/docs.js +++ b/lib/templates.js @@ -2,18 +2,31 @@ var fs = require('fs'); var path = require('path'); -var utils = require('./utils'); -var loaded; -var views = {}; +var includes = require('./includes'); +var badges = require('./badges'); +var loaded, views = {}; module.exports = function(options) { return function(app) { create(app, 'docs', 'renderable'); + create(app, 'badges', 'partial'); create(app, 'includes', 'partial'); create(app, 'layouts', 'layout'); - var userDocs = path.join.bind(path, process.cwd()); + /** + * DEPRECATED in the next release: Load includes and badges + */ + + for (var key in includes) { + app.include(key, {content: includes[key]}); + } + for (var key in badges) { + app.badge(key, {content: badges[key]}); + } + + + var userDocs = path.join.bind(path, process.cwd()); if (fs.existsSync(userDocs())) { app.docs.loadViews('.verb.md', {cwd: userDocs()}); diff --git a/lib/utils.js b/lib/utils.js index 4807f599..ddecd02c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,6 +1,7 @@ 'use strict'; var fs = require('fs'); +var path = require('path'); /** * Module dependencies @@ -19,22 +20,29 @@ require('helper-reflinks', 'reflinks'); require('helper-related', 'related'); // plugins and extensions -require('base-runner'); -require('base-store', 'store'); -require('base-questions', 'ask'); -require('assemble-loader', 'loader'); require('common-middleware', 'middleware'); +require('assemble-loader', 'loader'); +require('base-questions', 'ask'); +require('base-pipeline', 'pipeline'); +require('base-runner', 'runner'); +require('base-config', 'config'); +require('base-store', 'store'); +// streams +require('stream-exhaust', 'exhaust'); require('extend-shallow', 'extend'); -require('ansi-colors', 'colors'); -require('time-stamp', 'stamp'); +require('get-value', 'get'); require('matched', 'glob'); -require('success-symbol'); -require('assemble-core'); require('is-valid-glob'); +require('through2'); require('has-glob'); require('async'); +// cli +require('success-symbol'); +require('ansi-colors', 'colors'); +require('time-stamp', 'stamp'); + /** * Restore `require` */ diff --git a/package.json b/package.json index 57ba99a9..3c4c4ad4 100644 --- a/package.json +++ b/package.json @@ -28,20 +28,26 @@ "ansi-colors": "^0.1.0", "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", + "base-argv": "^0.3.0", + "base-config": "^0.3.2", + "base-pipeline": "^0.1.4", "base-questions": "^0.1.2", "base-runner": "^0.4.1", "base-store": "^0.2.0", "common-middleware": "^0.1.3", "engine-base": "^0.1.2", "extend-shallow": "^2.0.1", + "get-value": "^2.0.0", "has-glob": "^0.1.1", "helper-copyright": "^2.0.0", "helper-reflinks": "^2.2.1", "helper-related": "^0.12.1", "is-valid-glob": "^0.3.0", "lazy-cache": "^0.2.4", + "map-config": "^0.3.0", "matched": "^0.3.2", "minimist": "^1.2.0", + "stream-exhaust": "^1.0.1", "success-symbol": "^0.1.0", "time-stamp": "^0.1.3" }, @@ -55,7 +61,6 @@ "define-property": "^0.2.5", "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", - "get-value": "^2.0.0", "graceful-fs": "^4.1.2", "gulp": "^3.9.0", "gulp-eslint": "^1.1.1", diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..c6329428 --- /dev/null +++ b/readme.md @@ -0,0 +1,377 @@ +# verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/jonschlinkert/base.svg)](https://travis-ci.org/jonschlinkert/base) + +> Documentation generator + +## Install +Install with [npm](https://www.npmjs.com/) + +```sh +$ npm i verb --save +``` + +## Usage +## toc + + + +**TODO** + +- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API +- [x] support sub-apps (to any level of nesting) +- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] +- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +- [x] support _instance plugins_ that allow you to easily add functionality and features to verb +- [x] support any template engine +- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) +- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +- [x] 820+ unit tests +- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +- [ ] CLI docs (started) +- [ ] User help (e.g. when the user does `verb help` or just `verb`) +- [ ] API docs +- [ ] App guidelines and conventions + +## Install + +To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. + +**Install verb** + +Install globally with [npm](https://www.npmjs.com/) + +```sh +$ npm i -g verb +``` + +**Install a app** + +If you aren't familiar with verb, just take the `node` app for a test drive: + +```sh +$ npm i -g verb-node +``` + +**Run a app** + +If everything installed correctly, you should now be able to verb a new project with the following command (make sure you run the command from an empty directory!): + +```sh +$ verb node +``` + +*** + +## Usage + +```sh +$ verb [args] +``` + +## CLI + +_(WIP)_ + +### help + +_(TODO)_ + +Get started with Verb. + +```js +$ verb help +``` + +### init + +_(TODO)_ + +Get started with Verb. + +```js +$ verb init +``` + +Upon running `init`, verb will prompt you for answers to the following questions: + + +### Run apps + +```sh +$ verb [options] +``` + +**Example** + +Run app `abc` + +```sh +$ verb abc +``` + +### Run tasks + +To run a task on the `base` app, just pass the name of the task to run. + +```sh +$ verb [options] +``` + +Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. + +**Example** + +Run task `bar`: + +```sh +$ verb bar +``` + +### Run sub-apps + +> Sub-apps are normal apps that are called from (or registered by) other apps. + +Dot-notation is used for getting and runing sub-apps. + +```sh +$ verb . [options] +``` + +**Examples** + +Run sub-app `b` on app `a`: + +```sh +$ verb a.b [options] +``` + +Run sub-app `c`: + +```sh +$ verb a.b.c [options] +``` + +And so on... + + +### Run a app's tasks + +```sh +$ verb : [options] +``` + +**Example** + +Run task `bar` on app `foo`. + +```sh +$ verb foo:bar +``` + +### Run a sub-app's tasks + +```sh +$ verb .: [options] +``` + +**Example** + +Run task `foo` on sub.app `a.b.c`. + +```sh +$ verb a.b.c:foo +``` + +## API + + +### .getConfig + +Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. + +Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. + +**Params** + +* `filename` **{String}**: Then name of the config file to lookup. +* `returns` **{Object}**: Returns the "base" instance. + +**Example** + +```js +var verb = Verb.getConfig('verbfile.js'); +``` + +### .getTask + +Get task `name` from the `verb.tasks` object. + +**Params** + +* `name` **{String}** +* `returns` **{Object}** + +**Example** + +```js +verb.getTask('abc'); + +// get a task from app `foo` +verb.getTask('foo:abc'); + +// get a task from sub-app `foo.bar` +verb.getTask('foo.bar:abc'); +``` + +### .addApp + +Alias for `register`. Adds a `app` with the given `name` to the `verb.apps` object. + +**Params** + +* `name` **{String}**: The name of the config object to register +* `config` **{Object|Function}**: The config object or function + +**Example** + +```js +base.addApp('foo', function(app, base, env) { + // `app` is a `Verb` instance created for the app + // `base` is a "shared" instance that provides access to all loaded apps + // `env` is a configuration/environment object with details about the app, + // user cwd, etc +}); +``` + +### .hasApp + +Return true if app `name` is registered. Dot-notation may be used to check for [sub-apps](#sub-apps). + +**Params** + +* `name` **{String}** +* `returns` **{Boolean}** + +**Example** + +```js +base.hasApp('foo.bar.baz'); +``` + +### .getApp + +Return app `name` is registered. Dot-notation may be used to get [sub-apps](#sub-apps). + +**Params** + +* `name` **{String}** +* `returns` **{Boolean}** + +**Example** + +```js +base.getApp('foo'); +// or +base.getApp('foo.bar.baz'); +``` + +### .extendApp + +Extend an app. + +**Params** + +* `app` **{Object}** +* `returns` **{Object}**: Returns the instance for chaining. + +**Example** + +```js +var foo = base.getApp('foo'); +foo.extendApp(app); +``` + +### .invoke + +Invoke app `fn` with the given `base` instance. + +**Params** + +* `fn` **{Function}**: The app function. +* `app` **{Object}**: The "base" instance to use with the app. +* `returns` **{Object}** + +**Example** + +```js +verb.invoke(app.fn, app); +``` + +## Authoring apps + +_(TODO)_ + +### App naming conventions + +Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. + + +[assemble]: http://assemble.io +[assemble-core]: https://github.com/assemble/assemble-core +[base-methods]: https://github.com/jonschlinkert/base-methods +[gulp]: http://gulpjs.com +[scaffold]: https://github.com/jonschlinkert/scaffold +[verb]: https://github.com/verbose/verb +[vinyl]: http://github.com/gulpjs/vinyl + +## Related projects +* [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) +* [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) +* [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) +* [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) +* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) + +## Running tests +Install dev dependencies: + +```sh +$ npm i -d && npm test +``` + +## Contributing +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). + +## Author +**Jon Schlinkert** + ++ [github/jonschlinkert](https://github.com/jonschlinkert) ++ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) + +## License +Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) +Released under the MIT license. + +*** + +_This file was generated by [verb](https://github.com/verbose/verb) on Sat Dec 05 2015 10:37:11 GMT-0500 (EST)._ + +[ansi-colors]: https://github.com/doowb/ansi-colors +[assemble-core]: https://github.com/assemble/assemble-core +[assemble-loader]: https://github.com/jonschlinkert/assemble-loader +[base-questions]: https://github.com/jonschlinkert/base-questions +[base-runner]: https://github.com/jonschlinkert/base-runner +[base-store]: https://github.com/jonschlinkert/base-store +[common-middleware]: https://github.com/jonschlinkert/common-middleware +[engine-base]: https://github.com/jonschlinkert/engine-base +[extend-shallow]: https://github.com/jonschlinkert/extend-shallow +[has-glob]: https://github.com/jonschlinkert/has-glob +[helper-copyright]: https://github.com/helpers/helper-copyright +[helper-reflinks]: https://github.com/helpers/helper-reflinks +[helper-related]: https://github.com/helpers/helper-related +[is-valid-glob]: https://github.com/jonschlinkert/is-valid-glob +[lazy-cache]: https://github.com/jonschlinkert/lazy-cache +[matched]: https://github.com/jonschlinkert/matched +[minimist]: https://github.com/substack/minimist +[success-symbol]: https://github.com/jonschlinkert/success-symbol +[time-stamp]: https://github.com/jonschlinkert/time-stamp + diff --git a/verbfile.js b/verbfile.js index dc7d5a27..df7174d1 100644 --- a/verbfile.js +++ b/verbfile.js @@ -7,88 +7,40 @@ var argv = require('minimist')(process.argv.slice(2), { }); module.exports = function(verb, base, env) { - verb.on('error', function(err) { - console.log(err.message); - }); - - /** - * Event listeners - */ - - verb.onLoad(/\.verb\.md/, function(file, next) { - file.path = 'readme.md'; - next(); - }); - - verb.preLayout(/(\.verb|readme)\.md/i, function(file, next) { - file.layout = 'default'; - next(); - }); - - verb.include('install-npm.md', { - content: [ - 'Install with [npm](https://www.npmjs.com/)', - '', - '```sh', - '$ npm i base-argv --save', - '```' - ].join('\n') - }); - - verb.include('tests.md', { - content: [ - 'Install dev dependencies:', - '', - '```sh', - '$ npm i -d && npm test', - '```' - ].join('\n') - }); - - verb.include('contributing.md', { - content: 'Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= author.username %}/{%= name %}/issues/new).' - }); - - verb.include('author.md', { - content: [ - '**{%= author.name %}**', - '', - '+ [github/{%= author.username %}](https://github.com/{%= author.username %})', - '+ [twitter/{%= author.twitter %}](http://twitter.com/{%= author.twitter %})' - ].join('\n') - }); - - verb.include('footer.md', { - content: '_This file was generated by [{%= runner.name %}]({%= runner.url %}) on {%= date() %}._' - }); + if (argv.init) { + app.questions.options.forceAll = true; + } - var name = verb.name; - var user = base.store.get('author.username'); - verb.data({repo: user + '/' + name}); + var tasks = ['readme']; - if (!verb.cache.data.hasOwnProperty('description')) { - verb.data(verb.get('env.user.pkg') || {}); + function handle(stage) { + return utils.through.obj(function(file, enc, next) { + if (file.isNull()) return next(); + app.handle(stage, file, next); + }); } - // verb.helper('shield', function() { - // return 'https://img.shields.io/travis/USER/REPO.svg'; - // }); + verb.task('readme', function(cb) { + verb.ask(function(err, answers) { + if (err) return cb(err); - verb.data({ - badge: { - travis: '[![Build Status](https://img.shields.io/travis/{%= repo %}.svg)](https://travis-ci.org/{%= repo %})', - npm: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', - coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= repo %}.svg)](https://coveralls.io/r/{%= repo %})' - }, + verb.toStream('docs', function(key) { + return key === '.verb'; + }) + .on('error', cb) + .pipe(verb.renderFile('text', answers)) + .pipe(handle('onStream')) + .on('error', cb) + .pipe(app.pipeline(plugins)) + .pipe(handle('preWrite')) + .on('error', cb) + .pipe(verb.dest(dest('readme.md'))) + .pipe(utils.exhaust(handle('postWrite'))) + .on('error', cb) + .on('finish', cb); + }); }); - verb.data({license: 'Released under the MIT license.'}); - - verb.questions.set('author.twitter', 'Author\'s twitter username?'); - if (argv.init) { - verb.questions.options.forceAll = true; - } - verb.task('readme', function(cb) { verb.ask(function(err, answers) { if (err) return cb(err); @@ -99,7 +51,7 @@ module.exports = function(verb, base, env) { .pipe(verb.renderFile('text', answers)) .pipe(verb.dest(dest('readme.md'))) .on('finish', cb); - }); + }); }); verb.task('docs', function(cb) { @@ -116,14 +68,14 @@ module.exports = function(verb, base, env) { }); verb.register('store', function(app, base) { - app.task('del', function() { + app.task('del', function(cb) { verb.store.del({force: true}); console.log('deleted store.'); cb(); }); }); - verb.task('default', ['readme']); + verb.task('default', tasks); }; /** From 08588be59009c209f54b020d55b21ca20dc58cb7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Dec 2015 23:33:39 -0500 Subject: [PATCH 027/282] update defaults, get everything working --- cli.js | 23 ----- docs/src/readme/layouts/default.md | 9 +- index.js | 21 +++-- lib/badges.js | 4 +- lib/cli.js | 25 +++-- lib/config.js | 141 ++++++++++++++++++++++++++--- lib/data.js | 25 +++-- lib/defaults.js | 2 - lib/helpers.js | 16 ++-- lib/includes.js | 12 +++ lib/middleware.js | 4 - lib/utils.js | 38 +++++++- package.json | 4 +- readme.md | 13 ++- verbfile.js | 60 +++++++----- 15 files changed, 278 insertions(+), 119 deletions(-) diff --git a/cli.js b/cli.js index ac0c22ee..088e2f71 100755 --- a/cli.js +++ b/cli.js @@ -1,7 +1,6 @@ #!/usr/bin/env node var path = require('path'); -var mapCommands = require('map-config'); var processArgv = require('base-argv').processArgv(); var minimist = require('minimist'); var utils = require('./lib/utils'); @@ -12,10 +11,6 @@ var args = minimist(process.argv.slice(2), { alias: {verbose: 'v'} }); -Verb.use(function(app) { - app.set('cache.argv', processArgv(args)); -}); - // register `runner` as a mixin Verb.mixin(utils.runner('verb', 'verbApp')); @@ -28,24 +23,6 @@ Verb.mixin(utils.runner('verb', 'verbApp')); var base = Verb.getConfig('verbfile.js', __dirname); -/** - * Custom base-cli command mappings. - */ - -var cli = mapCommands(base) - .map('save', function(val) { - console.log(val) - }) - .map('data', function(val) { - app.visit('data', val); - }) - .map('cwd', function(fp) { - app.option('cwd', fp); - }); - -var argv = base.processArgv(args); -cli.process(argv.options); - /** * Resolve config files (`verbfile.js`) */ diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index 4bd88fd6..2d5efd94 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -1,15 +1,18 @@ -# {%= name %} {%= badge.npm %} {%= badge.travis %} +# {%= name %} {%= badge('npm') %} {%= badge('travis') %} > {%= description %} +{% if (typeof options !== 'undefined' && options.toc) { %} + +{% } %} ## Install {%= include("install-npm", {save: true}) %} ## Usage {% body %} -{% if (verb && verb.related && verb.related.list) { %} +{% if (verb && verb.related && verb.related.list && verb.related.list.length) { %} ## Related projects -{%= related((verb.related && verb.related.list) || []) %} +{%= related(verb.related.list) %} {% } %} ## Running tests {%= include("tests") %} diff --git a/index.js b/index.js index a4f6218a..8e0f0d8e 100644 --- a/index.js +++ b/index.js @@ -29,22 +29,27 @@ function Verb(options) { this.isVerb = true; this.verbApps = {}; - config(this); - cli(this); - - this.use(utils.middleware()) - .use(utils.pipeline()) - .use(utils.loader()) + var opts = this.options; + this.use(utils.middleware(opts)) + .use(utils.pipeline(opts)) .use(utils.config()) + .use(utils.loader()) .use(utils.store()) .use(templates()) - .use(utils.ask()); + .use(utils.ask()) + + config(this); + cli(this); - defaults(this, this.base, this.env); this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] }); + this.on('prebuild', function(name, task, app) { + var pkg = app.get('env.user.pkg') || {}; + app.config.process(pkg.verb); + }); + this.on('register', function(name, app) { // bubble up errors to `base` instance defaults(app, app.base, app.env); diff --git a/lib/badges.js b/lib/badges.js index 9b12863e..1f022177 100644 --- a/lib/badges.js +++ b/lib/badges.js @@ -1,8 +1,8 @@ 'use strict'; module.exports = { - travis: '[![Build Status](https://img.shields.io/travis/{%= repo %}.svg)](https://travis-ci.org/{%= repo %})', + travis: '[![Build Status](https://img.shields.io/travis/{%= author.username %}/{%= name %}.svg)](https://travis-ci.org/{%= author.username %}/{%= name %})', fury: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', npm: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', - coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= repo %}.svg)](https://coveralls.io/r/{%= repo %})' + coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= author.username %}/{%= name %}.svg)](https://coveralls.io/r/{%= author.username %}/{%= name %})' }; diff --git a/lib/cli.js b/lib/cli.js index e339acd4..5540a8e0 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,20 +1,17 @@ 'use strict'; -var utils = require('./utils'); - module.exports = function(app) { - // console.log(app.cli) - // app.cli - // .map('save', function(key, val) { - // console.log(key, val) - // }) - // .map('data', function(val) { - // app.visit('data', val); - // }) - // .map('cwd', function(fp) { - // app.option('cwd', fp); - // }); + app.cli + .map('save', function(key, val) { + console.log(key, val) + }) + .map('data', function(val) { + app.visit('data', val); + }) + .map('cwd', function(fp) { + app.option('cwd', fp); + }); - // app.define('commands', app.cli.keys); + app.define('commands', app.cli.keys); }; diff --git a/lib/config.js b/lib/config.js index b78f15f8..3b239e30 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,27 +1,138 @@ 'use strict'; +// require('time-require'); +var path = require('path'); var utils = require('./utils'); -module.exports = function (app) { +module.exports = function(app) { if (!app.isVerb) return; - app.use(utils.config()); + + /** + * Helpers + */ app.config - .map('collections', function(val) { - app.visit('create', val); - }) - .map('addViews') - .map('plugins') .map('helpers') - .map('asyncHelpers') - .map('data', function(val) { - app.visit('data', val); + .map('asyncHelpers'); + + /** + * Templates + */ + + app.config + .map('create', function(val) { + app.visit('create', val); }) - .map('reflinks', function(val) { - app.data({reflinks: val}); + .map('includes', views('includes')) + .map('layouts', views('layouts')) + .map('badges', views('badges')) + .map('docs', views('docs')); + + /** + * Middleware + */ + + app.config + .map('onLoad', function(fn) { + app.onLoad(fn) }) - .map('related', function(val) { - app.data({related: val}); - }); + /** + * Plugins + */ + + app.config.map('plugins', function(plugins) { + var obj = app.get('env.argv.plugins'); + var cwd = app.get('env.user.cwd'); + + + for (var key in plugins) { + var name = path.basename(key, path.extname(key)); + if (name === 'index') { + name = path.basename(cwd); + } + + var val = plugins[key]; + var fn; + + if (typeof val === 'function') { + app.plugin(name, {}, val); + + } else if (typeof val === 'string') { + fn = tryRequire(val, cwd, app.options); + app.plugin(name, {}, fn); + + } else if (val && typeof val === 'object') { + fn = tryRequire(key, cwd, app.options); + app.plugin(name, val, fn); + + } else { + var args = JSON.stringify([].slice.call(arguments)); + throw new Error('plugins configuration is not supported: ' + args); + } + } + }); + + // helper for loading views + function views(name) { + return function(options) { + app[name](options); + } + } }; + +function content(val) { + return typeof val === 'string' + ? { content: val } + : val +} + +/** + * Try to require the given module + * or file path. + */ + +function tryRequire(name, cwd, options) { + options = options || {}; + var attempts = [name], fp; + + try { + return require(name); + } catch (err) {} + + name = utils.resolveDir(name); + try { + return require(name); + } catch (err) {} + + try { + fp = path.resolve(name); + attempts.push(fp); + return require(fp); + } catch (err) {} + + try { + fp = path.resolve(utils.resolveDir(cwd), name); + attempts.push(fp); + return require(fp); + } catch (err) {} + + if (options.verbose !== true) { + return; + } + + var msg = utils.red('[base-pipeline] cannot find plugin at: \n') + + format(attempts) + + utils.colors.yellow(' check your configuration to ensure plugin paths are\n' + + ' correct (package.json config, options, etc) \n'); + + throw new Error(msg); +} + +function format(arr) { + var res = ''; + arr.forEach(function(ele) { + res += utils.colors.red(' ✖ ') + '\'' + ele + '\'' + '\n'; + }); + return res; +} diff --git a/lib/data.js b/lib/data.js index 617285df..18dcd3e9 100644 --- a/lib/data.js +++ b/lib/data.js @@ -1,26 +1,23 @@ 'use strict'; -module.exports = function(app, base, env) { - app.data({ +module.exports = function(verb, base, env) { + verb.data({ runner: { name: 'verb', url: 'https://github.com/verbose/verb' } }); - var name = app.name; + var name = verb.name; var user = base.store.get('author.username'); - app.data({repo: user + '/' + name}); + verb.data({repo: user + '/' + name}); - if (!app.cache.data.app) app.data({verb: {}}); - if (!app.cache.data.hasOwnProperty('description')) { - app.data(app.get('env.user.pkg') || {}); - } - - // app.helper('shield', function() { - // return 'https://img.shields.io/travis/USER/REPO.svg'; - // }); + verb.data({license: 'Released under the MIT license.'}); + verb.questions.set('author.twitter', 'Author\'s twitter username?'); - app.data({license: 'Released under the MIT license.'}); - app.questions.set('author.twitter', 'Author\'s twitter username?'); + if (!verb.cache.data.verb) verb.data({verb: {}}); + if (!verb.cache.data.hasOwnProperty('description')) { + var pkg = verb.get('env.user.pkg') || {}; + verb.data(pkg); + } }; diff --git a/lib/defaults.js b/lib/defaults.js index c1a60807..15b981b1 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -2,11 +2,9 @@ var middleware = require('./middleware'); var helpers = require('./helpers'); -var utils = require('./utils'); var data = require('./data'); module.exports = function(app, base, env) { - var plugins = base.get('env.argv.plugins'); middleware(app, base, env); helpers(app, base, env); data(app, base, env); diff --git a/lib/helpers.js b/lib/helpers.js index c0882dca..8993827a 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -2,19 +2,23 @@ var utils = require('./utils'); -module.exports = function(app, base, env) { - app.asyncHelper('apidocs', function(name, cb) { +module.exports = function(verb, base, env) { + verb.asyncHelper('apidocs', function(name, cb) { console.log(arguments) cb(null, ''); }); - app.asyncHelper('related', utils.related({verbose: true})); - app.asyncHelper('reflinks', utils.reflinks({verbose: true})); - app.helper('copyright', require('helper-copyright')({ + verb.helper('shield', function(name) { + return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; + }); + + verb.asyncHelper('related', utils.related({verbose: true})); + verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); + verb.helper('copyright', require('helper-copyright')({ linkify: true })); - app.helper('date', function() { + verb.helper('date', function() { return new Date(); }); }; diff --git a/lib/includes.js b/lib/includes.js index 61e40ed2..06d051c5 100644 --- a/lib/includes.js +++ b/lib/includes.js @@ -8,6 +8,15 @@ module.exports = { '$ npm i {%= name %} --save', '```' ].join('\n'), + + 'install-bower.md': [ + 'Install with [bower](http://bower.io/)', + '', + '```sh', + '$ bower install {%= name %} --save', + '```', + ].join('\n'), + 'install-global.md': [ 'Install globally with [npm](https://www.npmjs.com/)', '', @@ -15,6 +24,7 @@ module.exports = { '$ npm i -g {%= name %}', '```' ].join('\n'), + 'tests.md': [ 'Install dev dependencies:', '', @@ -22,12 +32,14 @@ module.exports = { '$ npm i -d && npm test', '```' ].join('\n'), + 'author.md': [ '**{%= author.name %}**', '', '+ [github/{%= author.username %}](https://github.com/{%= author.username %})', '+ [twitter/{%= author.twitter %}](http://twitter.com/{%= author.twitter %})' ].join('\n'), + 'contributing.md': 'Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= author.username %}/{%= name %}/issues/new).', 'footer.md': '_This file was generated by [{%= runner.name %}]({%= runner.url %}) on {%= date() %}._' }; diff --git a/lib/middleware.js b/lib/middleware.js index c64b5962..4ac90b85 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -3,10 +3,6 @@ var utils = require('./utils'); module.exports = function(app, base, env) { - app.on('error', function(err) { - console.log(err.stack); - }); - app.onLoad(/\.verb\.md/, function(file, next) { file.path = 'readme.md'; next(); diff --git a/lib/utils.js b/lib/utils.js index ddecd02c..3299e729 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,11 +31,13 @@ require('base-store', 'store'); // streams require('stream-exhaust', 'exhaust'); require('extend-shallow', 'extend'); +require('through2', 'through'); require('get-value', 'get'); require('matched', 'glob'); require('is-valid-glob'); -require('through2'); +require('resolve-dir'); require('has-glob'); +require('resolve'); require('async'); // cli @@ -84,6 +86,18 @@ utils.tryReaddir = function(dir) { return []; }; +/** + * Try to read a directory + */ + +utils.tryResolve = function(fp, cwd) { + try { + cwd = utils.resolveDir(cwd || process.cwd()); + return utils.resolve.sync(fp, { basedir: cwd }); + } catch (err) {} + return null; +}; + /** * Try to require a file */ @@ -99,6 +113,28 @@ utils.tryRequire = function(name) { return {}; }; +/** + * Resolve module the module to use from the given cwd. + * + * @param {String} `name` + * @return {String|Null} + */ + +utils.resolveModule = function(name, cwd) { + if (typeof name === 'undefined') { + throw new TypeError('expected name to be a string'); + } + name = utils.resolveDir(name); + if (cwd && path.basename(cwd) === name) { + var fp = utils.tryResolve(cwd); + if (fp) return fp; + } + + var main = utils.tryResolve(name, cwd) + || utils.tryResolve(name, utils.gm); + return main; +}; + /** * Expose `utils` modules */ diff --git a/package.json b/package.json index 3c4c4ad4..1987b3da 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,10 @@ "helper-related": "^0.12.1", "is-valid-glob": "^0.3.0", "lazy-cache": "^0.2.4", - "map-config": "^0.3.0", "matched": "^0.3.2", "minimist": "^1.2.0", + "resolve": "^1.1.6", + "resolve-dir": "^0.1.0", "stream-exhaust": "^1.0.1", "success-symbol": "^0.1.0", "time-stamp": "^0.1.3" @@ -79,6 +80,7 @@ "sinon": "^1.17.2", "swig": "^1.4.2", "through2": "^2.0.0", + "time-require": "github:jonschlinkert/time-require", "vinyl": "^1.1.0" }, "keywords": [ diff --git a/readme.md b/readme.md index c6329428..3667234e 100644 --- a/readme.md +++ b/readme.md @@ -44,7 +44,7 @@ Install globally with [npm](https://www.npmjs.com/) $ npm i -g verb ``` -**Install a app** +**Install a verb app** If you aren't familiar with verb, just take the `node` app for a test drive: @@ -349,21 +349,25 @@ Pull requests and stars are always welcome. For bugs and feature requests, [plea ## License Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) -Released under the MIT license. +MIT *** -_This file was generated by [verb](https://github.com/verbose/verb) on Sat Dec 05 2015 10:37:11 GMT-0500 (EST)._ +_This file was generated by [verb](https://github.com/verbose/verb) on Sat Dec 05 2015 16:57:40 GMT-0500 (EST)._ [ansi-colors]: https://github.com/doowb/ansi-colors [assemble-core]: https://github.com/assemble/assemble-core [assemble-loader]: https://github.com/jonschlinkert/assemble-loader +[base-argv]: https://github.com/jonschlinkert/base-argv +[base-config]: https://github.com/jonschlinkert/base-config +[base-pipeline]: https://github.com/jonschlinkert/base-pipeline [base-questions]: https://github.com/jonschlinkert/base-questions [base-runner]: https://github.com/jonschlinkert/base-runner [base-store]: https://github.com/jonschlinkert/base-store [common-middleware]: https://github.com/jonschlinkert/common-middleware [engine-base]: https://github.com/jonschlinkert/engine-base [extend-shallow]: https://github.com/jonschlinkert/extend-shallow +[get-value]: https://github.com/jonschlinkert/get-value [has-glob]: https://github.com/jonschlinkert/has-glob [helper-copyright]: https://github.com/helpers/helper-copyright [helper-reflinks]: https://github.com/helpers/helper-reflinks @@ -372,6 +376,9 @@ _This file was generated by [verb](https://github.com/verbose/verb) on Sat Dec 0 [lazy-cache]: https://github.com/jonschlinkert/lazy-cache [matched]: https://github.com/jonschlinkert/matched [minimist]: https://github.com/substack/minimist +[node-resolve]: https://github.com/substack/node-resolve +[resolve-dir]: https://github.com/jonschlinkert/resolve-dir +[stream-exhaust]: https://github.com/chrisdickinson/stream-exhaust [success-symbol]: https://github.com/jonschlinkert/success-symbol [time-stamp]: https://github.com/jonschlinkert/time-stamp diff --git a/verbfile.js b/verbfile.js index df7174d1..c102dbb0 100644 --- a/verbfile.js +++ b/verbfile.js @@ -2,46 +2,51 @@ var path = require('path'); var utils = require('./lib/utils'); +var defaults = require('./lib/defaults'); var argv = require('minimist')(process.argv.slice(2), { alias: {v: 'verbose'} }); module.exports = function(verb, base, env) { + defaults(verb, base, env); + if (argv.init) { - app.questions.options.forceAll = true; + verb.questions.options.forceAll = true; } - var tasks = ['readme']; - function handle(stage) { return utils.through.obj(function(file, enc, next) { if (file.isNull()) return next(); - app.handle(stage, file, next); + verb.handle(stage, file, next); }); } - verb.task('readme', function(cb) { - verb.ask(function(err, answers) { - if (err) return cb(err); + // verb.task('readme', function(cb) { + // verb.ask(function(err, answers) { + // if (err) return cb(err); - verb.toStream('docs', function(key) { - return key === '.verb'; - }) - .on('error', cb) - .pipe(verb.renderFile('text', answers)) - .pipe(handle('onStream')) - .on('error', cb) - .pipe(app.pipeline(plugins)) - .pipe(handle('preWrite')) - .on('error', cb) - .pipe(verb.dest(dest('readme.md'))) - .pipe(utils.exhaust(handle('postWrite'))) - .on('error', cb) - .on('finish', cb); - }); - }); + // verb.toStream('docs', function(key) { + // return key === '.verb'; + // }) + // .on('error', cb) + // .pipe(verb.renderFile('text', answers)) + // .pipe(handle('onStream')) + // .on('error', cb) + // .pipe(verb.pipeline(plugins)) + // .pipe(handle('preWrite')) + // .on('error', cb) + // .pipe(verb.dest(dest('readme.md'))) + // .pipe(utils.exhaust(handle('postWrite'))) + // .on('error', cb) + // .on('finish', cb); + // }); + // });` + + var tasks = verb.get('env.argv.tasks') || ['readme']; verb.task('readme', function(cb) { + var plugins = verb.get('env.argv.plugins') || verb.plugins; + verb.ask(function(err, answers) { if (err) return cb(err); @@ -49,7 +54,16 @@ module.exports = function(verb, base, env) { return key === '.verb'; }) .pipe(verb.renderFile('text', answers)) + .on('error', function(err) { + var m = /(\w+) is not a function/.exec(err.message); + if (m) console.log(err.message); + }) + .pipe(handle('onStream')) + .pipe(verb.pipeline(plugins)) + .pipe(handle('preWrite')) + .on('error', cb) .pipe(verb.dest(dest('readme.md'))) + .pipe(utils.exhaust(handle('postWrite'))) .on('finish', cb); }); }); From 3e3c1ae3eb8d4562316fbdd43e5cce83e4b7580c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Dec 2015 00:10:48 -0500 Subject: [PATCH 028/282] update `files` prop --- index.js | 2 +- package.json | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 8e0f0d8e..60647f3e 100644 --- a/index.js +++ b/index.js @@ -50,8 +50,8 @@ function Verb(options) { app.config.process(pkg.verb); }); + // bubble up errors to `base` instance, set defaults this.on('register', function(name, app) { - // bubble up errors to `base` instance defaults(app, app.base, app.env); app.use(templates()); }); diff --git a/package.json b/package.json index 1987b3da..3fb3cdb8 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "license": "MIT", "files": [ "lib/", + "cli.js", + "verbfile.js", "index.js" ], "main": "index.js", From 4bb07acfab7d7395621589ba0cce47cbfb52ba27 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Dec 2015 20:00:01 -0500 Subject: [PATCH 029/282] move runner-specific files into `lib/runner` --- lib/cli.js | 17 ----------------- lib/{ => runner}/badges.js | 0 lib/runner/cli.js | 21 +++++++++++++++++++++ lib/{ => runner}/data.js | 0 lib/{ => runner}/defaults.js | 0 lib/{ => runner}/helpers.js | 2 +- lib/{ => runner}/includes.js | 0 lib/runner/index.js | 1 + lib/{ => runner}/middleware.js | 0 lib/runner/runner.js | 28 ++++++++++++++++++++++++++++ lib/{ => runner}/templates.js | 0 11 files changed, 51 insertions(+), 18 deletions(-) delete mode 100644 lib/cli.js rename lib/{ => runner}/badges.js (100%) create mode 100644 lib/runner/cli.js rename lib/{ => runner}/data.js (100%) rename lib/{ => runner}/defaults.js (100%) rename lib/{ => runner}/helpers.js (95%) rename lib/{ => runner}/includes.js (100%) create mode 100644 lib/runner/index.js rename lib/{ => runner}/middleware.js (100%) create mode 100644 lib/runner/runner.js rename lib/{ => runner}/templates.js (100%) diff --git a/lib/cli.js b/lib/cli.js deleted file mode 100644 index 5540a8e0..00000000 --- a/lib/cli.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.cli - .map('save', function(key, val) { - console.log(key, val) - }) - .map('data', function(val) { - app.visit('data', val); - }) - .map('cwd', function(fp) { - app.option('cwd', fp); - }); - - app.define('commands', app.cli.keys); -}; - diff --git a/lib/badges.js b/lib/runner/badges.js similarity index 100% rename from lib/badges.js rename to lib/runner/badges.js diff --git a/lib/runner/cli.js b/lib/runner/cli.js new file mode 100644 index 00000000..2a45bb2b --- /dev/null +++ b/lib/runner/cli.js @@ -0,0 +1,21 @@ +'use strict'; + +module.exports = function(verb) { + if (!verb.cli) { + verb.use(require('base-cli')()); + } + + verb.cli + .map('save', function(key, val) { + console.log(key, val); + }) + .map('data', function(val) { + verb.visit('data', val); + }) + .map('cwd', function(fp) { + verb.option('cwd', fp); + }); + + verb.define('commands', verb.cli.keys); +}; + diff --git a/lib/data.js b/lib/runner/data.js similarity index 100% rename from lib/data.js rename to lib/runner/data.js diff --git a/lib/defaults.js b/lib/runner/defaults.js similarity index 100% rename from lib/defaults.js rename to lib/runner/defaults.js diff --git a/lib/helpers.js b/lib/runner/helpers.js similarity index 95% rename from lib/helpers.js rename to lib/runner/helpers.js index 8993827a..e0141274 100644 --- a/lib/helpers.js +++ b/lib/runner/helpers.js @@ -4,7 +4,7 @@ var utils = require('./utils'); module.exports = function(verb, base, env) { verb.asyncHelper('apidocs', function(name, cb) { - console.log(arguments) + console.log(arguments); cb(null, ''); }); diff --git a/lib/includes.js b/lib/runner/includes.js similarity index 100% rename from lib/includes.js rename to lib/runner/includes.js diff --git a/lib/runner/index.js b/lib/runner/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/runner/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/middleware.js b/lib/runner/middleware.js similarity index 100% rename from lib/middleware.js rename to lib/runner/middleware.js diff --git a/lib/runner/runner.js b/lib/runner/runner.js new file mode 100644 index 00000000..fe298347 --- /dev/null +++ b/lib/runner/runner.js @@ -0,0 +1,28 @@ +'use strict'; + +var utils = require('./utils'); +var templates = require('./templates'); +var defaults = require('./defaults'); +var config = require('./config'); +var cli = require('./cli'); + +module.exports = function(verb) { + verb.use(templates()); + verb.use(utils.ask()); + + config(verb); + cli(verb); + + defaults(verb, verb.base, verb.env); + + this.on('prebuild', function(name, task, app) { + var pkg = app.get('env.user.pkg') || {}; + app.config.process(pkg.app); + }); + + // bubble up errors to `base` instance, set defaults + this.on('register', function(name, app) { + defaults(app, app.base, app.env); + app.use(templates()); + }); +}; diff --git a/lib/templates.js b/lib/runner/templates.js similarity index 100% rename from lib/templates.js rename to lib/runner/templates.js From 1bf4f9eca7eca16b7ead4e2dd2ea9182f07100f9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Dec 2015 21:33:47 -0500 Subject: [PATCH 030/282] separate out `runner` code --- cli.js | 33 ++++- index.js | 311 ++++++++++++++++++++------------------- lib/config.js | 9 +- lib/runner/helpers.js | 6 +- lib/runner/index.js | 1 - lib/runner/middleware.js | 2 +- lib/runner/runner.js | 19 ++- lib/runner/templates.js | 2 +- package.json | 7 +- readme.md | 16 +- verbfile.js | 4 +- 11 files changed, 220 insertions(+), 190 deletions(-) delete mode 100644 lib/runner/index.js diff --git a/cli.js b/cli.js index 088e2f71..58810868 100755 --- a/cli.js +++ b/cli.js @@ -1,18 +1,36 @@ #!/usr/bin/env node var path = require('path'); +var gm = require('global-modules'); var processArgv = require('base-argv').processArgv(); var minimist = require('minimist'); +var runner = require('./lib/runner/runner'); var utils = require('./lib/utils'); -var Verb = require('./'); +var create = require('./').create; // parse argv var args = minimist(process.argv.slice(2), { alias: {verbose: 'v'} }); -// register `runner` as a mixin -Verb.mixin(utils.runner('verb', 'verbApp')); +/** + * Use Verb's static `create` method to pre-load CLI-specific + * plugins and middleware onto each instance created. In essence + * this creates a custom `Verb` constructor . + */ + +var Verb = create(runner); + +/** + * Register `runner` mixin with `Verb`, wich pre-loads + * CLI-specific methods onto our custom constructor. + * + * We also pass the `runner` function so that any user- + * defined `Verb` constructors will also be pre-loaded + * with our CLI plugins and middeware. + */ + +Verb.mixin(utils.runner('verb', 'verbApp', runner)); /** * Get the `base` instance of verb to use for @@ -24,10 +42,10 @@ Verb.mixin(utils.runner('verb', 'verbApp')); var base = Verb.getConfig('verbfile.js', __dirname); /** - * Resolve config files (`verbfile.js`) + * Resolve user config files, eg. `verbfile.js`. */ -base.resolve({pattern: 'verb-*/verbfile.js', cwd: '@/'}); +base.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); /** * Run verbApps and tasks @@ -44,6 +62,11 @@ base.cli.map('verbApps', function(verbApps) { } } + base.on('error', function(err) { + console.log(err); + process.exit(1); + }); + base.runVerbApps(verbApps, function(err) { if (err) return console.error(err); utils.timestamp('done'); diff --git a/index.js b/index.js index 60647f3e..733e8ff6 100644 --- a/index.js +++ b/index.js @@ -2,169 +2,180 @@ var async = require('async'); var Assemble = require('assemble-core'); -var templates = require('./lib/templates'); -var defaults = require('./lib/defaults'); +var templates = require('./lib/runner/templates'); +var defaults = require('./lib/runner/defaults'); +var runner = require('./lib/runner/runner'); var config = require('./lib/config'); var utils = require('./lib/utils'); -var cli = require('./lib/cli'); -/** - * Create an instance of `Verb` with the given `options` - * - * ```js - * var Verb = require('verb'); - * var verb = new Verb(options); - * ``` - * @param {Object} `options` Configuration options to initialize with. - * @api public - */ - -function Verb(options) { - if (!(this instanceof Verb)) { - return new Verb(options); +function create(runner) { + + /** + * Create an instance of `Verb` with the given `options` + * + * ```js + * var Verb = require('verb'); + * var verb = new Verb(options); + * ``` + * @param {Object} `options` Configuration options to initialize with. + * @api public + */ + + function Verb(options) { + if (!(this instanceof Verb)) { + return new Verb(options); + } + + Assemble.apply(this, arguments); + this.isVerb = true; + this.verbApps = {}; + + var opts = this.options; + this.name = opts.name || 'base'; + + this.use(utils.middleware(opts)) + .use(utils.pipeline(opts)) + .use(utils.config()) + .use(utils.loader()) + .use(utils.store()) + + this.engine(['md', 'text'], require('engine-base'), { + delims: ['{%', '%}'] + }); + + if (typeof runner === 'function') { + runner.call(this, this, this.base, this.env || {}); + } } - Assemble.apply(this, arguments); - this.name = this.options.name || 'base'; - this.isVerb = true; - this.verbApps = {}; - - var opts = this.options; - this.use(utils.middleware(opts)) - .use(utils.pipeline(opts)) - .use(utils.config()) - .use(utils.loader()) - .use(utils.store()) - .use(templates()) - .use(utils.ask()) - - config(this); - cli(this); - - this.engine(['md', 'text'], require('engine-base'), { - delims: ['{%', '%}'] - }); - - this.on('prebuild', function(name, task, app) { - var pkg = app.get('env.user.pkg') || {}; - app.config.process(pkg.verb); - }); - - // bubble up errors to `base` instance, set defaults - this.on('register', function(name, app) { - defaults(app, app.base, app.env); - app.use(templates()); + /** + * Inherit assemble-core + */ + + Assemble.extend(Verb); + + /** + * Similar to [copy](#copy) but calls a plugin `pipeline` if passed + * on the `options`. This allows plugin pipelines to be programmatically + * built-up and dynamically changed on-the-fly. + * + * ```js + * verb.process({src: ['a.txt', 'b.txt']}, options); + * ``` + * + * @param {Object} `files` + * @param {Object} `options` + * @param {Function} `cb` + * @return {Stream} Returns a [vinyl][] src stream + * @api public + */ + + Verb.prototype.process = function(files, options) { + options = options || {}; + files.options = files.options || {}; + var pipeline = files.options.pipeline || options.pipeline; + var opts = utils.extend({}, this.options, files.options, options); + + return this.src(files.src, opts) + .pipe(this.pipeline(pipeline, opts)) + .pipe(this.dest(files.dest, opts)); + }; + + /** + * Verb `files` configurations in parallel. + * + * ```js + * verb.each(files, function(err) { + * if (err) console.log(err); + * }); + * ``` + * @param {Object} `config` + * @param {Function} `cb` + * @api public + */ + + Verb.prototype.each = function(config, cb) { + async.each(config.files, function(files, next) { + this.process(files, files.options) + .on('error', next) + .on('end', next); + }.bind(this), cb); + return this; + }; + + /** + * Verb `files` configurations in series. + * + * ```js + * verb.eachSeries(files, function(err) { + * if (err) console.log(err); + * }); + * ``` + * @param {Object} `config` + * @param {Function} `cb` + * @api public + */ + + Verb.prototype.eachSeries = function(config, cb) { + async.eachSeries(config.files, function(files, next) { + this.process(files, files.options) + .on('error', next) + .on('end', next); + }.bind(this), cb); + }; + + /** + * Verb files from a declarative [scaffold][] configuration. + * + * ```js + * var Scaffold = require('scaffold'); + * var scaffold = new Scaffold({ + * options: {cwd: 'source'}, + * posts: { + * src: ['content/*.md'] + * }, + * pages: { + * src: ['templates/*.hbs'] + * } + * }); + * + * verb.scaffold(scaffold, function(err) { + * if (err) console.log(err); + * }); + * ``` + * @param {Object} `scaffold` Scaffold configuration + * @param {Function} `cb` Callback function + * @api public + */ + + Verb.prototype.scaffold = function(scaffold, cb) { + async.eachOf(scaffold, function(target, name, next) { + this.each(target, next); + }.bind(this), cb); + }; + + /** + * Get the `base` instance + */ + + Object.defineProperty(Verb.prototype, 'base', { + configurable: true, + get: function() { + return this.parent ? this.parent.base : this; + } }); -} - -/** - * Inherit assemble-core - */ - -Assemble.extend(Verb); - -/** - * Similar to [copy](#copy) but calls a plugin `pipeline` if passed - * on the `options`. This allows plugin pipelines to be programmatically - * built-up and dynamically changed on-the-fly. - * - * ```js - * verb.process({src: ['a.txt', 'b.txt']}, options); - * ``` - * - * @param {Object} `files` - * @param {Object} `options` - * @param {Function} `cb` - * @return {Stream} Returns a [vinyl][] src stream - * @api public - */ -Verb.prototype.process = function(files, options) { - options = options || {}; - files.options = files.options || {}; - var pipeline = files.options.pipeline || options.pipeline; - var opts = utils.extend({}, this.options, files.options, options); - - return this.src(files.src, opts) - .pipe(this.pipeline(pipeline, opts)) - .pipe(this.dest(files.dest, opts)); -}; - -/** - * Verb `files` configurations in parallel. - * - * ```js - * verb.each(files, function(err) { - * if (err) console.log(err); - * }); - * ``` - * @param {Object} `config` - * @param {Function} `cb` - * @api public - */ - -Verb.prototype.each = function(config, cb) { - async.each(config.files, function(files, next) { - this.process(files, files.options) - .on('error', next) - .on('end', next); - }.bind(this), cb); - return this; -}; - -/** - * Verb `files` configurations in series. - * - * ```js - * verb.eachSeries(files, function(err) { - * if (err) console.log(err); - * }); - * ``` - * @param {Object} `config` - * @param {Function} `cb` - * @api public - */ - -Verb.prototype.eachSeries = function(config, cb) { - async.eachSeries(config.files, function(files, next) { - this.process(files, files.options) - .on('error', next) - .on('end', next); - }.bind(this), cb); + return Verb; }; /** - * Verb files from a declarative [scaffold][] configuration. - * - * ```js - * var Scaffold = require('scaffold'); - * var scaffold = new Scaffold({ - * options: {cwd: 'source'}, - * posts: { - * src: ['content/*.md'] - * }, - * pages: { - * src: ['templates/*.hbs'] - * } - * }); - * - * verb.scaffold(scaffold, function(err) { - * if (err) console.log(err); - * }); - * ``` - * @param {Object} `scaffold` Scaffold configuration - * @param {Function} `cb` Callback function - * @api public + * Expose `Verb` */ -Verb.prototype.scaffold = function(scaffold, cb) { - async.eachOf(scaffold, function(target, name, next) { - this.each(target, next); - }.bind(this), cb); -}; +module.exports = create(); /** - * Expose `Verb` + * Expose `create` */ -module.exports = Verb; +module.exports.create = create; diff --git a/lib/config.js b/lib/config.js index 3b239e30..57336c4d 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,6 +1,5 @@ 'use strict'; -// require('time-require'); var path = require('path'); var utils = require('./utils'); @@ -34,8 +33,8 @@ module.exports = function(app) { app.config .map('onLoad', function(fn) { - app.onLoad(fn) - }) + app.onLoad(fn); + }); /** * Plugins @@ -77,14 +76,14 @@ module.exports = function(app) { function views(name) { return function(options) { app[name](options); - } + }; } }; function content(val) { return typeof val === 'string' ? { content: val } - : val + : val; } /** diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index e0141274..a5ac0e6f 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -1,6 +1,6 @@ 'use strict'; -var utils = require('./utils'); +var utils = require('../utils'); module.exports = function(verb, base, env) { verb.asyncHelper('apidocs', function(name, cb) { @@ -8,6 +8,10 @@ module.exports = function(verb, base, env) { cb(null, ''); }); + verb.helper('log', function(msg) { + console.log.apply(console, arguments); + }); + verb.helper('shield', function(name) { return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; }); diff --git a/lib/runner/index.js b/lib/runner/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/runner/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 4ac90b85..03ac426a 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -1,6 +1,6 @@ 'use strict'; -var utils = require('./utils'); +var utils = require('../utils'); module.exports = function(app, base, env) { app.onLoad(/\.verb\.md/, function(file, next) { diff --git a/lib/runner/runner.js b/lib/runner/runner.js index fe298347..5bf1120b 100644 --- a/lib/runner/runner.js +++ b/lib/runner/runner.js @@ -1,27 +1,26 @@ 'use strict'; -var utils = require('./utils'); +var utils = require('../utils'); +var config = require('../config'); var templates = require('./templates'); var defaults = require('./defaults'); -var config = require('./config'); var cli = require('./cli'); -module.exports = function(verb) { +module.exports = function preload(verb, base, env) { verb.use(templates()); verb.use(utils.ask()); + verb.use(config); + verb.use(cli); - config(verb); - cli(verb); + defaults(verb, base, env); - defaults(verb, verb.base, verb.env); - - this.on('prebuild', function(name, task, app) { + verb.on('prebuild', function(name, task, app) { var pkg = app.get('env.user.pkg') || {}; - app.config.process(pkg.app); + app.config.process(pkg.verb); }); // bubble up errors to `base` instance, set defaults - this.on('register', function(name, app) { + verb.on('register', function(name, app) { defaults(app, app.base, app.env); app.use(templates()); }); diff --git a/lib/runner/templates.js b/lib/runner/templates.js index 5dd97614..b0f785df 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -53,7 +53,7 @@ function loadDefaults(app) { if (loaded) return views; loaded = true; - var docs = path.join.bind(path, __dirname, '../docs/src/readme'); + var docs = path.join.bind(path, __dirname, '../../docs/src/readme'); app.includes.loadViews('*.md', {cwd: docs('includes')}); app.layouts.loadViews('*.md', {cwd: docs('layouts')}); app.docs.loadViews('*.md', {cwd: docs()}); diff --git a/package.json b/package.json index 3fb3cdb8..e4e19886 100644 --- a/package.json +++ b/package.json @@ -31,15 +31,17 @@ "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", + "base-cli": "^0.4.0", "base-config": "^0.3.2", "base-pipeline": "^0.1.4", - "base-questions": "^0.1.2", + "base-questions": "^0.1.3", "base-runner": "^0.4.1", "base-store": "^0.2.0", "common-middleware": "^0.1.3", "engine-base": "^0.1.2", "extend-shallow": "^2.0.1", "get-value": "^2.0.0", + "global-modules": "^0.2.0", "has-glob": "^0.1.1", "helper-copyright": "^2.0.0", "helper-reflinks": "^2.2.1", @@ -82,7 +84,6 @@ "sinon": "^1.17.2", "swig": "^1.4.2", "through2": "^2.0.0", - "time-require": "github:jonschlinkert/time-require", "vinyl": "^1.1.0" }, "keywords": [ @@ -111,4 +112,4 @@ ] } } -} +} \ No newline at end of file diff --git a/readme.md b/readme.md index 3667234e..fb4dd2bd 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,8 @@ -# verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/jonschlinkert/base.svg)](https://travis-ci.org/jonschlinkert/base) +# verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/jonschlinkert/verb.svg)](https://travis-ci.org/jonschlinkert/verb) > Documentation generator + ## Install Install with [npm](https://www.npmjs.com/) @@ -324,13 +325,6 @@ Use `verb-` as the prefix, followed by any words of your choosing to describe th [verb]: https://github.com/verbose/verb [vinyl]: http://github.com/gulpjs/vinyl -## Related projects -* [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) -* [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) -* [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) -* [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) -* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) - ## Running tests Install dev dependencies: @@ -349,16 +343,17 @@ Pull requests and stars are always welcome. For bugs and feature requests, [plea ## License Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) -MIT +Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on Sat Dec 05 2015 16:57:40 GMT-0500 (EST)._ +_This file was generated by [verb](https://github.com/verbose/verb) on Sun Dec 06 2015 21:33:05 GMT-0500 (EST)._ [ansi-colors]: https://github.com/doowb/ansi-colors [assemble-core]: https://github.com/assemble/assemble-core [assemble-loader]: https://github.com/jonschlinkert/assemble-loader [base-argv]: https://github.com/jonschlinkert/base-argv +[base-cli]: https://github.com/jonschlinkert/base-cli [base-config]: https://github.com/jonschlinkert/base-config [base-pipeline]: https://github.com/jonschlinkert/base-pipeline [base-questions]: https://github.com/jonschlinkert/base-questions @@ -368,6 +363,7 @@ _This file was generated by [verb](https://github.com/verbose/verb) on Sat Dec 0 [engine-base]: https://github.com/jonschlinkert/engine-base [extend-shallow]: https://github.com/jonschlinkert/extend-shallow [get-value]: https://github.com/jonschlinkert/get-value +[global-modules]: https://github.com/jonschlinkert/global-modules [has-glob]: https://github.com/jonschlinkert/has-glob [helper-copyright]: https://github.com/helpers/helper-copyright [helper-reflinks]: https://github.com/helpers/helper-reflinks diff --git a/verbfile.js b/verbfile.js index c102dbb0..f36b3692 100644 --- a/verbfile.js +++ b/verbfile.js @@ -2,14 +2,12 @@ var path = require('path'); var utils = require('./lib/utils'); -var defaults = require('./lib/defaults'); +var defaults = require('./lib/runner/defaults'); var argv = require('minimist')(process.argv.slice(2), { alias: {v: 'verbose'} }); module.exports = function(verb, base, env) { - defaults(verb, base, env); - if (argv.init) { verb.questions.options.forceAll = true; } From ca849655fb71c01a479eb85ab34c4caaa7db5d0a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Dec 2015 21:35:07 -0500 Subject: [PATCH 031/282] rename variable --- cli.js | 14 +++++++------- readme.md | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli.js b/cli.js index 58810868..4c27797c 100755 --- a/cli.js +++ b/cli.js @@ -39,21 +39,21 @@ Verb.mixin(utils.runner('verb', 'verbApp', runner)); * or a globally installed module */ -var base = Verb.getConfig('verbfile.js', __dirname); +var verb = Verb.getConfig('verbfile.js', __dirname); /** * Resolve user config files, eg. `verbfile.js`. */ -base.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); +verb.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); /** * Run verbApps and tasks */ -base.cli.map('verbApps', function(verbApps) { +verb.cli.map('verbApps', function(verbApps) { if (!verbApps.length) { - if (base.tasks.hasOwnProperty('default')) { + if (verb.tasks.hasOwnProperty('default')) { verbApps = [{base: ['default']}]; } else { console.log(' no default task is defined.'); @@ -62,12 +62,12 @@ base.cli.map('verbApps', function(verbApps) { } } - base.on('error', function(err) { + verb.on('error', function(err) { console.log(err); process.exit(1); }); - base.runVerbApps(verbApps, function(err) { + verb.runVerbApps(verbApps, function(err) { if (err) return console.error(err); utils.timestamp('done'); }); @@ -77,4 +77,4 @@ base.cli.map('verbApps', function(verbApps) { * Process args */ -base.cli.process(args); +verb.cli.process(args); diff --git a/readme.md b/readme.md index fb4dd2bd..2f39a0fe 100644 --- a/readme.md +++ b/readme.md @@ -347,7 +347,7 @@ Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on Sun Dec 06 2015 21:33:05 GMT-0500 (EST)._ +_This file was generated by [verb](https://github.com/verbose/verb) on Sun Dec 06 2015 21:35:43 GMT-0500 (EST)._ [ansi-colors]: https://github.com/doowb/ansi-colors [assemble-core]: https://github.com/assemble/assemble-core From 2e191be8f1da9366f758131a74c106946ba68022 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Dec 2015 23:28:15 -0500 Subject: [PATCH 032/282] organize code in runner files --- lib/runner/create.js | 28 ++++++++++++++++++++++++++++ lib/runner/data.js | 13 ++++++------- lib/runner/defaults.js | 4 +++- lib/runner/helpers.js | 7 ++----- lib/runner/questions.js | 5 +++++ lib/runner/runner.js | 14 +++++--------- lib/runner/templates.js | 17 ++--------------- 7 files changed, 51 insertions(+), 37 deletions(-) create mode 100644 lib/runner/create.js create mode 100644 lib/runner/questions.js diff --git a/lib/runner/create.js b/lib/runner/create.js new file mode 100644 index 00000000..95b8bb54 --- /dev/null +++ b/lib/runner/create.js @@ -0,0 +1,28 @@ +'use strict'; + +var path = require('path'); + +/** + * Create default template collections + */ + +module.exports = function(options) { + return function(app) { + create(app, 'docs', 'renderable'); + create(app, 'badges', 'partial'); + create(app, 'includes', 'partial'); + create(app, 'layouts', 'layout'); + }; +}; + +function create(app, name, type) { + // only create the collection if it doesn't already exist + if (app[name]) return; + + app.create(name, { + viewType: [type], + renameKey: function(key) { + return path.basename(key, path.extname(key)); + } + }); +} diff --git a/lib/runner/data.js b/lib/runner/data.js index 18dcd3e9..2efb8fca 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -8,16 +8,15 @@ module.exports = function(verb, base, env) { } }); - var name = verb.name; - var user = base.store.get('author.username'); - verb.data({repo: user + '/' + name}); - - verb.data({license: 'Released under the MIT license.'}); - verb.questions.set('author.twitter', 'Author\'s twitter username?'); + if (!verb.cache.data.verb) { + verb.data({verb: {}}); + } - if (!verb.cache.data.verb) verb.data({verb: {}}); if (!verb.cache.data.hasOwnProperty('description')) { var pkg = verb.get('env.user.pkg') || {}; verb.data(pkg); } + + var user = base.store.get('author.username'); + verb.data({license: 'Released under the MIT license.'}); }; diff --git a/lib/runner/defaults.js b/lib/runner/defaults.js index 15b981b1..83d7bd36 100644 --- a/lib/runner/defaults.js +++ b/lib/runner/defaults.js @@ -1,11 +1,13 @@ 'use strict'; var middleware = require('./middleware'); +var questions = require('./questions'); var helpers = require('./helpers'); var data = require('./data'); module.exports = function(app, base, env) { - middleware(app, base, env); helpers(app, base, env); + middleware(app, base, env); + questions(app, base, env); data(app, base, env); }; diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index a5ac0e6f..f1ee8f7c 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -4,7 +4,7 @@ var utils = require('../utils'); module.exports = function(verb, base, env) { verb.asyncHelper('apidocs', function(name, cb) { - console.log(arguments); + // console.log(arguments); cb(null, ''); }); @@ -16,13 +16,10 @@ module.exports = function(verb, base, env) { return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; }); + verb.helper('date', require('helper-date')); verb.asyncHelper('related', utils.related({verbose: true})); verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); verb.helper('copyright', require('helper-copyright')({ linkify: true })); - - verb.helper('date', function() { - return new Date(); - }); }; diff --git a/lib/runner/questions.js b/lib/runner/questions.js new file mode 100644 index 00000000..cfb3ec42 --- /dev/null +++ b/lib/runner/questions.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function(verb, base, env) { + verb.questions.set('author.twitter', 'Author\'s twitter username?'); +}; diff --git a/lib/runner/runner.js b/lib/runner/runner.js index 5bf1120b..f9c31f4f 100644 --- a/lib/runner/runner.js +++ b/lib/runner/runner.js @@ -7,21 +7,17 @@ var defaults = require('./defaults'); var cli = require('./cli'); module.exports = function preload(verb, base, env) { - verb.use(templates()); verb.use(utils.ask()); - verb.use(config); - verb.use(cli); + verb.use(utils.loader()); defaults(verb, base, env); - - verb.on('prebuild', function(name, task, app) { - var pkg = app.get('env.user.pkg') || {}; - app.config.process(pkg.verb); - }); + verb.use(templates()); + verb.use(config); + verb.use(cli); // bubble up errors to `base` instance, set defaults verb.on('register', function(name, app) { - defaults(app, app.base, app.env); + defaults(app, base, env); app.use(templates()); }); }; diff --git a/lib/runner/templates.js b/lib/runner/templates.js index b0f785df..92eae883 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -2,16 +2,14 @@ var fs = require('fs'); var path = require('path'); +var create = require('./create'); var includes = require('./includes'); var badges = require('./badges'); var loaded, views = {}; module.exports = function(options) { return function(app) { - create(app, 'docs', 'renderable'); - create(app, 'badges', 'partial'); - create(app, 'includes', 'partial'); - create(app, 'layouts', 'layout'); + app.use(create(options)); /** * DEPRECATED in the next release: Load includes and badges @@ -25,7 +23,6 @@ module.exports = function(options) { app.badge(key, {content: badges[key]}); } - var userDocs = path.join.bind(path, process.cwd()); if (fs.existsSync(userDocs())) { app.docs.loadViews('.verb.md', {cwd: userDocs()}); @@ -64,13 +61,3 @@ function loadDefaults(app) { views.docs = app.views.docs; return views; } - -function create(app, name, type) { - if (app[name]) return; - return app.create(name, { - viewType: [type], - renameKey: function(key) { - return path.basename(key, path.extname(key)); - } - }); -} From 916e6f3dafb54e5419c7d2a8a59e7243ec431b3b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 07:37:47 -0500 Subject: [PATCH 033/282] data docs --- docs/src/content/data.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/src/content/data.md diff --git a/docs/src/content/data.md b/docs/src/content/data.md new file mode 100644 index 00000000..e79df97b --- /dev/null +++ b/docs/src/content/data.md @@ -0,0 +1,39 @@ +--- +name: verb +title: Data +engine: hbs +description: "Learn how to get, set and delete data for templates, options and more." +reflinks: ['base-store', 'base-data'] +--- + +{{upper name}} keeps "data" on two different objects, depending on your needs. + +| **Storage object** | **Description** | **Methods**| +| --- | --- | --- | +| `{{name}}.cache.data` | Kept in-memory | `{{name}}.data()` (function) | +| `{{name}}.store.data` | Persisted to disk | `{{name}}.store` (object with methods) | + +## {{name}}.data + +```js +{{name}}.data({a: 'b'}); +// or +{{name}}.data('a', 'b'); +console.log({{name}}.cache.data); +//=> {a: 'b'} +``` + +The `{{name}}.data` method is powered by the [base-data][] plugin. Visit [that project][base-data] to see all available features and options. + +## {{name}}.store + +```js +{{name}}.store.set({a: 'b'}); +// or +{{name}}.store.set('a', 'b'); + +console.log({{name}}.store.data); +//=> {a: 'b'} +``` + +The `{{name}}.store` object is powered by the [base-store][] plugin. Visit [that project][base-store] to see all available features and options. From 04ee0f9d8005354c09b210fa221510172e74398d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 07:38:16 -0500 Subject: [PATCH 034/282] adds `app.env.set` method and `app.env.get` method for getting and setting env values --- lib/env.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/env.js diff --git a/lib/env.js b/lib/env.js new file mode 100644 index 00000000..e006edb1 --- /dev/null +++ b/lib/env.js @@ -0,0 +1,28 @@ +'use strict'; + +var utils = require('./utils'); + +/** + * Adds get/set methods to verb env + */ + +module.exports = function(options) { + return function(app) { + if (!app.env) { + app.env = { + config: {}, + module: {}, + user: {} + }; + } + + utils.define(app.env, 'set', function(key, value) { + utils.set(this, key, value); + return this; + }); + + utils.define(app.env, 'get', function(key) { + return utils.get(this, key); + }); + }; +}; From 3bb5937fe536733cb4f4a0ba3374d8d2807aab67 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 07:39:42 -0500 Subject: [PATCH 035/282] use variable for related list, move toc --- docs/src/readme/layouts/default.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index 2d5efd94..d650c609 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -2,17 +2,13 @@ > {%= description %} -{% if (typeof options !== 'undefined' && options.toc) { %} - -{% } %} ## Install {%= include("install-npm", {save: true}) %} - ## Usage {% body %} -{% if (verb && verb.related && verb.related.list && verb.related.list.length) { %} +{% var list = get("verb.related.list") || [] %}{% if (list.length) { %} ## Related projects -{%= related(verb.related.list) %} +{%= related(list) %} {% } %} ## Running tests {%= include("tests") %} @@ -31,4 +27,6 @@ {%= include("footer") %} -{%= reflinks(verb.reflinks || []) %} +{% if (verb && verb.reflinks && verb.reflinks.list && verb.reflinks.list.length) { %} +{%= reflinks(verb.reflinks) %} +{% } %} From 61c8d248bc97f60945a02917a23db44cadec5ab2 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:04:45 -0500 Subject: [PATCH 036/282] get plugins loading from verb config --- lib/config.js | 75 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/lib/config.js b/lib/config.js index 57336c4d..3be14762 100644 --- a/lib/config.js +++ b/lib/config.js @@ -3,14 +3,16 @@ var path = require('path'); var utils = require('./utils'); -module.exports = function(app) { - if (!app.isVerb) return; +module.exports = function(verb) { + if (!verb.isVerb) return; + + verb.config.map('option'); /** * Helpers */ - app.config + verb.config .map('helpers') .map('asyncHelpers'); @@ -18,9 +20,9 @@ module.exports = function(app) { * Templates */ - app.config + verb.config .map('create', function(val) { - app.visit('create', val); + verb.visit('create', val); }) .map('includes', views('includes')) .map('layouts', views('layouts')) @@ -31,19 +33,19 @@ module.exports = function(app) { * Middleware */ - app.config - .map('onLoad', function(fn) { - app.onLoad(fn); - }); + // verb.config + // .map('onLoad', function(fn) { + // verb.onLoad(fn); + // }); /** * Plugins */ - app.config.map('plugins', function(plugins) { - var obj = app.get('env.argv.plugins'); - var cwd = app.get('env.user.cwd'); - + verb.config.map('plugins', function(plugins) { + var obj = verb.get('env.argv.plugins'); + var cwd = verb.get('env.user.cwd'); + var opts = {}; for (var key in plugins) { var name = path.basename(key, path.extname(key)); @@ -51,35 +53,52 @@ module.exports = function(app) { name = path.basename(cwd); } - var val = plugins[key]; - var fn; - - if (typeof val === 'function') { - app.plugin(name, {}, val); + var plugin = plugins[key]; - } else if (typeof val === 'string') { - fn = tryRequire(val, cwd, app.options); - app.plugin(name, {}, fn); + if (typeof plugin === 'string') { + // use `name` + // resolve `plugin` - } else if (val && typeof val === 'object') { - fn = tryRequire(key, cwd, app.options); - app.plugin(name, val, fn); + } else if (utils.isObject(plugin)) { + opts = plugin; + plugin = utils.loadModule(name); + name = opts.name || name; + } - } else { - var args = JSON.stringify([].slice.call(arguments)); - throw new Error('plugins configuration is not supported: ' + args); + if (typeof plugin !== 'function') { + throw new Error('cannot load plugin: ' + plugin); } + + verb.plugin(name, opts, plugin); } }); // helper for loading views function views(name) { return function(options) { - app[name](options); + verb[name](options); }; } }; +function loadPlugin(verb, name, plugin) { + if (typeof plugin === 'function') { + verb.plugin(name, {}, plugin); + return true; + } + if (typeof plugin === 'string') { + fn = tryRequire(plugin, cwd, verb.options); + verb.plugin(name, {}, fn); + return true; + } + if (utils.isObject(plugin)) { + fn = tryRequire(key, cwd, verb.options); + verb.plugin(name, plugin, fn); + return true; + } + return false; +} + function content(val) { return typeof val === 'string' ? { content: val } From b3caf3f86f1db449f89fc92298f40f6fe6cd2015 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:05:08 -0500 Subject: [PATCH 037/282] make variables consistent --- lib/env.js | 10 ++++---- lib/runner/create.js | 16 ++++++------ lib/runner/defaults.js | 10 ++++---- lib/runner/templates.js | 56 +++++++++++++++++++++-------------------- 4 files changed, 47 insertions(+), 45 deletions(-) diff --git a/lib/env.js b/lib/env.js index e006edb1..a97247d6 100644 --- a/lib/env.js +++ b/lib/env.js @@ -7,21 +7,21 @@ var utils = require('./utils'); */ module.exports = function(options) { - return function(app) { - if (!app.env) { - app.env = { + return function(verb) { + if (!verb.env) { + verb.env = { config: {}, module: {}, user: {} }; } - utils.define(app.env, 'set', function(key, value) { + utils.define(verb.env, 'set', function(key, value) { utils.set(this, key, value); return this; }); - utils.define(app.env, 'get', function(key) { + utils.define(verb.env, 'get', function(key) { return utils.get(this, key); }); }; diff --git a/lib/runner/create.js b/lib/runner/create.js index 95b8bb54..5114727a 100644 --- a/lib/runner/create.js +++ b/lib/runner/create.js @@ -7,19 +7,19 @@ var path = require('path'); */ module.exports = function(options) { - return function(app) { - create(app, 'docs', 'renderable'); - create(app, 'badges', 'partial'); - create(app, 'includes', 'partial'); - create(app, 'layouts', 'layout'); + return function(verb) { + create(verb, 'docs', 'renderable'); + create(verb, 'badges', 'partial'); + create(verb, 'includes', 'partial'); + create(verb, 'layouts', 'layout'); }; }; -function create(app, name, type) { +function create(verb, name, type) { // only create the collection if it doesn't already exist - if (app[name]) return; + if (verb[name]) return; - app.create(name, { + verb.create(name, { viewType: [type], renameKey: function(key) { return path.basename(key, path.extname(key)); diff --git a/lib/runner/defaults.js b/lib/runner/defaults.js index 83d7bd36..9b573a92 100644 --- a/lib/runner/defaults.js +++ b/lib/runner/defaults.js @@ -5,9 +5,9 @@ var questions = require('./questions'); var helpers = require('./helpers'); var data = require('./data'); -module.exports = function(app, base, env) { - helpers(app, base, env); - middleware(app, base, env); - questions(app, base, env); - data(app, base, env); +module.exports = function(verb, base, env) { + helpers(verb, base, env); + middleware(verb, base, env); + questions(verb, base, env); + data(verb, base, env); }; diff --git a/lib/runner/templates.js b/lib/runner/templates.js index 92eae883..9090d54f 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -8,41 +8,43 @@ var badges = require('./badges'); var loaded, views = {}; module.exports = function(options) { - return function(app) { - app.use(create(options)); + return function(verb) { + verb.use(create(options)); /** * DEPRECATED in the next release: Load includes and badges */ - for (var key in includes) { - app.include(key, {content: includes[key]}); - } - - for (var key in badges) { - app.badge(key, {content: badges[key]}); - } - - var userDocs = path.join.bind(path, process.cwd()); - if (fs.existsSync(userDocs())) { - app.docs.loadViews('.verb.md', {cwd: userDocs()}); - - app.includes.loadViews('*.md', {cwd: userDocs('docs/includes')}); - app.layouts.loadViews('*.md', {cwd: userDocs('docs/layouts')}); - app.docs.loadViews('*.md', {cwd: userDocs('docs')}); - } - - var collections = loadDefaults(app); - if (collections) { - for (var key in collections) { - var collection = collections[key]; - for (var name in collection) { - if (!app.views[key][name]) { - app.views[key][name] = collection[name]; + verb.on('config-processed', function() { + for (var key in includes) { + verb.include(key, {content: includes[key]}); + } + + for (var key in badges) { + verb.badge(key, {content: badges[key]}); + } + + var userDocs = path.join.bind(path, process.cwd()); + if (fs.existsSync(userDocs())) { + verb.docs.loadViews('.verb.md', {cwd: userDocs()}); + + verb.includes.loadViews('*.md', {cwd: userDocs('docs/includes')}); + verb.layouts.loadViews('*.md', {cwd: userDocs('docs/layouts')}); + verb.docs.loadViews('*.md', {cwd: userDocs('docs')}); + } + + var collections = loadDefaults(verb); + if (collections) { + for (var key in collections) { + var collection = collections[key]; + for (var name in collection) { + if (!verb.views[key][name]) { + verb.views[key][name] = collection[name]; + } } } } - } + }); }; }; From c8122eca212aa5c924514d15c90e008f06d74e27 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:05:46 -0500 Subject: [PATCH 038/282] adds `ask` to cli.map --- lib/runner/cli.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/runner/cli.js b/lib/runner/cli.js index 2a45bb2b..061ecae8 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -2,10 +2,14 @@ module.exports = function(verb) { if (!verb.cli) { - verb.use(require('base-cli')()); + throw new Error('the base-cli plugin should be registered on every instance'); } verb.cli + .map('ask', function(key) { + verb.questions.enqueue(key); + verb.option('questions.init', key); + }) .map('save', function(key, val) { console.log(key, val); }) From 8f25e1667d7abe80af6dc63bf1377f0be02e4ef5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:05:59 -0500 Subject: [PATCH 039/282] remove redundant data --- lib/runner/data.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/runner/data.js b/lib/runner/data.js index 2efb8fca..5e0de8d4 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -7,16 +7,4 @@ module.exports = function(verb, base, env) { url: 'https://github.com/verbose/verb' } }); - - if (!verb.cache.data.verb) { - verb.data({verb: {}}); - } - - if (!verb.cache.data.hasOwnProperty('description')) { - var pkg = verb.get('env.user.pkg') || {}; - verb.data(pkg); - } - - var user = base.store.get('author.username'); - verb.data({license: 'Released under the MIT license.'}); }; From 1636e6d2cdfb8bab9dafb08d5fad0cc8640548a9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:06:08 -0500 Subject: [PATCH 040/282] adds `get` helper --- lib/runner/helpers.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index f1ee8f7c..5266ef3b 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -12,6 +12,10 @@ module.exports = function(verb, base, env) { console.log.apply(console, arguments); }); + verb.helper('get', function(key) { + return utils.get(this.context, key); + }); + verb.helper('shield', function(name) { return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; }); From 1a450cd1692420797cbbdab469faf3a741b648eb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:06:20 -0500 Subject: [PATCH 041/282] adds `toc` middleware --- lib/runner/middleware.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 03ac426a..1ac1071d 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -2,13 +2,18 @@ var utils = require('../utils'); -module.exports = function(app, base, env) { - app.onLoad(/\.verb\.md/, function(file, next) { +module.exports = function(verb, base, env) { + verb.preRender(/\.md/, function(file, next) { + verb.option('toc.noinsert', true); + utils.toc(verb)(file, next); + }); + + verb.onLoad(/\.verb\.md/, function(file, next) { file.path = 'readme.md'; next(); }); - app.preLayout(/(\.verb|readme)\.md/i, function(file, next) { + verb.preLayout(/(\.verb|readme)\.md/i, function(file, next) { if (needsLayout(file.content)) { file.layout = 'default'; } @@ -16,6 +21,7 @@ module.exports = function(app, base, env) { }); }; +// placeholder function needsLayout(str) { if (!/^# /.test(str)) { return true; From b5cc1114e404de16bcfc4ed90baa9900735a7f55 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:07:05 -0500 Subject: [PATCH 042/282] load data inside task, before rendering --- verbfile.js | 62 +++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/verbfile.js b/verbfile.js index f36b3692..26d5aaaf 100644 --- a/verbfile.js +++ b/verbfile.js @@ -19,47 +19,36 @@ module.exports = function(verb, base, env) { }); } - // verb.task('readme', function(cb) { - // verb.ask(function(err, answers) { - // if (err) return cb(err); - - // verb.toStream('docs', function(key) { - // return key === '.verb'; - // }) - // .on('error', cb) - // .pipe(verb.renderFile('text', answers)) - // .pipe(handle('onStream')) - // .on('error', cb) - // .pipe(verb.pipeline(plugins)) - // .pipe(handle('preWrite')) - // .on('error', cb) - // .pipe(verb.dest(dest('readme.md'))) - // .pipe(utils.exhaust(handle('postWrite'))) - // .on('error', cb) - // .on('finish', cb); - // }); - // });` - var tasks = verb.get('env.argv.tasks') || ['readme']; + /** + * Readme task + */ + verb.task('readme', function(cb) { var plugins = verb.get('env.argv.plugins') || verb.plugins; + // load package.json data and user options onto `verb.cache.data` + verb.data({options: verb.options}); + verb.data(env.user.pkg); + verb.data({license: 'Released under the MIT license.'}); + + // ask pre-configured questions, but only if they don't have + // answers yet verb.ask(function(err, answers) { - if (err) return cb(err); + if (err) { + cb(err); + return; + } verb.toStream('docs', function(key) { return key === '.verb'; }) - .pipe(verb.renderFile('text', answers)) - .on('error', function(err) { - var m = /(\w+) is not a function/.exec(err.message); - if (m) console.log(err.message); - }) .pipe(handle('onStream')) + .pipe(verb.renderFile('text', answers)) + .on('error', handleError) .pipe(verb.pipeline(plugins)) .pipe(handle('preWrite')) - .on('error', cb) .pipe(verb.dest(dest('readme.md'))) .pipe(utils.exhaust(handle('postWrite'))) .on('finish', cb); @@ -103,3 +92,20 @@ function dest(dest) { return file.base; }; } + +/** + * Handle render errors + */ + +function handleError(err) { + var m = /(\w+) is not a function/.exec(err.message); + console.log(m) + if (m) { + console.log('"' + m[1] + '" is a variable on `verb.cache.data`, but it is defined'); + console.log('as a helper in `.verb.md`') + } else if (app.options.verbose) { + console.log(err.stack); + } else { + console.log(err.message); + } +} From d7b2110fb652a862c741ab4cbf2eb7dbc8276c0a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:07:44 -0500 Subject: [PATCH 043/282] setup verb defaults --- index.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 733e8ff6..a744318c 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ 'use strict'; +var path = require('path'); var async = require('async'); var Assemble = require('assemble-core'); var templates = require('./lib/runner/templates'); @@ -7,6 +8,7 @@ var defaults = require('./lib/runner/defaults'); var runner = require('./lib/runner/runner'); var config = require('./lib/config'); var utils = require('./lib/utils'); +var env = require('./lib/env'); function create(runner) { @@ -31,20 +33,19 @@ function create(runner) { this.verbApps = {}; var opts = this.options; - this.name = opts.name || 'base'; + this.name = opts.name || 'verb'; this.use(utils.middleware(opts)) .use(utils.pipeline(opts)) - .use(utils.config()) - .use(utils.loader()) .use(utils.store()) + .use(env()) this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] }); if (typeof runner === 'function') { - runner.call(this, this, this.base, this.env || {}); + runner.call(this, this, this.base, this.env); } } @@ -169,13 +170,27 @@ function create(runner) { }; /** - * Expose `Verb` + * Expose `Verb` with our baseline defaults */ -module.exports = create(); +module.exports = create(function(verb) { + function renameKey(key) { + return path.basename(key, path.extname(key)); + } + verb.create('docs', { renameKey: renameKey }); + verb.create('includes', { + renameKey: renameKey, + viewType: ['partial'] + }); + verb.create('layouts', { + renameKey: renameKey, + viewType: ['layout'] + }); +}); /** - * Expose `create` + * Expose `create` to allow user to instantiate + * Verb with their own dafaults */ module.exports.create = create; From 4f5523586e4d5731a484d34a58feec4ffb7887fb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:21:30 -0500 Subject: [PATCH 044/282] get config/cli defaults setup --- cli.js | 24 ++++++++++----- index.js | 23 +++++++++----- lib/runner/runner.js | 5 ++-- lib/utils.js | 27 +++++++++++++---- test/questions.js | 71 +++++++++++++++++++++++++++++++------------- test/store.js | 26 ++++------------ verbfile.js | 14 ++++----- 7 files changed, 122 insertions(+), 68 deletions(-) diff --git a/cli.js b/cli.js index 4c27797c..78112ddd 100755 --- a/cli.js +++ b/cli.js @@ -41,6 +41,13 @@ Verb.mixin(utils.runner('verb', 'verbApp', runner)); var verb = Verb.getConfig('verbfile.js', __dirname); +// get `verb` property from package.json, if it exists +var userConfig = verb.get('env.user.pkg.verb'); +if (userConfig) { + verb.config.process(userConfig); + verb.emit('config-processed'); +} + /** * Resolve user config files, eg. `verbfile.js`. */ @@ -51,10 +58,10 @@ verb.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); * Run verbApps and tasks */ -verb.cli.map('verbApps', function(verbApps) { - if (!verbApps.length) { +verb.cli.map('verbApps', function(tasks) { + if (!tasks.length) { if (verb.tasks.hasOwnProperty('default')) { - verbApps = [{base: ['default']}]; + tasks = [{verb: ['default']}]; } else { console.log(' no default task is defined.'); utils.timestamp('done'); @@ -67,9 +74,12 @@ verb.cli.map('verbApps', function(verbApps) { process.exit(1); }); - verb.runVerbApps(verbApps, function(err) { - if (err) return console.error(err); - utils.timestamp('done'); + setImmediate(function() { + verb.runVerbApps(tasks, function(err) { + if (err) return console.error(err); + utils.timestamp('done'); + process.exit(0); + }); }); }); @@ -77,4 +87,4 @@ verb.cli.map('verbApps', function(verbApps) { * Process args */ -verb.cli.process(args); +verb.cli.processArgv(args); diff --git a/index.js b/index.js index a744318c..3483297d 100644 --- a/index.js +++ b/index.js @@ -3,12 +3,22 @@ var path = require('path'); var async = require('async'); var Assemble = require('assemble-core'); -var templates = require('./lib/runner/templates'); -var defaults = require('./lib/runner/defaults'); -var runner = require('./lib/runner/runner'); -var config = require('./lib/config'); var utils = require('./lib/utils'); -var env = require('./lib/env'); + +/** + * Create a customized `Verb` constructor that calls the given + * `fn` when an instance is created. + * + * ```js + * var Verb = create(function(verb) { + * // add stuff to `verb` + * }); + * var verb = new Verb(); + * ``` + * @param {Function} `fn` + * @return {Function} + * @api public + */ function create(runner) { @@ -38,7 +48,6 @@ function create(runner) { this.use(utils.middleware(opts)) .use(utils.pipeline(opts)) .use(utils.store()) - .use(env()) this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] @@ -190,7 +199,7 @@ module.exports = create(function(verb) { /** * Expose `create` to allow user to instantiate - * Verb with their own dafaults + * Verb with their own defaults */ module.exports.create = create; diff --git a/lib/runner/runner.js b/lib/runner/runner.js index f9c31f4f..e8efb0d9 100644 --- a/lib/runner/runner.js +++ b/lib/runner/runner.js @@ -12,8 +12,9 @@ module.exports = function preload(verb, base, env) { defaults(verb, base, env); verb.use(templates()); - verb.use(config); - verb.use(cli); + + config(verb); + cli(verb); // bubble up errors to `base` instance, set defaults verb.on('register', function(name, app) { diff --git a/lib/utils.js b/lib/utils.js index 3299e729..214fcef6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -25,14 +25,18 @@ require('assemble-loader', 'loader'); require('base-questions', 'ask'); require('base-pipeline', 'pipeline'); require('base-runner', 'runner'); -require('base-config', 'config'); require('base-store', 'store'); -// streams +// misc +require('define-property', 'define'); require('stream-exhaust', 'exhaust'); require('extend-shallow', 'extend'); +require('global-modules', 'gm'); +require('isobject', 'isObject'); require('through2', 'through'); +require('template-toc', 'toc'); require('get-value', 'get'); +require('set-value', 'set'); require('matched', 'glob'); require('is-valid-glob'); require('resolve-dir'); @@ -130,9 +134,22 @@ utils.resolveModule = function(name, cwd) { if (fp) return fp; } - var main = utils.tryResolve(name, cwd) - || utils.tryResolve(name, utils.gm); - return main; + return utils.tryResolve(name, cwd) + || utils.tryResolve(name, utils.gm) + || utils.tryResolve(name); +}; + +/** + * Try to resolve and load a module, either from the given + * cwd or from global npm packages. + * + * @param {String} `name` + * @return {String|Null} + */ + +utils.loadModule = function(name, cwd) { + var main = utils.resolveModule(name, cwd); + return main ? utils.tryRequire(main) : null; }; /** diff --git a/test/questions.js b/test/questions.js index 2441c787..cd336493 100644 --- a/test/questions.js +++ b/test/questions.js @@ -1,58 +1,89 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); var assert = require('assert'); var support = require('./support'); +var config = require('base-config'); +var ask = require('base-questions'); var App = support.resolve(); var app; -describe.skip('content', function () { - beforeEach(function () { +describe('content', function() { + beforeEach(function() { app = new App(); + app.use(config()); + app.use(ask()); + }); + + afterEach(function(cb) { + app.questions.del('a'); + cb(); }); - it('should store a question:', function () { + it('should store a question:', function() { app.question('a', 'b'); assert(app.questions); assert(app.questions.cache); assert(app.questions.cache.a); assert(app.questions.cache.a.name === 'a'); - assert(app.questions.cache.a.message === 'b'); + assert(app.questions.cache.a.options.message === 'b'); }); - it('should ask a question and use data value to answer:', function (done) { + it('should ask a question and use data value to answer:', function(cb) { app.question('a', 'b'); app.data('a', 'b'); - app.ask('a', function (err, answer) { + app.ask('a', function(err, answers) { assert(!err); - assert(answer); - assert(answer === 'b'); - done(); - }) + assert(answers.a === 'b'); + + app.data('a', 'zzz'); + app.ask('a', function(err, answers) { + assert(!err); + assert(answers.a === 'zzz'); + cb(); + }) + }); }); - it('should ask a question and use store value to answer:', function (done) { + it('should ask a question and use store value to answer:', function(cb) { app.question('a', 'b'); app.store.set('a', 'c'); - app.ask('a', function (err, answer) { + app.ask('a', function(err, answers) { assert(!err); - assert(answer); - assert(answer === 'c'); - done(); + assert(answers); + assert(answers.a === 'c'); + app.store.del('a'); + cb(); }) }); - it('should ask a question and use config value to answer:', function (done) { + it('should ask a question and use config value to answer:', function(cb) { app.question('a', 'b'); + app.config.process({data: {a: 'zzz'}}); + + app.ask('a', function(err, answer) { + assert(!err); + assert(answer); + assert(answer.a === 'zzz'); + cb(); + }); + }); + + it('should prefer data from config over store.data', function(cb) { + app.question('a', 'b'); + app.config.process({data: {a: 'zzz'}}); app.store.set('a', 'c'); - app.ask('a', function (err, answer) { + app.ask('a', function(err, answer) { assert(!err); assert(answer); - assert(answer === 'c'); - done(); - }) + assert(answer.a === 'zzz'); + app.store.del('a'); + cb(); + }); }); }); diff --git a/test/store.js b/test/store.js index 5791f579..c54b0fc5 100644 --- a/test/store.js +++ b/test/store.js @@ -9,7 +9,7 @@ var assert = require('assert'); var App = require('../'); var app; -describe.skip('store', function () { +describe('store', function () { beforeEach(function () { app = new App(); }); @@ -157,7 +157,7 @@ describe.skip('store', function () { }); }); -describe.skip('events', function () { +describe('events', function () { beforeEach(function () { app = new App(); app.store = new Store('verb-tests'); @@ -208,29 +208,15 @@ describe.skip('events', function () { keys.should.eql(['a', 'c']); }); - it.skip('should emit `del` when a value is deleted:', function () { + it('should emit `del` when a value is deleted:', function (cb) { app.store.on('del', function (keys) { - // keys.should.equal('a'); - // assert(typeof app.store.get('a') === 'undefined'); - // cb(); + keys.should.equal('a'); + assert(typeof app.store.get('a') === 'undefined'); + cb(); }); app.store.set('a', {b: 'c'}); app.store.get('a').should.eql({b: 'c'}); app.store.del('a'); }); - - it('should emit deleted keys on `del`:', function (cb) { - app.store.on('del', function (keys) { - keys.should.eql(['a', 'c', 'e']); - assert(Object.keys(app.store.data).length === 0); - cb(); - }); - - app.store.set('a', 'b'); - app.store.set('c', 'd'); - app.store.set('e', 'f'); - app.store.data.should.have.properties(['a', 'c', 'e']); - app.store.del({force: true}); - }); }); diff --git a/verbfile.js b/verbfile.js index 26d5aaaf..b8f13beb 100644 --- a/verbfile.js +++ b/verbfile.js @@ -12,13 +12,6 @@ module.exports = function(verb, base, env) { verb.questions.options.forceAll = true; } - function handle(stage) { - return utils.through.obj(function(file, enc, next) { - if (file.isNull()) return next(); - verb.handle(stage, file, next); - }); - } - var tasks = verb.get('env.argv.tasks') || ['readme']; /** @@ -77,6 +70,13 @@ module.exports = function(verb, base, env) { }); verb.task('default', tasks); + + function handle(stage) { + return utils.through.obj(function(file, enc, next) { + if (file.isNull()) return next(); + verb.handle(stage, file, next); + }); + } }; /** From 19052171efd66ff966d3e95a57c4d7d93f66c787 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:21:52 -0500 Subject: [PATCH 045/282] update docs --- .verb.md | 13 +-- docs/src/readme/layouts/default.md | 9 +- readme.md | 132 ++++++++++++----------------- 3 files changed, 63 insertions(+), 91 deletions(-) diff --git a/.verb.md b/.verb.md index 823fe567..837e1426 100644 --- a/.verb.md +++ b/.verb.md @@ -1,6 +1,6 @@ -## toc - +{% if (typeof options !== 'undefined' && options.toc) { %} +{% } %} **TODO** @@ -21,14 +21,12 @@ - [ ] App guidelines and conventions ## Install - To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. **Install verb** - {%= include("install-global") %} -**Install a verb app** +**Install a "verb app"** If you aren't familiar with verb, just take the `node` app for a test drive: @@ -36,7 +34,7 @@ If you aren't familiar with verb, just take the `node` app for a test drive: $ npm i -g verb-node ``` -**Run a app** +**Run a "verb app"** If everything installed correctly, you should now be able to verb a new project with the following command (make sure you run the command from an empty directory!): @@ -298,6 +296,3 @@ _(TODO)_ ### App naming conventions Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. - - -{%= reflinks(['verb', 'assemble', 'base-methods', 'assemble-core', 'gulp', 'vinyl', 'scaffold']) %} \ No newline at end of file diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index d650c609..ef716a63 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -2,10 +2,8 @@ > {%= description %} -## Install -{%= include("install-npm", {save: true}) %} -## Usage {% body %} + {% var list = get("verb.related.list") || [] %}{% if (list.length) { %} ## Related projects {%= related(list) %} @@ -27,6 +25,7 @@ {%= include("footer") %} -{% if (verb && verb.reflinks && verb.reflinks.list && verb.reflinks.list.length) { %} -{%= reflinks(verb.reflinks) %} +{% var links = get("verb.reflinks") || [] %} +{% if (links.length) { %} +{%= reflinks(links) %} {% } %} diff --git a/readme.md b/readme.md index 2f39a0fe..8ba2a8ae 100644 --- a/readme.md +++ b/readme.md @@ -2,36 +2,44 @@ > Documentation generator - -## Install -Install with [npm](https://www.npmjs.com/) - -```sh -$ npm i verb --save -``` - -## Usage -## toc - - +- [Install](#install) +- [Usage](#usage) +- [CLI](#cli) + * [help](#help) + * [init](#init) + * [Run apps](#run-apps) + * [Run tasks](#run-tasks) + * [Run sub-apps](#run-sub-apps) + * [Run a app's tasks](#run-a-app-s-tasks) + * [Run a sub-app's tasks](#run-a-sub-app-s-tasks) +- [API](#api) + * [.getConfig](#getconfig) + * [.getTask](#gettask) + * [.addApp](#addapp) + * [.hasApp](#hasapp) + * [.getApp](#getapp) + * [.extendApp](#extendapp) + * [.invoke](#invoke) +- [Authoring apps](#authoring-apps) + * [App naming conventions](#app-naming-conventions) **TODO** -- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API -- [x] support sub-apps (to any level of nesting) -- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] -- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -- [x] support _instance plugins_ that allow you to easily add functionality and features to verb -- [x] support any template engine -- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) -- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -- [x] 820+ unit tests -- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -- [ ] CLI docs (started) -- [ ] User help (e.g. when the user does `verb help` or just `verb`) -- [ ] API docs -- [ ] App guidelines and conventions +* [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API +* [x] support sub-apps (to any level of nesting) +* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) +* [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +* [x] support _instance plugins_ that allow you to easily add functionality and features to verb +* [x] support any template engine +* [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) +* [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +* [x] 820+ unit tests +* [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +* [ ] CLI docs (started) +* [ ] User help (e.g. when the user does `verb help` or just `verb`) +* [ ] API docs +* [ ] App guidelines and conventions ## Install @@ -45,7 +53,7 @@ Install globally with [npm](https://www.npmjs.com/) $ npm i -g verb ``` -**Install a verb app** +**Install a "verb app"** If you aren't familiar with verb, just take the `node` app for a test drive: @@ -53,7 +61,7 @@ If you aren't familiar with verb, just take the `node` app for a test drive: $ npm i -g verb-node ``` -**Run a app** +**Run a "verb app"** If everything installed correctly, you should now be able to verb a new project with the following command (make sure you run the command from an empty directory!): @@ -77,7 +85,7 @@ _(WIP)_ _(TODO)_ -Get started with Verb. +Get started with Verb. ```js $ verb help @@ -87,7 +95,7 @@ $ verb help _(TODO)_ -Get started with Verb. +Get started with Verb. ```js $ verb init @@ -95,7 +103,6 @@ $ verb init Upon running `init`, verb will prompt you for answers to the following questions: - ### Run apps ```sh @@ -118,7 +125,7 @@ To run a task on the `base` app, just pass the name of the task to run. $ verb [options] ``` -Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. +Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. **Example** @@ -132,7 +139,7 @@ $ verb bar > Sub-apps are normal apps that are called from (or registered by) other apps. -Dot-notation is used for getting and runing sub-apps. +Dot-notation is used for getting and runing sub-apps. ```sh $ verb . [options] @@ -154,7 +161,6 @@ $ verb a.b.c [options] And so on... - ### Run a app's tasks ```sh @@ -185,10 +191,9 @@ $ verb a.b.c:foo ## API - ### .getConfig -Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. +Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. @@ -316,16 +321,16 @@ _(TODO)_ Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. +## Related projects -[assemble]: http://assemble.io -[assemble-core]: https://github.com/assemble/assemble-core -[base-methods]: https://github.com/jonschlinkert/base-methods -[gulp]: http://gulpjs.com -[scaffold]: https://github.com/jonschlinkert/scaffold -[verb]: https://github.com/verbose/verb -[vinyl]: http://github.com/gulpjs/vinyl +* [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) +* [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) +* [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) +* [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) +* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) ## Running tests + Install dev dependencies: ```sh @@ -333,48 +338,21 @@ $ npm i -d && npm test ``` ## Contributing + Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). ## Author + **Jon Schlinkert** -+ [github/jonschlinkert](https://github.com/jonschlinkert) -+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ## License + Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on Sun Dec 06 2015 21:35:43 GMT-0500 (EST)._ - -[ansi-colors]: https://github.com/doowb/ansi-colors -[assemble-core]: https://github.com/assemble/assemble-core -[assemble-loader]: https://github.com/jonschlinkert/assemble-loader -[base-argv]: https://github.com/jonschlinkert/base-argv -[base-cli]: https://github.com/jonschlinkert/base-cli -[base-config]: https://github.com/jonschlinkert/base-config -[base-pipeline]: https://github.com/jonschlinkert/base-pipeline -[base-questions]: https://github.com/jonschlinkert/base-questions -[base-runner]: https://github.com/jonschlinkert/base-runner -[base-store]: https://github.com/jonschlinkert/base-store -[common-middleware]: https://github.com/jonschlinkert/common-middleware -[engine-base]: https://github.com/jonschlinkert/engine-base -[extend-shallow]: https://github.com/jonschlinkert/extend-shallow -[get-value]: https://github.com/jonschlinkert/get-value -[global-modules]: https://github.com/jonschlinkert/global-modules -[has-glob]: https://github.com/jonschlinkert/has-glob -[helper-copyright]: https://github.com/helpers/helper-copyright -[helper-reflinks]: https://github.com/helpers/helper-reflinks -[helper-related]: https://github.com/helpers/helper-related -[is-valid-glob]: https://github.com/jonschlinkert/is-valid-glob -[lazy-cache]: https://github.com/jonschlinkert/lazy-cache -[matched]: https://github.com/jonschlinkert/matched -[minimist]: https://github.com/substack/minimist -[node-resolve]: https://github.com/substack/node-resolve -[resolve-dir]: https://github.com/jonschlinkert/resolve-dir -[stream-exhaust]: https://github.com/chrisdickinson/stream-exhaust -[success-symbol]: https://github.com/jonschlinkert/success-symbol -[time-stamp]: https://github.com/jonschlinkert/time-stamp - +_This file was generated by [verb](https://github.com/verbose/verb) on December 07, 2015._ \ No newline at end of file From 752e06c711afd1b70484edce215417613f27c882 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 08:24:11 -0500 Subject: [PATCH 046/282] update deps --- package.json | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index e4e19886..6ebd8338 100644 --- a/package.json +++ b/package.json @@ -31,39 +31,42 @@ "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", - "base-cli": "^0.4.0", - "base-config": "^0.3.2", "base-pipeline": "^0.1.4", - "base-questions": "^0.1.3", + "base-questions": "^0.2.1", "base-runner": "^0.4.1", "base-store": "^0.2.0", "common-middleware": "^0.1.3", + "define-property": "^0.2.5", "engine-base": "^0.1.2", "extend-shallow": "^2.0.1", "get-value": "^2.0.0", "global-modules": "^0.2.0", "has-glob": "^0.1.1", "helper-copyright": "^2.0.0", + "helper-date": "^0.2.2", "helper-reflinks": "^2.2.1", "helper-related": "^0.12.1", "is-valid-glob": "^0.3.0", + "isobject": "^2.0.0", "lazy-cache": "^0.2.4", "matched": "^0.3.2", "minimist": "^1.2.0", "resolve": "^1.1.6", "resolve-dir": "^0.1.0", + "set-value": "^0.3.1", "stream-exhaust": "^1.0.1", "success-symbol": "^0.1.0", + "template-toc": "^0.4.0", "time-stamp": "^0.1.3" }, "devDependencies": { "async": "^1.5.0", + "base-config": "^0.3.2", "base-methods": "^0.6.1", "buffer-equal": "0.0.1", "consolidate": "^0.13.1", "coveralls": "^2.11.4", "data-store": "^0.12.0", - "define-property": "^0.2.5", "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", "graceful-fs": "^4.1.2", @@ -90,18 +93,26 @@ "app", "boilerplate", "create", - "verb", - "verbApp", "init", "initialize", "project", "scaffold", "template", "templates", + "verb", + "verbApp", "webapp", "yeoman" ], "verb": { + "plugins": { + "gulp-format-md": { + "name": "format-md" + } + }, + "options": { + "toc": true + }, "related": { "list": [ "base-methods", @@ -110,6 +121,15 @@ "base-runner", "base-resolver" ] - } + }, + "reflinks": [ + "verb", + "assemble", + "base-methods", + "assemble-core", + "gulp", + "vinyl", + "scaffold" + ] } -} \ No newline at end of file +} From 5af7630841276a3de0916488ba5d27b679ed8e8a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 12:28:29 -0500 Subject: [PATCH 047/282] update readme --- readme.md | 102 +++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/readme.md b/readme.md index 8ba2a8ae..c3fa5a82 100644 --- a/readme.md +++ b/readme.md @@ -2,51 +2,40 @@ > Documentation generator -- [Install](#install) -- [Usage](#usage) -- [CLI](#cli) - * [help](#help) - * [init](#init) - * [Run apps](#run-apps) - * [Run tasks](#run-tasks) - * [Run sub-apps](#run-sub-apps) - * [Run a app's tasks](#run-a-app-s-tasks) - * [Run a sub-app's tasks](#run-a-sub-app-s-tasks) -- [API](#api) - * [.getConfig](#getconfig) - * [.getTask](#gettask) - * [.addApp](#addapp) - * [.hasApp](#hasapp) - * [.getApp](#getapp) - * [.extendApp](#extendapp) - * [.invoke](#invoke) -- [Authoring apps](#authoring-apps) - * [App naming conventions](#app-naming-conventions) +## Install +Install with [npm](https://www.npmjs.com/) + +```sh +$ npm i verb --save +``` + +## Usage + + + **TODO** -* [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API -* [x] support sub-apps (to any level of nesting) -* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) -* [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -* [x] support _instance plugins_ that allow you to easily add functionality and features to verb -* [x] support any template engine -* [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) -* [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -* [x] 820+ unit tests -* [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -* [ ] CLI docs (started) -* [ ] User help (e.g. when the user does `verb help` or just `verb`) -* [ ] API docs -* [ ] App guidelines and conventions +- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API +- [x] support sub-apps (to any level of nesting) +- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] +- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +- [x] support _instance plugins_ that allow you to easily add functionality and features to verb +- [x] support any template engine +- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) +- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +- [x] 820+ unit tests +- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +- [ ] CLI docs (started) +- [ ] User help (e.g. when the user does `verb help` or just `verb`) +- [ ] API docs +- [ ] App guidelines and conventions ## Install - To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. **Install verb** - Install globally with [npm](https://www.npmjs.com/) ```sh @@ -85,7 +74,7 @@ _(WIP)_ _(TODO)_ -Get started with Verb. +Get started with Verb. ```js $ verb help @@ -95,7 +84,7 @@ $ verb help _(TODO)_ -Get started with Verb. +Get started with Verb. ```js $ verb init @@ -103,6 +92,7 @@ $ verb init Upon running `init`, verb will prompt you for answers to the following questions: + ### Run apps ```sh @@ -125,7 +115,7 @@ To run a task on the `base` app, just pass the name of the task to run. $ verb [options] ``` -Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. +Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. **Example** @@ -139,7 +129,7 @@ $ verb bar > Sub-apps are normal apps that are called from (or registered by) other apps. -Dot-notation is used for getting and runing sub-apps. +Dot-notation is used for getting and runing sub-apps. ```sh $ verb . [options] @@ -161,6 +151,7 @@ $ verb a.b.c [options] And so on... + ### Run a app's tasks ```sh @@ -191,9 +182,10 @@ $ verb a.b.c:foo ## API + ### .getConfig -Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. +Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. @@ -321,16 +313,16 @@ _(TODO)_ Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. -## Related projects + +## Related projects * [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) * [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) * [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) * [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) -* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) +* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) ## Running tests - Install dev dependencies: ```sh @@ -338,21 +330,29 @@ $ npm i -d && npm test ``` ## Contributing - Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). ## Author - **Jon Schlinkert** -* [github/jonschlinkert](https://github.com/jonschlinkert) -* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ++ [github/jonschlinkert](https://github.com/jonschlinkert) ++ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ## License - Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 07, 2015._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on December 07, 2015._ + + + +[assemble]: http://assemble.io +[assemble-core]: https://github.com/assemble/assemble-core +[base-methods]: https://github.com/jonschlinkert/base-methods +[gulp]: http://gulpjs.com +[scaffold]: https://github.com/jonschlinkert/scaffold +[verb]: https://github.com/verbose/verb +[vinyl]: http://github.com/gulpjs/vinyl + From a496f6ad53ffaa995d73bd7341b6564a606df939 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 13:43:25 -0500 Subject: [PATCH 048/282] add layouts, need to customize them still --- docs/src/readme/layouts/basic.md | 26 +++++++++++++++++++++++++ docs/src/readme/layouts/default.md | 4 ++++ docs/src/readme/layouts/full.md | 31 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 docs/src/readme/layouts/basic.md create mode 100644 docs/src/readme/layouts/full.md diff --git a/docs/src/readme/layouts/basic.md b/docs/src/readme/layouts/basic.md new file mode 100644 index 00000000..b81f7fe2 --- /dev/null +++ b/docs/src/readme/layouts/basic.md @@ -0,0 +1,26 @@ +# {%= name %} {%= badge('npm') %} {%= badge('travis') %} + +> {%= description %} + +## Install +{%= include('install-npm', {save: true}) %} + +## Usage +{% body %} + +## Running tests +{%= include("tests") %} + +## Contributing +{%= include("contributing") %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index ef716a63..ab8d2d57 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -2,6 +2,10 @@ > {%= description %} +## Install +{%= include('install-npm', {save: true}) %} + +## Usage {% body %} {% var list = get("verb.related.list") || [] %}{% if (list.length) { %} diff --git a/docs/src/readme/layouts/full.md b/docs/src/readme/layouts/full.md new file mode 100644 index 00000000..ef716a63 --- /dev/null +++ b/docs/src/readme/layouts/full.md @@ -0,0 +1,31 @@ +# {%= name %} {%= badge('npm') %} {%= badge('travis') %} + +> {%= description %} + +{% body %} + +{% var list = get("verb.related.list") || [] %}{% if (list.length) { %} +## Related projects +{%= related(list) %} +{% } %} +## Running tests +{%= include("tests") %} + +## Contributing +{%= include("contributing") %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} + +{% var links = get("verb.reflinks") || [] %} +{% if (links.length) { %} +{%= reflinks(links) %} +{% } %} From 045cc18e29d544056a454c18a32c9d073defe859 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 13:43:35 -0500 Subject: [PATCH 049/282] adds `ask` flag --- lib/runner/cli.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/runner/cli.js b/lib/runner/cli.js index 061ecae8..1aa6bd40 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -7,8 +7,12 @@ module.exports = function(verb) { verb.cli .map('ask', function(key) { - verb.questions.enqueue(key); - verb.option('questions.init', key); + if (key === true) { + verb.enable('questions.init'); + } else { + verb.questions.enqueue(key); + verb.option('questions.init', key); + } }) .map('save', function(key, val) { console.log(key, val); From d7d8b8266f64ebd398a2e45267d24d32c85a5e69 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 13:45:05 -0500 Subject: [PATCH 050/282] only add a layout if not already defined --- lib/runner/middleware.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 1ac1071d..ca03f7ac 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -13,8 +13,8 @@ module.exports = function(verb, base, env) { next(); }); - verb.preLayout(/(\.verb|readme)\.md/i, function(file, next) { - if (needsLayout(file.content)) { + verb.preLayout(/\.md/, function(file, next) { + if (needsLayout(file.content) && typeof file.layout !== 'string') { file.layout = 'default'; } next(); From de3a1625471ca29b43b236e9580f7d67d4bd1d88 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 7 Dec 2015 13:45:14 -0500 Subject: [PATCH 051/282] generate docs --- .verb.md | 3 ++ readme.md | 94 ++++++++++++++++++++++++++++++------------------------- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/.verb.md b/.verb.md index 837e1426..422f2a22 100644 --- a/.verb.md +++ b/.verb.md @@ -1,3 +1,6 @@ +--- +layout: full +--- {% if (typeof options !== 'undefined' && options.toc) { %} {% } %} diff --git a/readme.md b/readme.md index c3fa5a82..575c1712 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,7 @@ > Documentation generator ## Install + Install with [npm](https://www.npmjs.com/) ```sh @@ -11,31 +12,51 @@ $ npm i verb --save ## Usage - - +- [Install](#install) +- [Usage](#usage) +- [CLI](#cli) + * [help](#help) + * [init](#init) + * [Run apps](#run-apps) + * [Run tasks](#run-tasks) + * [Run sub-apps](#run-sub-apps) + * [Run a app's tasks](#run-a-app-s-tasks) + * [Run a sub-app's tasks](#run-a-sub-app-s-tasks) +- [API](#api) + * [.getConfig](#getconfig) + * [.getTask](#gettask) + * [.addApp](#addapp) + * [.hasApp](#hasapp) + * [.getApp](#getapp) + * [.extendApp](#extendapp) + * [.invoke](#invoke) +- [Authoring apps](#authoring-apps) + * [App naming conventions](#app-naming-conventions) **TODO** -- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API -- [x] support sub-apps (to any level of nesting) -- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] -- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -- [x] support _instance plugins_ that allow you to easily add functionality and features to verb -- [x] support any template engine -- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) -- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -- [x] 820+ unit tests -- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -- [ ] CLI docs (started) -- [ ] User help (e.g. when the user does `verb help` or just `verb`) -- [ ] API docs -- [ ] App guidelines and conventions +* [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API +* [x] support sub-apps (to any level of nesting) +* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) +* [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +* [x] support _instance plugins_ that allow you to easily add functionality and features to verb +* [x] support any template engine +* [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) +* [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +* [x] 820+ unit tests +* [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +* [ ] CLI docs (started) +* [ ] User help (e.g. when the user does `verb help` or just `verb`) +* [ ] API docs +* [ ] App guidelines and conventions ## Install + To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. **Install verb** + Install globally with [npm](https://www.npmjs.com/) ```sh @@ -74,7 +95,7 @@ _(WIP)_ _(TODO)_ -Get started with Verb. +Get started with Verb. ```js $ verb help @@ -84,7 +105,7 @@ $ verb help _(TODO)_ -Get started with Verb. +Get started with Verb. ```js $ verb init @@ -92,7 +113,6 @@ $ verb init Upon running `init`, verb will prompt you for answers to the following questions: - ### Run apps ```sh @@ -115,7 +135,7 @@ To run a task on the `base` app, just pass the name of the task to run. $ verb [options] ``` -Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. +Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. **Example** @@ -129,7 +149,7 @@ $ verb bar > Sub-apps are normal apps that are called from (or registered by) other apps. -Dot-notation is used for getting and runing sub-apps. +Dot-notation is used for getting and runing sub-apps. ```sh $ verb . [options] @@ -151,7 +171,6 @@ $ verb a.b.c [options] And so on... - ### Run a app's tasks ```sh @@ -182,10 +201,9 @@ $ verb a.b.c:foo ## API - ### .getConfig -Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. +Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. @@ -313,16 +331,16 @@ _(TODO)_ Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. - - ## Related projects + * [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) * [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) * [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) * [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) -* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) +* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) ## Running tests + Install dev dependencies: ```sh @@ -330,29 +348,21 @@ $ npm i -d && npm test ``` ## Contributing + Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). ## Author + **Jon Schlinkert** -+ [github/jonschlinkert](https://github.com/jonschlinkert) -+ [twitter/jonschlinkert](http://twitter.com/jonschlinkert) +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) ## License + Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 07, 2015._ - - - -[assemble]: http://assemble.io -[assemble-core]: https://github.com/assemble/assemble-core -[base-methods]: https://github.com/jonschlinkert/base-methods -[gulp]: http://gulpjs.com -[scaffold]: https://github.com/jonschlinkert/scaffold -[verb]: https://github.com/verbose/verb -[vinyl]: http://github.com/gulpjs/vinyl - +_This file was generated by [verb](https://github.com/verbose/verb) on December 07, 2015._ \ No newline at end of file From 5b3ea47a724ff3473a69428b3b1090546749072d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 8 Dec 2015 15:28:37 -0500 Subject: [PATCH 052/282] docs --- docs/src/content/data.md | 2 +- readme.md | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/src/content/data.md b/docs/src/content/data.md index e79df97b..6c3e81fd 100644 --- a/docs/src/content/data.md +++ b/docs/src/content/data.md @@ -8,7 +8,7 @@ reflinks: ['base-store', 'base-data'] {{upper name}} keeps "data" on two different objects, depending on your needs. -| **Storage object** | **Description** | **Methods**| +| **Storage object** | **Description** | **Methods** | | --- | --- | --- | | `{{name}}.cache.data` | Kept in-memory | `{{name}}.data()` (function) | | `{{name}}.store.data` | Persisted to disk | `{{name}}.store` (object with methods) | diff --git a/readme.md b/readme.md index 575c1712..8ba2a8ae 100644 --- a/readme.md +++ b/readme.md @@ -2,16 +2,6 @@ > Documentation generator -## Install - -Install with [npm](https://www.npmjs.com/) - -```sh -$ npm i verb --save -``` - -## Usage - - [Install](#install) - [Usage](#usage) - [CLI](#cli) From 1eb476436a415153635968020eb58f3462872a3a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 8 Dec 2015 15:29:32 -0500 Subject: [PATCH 053/282] rename `runner.js` to preload --- cli.js | 6 +++--- lib/runner/includes.js | 1 + lib/runner/{runner.js => preload.js} | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) rename lib/runner/{runner.js => preload.js} (89%) diff --git a/cli.js b/cli.js index 78112ddd..0c851c31 100755 --- a/cli.js +++ b/cli.js @@ -4,7 +4,7 @@ var path = require('path'); var gm = require('global-modules'); var processArgv = require('base-argv').processArgv(); var minimist = require('minimist'); -var runner = require('./lib/runner/runner'); +var preload = require('./lib/runner/preload'); var utils = require('./lib/utils'); var create = require('./').create; @@ -19,7 +19,7 @@ var args = minimist(process.argv.slice(2), { * this creates a custom `Verb` constructor . */ -var Verb = create(runner); +var Verb = create(preload); /** * Register `runner` mixin with `Verb`, wich pre-loads @@ -30,7 +30,7 @@ var Verb = create(runner); * with our CLI plugins and middeware. */ -Verb.mixin(utils.runner('verb', 'verbApp', runner)); +Verb.mixin(utils.runner('verb', 'verbApp', preload)); /** * Get the `base` instance of verb to use for diff --git a/lib/runner/includes.js b/lib/runner/includes.js index 06d051c5..6fb05b54 100644 --- a/lib/runner/includes.js +++ b/lib/runner/includes.js @@ -41,5 +41,6 @@ module.exports = { ].join('\n'), 'contributing.md': 'Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= author.username %}/{%= name %}/issues/new).', + 'footer.md': '_This file was generated by [{%= runner.name %}]({%= runner.url %}) on {%= date() %}._' }; diff --git a/lib/runner/runner.js b/lib/runner/preload.js similarity index 89% rename from lib/runner/runner.js rename to lib/runner/preload.js index e8efb0d9..3ec37a1b 100644 --- a/lib/runner/runner.js +++ b/lib/runner/preload.js @@ -16,7 +16,6 @@ module.exports = function preload(verb, base, env) { config(verb); cli(verb); - // bubble up errors to `base` instance, set defaults verb.on('register', function(name, app) { defaults(app, base, env); app.use(templates()); From 097c3943814c917c3063941d4c61f3206650038c Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Wed, 9 Dec 2015 17:51:37 -0500 Subject: [PATCH 054/282] add base-list dependency --- lib/runner/preload.js | 4 ++++ lib/utils.js | 1 + package.json | 1 + 3 files changed, 6 insertions(+) diff --git a/lib/runner/preload.js b/lib/runner/preload.js index 3ec37a1b..75f0e697 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -16,6 +16,10 @@ module.exports = function preload(verb, base, env) { config(verb); cli(verb); + verb.use(utils.list('verbApps', { + method: 'verbApp' + })); + verb.on('register', function(name, app) { defaults(app, base, env); app.use(templates()); diff --git a/lib/utils.js b/lib/utils.js index 214fcef6..6bfd7b3d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -26,6 +26,7 @@ require('base-questions', 'ask'); require('base-pipeline', 'pipeline'); require('base-runner', 'runner'); require('base-store', 'store'); +require('base-list', 'list'); // misc require('define-property', 'define'); diff --git a/package.json b/package.json index 6ebd8338..70d8e4b9 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", + "base-list": "^0.1.1", "base-pipeline": "^0.1.4", "base-questions": "^0.2.1", "base-runner": "^0.4.1", From 3050c85e25a04d32202d8b36e4065a3f002abe64 Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Wed, 9 Dec 2015 17:52:16 -0500 Subject: [PATCH 055/282] setup mappings to enable running base-list methods --- lib/runner/cli.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/runner/cli.js b/lib/runner/cli.js index 1aa6bd40..9f62c62a 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -14,6 +14,16 @@ module.exports = function(verb) { verb.option('questions.init', key); } }) + .map('choose', function(key) { + if (key === true) { + verb.enable('choose tasks'); + } + }) + .map('tasks', function(key) { + if (key === true) { + verb.enable('display tasks'); + } + }) .map('save', function(key, val) { console.log(key, val); }) From 4d6d56c84f376b385d4c4fa093ab6b190b107bbf Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Wed, 9 Dec 2015 17:53:30 -0500 Subject: [PATCH 056/282] ensure entire `verbApps` configuration is run after any settings have been set add displaying and/or choosing apps and tasks when flags are set. --- cli.js | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/cli.js b/cli.js index 0c851c31..f1ed1090 100755 --- a/cli.js +++ b/cli.js @@ -59,27 +59,48 @@ verb.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); */ verb.cli.map('verbApps', function(tasks) { - if (!tasks.length) { - if (verb.tasks.hasOwnProperty('default')) { - tasks = [{verb: ['default']}]; - } else { - console.log(' no default task is defined.'); + + // ensure this is run after other configuration is complete + setImmediate(function() { + if (verb.enabled('display tasks')) { + console.log(utils.colors.gray(' List of verbApps and their registered tasks:')); + verb.displayTasks(); utils.timestamp('done'); return; } - } - verb.on('error', function(err) { - console.log(err); - process.exit(1); - }); + if (verb.enabled('choose tasks')) { + verb.chooseTasks(function(err, results) { + if (err) throw err; + run([results.verbApps]); + }); + return; + } + + if (!tasks.length) { + if (verb.tasks.hasOwnProperty('default')) { + tasks = [{verb: ['default']}]; + } else { + console.log(' no default task is defined.'); + utils.timestamp('done'); + return; + } + } + + run(tasks); + function run(tasks) { + verb.on('error', function(err) { + console.log(err); + process.exit(1); + }); + + verb.runVerbApps(tasks, function(err) { + if (err) return console.error(err); + utils.timestamp('done'); + process.exit(0); + }); + } - setImmediate(function() { - verb.runVerbApps(tasks, function(err) { - if (err) return console.error(err); - utils.timestamp('done'); - process.exit(0); - }); }); }); From b2a2763df5ae836195cad932b11bbe83f619a759 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 20:49:21 -0500 Subject: [PATCH 057/282] add related link to data.md docs --- docs/src/content/data.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/content/data.md b/docs/src/content/data.md index 6c3e81fd..3a5f8405 100644 --- a/docs/src/content/data.md +++ b/docs/src/content/data.md @@ -3,6 +3,7 @@ name: verb title: Data engine: hbs description: "Learn how to get, set and delete data for templates, options and more." +related: ['#store'] reflinks: ['base-store', 'base-data'] --- From 261c9f1c1797b6d4c24574a47667c9552594c5b3 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 20:49:30 -0500 Subject: [PATCH 058/282] plugins/middleware docs --- docs/src/content/middleware.md | 20 +++++ docs/src/content/plugins.md | 140 +++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 docs/src/content/middleware.md create mode 100644 docs/src/content/plugins.md diff --git a/docs/src/content/middleware.md b/docs/src/content/middleware.md new file mode 100644 index 00000000..b7160297 --- /dev/null +++ b/docs/src/content/middleware.md @@ -0,0 +1,20 @@ +--- +name: verb +title: Middleware +engine: hbs +description: "" +related: ['en-route'] +reflinks: ['en-route'] +--- + +(Some verbiage was borrowed from [express's middleware docs][express]) + +Middleware functions are functions that have access to the `file` object (or in {{name}}'s case, the `view` object), and a callback function that represents the `next` middleware in the application’s build cycle. + +Middleware functions can perform the following tasks: + +- Execute any code. +- Make changes to the `file` object. +- Call the `next` middleware function in the stack. + +[express]: http://expressjs.com/en/guide/using-middleware.html \ No newline at end of file diff --git a/docs/src/content/plugins.md b/docs/src/content/plugins.md new file mode 100644 index 00000000..33dbbc1a --- /dev/null +++ b/docs/src/content/plugins.md @@ -0,0 +1,140 @@ +> How do you differentiate between assemble and metalsmith + +There is not much similar between the two. The examples show how to build projects using a similar style to metalsmith, but that's about it. + +- assemble has rich support for template collections +- assemble supports template "types": renderable, layout and partial - defined when a collection is created, to add special behavior and methods related to the type; and collections can use one or more type. +- assemble does not limit how and where you build files +- assemble supports instance plugins (like metalsmith), so you can augment an instance +- assemble supports pipeline plugins (like gulp) +- assemble collections can use plugins +- assemble views (templates) can use plugins (assemble views are vinyl files) +- assemble supports any template engine, and allows you to use more than one template engine during the same build. +- assemble supports middleware, similar to express. (plugins and middleware serve very different purposes, and are used in completely different ways. more on this below) + +## Plugins + +- **instance plugins**: Instance plugins are registered with the `.use()` method and are called immediately upon instantiation. The only parameter exposed to an instance plugin is the instance of `app` (assemble), `collection`, or `view`. +- **pipeline plugins**: Pipeline plugins are registered with `.pipe()` and are used on vinyl `file` objects in a stream (note that all assemble "views" are instances of vinyl files) + +### Instance plugins + +**Example instance plugin** + +```js +var assemble = require('assemble'); +var app = assemble(); + +app.use(function(app) { + // do stuff to app or `this` (the assemble instance) +}); +``` + +### Collection plugins + +Collections themselves are like mini-application instances, and collection plugins are registered and used the same way as instance plugins, with the `.use()` method, but on a specific collection. Collection plugins are called immediately upon instantiation of the collection. + +**Example collection plugin** + +```js +var assemble = require('assemble'); +var app = assemble(); + +// register a plugin to be used on all collections +app.use(function(app) { + // do stuff to app or `this` (the assemble instance) + + // return a function to be use used as a collection plugin + return function(collection) { + // do stuff to `collection` + }; +}); +``` + +The `app.create()` method (used for creating custom collections) returns the collection instance. So collection plugins can be chained from create as well. + +```js +app.use(function() { + return function(collection) { + // do stuff to (every) `collection` + } +}); + +app.create('pages') + .use(function(pages) { + // do stuff to `pages` collection + }); + +app.create('posts') + .use(function(posts) { + // do stuff to `posts` collection + }); +``` + +**Example view plugin** + +```js +var assemble = require('assemble'); +var app = assemble(); + +// register a plugin to be used on all views, from all collections +app.use(function(app) { + return function(collection) { + + // return a function from a collection plugin to be used + // as a view plugin + return function(view) { + // do stuff to `view` + }; + }; +}); +``` + +**Register view plugins on specific collections** + +```js +app.use(function(app) { + // do stuff to `app` + return function(collection) { + // do stuff to (every) `collection` + return function(view) { + // do stuff to (every) `view` + }; + }; +}); + +app.create('pages') + .use(function(pages) { + return function(page) { + // do stuff to `page` + }; + }); + +app.create('posts') + .use(function(posts) { + return function(post) { + // do something to `post` + }; + }); +``` + +**Use cases for collection/view plugins** + +Here are just a few examples + +- `permalinks`: You might have a permalink plugin that modifies the `dest` path a particular way for blog `posts`, and a different way for `pages`. You could register the same plugin with both collections, just using different settings/options. Also, you could implement this functionality at the view level or collection level, depending on how granular your plugin needs to be. +- `pagination` +- `groups` and `lists` +- `sorting` + +## Middleware + +(this description was inspired by express's middleware description) + +Middleware functions are functions that have access to the `file` object (or in assemble's case, the `view` object), and a callback function that represents the `next` middleware in the application’s build cycle. + +Middleware functions can perform the following tasks: + +- Execute any code. +- Make changes to the `file` object. +- Call the `next` middleware function in the stack. \ No newline at end of file From 34879122571eea15217c9d40c50e29bf990e4edc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 20:49:40 -0500 Subject: [PATCH 059/282] handle arrays of plugins --- lib/config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/config.js b/lib/config.js index 3be14762..d037ff36 100644 --- a/lib/config.js +++ b/lib/config.js @@ -47,6 +47,13 @@ module.exports = function(verb) { var cwd = verb.get('env.user.cwd'); var opts = {}; + if (Array.isArray(plugins)) { + plugins = plugins.reduce(function(acc, plugin) { + acc[plugin] = {}; + return acc; + }, {}); + } + for (var key in plugins) { var name = path.basename(key, path.extname(key)); if (name === 'index') { From 9a485bbbb56bf0510752f1f9499dbb3eafb710b4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 20:49:58 -0500 Subject: [PATCH 060/282] improve error messages in verbfile --- verbfile.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/verbfile.js b/verbfile.js index b8f13beb..8ff49711 100644 --- a/verbfile.js +++ b/verbfile.js @@ -39,11 +39,12 @@ module.exports = function(verb, base, env) { }) .pipe(handle('onStream')) .pipe(verb.renderFile('text', answers)) - .on('error', handleError) + .on('error', handleError(verb)) .pipe(verb.pipeline(plugins)) .pipe(handle('preWrite')) .pipe(verb.dest(dest('readme.md'))) .pipe(utils.exhaust(handle('postWrite'))) + .on('error', cb) .on('finish', cb); }); }); @@ -54,7 +55,7 @@ module.exports = function(verb, base, env) { verb.toStream('docs') .on('error', cb) - .pipe(verb.renderFile('text', answers)) + // .pipe(verb.renderFile('text', answers)) .on('error', cb) .pipe(verb.dest(dest('readme.md'))) .on('finish', cb); @@ -97,15 +98,22 @@ function dest(dest) { * Handle render errors */ -function handleError(err) { - var m = /(\w+) is not a function/.exec(err.message); - console.log(m) - if (m) { - console.log('"' + m[1] + '" is a variable on `verb.cache.data`, but it is defined'); - console.log('as a helper in `.verb.md`') - } else if (app.options.verbose) { - console.log(err.stack); - } else { - console.log(err.message); +function handleError(app) { + return function(err, cb) { + var m = /(\w+) is not a function/.exec(err.message); + var msg = ''; + if (m) { + msg = err.message + ': "' + m[1] + '()" is defined as a helper\n' + + 'in `.verb.md`, but "' + m[1] + '" is defined on ' + + 'verb.cache.data as a "' + typeof app.cache.data[m[1]] + '"'; + } + if (app.options.verbose) { + console.log(msg); + console.log(err.stack); + } else { + console.log(err.message); + console.log(msg); + } + process.exit(1); } } From 0add1048622e23907c40de57f00f8f44503cbeb5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 23:07:12 -0500 Subject: [PATCH 061/282] min layout/verb.md templates --- docs/src/readme/layouts/min.md | 16 ++++++++++++++++ docs/src/readme/min.md | 14 ++++++++++++++ docs/src/readme/verb.md | 0 3 files changed, 30 insertions(+) create mode 100644 docs/src/readme/layouts/min.md create mode 100644 docs/src/readme/min.md create mode 100644 docs/src/readme/verb.md diff --git a/docs/src/readme/layouts/min.md b/docs/src/readme/layouts/min.md new file mode 100644 index 00000000..74e345d6 --- /dev/null +++ b/docs/src/readme/layouts/min.md @@ -0,0 +1,16 @@ +# {%= name %} + +> {%= description %} + +{% body %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/docs/src/readme/min.md b/docs/src/readme/min.md new file mode 100644 index 00000000..a8f75669 --- /dev/null +++ b/docs/src/readme/min.md @@ -0,0 +1,14 @@ +# {%= name %} + +> {%= description %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/docs/src/readme/verb.md b/docs/src/readme/verb.md new file mode 100644 index 00000000..e69de29b From 4d0f80416359893340ecd02ca5d582978167fb64 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 23:07:53 -0500 Subject: [PATCH 062/282] don't apply layouts to partials in `preLayout` middleware --- lib/runner/middleware.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index ca03f7ac..cc77b94a 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -14,6 +14,9 @@ module.exports = function(verb, base, env) { }); verb.preLayout(/\.md/, function(file, next) { + if (file.options.viewType.indexOf('partial') > -1) { + return next(); + } if (needsLayout(file.content) && typeof file.layout !== 'string') { file.layout = 'default'; } From 2e6eeb4d517a8bc1d0dc0732074f29593bfe8bab Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 23:08:05 -0500 Subject: [PATCH 063/282] clean up --- lib/runner/helpers.js | 4 ++-- verbfile.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 5266ef3b..161be7e2 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -3,9 +3,9 @@ var utils = require('../utils'); module.exports = function(verb, base, env) { - verb.asyncHelper('apidocs', function(name, cb) { + verb.helper('apidocs', function(name) { // console.log(arguments); - cb(null, ''); + return; }); verb.helper('log', function(msg) { diff --git a/verbfile.js b/verbfile.js index 8ff49711..6a3a69b2 100644 --- a/verbfile.js +++ b/verbfile.js @@ -55,7 +55,7 @@ module.exports = function(verb, base, env) { verb.toStream('docs') .on('error', cb) - // .pipe(verb.renderFile('text', answers)) + .pipe(verb.renderFile('text', answers)) .on('error', cb) .pipe(verb.dest(dest('readme.md'))) .on('finish', cb); From 6d232a7824c68d949c747d1d6ee0a32cc807f38b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 23:08:46 -0500 Subject: [PATCH 064/282] generate readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8ba2a8ae..94acb8f3 100644 --- a/readme.md +++ b/readme.md @@ -355,4 +355,4 @@ Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 07, 2015._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on December 09, 2015._ \ No newline at end of file From 5308fec791680112dc366ea57b496c6ed8e44232 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 9 Dec 2015 23:08:59 -0500 Subject: [PATCH 065/282] update deps --- package.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 70d8e4b9..65f34417 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,11 @@ "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", - "base-list": "^0.1.1", + "base-list": "^0.1.4", "base-pipeline": "^0.1.4", "base-questions": "^0.2.1", - "base-runner": "^0.4.1", - "base-store": "^0.2.0", + "base-runner": "^0.4.3", + "base-store": "^0.3.0", "common-middleware": "^0.1.3", "define-property": "^0.2.5", "engine-base": "^0.1.2", @@ -49,7 +49,7 @@ "helper-related": "^0.12.1", "is-valid-glob": "^0.3.0", "isobject": "^2.0.0", - "lazy-cache": "^0.2.4", + "lazy-cache": "^1.0.2", "matched": "^0.3.2", "minimist": "^1.2.0", "resolve": "^1.1.6", @@ -73,6 +73,7 @@ "graceful-fs": "^4.1.2", "gulp": "^3.9.0", "gulp-eslint": "^1.1.1", + "gulp-format-md": "^0.1.0", "gulp-istanbul": "^0.10.3", "gulp-mocha": "^2.2.0", "is-buffer": "^1.1.0", From 931695f6e45102647fe53188c927e140e16c3bbd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 10 Dec 2015 13:32:51 -0500 Subject: [PATCH 066/282] start adding apidocs helper --- lib/runner/helpers.js | 14 +++++++------- package.json | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 161be7e2..c72ec8a0 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -3,11 +3,6 @@ var utils = require('../utils'); module.exports = function(verb, base, env) { - verb.helper('apidocs', function(name) { - // console.log(arguments); - return; - }); - verb.helper('log', function(msg) { console.log.apply(console, arguments); }); @@ -21,9 +16,14 @@ module.exports = function(verb, base, env) { }); verb.helper('date', require('helper-date')); - verb.asyncHelper('related', utils.related({verbose: true})); - verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); + // verb.helper('codelinks', require('helper-codelinks')(verb)); + // verb.helper('multiToc', require('helper-toc')(verb.option('toc'))); + verb.helper('apidocs', require('template-helper-apidocs')); + // verb.helper('coverage', require('helper-coverage')); verb.helper('copyright', require('helper-copyright')({ linkify: true })); + + verb.asyncHelper('related', utils.related({verbose: true})); + verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); }; diff --git a/package.json b/package.json index 65f34417..0421f85f 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "should": "*", "sinon": "^1.17.2", "swig": "^1.4.2", + "template-helper-apidocs": "^0.4.4", "through2": "^2.0.0", "vinyl": "^1.1.0" }, From 4905bdb8963c95682f7e6980874be7ceae328ba0 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 10 Dec 2015 13:52:46 -0500 Subject: [PATCH 067/282] emit plugins, enqueue default readme questions --- verbfile.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/verbfile.js b/verbfile.js index 6a3a69b2..33d36c07 100644 --- a/verbfile.js +++ b/verbfile.js @@ -19,6 +19,8 @@ module.exports = function(verb, base, env) { */ verb.task('readme', function(cb) { + verb.questions.enqueue('author', 'name', 'description'); + var plugins = verb.get('env.argv.plugins') || verb.plugins; // load package.json data and user options onto `verb.cache.data` @@ -34,6 +36,9 @@ module.exports = function(verb, base, env) { return; } + // placeholder for something better + verb.emit('info', 'plugins', Object.keys(plugins).join(', ')); + verb.toStream('docs', function(key) { return key === '.verb'; }) @@ -49,6 +54,19 @@ module.exports = function(verb, base, env) { }); }); + verb.task('verbmd', function(cb) { + verb.ask(function(err, answers) { + if (err) return cb(err); + + verb.toStream('docs', function(key) { + return key === 'min'; + }) + .pipe(verb.dest(dest('.verb.md'))) + .on('error', cb) + .on('finish', cb); + }); + }); + verb.task('docs', function(cb) { verb.ask(function(err, answers) { if (err) return cb(err); From 236834f5252094cf870bce1989771619ecc9952b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 10 Dec 2015 13:53:01 -0500 Subject: [PATCH 068/282] organize cli mappings --- lib/runner/cli.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/runner/cli.js b/lib/runner/cli.js index 9f62c62a..38e41f51 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -19,20 +19,27 @@ module.exports = function(verb) { verb.enable('choose tasks'); } }) + .map('cwd', function(fp) { + verb.option('cwd', fp); + }) + .map('data', function(val) { + verb.visit('data', val); + }) + .map('md', function(key, val) { + console.log('cli:md', verb.views) + verb.enable('ask.verbmd'); + }) + .map('related', function(key) { + console.log('cli:related', arguments) + }) + .map('save', function(key, val) { + console.log('cli:save', key, val); + }) .map('tasks', function(key) { if (key === true) { verb.enable('display tasks'); } }) - .map('save', function(key, val) { - console.log(key, val); - }) - .map('data', function(val) { - verb.visit('data', val); - }) - .map('cwd', function(fp) { - verb.option('cwd', fp); - }); verb.define('commands', verb.cli.keys); }; From 81518e060cd72fbe52a4b9f4c91375dec59c4414 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 10 Dec 2015 13:53:09 -0500 Subject: [PATCH 069/282] add helpers --- lib/runner/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index c72ec8a0..2bbdc362 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -15,11 +15,11 @@ module.exports = function(verb, base, env) { return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; }); - verb.helper('date', require('helper-date')); // verb.helper('codelinks', require('helper-codelinks')(verb)); // verb.helper('multiToc', require('helper-toc')(verb.option('toc'))); - verb.helper('apidocs', require('template-helper-apidocs')); // verb.helper('coverage', require('helper-coverage')); + verb.helper('date', require('helper-date')); + verb.helper('apidocs', require('template-helper-apidocs')); verb.helper('copyright', require('helper-copyright')({ linkify: true })); From 33da8d9ee77c3e5cdb37fc03c7ed1f14fe56bd2a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 10 Dec 2015 16:05:19 -0500 Subject: [PATCH 070/282] adds `updater` to udpate package.json properties --- index.js | 8 ++-- lib/config.js | 34 ++++++++++++++- lib/runner/cli.js | 11 +++-- lib/runner/create.js | 1 + lib/runner/middleware.js | 12 ++++++ lib/runner/preload.js | 4 +- lib/runner/templates.js | 1 + lib/updater.js | 93 ++++++++++++++++++++++++++++++++++++++++ lib/utils.js | 20 +++++---- package.json | 2 + verbfile.js | 11 ++--- 11 files changed, 170 insertions(+), 27 deletions(-) create mode 100644 lib/updater.js diff --git a/index.js b/index.js index 3483297d..8c32b87f 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var path = require('path'); var async = require('async'); var Assemble = require('assemble-core'); var utils = require('./lib/utils'); +var env = require('./lib/env'); /** * Create a customized `Verb` constructor that calls the given @@ -20,7 +21,7 @@ var utils = require('./lib/utils'); * @api public */ -function create(runner) { +function create(preload) { /** * Create an instance of `Verb` with the given `options` @@ -48,13 +49,14 @@ function create(runner) { this.use(utils.middleware(opts)) .use(utils.pipeline(opts)) .use(utils.store()) + .use(env()) this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] }); - if (typeof runner === 'function') { - runner.call(this, this, this.base, this.env); + if (typeof preload === 'function') { + preload.call(this, this, this.base, this.env); } } diff --git a/lib/config.js b/lib/config.js index d037ff36..9b78c2b5 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,12 +1,42 @@ 'use strict'; var path = require('path'); +var updater = require('./updater'); var utils = require('./utils'); -module.exports = function(verb) { +module.exports = function(verb, base, env) { if (!verb.isVerb) return; - verb.config.map('option'); + + verb.config + .map('option') + .map('save', function(obj) { + var pkg = verb.root.getView('package.json'); + if (!pkg) return; + + if (!pkg.json.verb) { + pkg.json.verb = {}; + } + + var update = updater(pkg.json.verb); + for (var key in obj) { + update(key, obj[key]); + } + }) + + .map('save', function(obj) { + var pkg = verb.get('env.pkg'); + if (!pkg) return; + + if (!pkg.verb) { + pkg.verb = {}; + } + + var update = updater(pkg.verb); + for (var key in obj) { + update(key, obj[key]); + } + }) /** * Helpers diff --git a/lib/runner/cli.js b/lib/runner/cli.js index 38e41f51..fa297819 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -1,6 +1,8 @@ 'use strict'; -module.exports = function(verb) { +var utils = require('../utils'); + +module.exports = function(verb, base, env) { if (!verb.cli) { throw new Error('the base-cli plugin should be registered on every instance'); } @@ -29,11 +31,8 @@ module.exports = function(verb) { console.log('cli:md', verb.views) verb.enable('ask.verbmd'); }) - .map('related', function(key) { - console.log('cli:related', arguments) - }) - .map('save', function(key, val) { - console.log('cli:save', key, val); + .map('save', function(obj) { + verb.config.process({pkg: obj}); }) .map('tasks', function(key) { if (key === true) { diff --git a/lib/runner/create.js b/lib/runner/create.js index 5114727a..135f9741 100644 --- a/lib/runner/create.js +++ b/lib/runner/create.js @@ -8,6 +8,7 @@ var path = require('path'); module.exports = function(options) { return function(verb) { + create(verb, 'root', 'renderable'); create(verb, 'docs', 'renderable'); create(verb, 'badges', 'partial'); create(verb, 'includes', 'partial'); diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index cc77b94a..fb57058a 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -8,8 +8,20 @@ module.exports = function(verb, base, env) { utils.toc(verb)(file, next); }); + verb.onLoad(/\.json/, function(file, next) { + file.json = JSON.parse(file.content); + next(); + }); + + verb.preWrite(/\.json/, function(file, next) { + file.content = JSON.stringify(file.json, null, 2); + file.dest = 'package.json'; + next(); + }); + verb.onLoad(/\.verb\.md/, function(file, next) { file.path = 'readme.md'; + file.dest = 'readme.md'; next(); }); diff --git a/lib/runner/preload.js b/lib/runner/preload.js index 75f0e697..b0b33ae2 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -13,8 +13,8 @@ module.exports = function preload(verb, base, env) { defaults(verb, base, env); verb.use(templates()); - config(verb); - cli(verb); + config(verb, base, env); + cli(verb, base, env); verb.use(utils.list('verbApps', { method: 'verbApp' diff --git a/lib/runner/templates.js b/lib/runner/templates.js index 9090d54f..1ff1bdf1 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -26,6 +26,7 @@ module.exports = function(options) { var userDocs = path.join.bind(path, process.cwd()); if (fs.existsSync(userDocs())) { + verb.root.loadViews('*', {cwd: userDocs(), dot: true}); verb.docs.loadViews('.verb.md', {cwd: userDocs()}); verb.includes.loadViews('*.md', {cwd: userDocs('docs/includes')}); diff --git a/lib/updater.js b/lib/updater.js new file mode 100644 index 00000000..1a316427 --- /dev/null +++ b/lib/updater.js @@ -0,0 +1,93 @@ +'use strict'; + +var utils = require('./utils'); + +var schema = { + related: { + type: 'object', + list: { + type: 'array' + }, + // validate: function() { + + // } + }, + reflinks: { + type: 'array|object', + list: { + type: 'array' + } + }, + plugins: { + type: 'array|object' + } +}; + +schema.validate = function(prop, val) { + var target = utils.get(schema, prop); + if (typeof target === 'undefined') { + return; + } + if (typeof target.validate === 'function') { + target.validate(val); + } + + if (!target.type) return; + var types = target.types.split(/\W/); + if (types.indexOf(typeOf(val)) > -1) { + throw new TypeError('expected "' + val + '" to be ' + article(types)); + } +}; + +/** + * Adds get/set methods to verb env + */ + +module.exports = function(options) { + return function(verb) { + + utils.define(verb, 'update', function(pkg) { + return function(prop, val) { + update(pkg, prop, val, schema); + return pkg; + }; + }); + }; +}; + +function article(types) { + if (typeof types === 'string' || types.length === 1) { + return (/[aeiou]/.test(types[0]) ? 'an ' : 'a ') + types[0]; + } + return types.map(function(type) { + return article(type); + }).join(' or '); +} + +function update(obj, prop, newVal, schema) { + if (schema) { + schema.validate(prop, newVal); + } + + var val = utils.get(obj, prop); + if (typeof val === 'undefined' || utils.isPrimative(newVal)) { + utils.set(obj, prop, newVal); + return obj; + } + + if (utils.isObject(newVal)) { + utils.set(obj, prop, utils.extend({}, val, newVal)); + return obj; + } + + if (Array.isArray(newVal)) { + var len = newVal.length; + while (len--) { + if (val.indexOf(newVal[len]) < 0) { + val.push(newVal[len]); + } + } + utils.set(obj, prop, val); + return obj; + } +} diff --git a/lib/utils.js b/lib/utils.js index 6bfd7b3d..3745bb92 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -29,21 +29,23 @@ require('base-store', 'store'); require('base-list', 'list'); // misc +require('async'); require('define-property', 'define'); -require('stream-exhaust', 'exhaust'); require('extend-shallow', 'extend'); +require('get-value', 'get'); require('global-modules', 'gm'); +require('has-glob'); +require('is-primitive'); +require('is-valid-glob'); require('isobject', 'isObject'); -require('through2', 'through'); -require('template-toc', 'toc'); -require('get-value', 'get'); -require('set-value', 'set'); require('matched', 'glob'); -require('is-valid-glob'); -require('resolve-dir'); -require('has-glob'); +require('object-visit', 'visit'); require('resolve'); -require('async'); +require('resolve-dir'); +require('set-value', 'set'); +require('stream-exhaust', 'exhaust'); +require('template-toc', 'toc'); +require('through2', 'through'); // cli require('success-symbol'); diff --git a/package.json b/package.json index 0421f85f..ef255081 100644 --- a/package.json +++ b/package.json @@ -47,11 +47,13 @@ "helper-date": "^0.2.2", "helper-reflinks": "^2.2.1", "helper-related": "^0.12.1", + "is-primitive": "^2.0.0", "is-valid-glob": "^0.3.0", "isobject": "^2.0.0", "lazy-cache": "^1.0.2", "matched": "^0.3.2", "minimist": "^1.2.0", + "object-visit": "^0.3.4", "resolve": "^1.1.6", "resolve-dir": "^0.1.0", "set-value": "^0.3.1", diff --git a/verbfile.js b/verbfile.js index 33d36c07..500f7835 100644 --- a/verbfile.js +++ b/verbfile.js @@ -40,14 +40,14 @@ module.exports = function(verb, base, env) { verb.emit('info', 'plugins', Object.keys(plugins).join(', ')); verb.toStream('docs', function(key) { - return key === '.verb'; + return key === '.verb' || key === 'package'; }) .pipe(handle('onStream')) .pipe(verb.renderFile('text', answers)) .on('error', handleError(verb)) .pipe(verb.pipeline(plugins)) .pipe(handle('preWrite')) - .pipe(verb.dest(dest('readme.md'))) + .pipe(verb.dest(dest())) .pipe(utils.exhaust(handle('postWrite'))) .on('error', cb) .on('finish', cb); @@ -75,7 +75,7 @@ module.exports = function(verb, base, env) { .on('error', cb) .pipe(verb.renderFile('text', answers)) .on('error', cb) - .pipe(verb.dest(dest('readme.md'))) + .pipe(verb.dest(dest())) .on('finish', cb); }); }); @@ -104,8 +104,9 @@ module.exports = function(verb, base, env) { function dest(dest) { return function(file) { - file.base = path.dirname(dest); - file.path = dest; + var fp = file.dest || dest || ''; + file.base = path.dirname(fp); + file.path = fp; file.basename = file.basename.replace(/^_/, '.'); file.basename = file.basename.replace(/^\$/, ''); return file.base; From 50130ec96ad414031b82c46c52086f233c9f98d5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 11 Dec 2015 19:42:44 -0500 Subject: [PATCH 071/282] don't use `get`, un-abstract --- docs/src/readme/layouts/default.md | 19 +++++++++++++------ docs/src/readme/layouts/full.md | 14 +++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index ab8d2d57..6c63df4e 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -2,16 +2,20 @@ > {%= description %} +{% if (typeof options !== 'undefined' && options.toc) { %} + +{% } %} + ## Install {%= include('install-npm', {save: true}) %} -## Usage {% body %} -{% var list = get("verb.related.list") || [] %}{% if (list.length) { %} +{% if (verb.related && verb.related.list && verb.related.list.length) { %} ## Related projects -{%= related(list) %} +{%= related(verb.related.list) %} {% } %} + ## Running tests {%= include("tests") %} @@ -29,7 +33,10 @@ {%= include("footer") %} -{% var links = get("verb.reflinks") || [] %} -{% if (links.length) { %} -{%= reflinks(links) %} +{% if (verb.reflinks) { %} +{% if (Array.isArray(verb.reflinks) && verb.reflinks.length) { %} +{%= reflinks(verb.reflinks) %} +{% } else if (verb.reflinks.list && verb.reflinks.list.length) { %} +{%= reflinks(verb.reflinks.list) %} +{% } %} {% } %} diff --git a/docs/src/readme/layouts/full.md b/docs/src/readme/layouts/full.md index ef716a63..5b8cf2d4 100644 --- a/docs/src/readme/layouts/full.md +++ b/docs/src/readme/layouts/full.md @@ -4,10 +4,11 @@ {% body %} -{% var list = get("verb.related.list") || [] %}{% if (list.length) { %} +{% if (verb.related && verb.related.list && verb.related.list.length) { %} ## Related projects -{%= related(list) %} +{%= related(verb.related.list) %} {% } %} + ## Running tests {%= include("tests") %} @@ -25,7 +26,10 @@ {%= include("footer") %} -{% var links = get("verb.reflinks") || [] %} -{% if (links.length) { %} -{%= reflinks(links) %} +{% if (verb.reflinks) { %} +{% if (Array.isArray(verb.reflinks) && verb.reflinks.length) { %} +{%= reflinks(verb.reflinks) %} +{% } else if (verb.reflinks.list && verb.reflinks.list.length) { %} +{%= reflinks(verb.reflinks.list) %} +{% } %} {% } %} From 05ab86dabdc0aa2259ef8ccd503f02a5b9c95442 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 11 Dec 2015 19:43:45 -0500 Subject: [PATCH 072/282] get data, middleware and plugins loading as expected on all instances --- .travis.yml | 1 + .verb.md | 9 ++- cli.js | 49 ++++++++++++++-- index.js | 3 + lib/config.js | 94 ++++++++++++++++--------------- lib/runner/cli.js | 45 +++++++++++++-- lib/runner/create.js | 7 ++- lib/runner/data.js | 42 ++++++++++++++ lib/runner/helpers.js | 7 +-- lib/runner/middleware.js | 32 ++++++----- lib/runner/preload.js | 7 +++ lib/runner/templates.js | 6 +- lib/updater.js | 102 ++++++++++++++++----------------- lib/utils.js | 19 ++++++- package.json | 42 ++++++++------ readme.md | 118 ++++++++++++++++++++++++++++++++++++++- verbfile.js | 54 ++++++++++++++---- 17 files changed, 468 insertions(+), 169 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6e658ef..6caa76a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ sudo: false language: node_js node_js: - "stable" + - "4" - "0.12" - "0.10" matrix: diff --git a/.verb.md b/.verb.md index 422f2a22..989b2342 100644 --- a/.verb.md +++ b/.verb.md @@ -1,9 +1,5 @@ ---- -layout: full ---- -{% if (typeof options !== 'undefined' && options.toc) { %} + -{% } %} **TODO** @@ -27,6 +23,9 @@ layout: full To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. **Install verb** + +## Install + {%= include("install-global") %} **Install a "verb app"** diff --git a/cli.js b/cli.js index f1ed1090..e04fbb57 100755 --- a/cli.js +++ b/cli.js @@ -1,10 +1,13 @@ #!/usr/bin/env node +var fs = require('fs'); var path = require('path'); var gm = require('global-modules'); var processArgv = require('base-argv').processArgv(); var minimist = require('minimist'); +var defaults = require('./lib/runner/defaults'); var preload = require('./lib/runner/preload'); +var data = require('./lib/runner/data'); var utils = require('./lib/utils'); var create = require('./').create; @@ -19,7 +22,9 @@ var args = minimist(process.argv.slice(2), { * this creates a custom `Verb` constructor . */ -var Verb = create(preload); +var Verb = create(function(app, base, env) { + return preload(app, base, env || app.env); +}); /** * Register `runner` mixin with `Verb`, wich pre-loads @@ -40,14 +45,23 @@ Verb.mixin(utils.runner('verb', 'verbApp', preload)); */ var verb = Verb.getConfig('verbfile.js', __dirname); +verb.on('error', function(err) { + console.log(err.stack); +}); // get `verb` property from package.json, if it exists var userConfig = verb.get('env.user.pkg.verb'); if (userConfig) { verb.config.process(userConfig); - verb.emit('config-processed'); } +// else if (!verb.get('env.user.pkg') || !fs.existsSync('.verb.md')) { +// verb.emit('config-processed'); +// verb.enable('ask.verbmd'); +// } +verb.emit('config-processed'); + + /** * Resolve user config files, eg. `verbfile.js`. */ @@ -59,17 +73,39 @@ verb.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); */ verb.cli.map('verbApps', function(tasks) { - // ensure this is run after other configuration is complete setImmediate(function() { - if (verb.enabled('display tasks')) { + // preload(verb, verb.base, verb.env); + verb.questions.set('verbmd', 'Looks like ".verb.md" is missing, want to add one?'); + // verb.data(verb.get('env.user.pkg') || {}); + + if (verb.enabled('ask.verbmd')) { + verb.ask('verbmd', function(err, answers) { + if (err) throw err; + + if (!answers.verbmd) { + console.log('no worries!'); + return; + } + + console.log('got it! copying now.'); + + verb.build('verbmd', function(err) { + if (err) throw err; + console.log('done!'); + }); + }); + return; + } + + if (verb.enabled('tasks.display')) { console.log(utils.colors.gray(' List of verbApps and their registered tasks:')); verb.displayTasks(); utils.timestamp('done'); return; } - if (verb.enabled('choose tasks')) { + if (verb.enabled('tasks.choose')) { verb.chooseTasks(function(err, results) { if (err) throw err; run([results.verbApps]); @@ -89,6 +125,8 @@ verb.cli.map('verbApps', function(tasks) { run(tasks); function run(tasks) { + data.updateData(verb, verb.base, verb.env); + verb.on('error', function(err) { console.log(err); process.exit(1); @@ -100,7 +138,6 @@ verb.cli.map('verbApps', function(tasks) { process.exit(0); }); } - }); }); diff --git a/index.js b/index.js index 8c32b87f..69a58a42 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ var path = require('path'); var async = require('async'); var Assemble = require('assemble-core'); +var expand = require('./lib/expand'); var utils = require('./lib/utils'); var env = require('./lib/env'); @@ -41,6 +42,7 @@ function create(preload) { Assemble.apply(this, arguments); this.isVerb = true; + this.isApp = true; this.verbApps = {}; var opts = this.options; @@ -49,6 +51,7 @@ function create(preload) { this.use(utils.middleware(opts)) .use(utils.pipeline(opts)) .use(utils.store()) + .use(expand()) .use(env()) this.engine(['md', 'text'], require('engine-base'), { diff --git a/lib/config.js b/lib/config.js index 9b78c2b5..bebd29b8 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,42 +1,36 @@ 'use strict'; +var fs = require('fs'); var path = require('path'); var updater = require('./updater'); var utils = require('./utils'); module.exports = function(verb, base, env) { - if (!verb.isVerb) return; - + // if (!verb.isVerb) return; + verb.use(updater()); verb.config - .map('option') - .map('save', function(obj) { - var pkg = verb.root.getView('package.json'); - if (!pkg) return; - - if (!pkg.json.verb) { - pkg.json.verb = {}; - } - - var update = updater(pkg.json.verb); - for (var key in obj) { - update(key, obj[key]); + .map('init', function(val) { + if (val === true) { + verb.enable('init'); } }) + .map('option') + .map('update', function(obj) { + var view = verb.docs.getView('package.json'); + if (!view) return; - .map('save', function(obj) { - var pkg = verb.get('env.pkg'); + var pkg = env.user.pkg; if (!pkg) return; - if (!pkg.verb) { - pkg.verb = {}; + try { + pkg.verb = pkg.verb || {}; + verb.update(pkg.verb, obj); + view.json = pkg; + } catch (err) { + console.log(err); } - - var update = updater(pkg.verb); - for (var key in obj) { - update(key, obj[key]); - } - }) + }); /** * Helpers @@ -77,6 +71,10 @@ module.exports = function(verb, base, env) { var cwd = verb.get('env.user.cwd'); var opts = {}; + if (typeof plugins === 'string') { + plugins = [plugins]; + } + if (Array.isArray(plugins)) { plugins = plugins.reduce(function(acc, plugin) { acc[plugin] = {}; @@ -86,12 +84,18 @@ module.exports = function(verb, base, env) { for (var key in plugins) { var name = path.basename(key, path.extname(key)); + var plugin = plugins[key]; + if (name === 'index') { - name = path.basename(cwd); + var dir = path.resolve(cwd); + var fp = utils.tryResolve(dir); + if (fs.existsSync(fp)) { + opts = plugin; + plugin = utils.tryRequire(fp); + name = path.basename(dir); + } } - var plugin = plugins[key]; - if (typeof plugin === 'string') { // use `name` // resolve `plugin` @@ -105,7 +109,6 @@ module.exports = function(verb, base, env) { if (typeof plugin !== 'function') { throw new Error('cannot load plugin: ' + plugin); } - verb.plugin(name, opts, plugin); } }); @@ -118,23 +121,24 @@ module.exports = function(verb, base, env) { } }; -function loadPlugin(verb, name, plugin) { - if (typeof plugin === 'function') { - verb.plugin(name, {}, plugin); - return true; - } - if (typeof plugin === 'string') { - fn = tryRequire(plugin, cwd, verb.options); - verb.plugin(name, {}, fn); - return true; - } - if (utils.isObject(plugin)) { - fn = tryRequire(key, cwd, verb.options); - verb.plugin(name, plugin, fn); - return true; - } - return false; -} +// function loadPlugin(verb, name, plugin) { +// if (typeof plugin === 'function') { +// verb.plugin(name, {}, plugin); +// return true; +// } +// var fn; +// if (typeof plugin === 'string') { +// fn = tryRequire(plugin, cwd, verb.options); +// verb.plugin(name, {}, fn); +// return true; +// } +// if (utils.isObject(plugin)) { +// fn = tryRequire(key, cwd, verb.options); +// verb.plugin(name, plugin, fn); +// return true; +// } +// return false; +// } function content(val) { return typeof val === 'string' diff --git a/lib/runner/cli.js b/lib/runner/cli.js index fa297819..cbfeb84e 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -1,5 +1,6 @@ 'use strict'; +var path = require('path'); var utils = require('../utils'); module.exports = function(verb, base, env) { @@ -7,6 +8,10 @@ module.exports = function(verb, base, env) { throw new Error('the base-cli plugin should be registered on every instance'); } + verb.store.on('set', function(key, val) { + console.log('set >', key, val); + }); + verb.cli .map('ask', function(key) { if (key === true) { @@ -16,27 +21,55 @@ module.exports = function(verb, base, env) { verb.option('questions.init', key); } }) - .map('choose', function(key) { - if (key === true) { - verb.enable('choose tasks'); + .map('open', function(name) { + if (name === 'answers') { + var dest = verb.get('questions.dest'); + if (dest) { + console.log('opening answers data directory >', dest); + utils.opn(dest); + process.exit(0); + } + } else if (name === 'store') { + var dir = path.dirname(verb.get('store.path')); + if (dir) { + console.log('opening store data directory >', dir); + utils.opn(dir); + process.exit(0); + } } }) + .map('save', function(val) { + verb.store.set(val); + }) + .map('init', function(fp) { + verb.enable('init'); + console.log(arguments); + }) .map('cwd', function(fp) { verb.option('cwd', fp); }) .map('data', function(val) { verb.visit('data', val); + console.log(verb.cache.data) }) .map('md', function(key, val) { console.log('cli:md', verb.views) verb.enable('ask.verbmd'); }) - .map('save', function(obj) { - verb.config.process({pkg: obj}); + // .map('config', function(obj) { + // verb.config.process({update: obj}); + // }) + + // task related + verb.cli + .map('choose', function(key) { + if (key === true) { + verb.enable('tasks.choose'); + } }) .map('tasks', function(key) { if (key === true) { - verb.enable('display tasks'); + verb.enable('tasks.display'); } }) diff --git a/lib/runner/create.js b/lib/runner/create.js index 135f9741..9c177c71 100644 --- a/lib/runner/create.js +++ b/lib/runner/create.js @@ -8,8 +8,8 @@ var path = require('path'); module.exports = function(options) { return function(verb) { - create(verb, 'root', 'renderable'); - create(verb, 'docs', 'renderable'); + create(verb, 'docs'); + create(verb, 'libFiles'); create(verb, 'badges', 'partial'); create(verb, 'includes', 'partial'); create(verb, 'layouts', 'layout'); @@ -21,7 +21,8 @@ function create(verb, name, type) { if (verb[name]) return; verb.create(name, { - viewType: [type], + engine: 'text', + viewType: [type || 'renderable'], renameKey: function(key) { return path.basename(key, path.extname(key)); } diff --git a/lib/runner/data.js b/lib/runner/data.js index 5e0de8d4..924d5d84 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -5,6 +5,48 @@ module.exports = function(verb, base, env) { runner: { name: 'verb', url: 'https://github.com/verbose/verb' + }, + verb: { + reflinks: [], + related: { + list: [] + } } }); }; + +module.exports.updateData = function(app, base, env) { + app.questions.enqueue('author', 'name', 'description'); + + var plugins = app.get('env.argv.plugins') || app.plugins; + var pkg = env.user.pkg; + var config = pkg.app || {}; + + app.option(config.options || {}); + + // load package.json data and user options onto `app.cache.data` + app.data({options: app.options}); + app.data(pkg); + app.data(app.base.get('cache.expanded')); + app.data({license: license(pkg, app.options)}); +}; + +/** + * Format license + */ + +function license(pkg, options) { + options = options || {}; + if (typeof options.license === 'string') { + return options.license; + } + var str = pkg.license; + if (Array.isArray(pkg.licenses)) { + str = pkg.licenses[0]; + } + if (typeof str === 'undefined') { + return ''; + } + return 'Released under the ' + str + ' license.'; +} + diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 2bbdc362..9d9b425d 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -3,6 +3,8 @@ var utils = require('../utils'); module.exports = function(verb, base, env) { + var apidocs = require('helper-apidocs'); + verb.helper('log', function(msg) { console.log.apply(console, arguments); }); @@ -15,11 +17,8 @@ module.exports = function(verb, base, env) { return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; }); - // verb.helper('codelinks', require('helper-codelinks')(verb)); - // verb.helper('multiToc', require('helper-toc')(verb.option('toc'))); - // verb.helper('coverage', require('helper-coverage')); + verb.helper('apidocs', apidocs({})); verb.helper('date', require('helper-date')); - verb.helper('apidocs', require('template-helper-apidocs')); verb.helper('copyright', require('helper-copyright')({ linkify: true })); diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index fb57058a..e05869ad 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -3,25 +3,21 @@ var utils = require('../utils'); module.exports = function(verb, base, env) { - verb.preRender(/\.md/, function(file, next) { - verb.option('toc.noinsert', true); - utils.toc(verb)(file, next); - }); - - verb.onLoad(/\.json/, function(file, next) { - file.json = JSON.parse(file.content); + verb.onLoad(/\.verb\.md/, function(file, next) { + file.path = 'readme.md'; + file.dest = 'readme.md'; next(); }); - verb.preWrite(/\.json/, function(file, next) { - file.content = JSON.stringify(file.json, null, 2); - file.dest = 'package.json'; - next(); + verb.preRender(/\.md/, function(file, next) { + if (file.options.viewType.indexOf('partial') > -1) { + return next(); + } + utils.toc(verb)(file, next); }); - verb.onLoad(/\.verb\.md/, function(file, next) { - file.path = 'readme.md'; - file.dest = 'readme.md'; + verb.preWrite(/package\.json$/, function(file, next) { + file.dest = 'package.json'; next(); }); @@ -29,6 +25,14 @@ module.exports = function(verb, base, env) { if (file.options.viewType.indexOf('partial') > -1) { return next(); } + + // working on a better method for this! + var layout = verb.get('env.user.pkg.verb.layout'); + if (typeof layout !== 'undefined') { + file.layout = layout; + return next(); + } + if (needsLayout(file.content) && typeof file.layout !== 'string') { file.layout = 'default'; } diff --git a/lib/runner/preload.js b/lib/runner/preload.js index b0b33ae2..d345ff25 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -2,6 +2,7 @@ var utils = require('../utils'); var config = require('../config'); +var expand = require('../expand'); var templates = require('./templates'); var defaults = require('./defaults'); var cli = require('./cli'); @@ -21,7 +22,13 @@ module.exports = function preload(verb, base, env) { })); verb.on('register', function(name, app) { + var toc = verb.get('env.user.pkg.verb.toc'); + if (typeof toc === 'undefined') { + verb.set('env.user.pkg.verb.toc', {insert: true}); + } + defaults(app, base, env); app.use(templates()); + app.use(expand()); }); }; diff --git a/lib/runner/templates.js b/lib/runner/templates.js index 1ff1bdf1..d3aecf6f 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -26,9 +26,11 @@ module.exports = function(options) { var userDocs = path.join.bind(path, process.cwd()); if (fs.existsSync(userDocs())) { - verb.root.loadViews('*', {cwd: userDocs(), dot: true}); - verb.docs.loadViews('.verb.md', {cwd: userDocs()}); + // api docs + verb.libFiles.loadViews(['*.js', 'lib/*.js'], {cwd: userDocs()}); + // templates + verb.docs.loadViews('*', {cwd: userDocs(), dot: true}); verb.includes.loadViews('*.md', {cwd: userDocs('docs/includes')}); verb.layouts.loadViews('*.md', {cwd: userDocs('docs/layouts')}); verb.docs.loadViews('*.md', {cwd: userDocs('docs')}); diff --git a/lib/updater.js b/lib/updater.js index 1a316427..6829bbd4 100644 --- a/lib/updater.js +++ b/lib/updater.js @@ -1,87 +1,55 @@ 'use strict'; +var Schema = require('./schema'); var utils = require('./utils'); -var schema = { - related: { - type: 'object', - list: { - type: 'array' - }, - // validate: function() { - - // } - }, - reflinks: { - type: 'array|object', - list: { - type: 'array' - } - }, - plugins: { - type: 'array|object' - } -}; - -schema.validate = function(prop, val) { - var target = utils.get(schema, prop); - if (typeof target === 'undefined') { - return; - } - if (typeof target.validate === 'function') { - target.validate(val); - } - - if (!target.type) return; - var types = target.types.split(/\W/); - if (types.indexOf(typeOf(val)) > -1) { - throw new TypeError('expected "' + val + '" to be ' + article(types)); - } -}; - /** * Adds get/set methods to verb env */ module.exports = function(options) { return function(verb) { + utils.define(verb, 'update', function(pkg, args) { + if (!pkg) return; + + var schema = new Schema(pkg, options); + // console.log(schema) + var config = utils.mapper(schema) + .map('set') + .map('del'); - utils.define(verb, 'update', function(pkg) { - return function(prop, val) { - update(pkg, prop, val, schema); - return pkg; - }; + config.process(args); }); }; }; -function article(types) { - if (typeof types === 'string' || types.length === 1) { - return (/[aeiou]/.test(types[0]) ? 'an ' : 'a ') + types[0]; - } - return types.map(function(type) { - return article(type); - }).join(' or '); +function updater(pkg) { + } -function update(obj, prop, newVal, schema) { - if (schema) { - schema.validate(prop, newVal); - } + +function update(obj, prop, newVal, schema, method) { + if (schema && schema.validate) { + newVal = schema.validate(prop, newVal); + } var val = utils.get(obj, prop); - if (typeof val === 'undefined' || utils.isPrimative(newVal)) { + if (typeof val === 'undefined' || utils.isPrimitive(newVal)) { utils.set(obj, prop, newVal); return obj; } if (utils.isObject(newVal)) { - utils.set(obj, prop, utils.extend({}, val, newVal)); + for (var key in newVal) { + update(val, key, newVal[key], schema[prop]); + } return obj; } if (Array.isArray(newVal)) { var len = newVal.length; + val = utils.arrayify(val); + while (len--) { if (val.indexOf(newVal[len]) < 0) { val.push(newVal[len]); @@ -91,3 +59,27 @@ function update(obj, prop, newVal, schema) { return obj; } } + +// module.exports = function(options) { +// var Schema = new Schema(options); + +// return function(verb) { +// utils.define(verb, 'update', function(pkg, args) { +// if (!pkg) return; + +// var config = utils.mapper(schema) +// .map('set', function(obj) { +// for(var key in obj) { +// update(pkg, key, obj[key], schema, 'set'); +// } +// }) +// .map('del', function(obj) { +// for(var key in obj) { +// update(pkg, key, obj[key], schema, 'del'); +// } +// }); + +// config.process(args); +// }); +// }; +// }; diff --git a/lib/utils.js b/lib/utils.js index 3745bb92..cfa7ecbd 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -25,10 +25,12 @@ require('assemble-loader', 'loader'); require('base-questions', 'ask'); require('base-pipeline', 'pipeline'); require('base-runner', 'runner'); +require('template-toc', 'toc'); require('base-store', 'store'); require('base-list', 'list'); // misc +require('opn'); require('async'); require('define-property', 'define'); require('extend-shallow', 'extend'); @@ -38,13 +40,14 @@ require('has-glob'); require('is-primitive'); require('is-valid-glob'); require('isobject', 'isObject'); +require('kind-of', 'typeOf'); +require('map-config', 'mapper'); require('matched', 'glob'); require('object-visit', 'visit'); require('resolve'); require('resolve-dir'); require('set-value', 'set'); require('stream-exhaust', 'exhaust'); -require('template-toc', 'toc'); require('through2', 'through'); // cli @@ -52,12 +55,26 @@ require('success-symbol'); require('ansi-colors', 'colors'); require('time-stamp', 'stamp'); +// expanders +require('parse-author'); + /** * Restore `require` */ require = fn; +/** + * Cast val to an array. + */ + +utils.arrayify = function(val) { + if (typeof val === 'undefined' || val === null || val === '') { + return []; + } + return Array.isArray(val) ? val : [val]; +}; + /** * Convenience method for loading files. */ diff --git a/package.json b/package.json index ef255081..8aee756a 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,11 @@ "base-questions": "^0.2.1", "base-runner": "^0.4.3", "base-store": "^0.3.0", - "common-middleware": "^0.1.3", + "common-middleware": "^0.2.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", "extend-shallow": "^2.0.1", - "get-value": "^2.0.0", + "get-value": "^2.0.2", "global-modules": "^0.2.0", "has-glob": "^0.1.1", "helper-copyright": "^2.0.0", @@ -50,16 +50,21 @@ "is-primitive": "^2.0.0", "is-valid-glob": "^0.3.0", "isobject": "^2.0.0", + "kind-of": "^3.0.2", "lazy-cache": "^1.0.2", + "map-config": "^0.3.0", "matched": "^0.3.2", "minimist": "^1.2.0", "object-visit": "^0.3.4", + "opn": "^3.0.3", + "parse-author": "^0.2.0", "resolve": "^1.1.6", "resolve-dir": "^0.1.0", - "set-value": "^0.3.1", + "set-value": "^0.3.2", "stream-exhaust": "^1.0.1", "success-symbol": "^0.1.0", - "template-toc": "^0.4.0", + "tableize": "^0.1.0", + "template-toc": "^0.5.0", "time-stamp": "^0.1.3" }, "devDependencies": { @@ -80,14 +85,13 @@ "gulp-mocha": "^2.2.0", "is-buffer": "^1.1.0", "istanbul": "^0.4.1", - "kind-of": "^3.0.2", "load-pkg": "^3.0.0", - "mocha": "*", + "mocha": "_", "parser-front-matter": "^1.3.0", "resolve-glob": "^0.1.7", "rimraf": "^2.4.4", "scaffold": "^0.2.1", - "should": "*", + "should": "_", "sinon": "^1.17.2", "swig": "^1.4.2", "template-helper-apidocs": "^0.4.4", @@ -110,23 +114,29 @@ "yeoman" ], "verb": { - "plugins": { - "gulp-format-md": { - "name": "format-md" - } - }, - "options": { - "toc": true - }, + "layout": "full", "related": { "list": [ "base-methods", "assemble-core", "resolve-modules", "base-runner", - "base-resolver" + "base-resolver", + "micromatch", + "assemble" ] }, + "plugins": { + "gulp-format-md": { + "name": "format-md" + } + }, + "options": { + "toc": { + "render": true, + "insert": false + } + }, "reflinks": [ "verb", "assemble", diff --git a/readme.md b/readme.md index 94acb8f3..cbf0a278 100644 --- a/readme.md +++ b/readme.md @@ -47,6 +47,8 @@ To get started, you'll first need to install `verb` globally using [npm][], alon **Install verb** +## Install + Install globally with [npm](https://www.npmjs.com/) ```sh @@ -191,6 +193,118 @@ $ verb a.b.c:foo ## API +### [create](index.js#L25) + +Create a customized `Verb` constructor that calls the given `fn` when an instance is created. + +**Params** + +* `fn` **{Function}** +* `returns` **{Function}** + +**Example** + +```js +var Verb = create(function(verb) { + // add stuff to `verb` +}); +var verb = new Verb(); +``` + +### [Verb](index.js#L38) + +Create an instance of `Verb` with the given `options` + +**Params** + +* `options` **{Object}**: Configuration options to initialize with. + +**Example** + +```js +var Verb = require('verb'); +var verb = new Verb(options); +``` + +### [.process](index.js#L88) + +Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options`. This allows plugin pipelines to be programmatically built-up and dynamically changed on-the-fly. + +**Params** + +* `files` **{Object}** +* `options` **{Object}** +* `cb` **{Function}** +* `returns` **{Stream}**: Returns a [vinyl](http://github.com/gulpjs/vinyl) src stream + +**Example** + +```js +verb.process({src: ['a.txt', 'b.txt']}, options); +``` + +### [.each](index.js#L112) + +Verb `files` configurations in parallel. + +**Params** + +* `config` **{Object}** +* `cb` **{Function}** + +**Example** + +```js +verb.each(files, function(err) { + if (err) console.log(err); +}); +``` + +### [.eachSeries](index.js#L134) + +Verb `files` configurations in series. + +**Params** + +* `config` **{Object}** +* `cb` **{Function}** + +**Example** + +```js +verb.eachSeries(files, function(err) { + if (err) console.log(err); +}); +``` + +### [.scaffold](index.js#L166) + +Verb files from a declarative [scaffold](https://github.com/jonschlinkert/scaffold) configuration. + +**Params** + +* `scaffold` **{Object}**: Scaffold configuration +* `cb` **{Function}**: Callback function + +**Example** + +```js +var Scaffold = require('scaffold'); +var scaffold = new Scaffold({ + options: {cwd: 'source'}, + posts: { + src: ['content/*.md'] + }, + pages: { + src: ['templates/*.hbs'] + } +}); + +verb.scaffold(scaffold, function(err) { + if (err) console.log(err); +}); +``` + ### .getConfig Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. @@ -323,10 +437,12 @@ Use `verb-` as the prefix, followed by any words of your choosing to describe th ## Related projects +* [assemble](https://www.npmjs.com/package/assemble): Static site generator for Grunt.js, Yeoman and Node.js. Used by Zurb Foundation, Zurb Ink, H5BP/Effeckt,… [more](https://www.npmjs.com/package/assemble) | [homepage](http://assemble.io) * [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) * [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) * [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) * [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) +* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. Just… [more](https://www.npmjs.com/package/micromatch) | [homepage](https://github.com/jonschlinkert/micromatch) * [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) ## Running tests @@ -355,4 +471,4 @@ Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 09, 2015._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on December 11, 2015._ \ No newline at end of file diff --git a/verbfile.js b/verbfile.js index 500f7835..5ef606eb 100644 --- a/verbfile.js +++ b/verbfile.js @@ -12,7 +12,7 @@ module.exports = function(verb, base, env) { verb.questions.options.forceAll = true; } - var tasks = verb.get('env.argv.tasks') || ['readme']; + var tasks = verb.get('env.argv.tasks') || ['readme', 'package']; /** * Readme task @@ -22,25 +22,27 @@ module.exports = function(verb, base, env) { verb.questions.enqueue('author', 'name', 'description'); var plugins = verb.get('env.argv.plugins') || verb.plugins; + var pkg = env.user.pkg; + var config = pkg.verb || {}; + + verb.option(config.options || {}); // load package.json data and user options onto `verb.cache.data` verb.data({options: verb.options}); - verb.data(env.user.pkg); - verb.data({license: 'Released under the MIT license.'}); + verb.data(pkg); + verb.data(verb.base.get('cache.expanded')); + verb.data({license: license(pkg, verb.options)}); - // ask pre-configured questions, but only if they don't have - // answers yet + // ask pre-configured questions, but only if + // they don't have answers yet verb.ask(function(err, answers) { - if (err) { - cb(err); - return; - } + if (err) return cb(err); // placeholder for something better verb.emit('info', 'plugins', Object.keys(plugins).join(', ')); verb.toStream('docs', function(key) { - return key === '.verb' || key === 'package'; + return key === '.verb'; }) .pipe(handle('onStream')) .pipe(verb.renderFile('text', answers)) @@ -54,6 +56,17 @@ module.exports = function(verb, base, env) { }); }); + verb.task('package', function(cb) { + verb.toStream('docs', function(key, view) { + return view.basename === 'package.json'; + }) + .pipe(handle('preWrite')) + .pipe(verb.dest(dest())) + .pipe(utils.exhaust(handle('postWrite'))) + .on('error', cb) + .on('finish', cb); + }); + verb.task('verbmd', function(cb) { verb.ask(function(err, answers) { if (err) return cb(err); @@ -105,7 +118,7 @@ module.exports = function(verb, base, env) { function dest(dest) { return function(file) { var fp = file.dest || dest || ''; - file.base = path.dirname(fp); + file.base = fp ? path.dirname(fp) : file.base; file.path = fp; file.basename = file.basename.replace(/^_/, '.'); file.basename = file.basename.replace(/^\$/, ''); @@ -113,6 +126,25 @@ function dest(dest) { }; } +/** + * Format license + */ + +function license(pkg, options) { + options = options || {}; + if (typeof options.license === 'string') { + return options.license; + } + var str = pkg.license; + if (Array.isArray(pkg.licenses)) { + str = pkg.licenses[0]; + } + if (typeof str === 'undefined') { + return ''; + } + return 'Released under the ' + str + ' license.'; +} + /** * Handle render errors */ From 9b005ff9897b37c8393b022e7bb39a84713463cb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 11 Dec 2015 19:45:37 -0500 Subject: [PATCH 073/282] adds `expand` and `schema`, both wip - schema will be used for validating verb config values - expand will be used to convert string values to their object equivalents in memory (e.g. the `author` string in package.json will be expanded to an object for templates) --- lib/expand.js | 21 +++++++ lib/schema.js | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 lib/expand.js create mode 100644 lib/schema.js diff --git a/lib/expand.js b/lib/expand.js new file mode 100644 index 00000000..f2bc6be7 --- /dev/null +++ b/lib/expand.js @@ -0,0 +1,21 @@ +'use strict'; + +var utils = require('./utils'); + +/** + * Adds get/set methods to verb env + */ + +module.exports = function(options) { + return function(verb) { + var pkg = verb.get('env.user.pkg'); + if (!pkg) return; + + var exp = utils.extend({}, pkg); + if (typeof pkg.author === 'string') { + exp.author = utils.parseAuthor(pkg.author); + } + + verb.base.set('cache.expanded', exp); + }; +}; diff --git a/lib/schema.js b/lib/schema.js new file mode 100644 index 00000000..02be1664 --- /dev/null +++ b/lib/schema.js @@ -0,0 +1,157 @@ +'use strict'; + +var tableize = require('tableize'); +var utils = require('./utils'); + +function Field(type, options) { + if (utils.isObject(type)) { + options = type; + type = options.type || options.types; + } + options = options || {}; + this.types = typeof type === 'string' + ? type.split(/\W/) + : type; + + for (var key in options) { + this[key] = options[key]; + } + if (!options.hasOwnProperty('optional')) { + this.optional = true; + } + if (!options.hasOwnProperty('required')) { + this.required = false; + } +} + +Field.prototype.validate = function(val) { + return this.types.indexOf(utils.typeOf(val)) > -1; +}; + +function Schema(options) { + this.options = options || {}; + this.errors = []; + this.fields = {}; + + if (this.options.fields) { + for (var key in this.options.fields) { + this.fields[key] = this.options.fields[key]; + } + } +} + +Schema.prototype.field = function(name, type, options) { + return (this.fields[name] = new Field(type, options)); +}; + +Schema.prototype.get = function(name, prop) { + return utils.get(this.fields, name, prop); +}; + +Schema.prototype.isValid = function(name, val) { + var field = this.get(name); + if (this.options.knownOnly === true && typeof field === 'undefined') { + return false; + } + if (typeof field === 'undefined') { + return true; + } + return field.validate(val); +}; + +Schema.prototype.isOptional = function(name) { + return this.get(name, 'optional'); +}; + +Schema.prototype.isRequired = function(name) { + return this.get(name, 'required'); +}; + +Schema.prototype.error = function(method, msg) { + this.errors.push({method: method, msg: msg}); + return this; +}; + +Schema.prototype.validate = function(context) { + if (!utils.isObject(context)) { + this.error('schema.validate', 'invalid context object'); + } else { + var ctx = tableize(context); + for (var key in ctx) { + var field = ctx[key]; + var isValid = this.isValid(key, field); + if (!isValid) { + var types = this.get(key, 'types'); + var val = JSON.stringify(field); + this.error(key, 'expected "' + field + '" to be ' + article(types)); + } + } + } + return this.errors; +}; + +var schema = new Schema(); + +schema.field('data', ['object']); +schema.field('options', ['object']); +schema.field('plugins', ['array', 'object']); +schema.field('reflinks', ['array', 'object']); +schema.field('reflinks.list', ['array']); +schema.field('related', ['array', 'object']); +schema.field('reflinks.list', ['array'], { + validate: function(val) { + return Array.isArray(val); + } +}); + +console.log(schema.isValid('reflinks.list', '')) +console.log(schema.isValid('whatever')) + +var pkg = { + verb: { + reflinks: 'foo', + related: { + list: ['foo', 'bar', 'baz'] + }, + } +}; + +var results = schema.validate(pkg.verb); +// console.log(results) + +function article(types) { + if (typeof types === 'string' || types.length === 1) { + return (/^[aeiou]/.test(types) ? 'an ' : 'a ') + types; + } + return types.map(function(type) { + return article(type); + }).join(' or '); +} + +/** + * Expose `Schema` + */ + +module.exports = Schema; + +Schema.prototype.validate = function(prop, val) { + var target = utils.get(schema, prop); + if (typeof target === 'undefined') { + return val; + } + if (typeof target.fn === 'function') { + val = target.fn(val, target); + } + if (typeof target.validate === 'function') { + target.validate(val); + } + + if (!target.type) return val; + var types = target.type.split(/\W/); + if (types.indexOf(utils.typeOf(val)) > -1) { + val = JSON.stringify(val); + throw new TypeError(prop + ' expects "' + val + '" to be ' + article(types)); + } + return val; +}; + From 5adf946bc965633a44171f1a26831f242d682419 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:18:04 -0500 Subject: [PATCH 074/282] fix delims --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05105c9e..5e164f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,4 +2,4 @@ ## v0.9 (pending) -- `body` tag delimiters have changed. the layout tag is now `{{body}}` (instead of `<<% body %>>`) +- `body` tag delimiters have changed. the layout tag is now `{% body %}` (instead of `<<% body %>>`) From 835da85ec70ddb311d1ac1f529c73fff077a3336 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:18:40 -0500 Subject: [PATCH 075/282] add contributing template, needs to be updated --- docs/src/files/contributing.md | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/src/files/contributing.md diff --git a/docs/src/files/contributing.md b/docs/src/files/contributing.md new file mode 100644 index 00000000..d0a1b15d --- /dev/null +++ b/docs/src/files/contributing.md @@ -0,0 +1,44 @@ +# Contributing to {{name}} + +First and foremost, thank you! We appreciate that you want to contribute to {{name}}, your time is valuable, and your contributions mean a lot to us. + +**What does "contributing" mean?** + +Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following: + +- Updating or correcting documentation +- Feature requests +- Bug reports + + +## Issues + +**Before creating an issue** + +Please make sure you're creating one in the right place: + +- do you have a template syntax question? Like how to accomplish something with handlebars? The best place to get answers for this is [stackoverflow.com][], the [handlebars docs](handlebarsjs.com), or the documentation for the template engine you're using. +- Are you having an issue with an {{name}} feature that is powered by an underlying lib? This is sometimes difficult to know, but sometimes it can be pretty easy to find out. For example, if you use a glob pattern somewhere and you found what you believe to be a matching bug, that would probably be an issue for [node-glob][] or [micromatch][] + +**Creating an issue** + +Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue: + +- what version of assemble are you using? +- is the issue helper-related? If so, this issue should probably be opened on the repo related to the helper being used. +- do you have any custom helpers defined? Is the issue related to the helper itself, data (context) being passed to the helper, or actually registering the helper in the first place? +- are you using middleware? +- any plugins? + + +## Above and beyond + +Here are some tips for creating idiomatic issues. Taking just a little bit extra time will make your issue easier to read, easier to resolve, more likely to be found by others who have the same or similar issue in the future. + +- Take some time to learn basic markdown. This [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) is super helpful, as is the GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/). +- Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/). +- use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others +- use syntax highlighting by adding the correct language name after the first "code fence" + +[node-glob]: https://github.com/isaacs/node-glob +[micromatch]: https://github.com/jonschlinkert/micromatch \ No newline at end of file From b90054e45fc073dbbf6e1f0a601e1e0d647be93c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:18:56 -0500 Subject: [PATCH 076/282] adds `description` to related list sections --- docs/src/readme/layouts/default.md | 1 + docs/src/readme/layouts/full.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index 6c63df4e..bfcf75f1 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -13,6 +13,7 @@ {% if (verb.related && verb.related.list && verb.related.list.length) { %} ## Related projects +{%= verb.related.description || '' %} {%= related(verb.related.list) %} {% } %} diff --git a/docs/src/readme/layouts/full.md b/docs/src/readme/layouts/full.md index 5b8cf2d4..49f43ed2 100644 --- a/docs/src/readme/layouts/full.md +++ b/docs/src/readme/layouts/full.md @@ -6,6 +6,7 @@ {% if (verb.related && verb.related.list && verb.related.list.length) { %} ## Related projects +{%= verb.related.description || '' %} {%= related(verb.related.list) %} {% } %} From 95d40852bec4fb2bc834967a6f79fd794aa830ff Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:19:30 -0500 Subject: [PATCH 077/282] rename `verbApps` to `apps` --- lib/runner/preload.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/runner/preload.js b/lib/runner/preload.js index d345ff25..013ed083 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -17,8 +17,8 @@ module.exports = function preload(verb, base, env) { config(verb, base, env); cli(verb, base, env); - verb.use(utils.list('verbApps', { - method: 'verbApp' + verb.use(utils.list('apps', { + method: 'app' })); verb.on('register', function(name, app) { From d23ec8b7019ed89c7a27e47ae4d6d40290bbdf45 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:21:01 -0500 Subject: [PATCH 078/282] rename helper fn to `formatLicense` --- lib/runner/data.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/runner/data.js b/lib/runner/data.js index 924d5d84..1a3d7678 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -27,15 +27,15 @@ module.exports.updateData = function(app, base, env) { // load package.json data and user options onto `app.cache.data` app.data({options: app.options}); app.data(pkg); - app.data(app.base.get('cache.expanded')); - app.data({license: license(pkg, app.options)}); + app.data(app.base.get('cache.expanded') || {}); + app.data({license: formatLicense(pkg, app.options)}); }; /** * Format license */ -function license(pkg, options) { +function formatLicense(pkg, options) { options = options || {}; if (typeof options.license === 'string') { return options.license; From f2ecf1d1161dd8ee5986f2fc5e3090821d1e3e6e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:21:44 -0500 Subject: [PATCH 079/282] adds `expandConfig` util, to resolve templates in verb config --- cli.js | 67 +++++++++++++++------------------------------------ lib/config.js | 5 +++- lib/utils.js | 34 +++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/cli.js b/cli.js index e04fbb57..d9439be5 100755 --- a/cli.js +++ b/cli.js @@ -9,7 +9,9 @@ var defaults = require('./lib/runner/defaults'); var preload = require('./lib/runner/preload'); var data = require('./lib/runner/data'); var utils = require('./lib/utils'); -var create = require('./').create; +var colors = utils.colors; +var Verb = require('./'); +var create = Verb.create; // parse argv var args = minimist(process.argv.slice(2), { @@ -22,7 +24,7 @@ var args = minimist(process.argv.slice(2), { * this creates a custom `Verb` constructor . */ -var Verb = create(function(app, base, env) { +Verb = create(function(app, base, env) { return preload(app, base, env || app.env); }); @@ -35,7 +37,7 @@ var Verb = create(function(app, base, env) { * with our CLI plugins and middeware. */ -Verb.mixin(utils.runner('verb', 'verbApp', preload)); +Verb.mixin(utils.runner('verb', 'app', preload)); /** * Get the `base` instance of verb to use for @@ -45,61 +47,37 @@ Verb.mixin(utils.runner('verb', 'verbApp', preload)); */ var verb = Verb.getConfig('verbfile.js', __dirname); -verb.on('error', function(err) { - console.log(err.stack); -}); // get `verb` property from package.json, if it exists -var userConfig = verb.get('env.user.pkg.verb'); +var pkg = verb.get('env.user.pkg'); +var userConfig = pkg.verb; + if (userConfig) { - verb.config.process(userConfig); + var config = utils.expandConfig(userConfig, pkg); + verb.config.process(config); + verb.emit('config-loaded'); } -// else if (!verb.get('env.user.pkg') || !fs.existsSync('.verb.md')) { -// verb.emit('config-processed'); -// verb.enable('ask.verbmd'); -// } -verb.emit('config-processed'); - - /** * Resolve user config files, eg. `verbfile.js`. */ -verb.resolve({pattern: 'verb-*/verbfile.js', cwd: gm}); +verb.resolve('default', {pattern: 'verbfile.js', cwd: __dirname}); +verb.resolve('global', {pattern: 'verb-*/verbfile.js', cwd: gm}); /** - * Run verbApps and tasks + * Run apps and tasks */ -verb.cli.map('verbApps', function(tasks) { +verb.cli.map('apps', function(tasks) { // ensure this is run after other configuration is complete setImmediate(function() { - // preload(verb, verb.base, verb.env); - verb.questions.set('verbmd', 'Looks like ".verb.md" is missing, want to add one?'); - // verb.data(verb.get('env.user.pkg') || {}); - - if (verb.enabled('ask.verbmd')) { - verb.ask('verbmd', function(err, answers) { - if (err) throw err; + if (verb.enabled('generate.init')) { - if (!answers.verbmd) { - console.log('no worries!'); - return; - } - - console.log('got it! copying now.'); - - verb.build('verbmd', function(err) { - if (err) throw err; - console.log('done!'); - }); - }); - return; } if (verb.enabled('tasks.display')) { - console.log(utils.colors.gray(' List of verbApps and their registered tasks:')); + console.log(colors.gray(' Verb apps and registered tasks:')); verb.displayTasks(); utils.timestamp('done'); return; @@ -108,14 +86,14 @@ verb.cli.map('verbApps', function(tasks) { if (verb.enabled('tasks.choose')) { verb.chooseTasks(function(err, results) { if (err) throw err; - run([results.verbApps]); + run([results.apps]); }); return; } if (!tasks.length) { if (verb.tasks.hasOwnProperty('default')) { - tasks = [{verb: ['default']}]; + tasks = [{base: ['default']}]; } else { console.log(' no default task is defined.'); utils.timestamp('done'); @@ -127,12 +105,7 @@ verb.cli.map('verbApps', function(tasks) { function run(tasks) { data.updateData(verb, verb.base, verb.env); - verb.on('error', function(err) { - console.log(err); - process.exit(1); - }); - - verb.runVerbApps(tasks, function(err) { + verb.runApps(tasks, function(err) { if (err) return console.error(err); utils.timestamp('done'); process.exit(0); diff --git a/lib/config.js b/lib/config.js index bebd29b8..bf28e7ce 100644 --- a/lib/config.js +++ b/lib/config.js @@ -28,7 +28,7 @@ module.exports = function(verb, base, env) { verb.update(pkg.verb, obj); view.json = pkg; } catch (err) { - console.log(err); + new verb.VerbError('config', 'cannot update pkg.verb', err); } }); @@ -45,6 +45,9 @@ module.exports = function(verb, base, env) { */ verb.config + .map('sections', function(val) { + console.log(val) + }) .map('create', function(val) { verb.visit('create', val); }) diff --git a/lib/utils.js b/lib/utils.js index cfa7ecbd..60e69356 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -21,6 +21,7 @@ require('helper-related', 'related'); // plugins and extensions require('common-middleware', 'middleware'); +require('composer-runtimes', 'runtimes'); require('assemble-loader', 'loader'); require('base-questions', 'ask'); require('base-pipeline', 'pipeline'); @@ -32,6 +33,9 @@ require('base-list', 'list'); // misc require('opn'); require('async'); +require('expand'); +require('array-unique', 'unique'); +require('arr-flatten', 'flatten'); require('define-property', 'define'); require('extend-shallow', 'extend'); require('get-value', 'get'); @@ -64,6 +68,34 @@ require('parse-author'); require = fn; +utils.expandConfig = function(config, data) { + var expand, res; + try { + expand = utils.expand(config); + data = utils.extend({}, config, data); + res = expand(config, data); + } catch (err) { + return config; + } + + function format(obj) { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var val = obj[key]; + if (Array.isArray(val)) { + obj[key] = utils.unique(utils.flatten(val)).sort(); + } else if (utils.isObject(val)) { + obj[key] = format(val); + } else { + obj[key] = val; + } + } + } + return obj; + } + return format(res); +}; + /** * Cast val to an array. */ @@ -95,7 +127,7 @@ utils.globFiles = function(patterns, options) { */ utils.timestamp = function(msg) { - var time = ' ' + utils.colors.gray(utils.stamp('HH:mm:ss.ms', new Date())); + var time = '[' + utils.colors.gray(utils.stamp('HH:mm:ss', new Date())) + ']'; return console.log(time, msg, utils.colors.green(utils.successSymbol)); }; From 35b47fcb6af7781c294f68d1e7fc384952988ac5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:22:18 -0500 Subject: [PATCH 080/282] a number of middleware improvements, adds `base-diff` --- lib/runner/middleware.js | 64 +++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index e05869ad..2b191a16 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -1,40 +1,61 @@ 'use strict'; +var falsey = require('falsey'); +var diff = require('base-diff'); var utils = require('../utils'); -module.exports = function(verb, base, env) { - verb.onLoad(/\.verb\.md/, function(file, next) { - file.path = 'readme.md'; - file.dest = 'readme.md'; +module.exports = function(app, base, env) { + app.use(function fn(view) { + if (!view.isView) return fn; + + view.define('isType', function(type) { + return this.options.viewType.indexOf(type) !== -1; + }); + }); + + app.onLoad(/\.verb\.md/, diff.view('diffWords')); + app.onLoad(/\.verb\.md/, function(view, next) { + view.path = view.dest = 'readme.md'; next(); }); - verb.preRender(/\.md/, function(file, next) { - if (file.options.viewType.indexOf('partial') > -1) { - return next(); + var append = '\n\n_(TOC generated by [verb](https://github.com/verbose/verb))_'; + app.preRender(/\.md/, function(view, next) { + if (!view.isType('partial')) { + var toc = utils.toc(app, {append: append}); + return toc(view, next); } - utils.toc(verb)(file, next); + return next(); }); - verb.preWrite(/package\.json$/, function(file, next) { - file.dest = 'package.json'; + app.preWrite(/readme.md$/i, function(view, next) { + if (app.enabled('diff')) { + view.diff(); + } next(); }); - verb.preLayout(/\.md/, function(file, next) { - if (file.options.viewType.indexOf('partial') > -1) { - return next(); - } + app.preWrite(/package\.json$/, function(view, next) { + view.dest = 'package.json'; + next(); + }); + + app.preLayout(/\.md/, function(view, next) { + if (view.isType('partial')) return next(); // working on a better method for this! - var layout = verb.get('env.user.pkg.verb.layout'); + var layout = app.get('env.user.pkg.verb.layout'); if (typeof layout !== 'undefined') { - file.layout = layout; + view.layout = layout; return next(); } - if (needsLayout(file.content) && typeof file.layout !== 'string') { - file.layout = 'default'; + if (typeof view.layout === 'string' || isFalsey(view)) { + return next(); + } + + if (needsLayout(view.content)) { + view.layout = 'default'; } next(); }); @@ -47,3 +68,10 @@ function needsLayout(str) { } return false; } + +function isFalsey(view) { + if (typeof view.layout === 'undefined' && falsey(view.layout)) { + return true; + } + return false; +} From ede3ecde1b57fc66f13377ed5e48bf3ab2234fa6 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:23:00 -0500 Subject: [PATCH 081/282] move preload to `initVerb` method --- index.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 69a58a42..12fbb29d 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ var path = require('path'); var async = require('async'); -var Assemble = require('assemble-core'); +var Base = require('assemble-core'); var expand = require('./lib/expand'); var utils = require('./lib/utils'); var env = require('./lib/env'); @@ -40,10 +40,12 @@ function create(preload) { return new Verb(options); } - Assemble.apply(this, arguments); + this.env = {}; + + Base.apply(this, arguments); this.isVerb = true; this.isApp = true; - this.verbApps = {}; + this.apps = {}; var opts = this.options; this.name = opts.name || 'verb'; @@ -58,16 +60,20 @@ function create(preload) { delims: ['{%', '%}'] }); - if (typeof preload === 'function') { - preload.call(this, this, this.base, this.env); - } + this.initVerb(this); } /** * Inherit assemble-core */ - Assemble.extend(Verb); + Base.extend(Verb); + + Verb.prototype.initVerb = function(app) { + if (typeof preload === 'function') { + preload.call(this, this, this.base, this.env); + } + }; /** * Similar to [copy](#copy) but calls a plugin `pipeline` if passed From 8bdde4450e3994d2463db8846d577df3306c8e49 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:23:23 -0500 Subject: [PATCH 082/282] fix name from template in contributing.md --- contributing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contributing.md b/contributing.md index 5c557bae..80130600 100644 --- a/contributing.md +++ b/contributing.md @@ -1,6 +1,6 @@ -# Contributing to Assemble +# Contributing to Verb -First and foremost, thank you! We appreciate that you want to contribute to Assemble, your time is valuable, and your contributions mean a lot to us. +First and foremost, thank you! We appreciate that you want to contribute to Verb, your time is valuable, and your contributions mean a lot to us. **What does "contributing" mean?** @@ -17,7 +17,7 @@ Creating an issue is the simplest form of contributing to a project. But there a Please make sure you're creating one in the right place: - do you have a template syntax question? Like how to accomplish something with handlebars? The best place to get answers for this is [stackoverflow.com][], the [handlebars docs](handlebarsjs.com), or the documentation for the template engine you're using. -- Are you having an issue with an Assemble feature that is powered by an underlying lib? This is sometimes difficult to know, but sometimes it can be pretty easy to find out. For example, if you use a glob pattern somewhere and you found what you believe to be a matching bug, that would probably be an issue for [node-glob][] or [micromatch][] +- Are you having an issue with an Verb feature that is powered by an underlying lib? This is sometimes difficult to know, but sometimes it can be pretty easy to find out. For example, if you use a glob pattern somewhere and you found what you believe to be a matching bug, that would probably be an issue for [node-glob][] or [micromatch][] **Creating an issue** From 8dbdfa4f8a3da8656179fe26f1c723b64deb9616 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 20:28:07 -0500 Subject: [PATCH 083/282] lint --- lib/runner/data.js | 4 +--- lib/runner/helpers.js | 4 ++-- lib/runner/includes.js | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/runner/data.js b/lib/runner/data.js index 1a3d7678..b3b5351c 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -17,10 +17,8 @@ module.exports = function(verb, base, env) { module.exports.updateData = function(app, base, env) { app.questions.enqueue('author', 'name', 'description'); - - var plugins = app.get('env.argv.plugins') || app.plugins; var pkg = env.user.pkg; - var config = pkg.app || {}; + var config = pkg.verb || {}; app.option(config.options || {}); diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 9d9b425d..2a968bb6 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -13,8 +13,8 @@ module.exports = function(verb, base, env) { return utils.get(this.context, key); }); - verb.helper('shield', function(name) { - return 'https://img.shields.io/' + name + '/{%= repo %}.svg'; + verb.helper('shield', function(type) { + return 'https://img.shields.io/' + type + '/{%= author.username %}/{%= name %}.svg'; }); verb.helper('apidocs', apidocs({})); diff --git a/lib/runner/includes.js b/lib/runner/includes.js index 6fb05b54..554bab09 100644 --- a/lib/runner/includes.js +++ b/lib/runner/includes.js @@ -14,7 +14,7 @@ module.exports = { '', '```sh', '$ bower install {%= name %} --save', - '```', + '```' ].join('\n'), 'install-global.md': [ From fbc57d37828343b82da8d722de85ffb6a7e9d013 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:36:28 -0500 Subject: [PATCH 084/282] clean up cli, scaffold out `init` and `help` mappings --- lib/runner/cli.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/runner/cli.js b/lib/runner/cli.js index cbfeb84e..a462a754 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -13,6 +13,18 @@ module.exports = function(verb, base, env) { }); verb.cli + .map('init', function(fp) { + console.log('cli > init (implement me!)'); + // do init stuff + // verb.enable('init'); + process.exit(0); + }) + .map('help', function(fp) { + console.log('cli > help (implement me!)'); + // do help stuff + // verb.enable('help'); + process.exit(0); + }) .map('ask', function(key) { if (key === true) { verb.enable('questions.init'); @@ -38,29 +50,21 @@ module.exports = function(verb, base, env) { } } }) + .map('diff', function(fp) { + verb.enable('diff'); + }) .map('save', function(val) { verb.store.set(val); }) - .map('init', function(fp) { - verb.enable('init'); - console.log(arguments); - }) .map('cwd', function(fp) { verb.option('cwd', fp); }) .map('data', function(val) { - verb.visit('data', val); - console.log(verb.cache.data) + verb.data(val); }) - .map('md', function(key, val) { - console.log('cli:md', verb.views) - verb.enable('ask.verbmd'); - }) - // .map('config', function(obj) { - // verb.config.process({update: obj}); - // }) - // task related + + // task-related verb.cli .map('choose', function(key) { if (key === true) { From 19e6971f4b9a7f472dac27ae3abb17c2290d2d0f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:56:22 -0500 Subject: [PATCH 085/282] compartmentalize toc logic --- lib/runner/middleware.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 2b191a16..2882d2c5 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -29,6 +29,7 @@ module.exports = function(app, base, env) { }); app.preWrite(/readme.md$/i, function(view, next) { + insertToc(app, view); if (app.enabled('diff')) { view.diff(); } @@ -61,7 +62,21 @@ module.exports = function(app, base, env) { }); }; -// placeholder +/** + * Inject the TOC created by the toc middleware. + */ + +function insertToc(app, view) { + var toc = view.data.toc; + var str = view.content; + if (toc && app.option('toc.insert') !== false) { + view.content = str.split('').join(toc); + } else { + view.content = str.split('').join(''); + } +} + +// TODO: implement better check function needsLayout(str) { if (!/^# /.test(str)) { return true; From 85de746086e069524b5bdfe8cbc5409b41481904 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:57:03 -0500 Subject: [PATCH 086/282] rename `userDocs` var to `userDir` --- lib/runner/templates.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/runner/templates.js b/lib/runner/templates.js index d3aecf6f..6103a2f1 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -3,11 +3,12 @@ var fs = require('fs'); var path = require('path'); var create = require('./create'); -var includes = require('./includes'); -var badges = require('./badges'); var loaded, views = {}; module.exports = function(options) { + var includes = require('./includes'); + var badges = require('./badges'); + return function(verb) { verb.use(create(options)); @@ -15,7 +16,7 @@ module.exports = function(options) { * DEPRECATED in the next release: Load includes and badges */ - verb.on('config-processed', function() { + verb.on('config-loaded', function() { for (var key in includes) { verb.include(key, {content: includes[key]}); } @@ -24,16 +25,16 @@ module.exports = function(options) { verb.badge(key, {content: badges[key]}); } - var userDocs = path.join.bind(path, process.cwd()); - if (fs.existsSync(userDocs())) { + var userDir = path.join.bind(path, process.cwd()); + if (fs.existsSync(userDir())) { // api docs - verb.libFiles.loadViews(['*.js', 'lib/*.js'], {cwd: userDocs()}); + verb.libFiles.loadViews(['*.js', 'lib/*.js'], {cwd: userDir()}); // templates - verb.docs.loadViews('*', {cwd: userDocs(), dot: true}); - verb.includes.loadViews('*.md', {cwd: userDocs('docs/includes')}); - verb.layouts.loadViews('*.md', {cwd: userDocs('docs/layouts')}); - verb.docs.loadViews('*.md', {cwd: userDocs('docs')}); + verb.docs.loadViews('*', {cwd: userDir(), dot: true}); + verb.includes.loadViews('*.md', {cwd: userDir('docs/includes')}); + verb.layouts.loadViews('*.md', {cwd: userDir('docs/layouts')}); + verb.docs.loadViews('*.md', {cwd: userDir('docs')}); } var collections = loadDefaults(verb); From 2401e7feb0dd6b54fa2606cd4f268f7588a305b2 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:57:41 -0500 Subject: [PATCH 087/282] start cleaning up verbfile --- verbfile.js | 137 ++++++++++++++++++---------------------------------- 1 file changed, 46 insertions(+), 91 deletions(-) diff --git a/verbfile.js b/verbfile.js index 5ef606eb..884abd64 100644 --- a/verbfile.js +++ b/verbfile.js @@ -2,11 +2,19 @@ var path = require('path'); var utils = require('./lib/utils'); -var defaults = require('./lib/runner/defaults'); +var data = require('./lib/runner/data'); var argv = require('minimist')(process.argv.slice(2), { - alias: {v: 'verbose'} + alias: { + v: 'verbose' + } }); +/** + * !HEADS UP: this is not what a normal verfile.js would look like. + * We're just trying to break everyting right now, so this has a + * lot of stuff in it. + */ + module.exports = function(verb, base, env) { if (argv.init) { verb.questions.options.forceAll = true; @@ -14,43 +22,42 @@ module.exports = function(verb, base, env) { var tasks = verb.get('env.argv.tasks') || ['readme', 'package']; + verb.task('a', function(cb) { + console.log('verb base > a'); + cb(); + }); + verb.task('b', function(cb) { + console.log('verb base > b'); + cb(); + }); + verb.task('c', function(cb) { + console.log('verb base > c'); + cb(); + }); + /** * Readme task */ verb.task('readme', function(cb) { - verb.questions.enqueue('author', 'name', 'description'); - var plugins = verb.get('env.argv.plugins') || verb.plugins; - var pkg = env.user.pkg; - var config = pkg.verb || {}; - - verb.option(config.options || {}); - - // load package.json data and user options onto `verb.cache.data` - verb.data({options: verb.options}); - verb.data(pkg); - verb.data(verb.base.get('cache.expanded')); - verb.data({license: license(pkg, verb.options)}); + data.updateData(verb, verb.base, verb.env); // ask pre-configured questions, but only if // they don't have answers yet verb.ask(function(err, answers) { if (err) return cb(err); - // placeholder for something better - verb.emit('info', 'plugins', Object.keys(plugins).join(', ')); - - verb.toStream('docs', function(key) { - return key === '.verb'; - }) - .pipe(handle('onStream')) + verb.toStream('docs', function(key, view) { + return key === '.verb'; + }) + .pipe(handle(verb, 'onStream')) .pipe(verb.renderFile('text', answers)) .on('error', handleError(verb)) .pipe(verb.pipeline(plugins)) - .pipe(handle('preWrite')) + .pipe(handle(verb, 'preWrite')) .pipe(verb.dest(dest())) - .pipe(utils.exhaust(handle('postWrite'))) + .pipe(utils.exhaust(handle(verb, 'postWrite'))) .on('error', cb) .on('finish', cb); }); @@ -58,59 +65,28 @@ module.exports = function(verb, base, env) { verb.task('package', function(cb) { verb.toStream('docs', function(key, view) { - return view.basename === 'package.json'; - }) - .pipe(handle('preWrite')) + return view.basename === 'package.json'; + }) + .pipe(handle(verb, 'preWrite')) .pipe(verb.dest(dest())) - .pipe(utils.exhaust(handle('postWrite'))) + .pipe(utils.exhaust(handle(verb, 'postWrite'))) .on('error', cb) .on('finish', cb); }); - verb.task('verbmd', function(cb) { - verb.ask(function(err, answers) { - if (err) return cb(err); - - verb.toStream('docs', function(key) { - return key === 'min'; - }) - .pipe(verb.dest(dest('.verb.md'))) - .on('error', cb) - .on('finish', cb); - }); - }); - - verb.task('docs', function(cb) { - verb.ask(function(err, answers) { - if (err) return cb(err); - - verb.toStream('docs') - .on('error', cb) - .pipe(verb.renderFile('text', answers)) - .on('error', cb) - .pipe(verb.dest(dest())) - .on('finish', cb); - }); - }); - - verb.register('store', function(app, base) { - app.task('del', function(cb) { - verb.store.del({force: true}); - console.log('deleted store.'); - cb(); - }); - }); - verb.task('default', tasks); - - function handle(stage) { - return utils.through.obj(function(file, enc, next) { - if (file.isNull()) return next(); - verb.handle(stage, file, next); - }); - } }; +function handle(app, stage) { + return utils.through.obj(function(file, enc, next) { + if (file.isNull()) return next(); + if (typeof app.handle !== 'function') { + return next(null, file); + } + app.handle(stage, file, next); + }); +} + /** * Rename template files */ @@ -126,25 +102,6 @@ function dest(dest) { }; } -/** - * Format license - */ - -function license(pkg, options) { - options = options || {}; - if (typeof options.license === 'string') { - return options.license; - } - var str = pkg.license; - if (Array.isArray(pkg.licenses)) { - str = pkg.licenses[0]; - } - if (typeof str === 'undefined') { - return ''; - } - return 'Released under the ' + str + ' license.'; -} - /** * Handle render errors */ @@ -154,9 +111,7 @@ function handleError(app) { var m = /(\w+) is not a function/.exec(err.message); var msg = ''; if (m) { - msg = err.message + ': "' + m[1] + '()" is defined as a helper\n' - + 'in `.verb.md`, but "' + m[1] + '" is defined on ' - + 'verb.cache.data as a "' + typeof app.cache.data[m[1]] + '"'; + msg = err.message + ': "' + m[1] + '()" is defined as a helper\n' + 'in `.verb.md`, but "' + m[1] + '" is defined on ' + 'verb.cache.data as a "' + typeof app.cache.data[m[1]] + '"'; } if (app.options.verbose) { console.log(msg); @@ -166,5 +121,5 @@ function handleError(app) { console.log(msg); } process.exit(1); - } + }; } From 66eef12f9c2434987accec24b993390d87bd7468 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:58:23 -0500 Subject: [PATCH 088/282] updte deps --- package.json | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 8aee756a..9a9afb7d 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ }, "dependencies": { "ansi-colors": "^0.1.0", + "arr-flatten": "^1.0.1", + "array-unique": "^0.2.1", "assemble-core": "^0.5.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", @@ -37,9 +39,12 @@ "base-runner": "^0.4.3", "base-store": "^0.3.0", "common-middleware": "^0.2.0", + "composer-runtimes": "^0.7.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", + "expand": "^0.4.0", "extend-shallow": "^2.0.1", + "falsey": "^0.2.1", "get-value": "^2.0.2", "global-modules": "^0.2.0", "has-glob": "^0.1.1", @@ -94,7 +99,6 @@ "should": "_", "sinon": "^1.17.2", "swig": "^1.4.2", - "template-helper-apidocs": "^0.4.4", "through2": "^2.0.0", "vinyl": "^1.1.0" }, @@ -115,6 +119,9 @@ ], "verb": { "layout": "full", + "plugins": [ + "gulp-format-md" + ], "related": { "list": [ "base-methods", @@ -126,17 +133,6 @@ "assemble" ] }, - "plugins": { - "gulp-format-md": { - "name": "format-md" - } - }, - "options": { - "toc": { - "render": true, - "insert": false - } - }, "reflinks": [ "verb", "assemble", @@ -147,4 +143,4 @@ "scaffold" ] } -} +} \ No newline at end of file From dc9f1a05b8a0da1663a6b294ab2b59fe70b08c8a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:58:37 -0500 Subject: [PATCH 089/282] lint --- index.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 12fbb29d..6e036ede 100644 --- a/index.js +++ b/index.js @@ -41,7 +41,6 @@ function create(preload) { } this.env = {}; - Base.apply(this, arguments); this.isVerb = true; this.isApp = true; @@ -54,7 +53,7 @@ function create(preload) { .use(utils.pipeline(opts)) .use(utils.store()) .use(expand()) - .use(env()) + .use(env()); this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] @@ -69,9 +68,13 @@ function create(preload) { Base.extend(Verb); + /** + * Initialize verb + */ + Verb.prototype.initVerb = function(app) { if (typeof preload === 'function') { - preload.call(this, this, this.base, this.env); + preload.call(app, app, this.base, this.env); } }; @@ -187,7 +190,7 @@ function create(preload) { }); return Verb; -}; +} /** * Expose `Verb` with our baseline defaults From d26b8000d3fa921d633919c2588acf4792ee9096 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 13 Dec 2015 21:58:44 -0500 Subject: [PATCH 090/282] generate readme --- readme.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index cbf0a278..5a753696 100644 --- a/readme.md +++ b/readme.md @@ -23,6 +23,8 @@ - [Authoring apps](#authoring-apps) * [App naming conventions](#app-naming-conventions) +_(TOC generated by [verb](https://github.com/verbose/verb))_ + **TODO** * [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API @@ -226,7 +228,7 @@ var Verb = require('verb'); var verb = new Verb(options); ``` -### [.process](index.js#L88) +### [.process](index.js#L97) Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options`. This allows plugin pipelines to be programmatically built-up and dynamically changed on-the-fly. @@ -243,7 +245,7 @@ Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options verb.process({src: ['a.txt', 'b.txt']}, options); ``` -### [.each](index.js#L112) +### [.each](index.js#L121) Verb `files` configurations in parallel. @@ -260,7 +262,7 @@ verb.each(files, function(err) { }); ``` -### [.eachSeries](index.js#L134) +### [.eachSeries](index.js#L143) Verb `files` configurations in series. @@ -277,7 +279,7 @@ verb.eachSeries(files, function(err) { }); ``` -### [.scaffold](index.js#L166) +### [.scaffold](index.js#L175) Verb files from a declarative [scaffold](https://github.com/jonschlinkert/scaffold) configuration. @@ -471,4 +473,4 @@ Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 11, 2015._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on December 13, 2015._ \ No newline at end of file From e3ccb11a1fc71a25b677f535d2943bf544e0b6ec Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 14 Dec 2015 02:04:21 -0500 Subject: [PATCH 091/282] dont test instances on cloned `views` (since we already cover this in ancestor libs, and we will probably be getting a different ctor here) --- test/item.js | 2 -- test/view.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/test/item.js b/test/item.js index 2c3becda..7550a076 100644 --- a/test/item.js +++ b/test/item.js @@ -477,8 +477,6 @@ describe('Item', function() { assert(copy.stat.isFile()); assert(!copy.stat.isDirectory()); - assert(item.stat instanceof fs.Stats); - assert(copy.stat instanceof fs.Stats); done(); }); diff --git a/test/view.js b/test/view.js index c16e48de..3890bb81 100644 --- a/test/view.js +++ b/test/view.js @@ -562,8 +562,6 @@ describe('View', function() { assert(copy.stat.isFile()); assert(!copy.stat.isDirectory()); - assert(view.stat instanceof fs.Stats); - assert(copy.stat instanceof fs.Stats); done(); }); From 23962b9d273897ec96c9443ffcf0c7a46829037a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 14 Dec 2015 02:27:14 -0500 Subject: [PATCH 092/282] improve mappings for config/cli properties --- cli.js | 12 +++++++++--- lib/config.js | 18 +++++++++++++++--- lib/runner/cli.js | 18 ++++++++---------- lib/runner/helpers.js | 4 +++- lib/runner/middleware.js | 25 +++++++++++++++++++++---- 5 files changed, 56 insertions(+), 21 deletions(-) diff --git a/cli.js b/cli.js index d9439be5..1e2b43ca 100755 --- a/cli.js +++ b/cli.js @@ -70,6 +70,7 @@ verb.resolve('global', {pattern: 'verb-*/verbfile.js', cwd: gm}); */ verb.cli.map('apps', function(tasks) { + // ensure this is run after other configuration is complete setImmediate(function() { if (verb.enabled('generate.init')) { @@ -105,10 +106,15 @@ verb.cli.map('apps', function(tasks) { function run(tasks) { data.updateData(verb, verb.base, verb.env); - verb.runApps(tasks, function(err) { + verb.ask(function(err, answers) { if (err) return console.error(err); - utils.timestamp('done'); - process.exit(0); + verb.data(answers); + + verb.runApps(tasks, function(err) { + if (err) return console.error(err); + utils.timestamp('done'); + process.exit(0); + }); }); } }); diff --git a/lib/config.js b/lib/config.js index bf28e7ce..e971db2e 100644 --- a/lib/config.js +++ b/lib/config.js @@ -6,17 +6,25 @@ var updater = require('./updater'); var utils = require('./utils'); module.exports = function(verb, base, env) { - // if (!verb.isVerb) return; verb.use(updater()); + /** + * Update verb config (`verb` object in package.json) + * `init` force verb to init a project + * `data` add or extend verb config data to be used in templates + * `update` this mapping is called by the cli `save` mapping, and + * is used to extend, add, delete or otherwise update any given + * property on the config object + */ + verb.config .map('init', function(val) { if (val === true) { verb.enable('init'); } }) - .map('option') .map('update', function(obj) { + console.log(obj) var view = verb.docs.getView('package.json'); if (!view) return; @@ -30,7 +38,11 @@ module.exports = function(verb, base, env) { } catch (err) { new verb.VerbError('config', 'cannot update pkg.verb', err); } - }); + }) + .map('data', function(val) { + verb.data(val); + verb.store.set(val); + }) /** * Helpers diff --git a/lib/runner/cli.js b/lib/runner/cli.js index a462a754..e9190946 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -14,10 +14,8 @@ module.exports = function(verb, base, env) { verb.cli .map('init', function(fp) { - console.log('cli > init (implement me!)'); - // do init stuff - // verb.enable('init'); - process.exit(0); + // console.log('cli > init (implement me!)'); + verb.option('questions.force', true); }) .map('help', function(fp) { console.log('cli > help (implement me!)'); @@ -50,14 +48,14 @@ module.exports = function(verb, base, env) { } } }) - .map('diff', function(fp) { - verb.enable('diff'); - }) .map('save', function(val) { - verb.store.set(val); + verb.config.process(val); + }) + .map('diff', function(val) { + verb.option('diff', val); }) - .map('cwd', function(fp) { - verb.option('cwd', fp); + .map('cwd', function(val) { + verb.option('cwd', val); }) .map('data', function(val) { verb.data(val); diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 2a968bb6..36d20dce 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -17,7 +17,9 @@ module.exports = function(verb, base, env) { return 'https://img.shields.io/' + type + '/{%= author.username %}/{%= name %}.svg'; }); - verb.helper('apidocs', apidocs({})); + verb.helper('apidocs', apidocs({ + delims: ['{%', '%}'] + })); verb.helper('date', require('helper-date')); verb.helper('copyright', require('helper-copyright')({ linkify: true diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 2882d2c5..c8ff77b9 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -13,12 +13,19 @@ module.exports = function(app, base, env) { }); }); - app.onLoad(/\.verb\.md/, diff.view('diffWords')); + /** + * Readme dest path + */ + app.onLoad(/\.verb\.md/, function(view, next) { view.path = view.dest = 'readme.md'; next(); }); + /** + * Table of contents > adds `file.data.toc` property + */ + var append = '\n\n_(TOC generated by [verb](https://github.com/verbose/verb))_'; app.preRender(/\.md/, function(view, next) { if (!view.isType('partial')) { @@ -28,11 +35,21 @@ module.exports = function(app, base, env) { return next(); }); - app.preWrite(/readme.md$/i, function(view, next) { - insertToc(app, view); - if (app.enabled('diff')) { + /** + * Diff files (currently just testing readme) + */ + + app.preRender(/./i, diff.view('diffLines')); + app.postRender(/./i, function(view, next) { + var diff = app.option('diff'); + if (diff && diff === true || diff === view.stem) { view.diff(); } + return next(); + }); + + app.preWrite(/readme\.md$/i, function(view, next) { + insertToc(app, view); next(); }); From 76d144b0f24f20f99575cae5479c69329c1078d3 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 14 Dec 2015 03:36:24 -0500 Subject: [PATCH 093/282] set config values from the command line --- .verb.md | 56 ++++++++++++-------- lib/config.js | 31 ++++++----- lib/runner/cli.js | 10 ++-- lib/runner/data.js | 10 ++++ lib/schema.js | 4 +- lib/updater.js | 125 ++++++++++++++++++++++++++------------------- lib/utils.js | 2 + package.json | 25 ++++----- readme.md | 68 +++++++++++++++--------- 9 files changed, 198 insertions(+), 133 deletions(-) diff --git a/.verb.md b/.verb.md index 989b2342..48426f6b 100644 --- a/.verb.md +++ b/.verb.md @@ -1,24 +1,5 @@ - -**TODO** - -- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API -- [x] support sub-apps (to any level of nesting) -- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] -- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -- [x] support _instance plugins_ that allow you to easily add functionality and features to verb -- [x] support any template engine -- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) -- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -- [x] 820+ unit tests -- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -- [ ] CLI docs (started) -- [ ] User help (e.g. when the user does `verb help` or just `verb`) -- [ ] API docs -- [ ] App guidelines and conventions - ## Install To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. @@ -56,7 +37,9 @@ $ verb [args] _(WIP)_ -### help +### Commands + +#### help _(TODO)_ @@ -66,7 +49,7 @@ Get started with Verb. $ verb help ``` -### init +#### init _(TODO)_ @@ -78,6 +61,19 @@ $ verb init Upon running `init`, verb will prompt you for answers to the following questions: +#### diff + +Show a diff of a file's contents, pre- and post-render. + +```js +$ verb --diff +``` + +**Example diff** + +screen shot 2015-12-14 at 1 50 55 am + +_(gray is unchanged, red is deleted, green is new)_ ### Run apps @@ -298,3 +294,21 @@ _(TODO)_ ### App naming conventions Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. + +## TODO + +- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! +- [x] support sub-apps (to any level of nesting) +- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] +- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +- [x] support _instance plugins_ that allow you to easily add functionality and features to verb +- [x] support any template engine +- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) +- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +- [x] 820+ unit tests +- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +- [ ] CLI docs (started) +- [ ] User help (e.g. when the user does `verb help` or just `verb`) +- [ ] API docs +- [ ] App guidelines and conventions diff --git a/lib/config.js b/lib/config.js index e971db2e..f444640f 100644 --- a/lib/config.js +++ b/lib/config.js @@ -12,9 +12,10 @@ module.exports = function(verb, base, env) { * Update verb config (`verb` object in package.json) * `init` force verb to init a project * `data` add or extend verb config data to be used in templates - * `update` this mapping is called by the cli `save` mapping, and - * is used to extend, add, delete or otherwise update any given - * property on the config object + * `update` this mapping is setup to be called by the cli + * `--config` command, and is used to extend, add, + * delete or otherwise update any given property + * on the config object */ verb.config @@ -23,26 +24,24 @@ module.exports = function(verb, base, env) { verb.enable('init'); } }) - .map('update', function(obj) { - console.log(obj) - var view = verb.docs.getView('package.json'); - if (!view) return; + .map('update', function(val) { + verb.emit('config.update', val); + env = env || verb.env; + + var file = verb.docs.getView('package.json'); + if (!file || !file.json) return; var pkg = env.user.pkg; if (!pkg) return; + pkg.verb = pkg.verb || {}; try { - pkg.verb = pkg.verb || {}; - verb.update(pkg.verb, obj); - view.json = pkg; + file.json.verb = verb.update(pkg.verb, val); + verb.cache.data = utils.merge({}, verb.cache.data, file.json.verb.data); } catch (err) { - new verb.VerbError('config', 'cannot update pkg.verb', err); + throw new Error('config', 'cannot update pkg.verb', err.stack); } - }) - .map('data', function(val) { - verb.data(val); - verb.store.set(val); - }) + }); /** * Helpers diff --git a/lib/runner/cli.js b/lib/runner/cli.js index e9190946..569c0d12 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -8,9 +8,9 @@ module.exports = function(verb, base, env) { throw new Error('the base-cli plugin should be registered on every instance'); } - verb.store.on('set', function(key, val) { - console.log('set >', key, val); - }); + // verb.store.on('set', function(key, val) { + // console.log('set >', key, val); + // }); verb.cli .map('init', function(fp) { @@ -48,8 +48,8 @@ module.exports = function(verb, base, env) { } } }) - .map('save', function(val) { - verb.config.process(val); + .map('config', function(val) { + verb.config.process({update: val}); }) .map('diff', function(val) { verb.option('diff', val); diff --git a/lib/runner/data.js b/lib/runner/data.js index b3b5351c..d66b89d6 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -1,5 +1,7 @@ 'use strict'; +var utils = require('../utils'); + module.exports = function(verb, base, env) { verb.data({ runner: { @@ -26,6 +28,14 @@ module.exports.updateData = function(app, base, env) { app.data({options: app.options}); app.data(pkg); app.data(app.base.get('cache.expanded') || {}); + var config = app.get('cache.data.verb.data'); + if (config) { + var obj = utils.tableize(config); + for (var key in obj) { + var val = obj[key]; + utils.set(app.cache.data, key, obj[key]); + } + } app.data({license: formatLicense(pkg, app.options)}); }; diff --git a/lib/schema.js b/lib/schema.js index 02be1664..866d2737 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -104,8 +104,8 @@ schema.field('reflinks.list', ['array'], { } }); -console.log(schema.isValid('reflinks.list', '')) -console.log(schema.isValid('whatever')) +// console.log(schema.isValid('reflinks.list', '')) +// console.log(schema.isValid('whatever')) var pkg = { verb: { diff --git a/lib/updater.js b/lib/updater.js index 6829bbd4..69ed480d 100644 --- a/lib/updater.js +++ b/lib/updater.js @@ -1,85 +1,106 @@ 'use strict'; -var Schema = require('./schema'); var utils = require('./utils'); -/** - * Adds get/set methods to verb env - */ - module.exports = function(options) { return function(verb) { - utils.define(verb, 'update', function(pkg, args) { - if (!pkg) return; - - var schema = new Schema(pkg, options); - // console.log(schema) - var config = utils.mapper(schema) - .map('set') - .map('del'); + verb.define('update', function(config, obj) { + var table = utils.tableize(obj); - config.process(args); + for (var key in table) { + updateProperty(config, key, table[key]); + } + return config; }); + + // utils.define(verb, 'update', function(pkg, args) { + // if (!pkg) return; + + // var config = utils.mapper(schema) + // .map('set', function(val) { + // update(pkg, 'set', val); + // }) + // .map('del', function(val) { + // update(pkg, 'del', val); + // }) + // .map('data', function(val) { + // update(pkg, 'data', val); + // }); + + // config.process(args); + // }); }; }; -function updater(pkg) { - -} +function updateProperty(config, prop, val) { + config = config || {}; + if (typeof prop === 'undefined') { + return config; + } + if (typeof val === 'undefined') { + return config; + } -function update(obj, prop, newVal, schema, method) { - if (schema && schema.validate) { - newVal = schema.validate(prop, newVal); + var current = utils.get(config, prop); + if (typeof current === 'undefined') { + utils.set(config, prop, val); + return config; } - var val = utils.get(obj, prop); - if (typeof val === 'undefined' || utils.isPrimitive(newVal)) { - utils.set(obj, prop, newVal); - return obj; + + + // replace existing primitive with whatever `val` is + if (utils.isPrimitive(current)) { + utils.set(config, prop, val); + return config; } - if (utils.isObject(newVal)) { - for (var key in newVal) { - update(val, key, newVal[key], schema[prop]); - } - return obj; + if (utils.isObject(val) && !Array.isArray(current)) { + val = utils.merge({}, current, val); + utils.set(config, prop, val); + return config; } - if (Array.isArray(newVal)) { - var len = newVal.length; + // if either is an array, we'll assume it should be an array + if (Array.isArray(val) || Array.isArray(current)) { + current = utils.arrayify(current); val = utils.arrayify(val); + var len = val.length; while (len--) { - if (val.indexOf(newVal[len]) < 0) { - val.push(newVal[len]); + if (current.indexOf(val[len]) < 0) { + current.push(val[len]); } } - utils.set(obj, prop, val); - return obj; + + current.sort(); + utils.set(config, prop, current); + return config; } } -// module.exports = function(options) { -// var Schema = new Schema(options); +/** + * Adds get/set methods to verb env + */ +// module.exports = function(options) { // return function(verb) { // utils.define(verb, 'update', function(pkg, args) { -// if (!pkg) return; - -// var config = utils.mapper(schema) -// .map('set', function(obj) { -// for(var key in obj) { -// update(pkg, key, obj[key], schema, 'set'); -// } -// }) -// .map('del', function(obj) { -// for(var key in obj) { -// update(pkg, key, obj[key], schema, 'del'); -// } -// }); - -// config.process(args); +// update() +// // if (!pkg) return; + +// // var schema = new Schema(pkg, options); +// // // console.log(schema) +// // var config = utils.mapper(schema) +// // .map('set') +// // .map('del'); + +// // config.process(args); // }); // }; // }; + +// function updater(pkg) { + +// } diff --git a/lib/utils.js b/lib/utils.js index 60e69356..600987fa 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -38,6 +38,7 @@ require('array-unique', 'unique'); require('arr-flatten', 'flatten'); require('define-property', 'define'); require('extend-shallow', 'extend'); +require('mixin-deep', 'merge'); require('get-value', 'get'); require('global-modules', 'gm'); require('has-glob'); @@ -53,6 +54,7 @@ require('resolve-dir'); require('set-value', 'set'); require('stream-exhaust', 'exhaust'); require('through2', 'through'); +require('tableize'); // cli require('success-symbol'); diff --git a/package.json b/package.json index 9a9afb7d..59393c26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "verb", - "description": "Documentation generator", + "description": "Powerful, highly pluggable and easy-to-use documentation generator with an expressive API. Use any docs parser or template engine, supports gulp plugins, middleware...", "version": "0.9.0", "homepage": "https://github.com/jonschlinkert/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", @@ -30,7 +30,7 @@ "ansi-colors": "^0.1.0", "arr-flatten": "^1.0.1", "array-unique": "^0.2.1", - "assemble-core": "^0.5.0", + "assemble-core": "^0.6.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", "base-list": "^0.1.4", @@ -38,7 +38,7 @@ "base-questions": "^0.2.1", "base-runner": "^0.4.3", "base-store": "^0.3.0", - "common-middleware": "^0.2.0", + "common-middleware": "^0.2.1", "composer-runtimes": "^0.7.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", @@ -48,9 +48,10 @@ "get-value": "^2.0.2", "global-modules": "^0.2.0", "has-glob": "^0.1.1", + "helper-apidocs": "^0.5.0", "helper-copyright": "^2.0.0", "helper-date": "^0.2.2", - "helper-reflinks": "^2.2.1", + "helper-reflinks": "^3.0.1", "helper-related": "^0.12.1", "is-primitive": "^2.0.0", "is-valid-glob": "^0.3.0", @@ -60,6 +61,7 @@ "map-config": "^0.3.0", "matched": "^0.3.2", "minimist": "^1.2.0", + "mixin-deep": "^1.1.3", "object-visit": "^0.3.4", "opn": "^3.0.3", "parse-author": "^0.2.0", @@ -91,12 +93,12 @@ "is-buffer": "^1.1.0", "istanbul": "^0.4.1", "load-pkg": "^3.0.0", - "mocha": "_", + "mocha": "*", "parser-front-matter": "^1.3.0", "resolve-glob": "^0.1.7", "rimraf": "^2.4.4", "scaffold": "^0.2.1", - "should": "_", + "should": "*", "sinon": "^1.17.2", "swig": "^1.4.2", "through2": "^2.0.0", @@ -118,10 +120,6 @@ "yeoman" ], "verb": { - "layout": "full", - "plugins": [ - "gulp-format-md" - ], "related": { "list": [ "base-methods", @@ -129,10 +127,13 @@ "resolve-modules", "base-runner", "base-resolver", - "micromatch", "assemble" ] }, + "layout": "full", + "plugins": [ + "gulp-format-md" + ], "reflinks": [ "verb", "assemble", @@ -143,4 +144,4 @@ "scaffold" ] } -} \ No newline at end of file +} diff --git a/readme.md b/readme.md index 5a753696..6204ea98 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,14 @@ # verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/jonschlinkert/verb.svg)](https://travis-ci.org/jonschlinkert/verb) -> Documentation generator +> Powerful, highly pluggable and easy-to-use documentation generator with an expressive API. Use any docs parser or template engine, supports gulp plugins, middleware... - [Install](#install) - [Usage](#usage) - [CLI](#cli) - * [help](#help) - * [init](#init) + * [Commands](#commands) + + [help](#help) + + [init](#init) + + [diff](#diff) * [Run apps](#run-apps) * [Run tasks](#run-tasks) * [Run sub-apps](#run-sub-apps) @@ -22,27 +24,10 @@ * [.invoke](#invoke) - [Authoring apps](#authoring-apps) * [App naming conventions](#app-naming-conventions) +- [TODO](#todo) _(TOC generated by [verb](https://github.com/verbose/verb))_ -**TODO** - -* [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API -* [x] support sub-apps (to any level of nesting) -* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) -* [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -* [x] support _instance plugins_ that allow you to easily add functionality and features to verb -* [x] support any template engine -* [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) -* [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -* [x] 820+ unit tests -* [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -* [ ] CLI docs (started) -* [ ] User help (e.g. when the user does `verb help` or just `verb`) -* [ ] API docs -* [ ] App guidelines and conventions - ## Install To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. @@ -85,7 +70,9 @@ $ verb [args] _(WIP)_ -### help +### Commands + +#### help _(TODO)_ @@ -95,7 +82,7 @@ Get started with Verb. $ verb help ``` -### init +#### init _(TODO)_ @@ -107,6 +94,20 @@ $ verb init Upon running `init`, verb will prompt you for answers to the following questions: +#### diff + +Show a diff of a file's contents, pre- and post-render. + +```js +$ verb --diff +``` + +**Example diff** + +screen shot 2015-12-14 at 1 50 55 am + +_(gray is unchanged, red is deleted, green is new)_ + ### Run apps ```sh @@ -437,6 +438,24 @@ _(TODO)_ Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. +## TODO + +* [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! +* [x] support sub-apps (to any level of nesting) +* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) +* [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI +* [x] support _instance plugins_ that allow you to easily add functionality and features to verb +* [x] support any template engine +* [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type +* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) +* [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) +* [x] 820+ unit tests +* [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) +* [ ] CLI docs (started) +* [ ] User help (e.g. when the user does `verb help` or just `verb`) +* [ ] API docs +* [ ] App guidelines and conventions + ## Related projects * [assemble](https://www.npmjs.com/package/assemble): Static site generator for Grunt.js, Yeoman and Node.js. Used by Zurb Foundation, Zurb Ink, H5BP/Effeckt,… [more](https://www.npmjs.com/package/assemble) | [homepage](http://assemble.io) @@ -444,7 +463,6 @@ Use `verb-` as the prefix, followed by any words of your choosing to describe th * [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) * [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) * [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) -* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. Just… [more](https://www.npmjs.com/package/micromatch) | [homepage](https://github.com/jonschlinkert/micromatch) * [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) ## Running tests @@ -473,4 +491,4 @@ Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 13, 2015._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on December 14, 2015._ \ No newline at end of file From 152c65326797916c1e5f0d1900ac75c9bb3c27f9 Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Mon, 14 Dec 2015 15:38:53 -0500 Subject: [PATCH 094/282] adding `base-diff` to dependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 59393c26..73cfdca9 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "assemble-core": "^0.6.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", + "base-diff": "^0.1.0", "base-list": "^0.1.4", "base-pipeline": "^0.1.4", "base-questions": "^0.2.1", From 32365223c6b195159fc9a50206d519b39e7bd9a9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 20 Dec 2015 14:52:31 -0500 Subject: [PATCH 095/282] split out some commands --- lib/runner/commands/ask.js | 23 ++++++++ lib/runner/commands/help.js | 106 +++++++++++++++++++++++++++++++++++ lib/runner/commands/index.js | 1 + lib/runner/commands/open.js | 26 +++++++++ lib/runner/commands/show.js | 22 ++++++++ 5 files changed, 178 insertions(+) create mode 100644 lib/runner/commands/ask.js create mode 100644 lib/runner/commands/help.js create mode 100644 lib/runner/commands/index.js create mode 100644 lib/runner/commands/open.js create mode 100644 lib/runner/commands/show.js diff --git a/lib/runner/commands/ask.js b/lib/runner/commands/ask.js new file mode 100644 index 00000000..f3b34b07 --- /dev/null +++ b/lib/runner/commands/ask.js @@ -0,0 +1,23 @@ +'use strict'; + +var utils = require('../../utils'); + +module.exports = function(app) { + return function(val) { + if (val === true) { + app.enable('questions.init'); + return; + } + + if (utils.isObject(val)) { + var keys = Object.keys(utils.tableize(val)); + app.questions.enqueue(keys); + app.option('questions.init', keys); + return; + } + + var keys = utils.arrayify(val); + app.questions.enqueue(keys); + app.option('questions.init', keys); + } +}; diff --git a/lib/runner/commands/help.js b/lib/runner/commands/help.js new file mode 100644 index 00000000..6af7bd46 --- /dev/null +++ b/lib/runner/commands/help.js @@ -0,0 +1,106 @@ +'use strict'; + +var wrap = require('word-wrap'); + +module.exports = function(app) { + return function(key) { + // var commands = help(); + // console.log(commands.options[key]); + // process.exit(0); + }; +}; + +/** + * Create `help` documentation + */ + +function help() { + return { + heading: '', + options: { + init: { + description: 'Force initialization questions to be re-asked.', + example: '', + short: 'i', + }, + help: { + description: '', + example: '', + short: 'h', + }, + show: { + description: '', + example: '', + short: null, + }, + ask: { + description: '', + example: '', + short: null, + }, + open: { + description: '', + example: '', + short: 'o', + }, + config: { + description: '', + example: '', + short: 'c', + }, + diff: { + description: '', + example: '', + short: null, + }, + cwd: { + description: '', + example: '', + short: null, + }, + data: { + description: '', + example: '', + short: 'd', + }, + choose: { + description: '', + example: '', + short: null, + }, + tasks: { + description: '', + example: '', + short: null, + } + }, + footer: '' + }; +} + +function format(obj) { + var heading = obj.heading || ''; + var optsList = ''; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var val = obj[key]; + + optsList += toFlag(key, val.short); + optsList += wrap(val.description); + } + } + + return heading + '\n' + + optsList + '\n' + + obj.footer || ''; +} + +function toFlag(key, short) { + return shortKey(short) + '--' + key + ' '; +} + +function shortKey(sh) { + return sh ? ('-' + sh + ', ') : ' '; +} + +// console.log(format(help())) diff --git a/lib/runner/commands/index.js b/lib/runner/commands/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/runner/commands/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/runner/commands/open.js b/lib/runner/commands/open.js new file mode 100644 index 00000000..71cc0402 --- /dev/null +++ b/lib/runner/commands/open.js @@ -0,0 +1,26 @@ +'use strict'; + +var path = require('path'); +var utils = require('../../utils'); + +module.exports = function(app) { + return function(val) { + if (val === 'answers') { + var dest = app.get('questions.dest'); + if (dest) { + console.log('opening answers data directory >', '"' + dest + '"'); + utils.opn(dest); + process.exit(0); + } + } + + if (val === 'store') { + var dir = path.dirname(app.get('store.path')); + if (dir) { + console.log('opening store data directory >', '"' + dir + '"'); + utils.opn(dir); + process.exit(0); + } + } + }; +}; diff --git a/lib/runner/commands/show.js b/lib/runner/commands/show.js new file mode 100644 index 00000000..9760404c --- /dev/null +++ b/lib/runner/commands/show.js @@ -0,0 +1,22 @@ +'use strict'; + +var path = require('path'); +var utils = require('../../utils'); + +module.exports = function(app) { + return function(key) { + if (utils.isObject(key)) { + key = utils.tableize(key); + } + + if (key === 'answers') { + app.on('answers', console.log); + return; + } + + if (key === 'commands') { + console.log(app.commands.sort()); + return; + } + }; +}; From c3e9e14ff09450cc67ed07f097a25eb31d910c83 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 20 Dec 2015 14:53:17 -0500 Subject: [PATCH 096/282] move templates into `lib/templates` --- lib/runner/templates.js | 44 ++++++++++++--------------- lib/{runner => templates}/badges.js | 0 lib/{runner => templates}/includes.js | 19 +++++++----- lib/templates/sections.js | 32 +++++++++++++++++++ 4 files changed, 62 insertions(+), 33 deletions(-) rename lib/{runner => templates}/badges.js (100%) rename lib/{runner => templates}/includes.js (77%) create mode 100644 lib/templates/sections.js diff --git a/lib/runner/templates.js b/lib/runner/templates.js index 6103a2f1..dd921425 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -6,37 +6,31 @@ var create = require('./create'); var loaded, views = {}; module.exports = function(options) { - var includes = require('./includes'); - var badges = require('./badges'); + var includes = require('../templates/includes'); + var badges = require('../templates/badges'); return function(verb) { verb.use(create(options)); - /** - * DEPRECATED in the next release: Load includes and badges - */ - verb.on('config-loaded', function() { - for (var key in includes) { - verb.include(key, {content: includes[key]}); - } - - for (var key in badges) { - verb.badge(key, {content: badges[key]}); - } + verb.includes(includes); + verb.badges(badges); + // get the user's CWD var userDir = path.join.bind(path, process.cwd()); - if (fs.existsSync(userDir())) { - // api docs - verb.libFiles.loadViews(['*.js', 'lib/*.js'], {cwd: userDir()}); - // templates - verb.docs.loadViews('*', {cwd: userDir(), dot: true}); - verb.includes.loadViews('*.md', {cwd: userDir('docs/includes')}); - verb.layouts.loadViews('*.md', {cwd: userDir('docs/layouts')}); - verb.docs.loadViews('*.md', {cwd: userDir('docs')}); - } + // load lib files, for API documentation + verb.jsFiles(['*.js', 'lib/*.js'], {cwd: userDir()}); + + // load root files, including `.verb.dm` + verb.docs('*', {cwd: userDir(), dot: true}); + + // load templates from `docs` directory + verb.includes('*.md', {cwd: userDir('docs/includes')}); + verb.layouts('*.md', {cwd: userDir('docs/layouts')}); + verb.docs('*.md', {cwd: userDir('docs')}); + // load `verb` built-in templates onto the instance var collections = loadDefaults(verb); if (collections) { for (var key in collections) { @@ -57,9 +51,9 @@ function loadDefaults(app) { loaded = true; var docs = path.join.bind(path, __dirname, '../../docs/src/readme'); - app.includes.loadViews('*.md', {cwd: docs('includes')}); - app.layouts.loadViews('*.md', {cwd: docs('layouts')}); - app.docs.loadViews('*.md', {cwd: docs()}); + app.includes('*.md', {cwd: docs('includes')}); + app.layouts('*.md', {cwd: docs('layouts')}); + app.docs('*.md', {cwd: docs()}); // cache views views.includes = app.views.includes; diff --git a/lib/runner/badges.js b/lib/templates/badges.js similarity index 100% rename from lib/runner/badges.js rename to lib/templates/badges.js diff --git a/lib/runner/includes.js b/lib/templates/includes.js similarity index 77% rename from lib/runner/includes.js rename to lib/templates/includes.js index 554bab09..7e279765 100644 --- a/lib/runner/includes.js +++ b/lib/templates/includes.js @@ -1,19 +1,22 @@ 'use strict'; +var install = [ + 'Install with [npm](https://www.npmjs.com/)', + '', + '```sh', + '$ npm i {%= name %}{%= save === true ? " --save" : "" %}', + '```' +].join('\n'); + module.exports = { - 'install-npm.md': [ - 'Install with [npm](https://www.npmjs.com/)', - '', - '```sh', - '$ npm i {%= name %} --save', - '```' - ].join('\n'), + 'install-npm.md': install, + 'install.md': install, 'install-bower.md': [ 'Install with [bower](http://bower.io/)', '', '```sh', - '$ bower install {%= name %} --save', + '$ bower install {%= name %}{%= save === true ? " --save" : "" %}', '```' ].join('\n'), diff --git a/lib/templates/sections.js b/lib/templates/sections.js new file mode 100644 index 00000000..8e49a543 --- /dev/null +++ b/lib/templates/sections.js @@ -0,0 +1,32 @@ +'use strict'; + +module.exports = { + apidocs: { + heading: 'API', + content: '{%= section("apidocs") %}', + }, + related: { + heading: 'Related projects', + content: '{%= related(verb.related.list) %}', + }, + tests: { + heading: 'Running tests', + content: '{%= include("tests") %}', + }, + contributing: { + heading: 'Contributing', + content: '{%= include("contributing") %}', + }, + author: { + heading: 'Author', + content: '{%= include("author") %}', + }, + license: { + heading: 'License', + content: '{%= copyright() %}\n{%= license %}' + }, + footer: { + heading: '***', + content: '{%= include("footer") %}' + } +}; From 693ab48089d5a12232eda00fddb35ff185d76527 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 20 Dec 2015 14:54:54 -0500 Subject: [PATCH 097/282] remove `full` layout since it was almost the same as `default` start adding metadata to default templates --- docs/src/readme/layouts/basic.md | 5 +++++ docs/src/readme/layouts/default.md | 11 +++++++-- docs/src/readme/layouts/full.md | 36 ------------------------------ docs/src/readme/layouts/min.md | 5 +++++ 4 files changed, 19 insertions(+), 38 deletions(-) delete mode 100644 docs/src/readme/layouts/full.md diff --git a/docs/src/readme/layouts/basic.md b/docs/src/readme/layouts/basic.md index b81f7fe2..cd5d5290 100644 --- a/docs/src/readme/layouts/basic.md +++ b/docs/src/readme/layouts/basic.md @@ -1,3 +1,8 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Basic layout +--- # {%= name %} {%= badge('npm') %} {%= badge('travis') %} > {%= description %} diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md index bfcf75f1..c46a4d93 100644 --- a/docs/src/readme/layouts/default.md +++ b/docs/src/readme/layouts/default.md @@ -1,13 +1,20 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Default layout +--- # {%= name %} {%= badge('npm') %} {%= badge('travis') %} > {%= description %} -{% if (typeof options !== 'undefined' && options.toc) { %} -{% } %} +{%= section("install") %} + +{% if (verb.sections.install !== false) { %} ## Install {%= include('install-npm', {save: true}) %} +{% } %} {% body %} diff --git a/docs/src/readme/layouts/full.md b/docs/src/readme/layouts/full.md deleted file mode 100644 index 49f43ed2..00000000 --- a/docs/src/readme/layouts/full.md +++ /dev/null @@ -1,36 +0,0 @@ -# {%= name %} {%= badge('npm') %} {%= badge('travis') %} - -> {%= description %} - -{% body %} - -{% if (verb.related && verb.related.list && verb.related.list.length) { %} -## Related projects -{%= verb.related.description || '' %} -{%= related(verb.related.list) %} -{% } %} - -## Running tests -{%= include("tests") %} - -## Contributing -{%= include("contributing") %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} - -{% if (verb.reflinks) { %} -{% if (Array.isArray(verb.reflinks) && verb.reflinks.length) { %} -{%= reflinks(verb.reflinks) %} -{% } else if (verb.reflinks.list && verb.reflinks.list.length) { %} -{%= reflinks(verb.reflinks.list) %} -{% } %} -{% } %} diff --git a/docs/src/readme/layouts/min.md b/docs/src/readme/layouts/min.md index 74e345d6..46a559d4 100644 --- a/docs/src/readme/layouts/min.md +++ b/docs/src/readme/layouts/min.md @@ -1,3 +1,8 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Minimal layout +--- # {%= name %} > {%= description %} From 5dba49a8e5034a4aa6208d7e2450330718e2f5f4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 20 Dec 2015 14:56:54 -0500 Subject: [PATCH 098/282] move `ask` task to `cli.js` --- verbfile.js | 50 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/verbfile.js b/verbfile.js index 884abd64..1968e3e5 100644 --- a/verbfile.js +++ b/verbfile.js @@ -16,12 +16,6 @@ var argv = require('minimist')(process.argv.slice(2), { */ module.exports = function(verb, base, env) { - if (argv.init) { - verb.questions.options.forceAll = true; - } - - var tasks = verb.get('env.argv.tasks') || ['readme', 'package']; - verb.task('a', function(cb) { console.log('verb base > a'); cb(); @@ -41,26 +35,24 @@ module.exports = function(verb, base, env) { verb.task('readme', function(cb) { var plugins = verb.get('env.argv.plugins') || verb.plugins; - data.updateData(verb, verb.base, verb.env); // ask pre-configured questions, but only if // they don't have answers yet - verb.ask(function(err, answers) { - if (err) return cb(err); - - verb.toStream('docs', function(key, view) { - return key === '.verb'; - }) - .pipe(handle(verb, 'onStream')) - .pipe(verb.renderFile('text', answers)) - .on('error', handleError(verb)) - .pipe(verb.pipeline(plugins)) - .pipe(handle(verb, 'preWrite')) - .pipe(verb.dest(dest())) - .pipe(utils.exhaust(handle(verb, 'postWrite'))) - .on('error', cb) - .on('finish', cb); - }); + verb.toStream('docs', function(key, view) { + return key === '.verb'; + }) + .pipe(handle(verb, 'onStream')) + .on('error', cb) + .pipe(verb.renderFile('text')) + .on('error', handleError(verb)) + .pipe(verb.pipeline(plugins)) + .on('error', cb) + .pipe(handle(verb, 'preWrite')) + .pipe(verb.dest(rename())) + .on('error', cb) + .pipe(utils.exhaust(handle(verb, 'postWrite'))) + .on('error', cb) + .on('finish', cb); }); verb.task('package', function(cb) { @@ -68,13 +60,13 @@ module.exports = function(verb, base, env) { return view.basename === 'package.json'; }) .pipe(handle(verb, 'preWrite')) - .pipe(verb.dest(dest())) + .pipe(verb.dest(rename())) .pipe(utils.exhaust(handle(verb, 'postWrite'))) .on('error', cb) .on('finish', cb); }); - verb.task('default', tasks); + verb.task('default', ['readme', 'package']); }; function handle(app, stage) { @@ -91,7 +83,7 @@ function handle(app, stage) { * Rename template files */ -function dest(dest) { +function rename(dest) { return function(file) { var fp = file.dest || dest || ''; file.base = fp ? path.dirname(fp) : file.base; @@ -111,7 +103,11 @@ function handleError(app) { var m = /(\w+) is not a function/.exec(err.message); var msg = ''; if (m) { - msg = err.message + ': "' + m[1] + '()" is defined as a helper\n' + 'in `.verb.md`, but "' + m[1] + '" is defined on ' + 'verb.cache.data as a "' + typeof app.cache.data[m[1]] + '"'; + msg = err.message + ': "' + m[1] + + '()" is defined as a helper\n' + + 'in `.verb.md`, but "' + m[1] + + '" is defined on verb.cache.data as a "' + + typeof app.cache.data[m[1]] + '"'; } if (app.options.verbose) { console.log(msg); From 28e5877d85bc464edf52ae4b3da8561cd51abff7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 20 Dec 2015 15:42:57 -0500 Subject: [PATCH 099/282] ensure TOC is generated and inserted only when specified by the user --- lib/runner/middleware.js | 82 +++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index c8ff77b9..9549685a 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -1,8 +1,8 @@ 'use strict'; +var utils = require('../utils'); var falsey = require('falsey'); var diff = require('base-diff'); -var utils = require('../utils'); module.exports = function(app, base, env) { app.use(function fn(view) { @@ -27,34 +27,18 @@ module.exports = function(app, base, env) { */ var append = '\n\n_(TOC generated by [verb](https://github.com/verbose/verb))_'; - app.preRender(/\.md/, function(view, next) { - if (!view.isType('partial')) { - var toc = utils.toc(app, {append: append}); - return toc(view, next); - } - return next(); - }); + app.preRender(/\.md/, renderToc(app, 'preRender', append)); /** * Diff files (currently just testing readme) */ - app.preRender(/./i, diff.view('diffLines')); - app.postRender(/./i, function(view, next) { + app.preRender(/./, diff.view('diffLines')); + app.postRender(/./, function(view, next) { var diff = app.option('diff'); if (diff && diff === true || diff === view.stem) { view.diff(); } - return next(); - }); - - app.preWrite(/readme\.md$/i, function(view, next) { - insertToc(app, view); - next(); - }); - - app.preWrite(/package\.json$/, function(view, next) { - view.dest = 'package.json'; next(); }); @@ -77,21 +61,14 @@ module.exports = function(app, base, env) { } next(); }); -}; -/** - * Inject the TOC created by the toc middleware. - */ - -function insertToc(app, view) { - var toc = view.data.toc; - var str = view.content; - if (toc && app.option('toc.insert') !== false) { - view.content = str.split('').join(toc); - } else { - view.content = str.split('').join(''); - } -} + app.postLayout(/./, renderToc(app, 'postLayout', append)); + + app.preWrite(/package\.json$/, function(view, next) { + view.dest = 'package.json'; + next(); + }); +}; // TODO: implement better check function needsLayout(str) { @@ -107,3 +84,40 @@ function isFalsey(view) { } return false; } + +function renderToc(app, stage, append) { + return function(view, next) { + if (view.isType('partial') || isDisabled(app, view, 'toc')) { + view.content = stripToc(view.content); + next(); + return; + } + + var opts = utils.merge({append: append, toc: {}}, app.options); + if (typeof opts.toc.insert === 'undefined') { + opts.toc.insert = false; + } + var toc = utils.toc(app, opts); + toc(view, next); + }; +} + +function stripToc(str) { + return str.split('').join(''); +} + +function isDisabled(app, view, prop) { + return isFalsey(app.option(prop)) + || isFalsey(view.options[prop]) + || isFalsey(view.data[prop]); +} + +function isFalsey(val) { + if (typeof val === 'undefined') { + return false; + } + if (val && !isPrimitive(val)) { + return false; + } + return falsey(val); +} From e5b6974f25da6bfb4065ccdd20dc8deb1138b22e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 20 Dec 2015 15:43:20 -0500 Subject: [PATCH 100/282] adds `require`, `apidocs` and `issue` helpers --- lib/runner/helpers.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 36d20dce..bb9a3565 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -4,11 +4,24 @@ var utils = require('../utils'); module.exports = function(verb, base, env) { var apidocs = require('helper-apidocs'); + var issue = require('helper-issue'); verb.helper('log', function(msg) { console.log.apply(console, arguments); }); + verb.helper('require', function(name) { + return utils.tryRequire(name); + }); + + verb.helper('list', function(arr) { + var list = arr.map(function(ele) { + return '* `' + ele + '`' + }); + + return list.join('\n'); + }); + verb.helper('get', function(key) { return utils.get(this.context, key); }); @@ -17,10 +30,15 @@ module.exports = function(verb, base, env) { return 'https://img.shields.io/' + type + '/{%= author.username %}/{%= name %}.svg'; }); - verb.helper('apidocs', apidocs({ - delims: ['{%', '%}'] - })); verb.helper('date', require('helper-date')); + verb.helper('issue', function(options) { + var opts = utils.extend({}, options); + opts.owner = opts.owner || utils.get(this, 'context.author.username'); + opts.repo = this.context.name; + return issue(opts); + }); + + verb.helper('apidocs', apidocs({delims: ['{%', '%}']})); verb.helper('copyright', require('helper-copyright')({ linkify: true })); From f4a71ba2b047c58e229cc874dc15a0e058035bd5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:39:45 -0500 Subject: [PATCH 101/282] move contributing to templates --- docs/src/{files => templates}/contributing.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/src/{files => templates}/contributing.md (100%) diff --git a/docs/src/files/contributing.md b/docs/src/templates/contributing.md similarity index 100% rename from docs/src/files/contributing.md rename to docs/src/templates/contributing.md From 94e16c85881d48ab6c986e9708e3386970d28461 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:42:20 -0500 Subject: [PATCH 102/282] only add twitter question if it doesn't exist --- lib/runner/questions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/runner/questions.js b/lib/runner/questions.js index cfb3ec42..4bd824a5 100644 --- a/lib/runner/questions.js +++ b/lib/runner/questions.js @@ -1,5 +1,7 @@ 'use strict'; module.exports = function(verb, base, env) { - verb.questions.set('author.twitter', 'Author\'s twitter username?'); + if (!verb.questions.has('author.twitter')) { + verb.questions.set('author.twitter', 'Author\'s twitter username?'); + } }; From 675ce956a4ed6dfe8a7edd761f2b4e07fa915d67 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:42:46 -0500 Subject: [PATCH 103/282] remove list helper --- lib/runner/helpers.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index bb9a3565..0b7f4398 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -14,14 +14,6 @@ module.exports = function(verb, base, env) { return utils.tryRequire(name); }); - verb.helper('list', function(arr) { - var list = arr.map(function(ele) { - return '* `' + ele + '`' - }); - - return list.join('\n'); - }); - verb.helper('get', function(key) { return utils.get(this.context, key); }); From a2d69defe460a47205d66c761cbedf996228cbc9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:43:22 -0500 Subject: [PATCH 104/282] improve logic to determine if a table of contents is specified --- lib/runner/middleware.js | 81 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 9549685a..01f2150a 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -5,6 +5,7 @@ var falsey = require('falsey'); var diff = require('base-diff'); module.exports = function(app, base, env) { + // `view` plugin that add a `isType` method to views app.use(function fn(view) { if (!view.isView) return fn; @@ -22,13 +23,32 @@ module.exports = function(app, base, env) { next(); }); + app.onLoad(/\.md/, function(view, next) { + view.options.foo = 'bar' + if (hasToc(view.content)) { + // don't render or insert a TOC + view.options.toc = view.options.toc || {}; + view.options.toc.render = true; + view.options.toc.insert = true; + // don't break toc helpers or variables + verb.data.toc = ''; + } + next(); + }); + /** * Table of contents > adds `file.data.toc` property */ - var append = '\n\n_(TOC generated by [verb](https://github.com/verbose/verb))_'; + var append = '\n\n_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_'; app.preRender(/\.md/, renderToc(app, 'preRender', append)); + + app.preRender(/./, function(view, next) { + view.data.options = view.options; + next(); + }); + /** * Diff files (currently just testing readme) */ @@ -42,11 +62,16 @@ module.exports = function(app, base, env) { next(); }); + app.preLayout(/\.md/, function(view, next) { if (view.isType('partial')) return next(); - // working on a better method for this! - var layout = app.get('env.user.pkg.verb.layout'); + var layout = app.pkg.get('verb.layout') + if (utils.isObject(layout)) { + var opts = layout; + layout = layout.name; + } + if (typeof layout !== 'undefined') { view.layout = layout; return next(); @@ -55,10 +80,6 @@ module.exports = function(app, base, env) { if (typeof view.layout === 'string' || isFalsey(view)) { return next(); } - - if (needsLayout(view.content)) { - view.layout = 'default'; - } next(); }); @@ -70,54 +91,44 @@ module.exports = function(app, base, env) { }); }; -// TODO: implement better check -function needsLayout(str) { - if (!/^# /.test(str)) { - return true; - } - return false; -} - -function isFalsey(view) { - if (typeof view.layout === 'undefined' && falsey(view.layout)) { - return true; - } - return false; -} - function renderToc(app, stage, append) { return function(view, next) { - if (view.isType('partial') || isDisabled(app, view, 'toc')) { + var opts = utils.merge({append: append, toc: {}}, app.options); + if (view.isType('partial') || isDisabled(opts, view, 'toc')) { view.content = stripToc(view.content); next(); return; } - var opts = utils.merge({append: append, toc: {}}, app.options); - if (typeof opts.toc.insert === 'undefined') { - opts.toc.insert = false; - } var toc = utils.toc(app, opts); + + // ensure the TOC isn't injected until after formatting + view.options.toc = { insert: false }; toc(view, next); }; } +function hasToc(str) { + return str.indexOf('') !== -1; +} + function stripToc(str) { - return str.split('').join(''); + str = str.split('').join(''); + str = str.split('').join(''); + return str; } -function isDisabled(app, view, prop) { - return isFalsey(app.option(prop)) - || isFalsey(view.options[prop]) - || isFalsey(view.data[prop]); +function isDisabled(opts, view, prop) { + return isFalsey(utils.get(opts, prop)) + || isFalsey(utils.get(view.options, prop)) + || isFalsey(utils.get(view.data, prop)) } function isFalsey(val) { if (typeof val === 'undefined') { return false; } - if (val && !isPrimitive(val)) { - return false; + if (!utils.isObject(val)) { + return falsey(val); } - return falsey(val); } From 531d3a7e6faff0cc0c888410cc941d83670d08b9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:44:07 -0500 Subject: [PATCH 105/282] adds config mappings for helpers, middleware. several fixes --- lib/config.js | 287 +++++++++++++++++++++++++++++++------------------- 1 file changed, 178 insertions(+), 109 deletions(-) diff --git a/lib/config.js b/lib/config.js index f444640f..8636a995 100644 --- a/lib/config.js +++ b/lib/config.js @@ -2,6 +2,7 @@ var fs = require('fs'); var path = require('path'); +var sections = require('./runner/sections'); var updater = require('./updater'); var utils = require('./utils'); @@ -10,12 +11,12 @@ module.exports = function(verb, base, env) { /** * Update verb config (`verb` object in package.json) - * `init` force verb to init a project - * `data` add or extend verb config data to be used in templates - * `update` this mapping is setup to be called by the cli - * `--config` command, and is used to extend, add, - * delete or otherwise update any given property - * on the config object + * + * * `--init`: force verb to init a project + * * `--data`: add or extend verb config data to be used in templates + * * `--update`: this mapping is setup to be called by the cli `--config` + * command, and is used to extend, add, delete or otherwise update + * any given property on the config object */ verb.config @@ -24,6 +25,14 @@ module.exports = function(verb, base, env) { verb.enable('init'); } }) + .map('options', function(options) { + for (var key in options) { + var val = options[key]; + } + }) + .map('option', function(val) { + verb.option(val); + }) .map('update', function(val) { verb.emit('config.update', val); env = env || verb.env; @@ -37,9 +46,9 @@ module.exports = function(verb, base, env) { pkg.verb = pkg.verb || {}; try { file.json.verb = verb.update(pkg.verb, val); - verb.cache.data = utils.merge({}, verb.cache.data, file.json.verb.data); + verb.set('cache.config', file.json.verb); } catch (err) { - throw new Error('config', 'cannot update pkg.verb', err.stack); + throw new Error('config', 'cannot update pkg.verb', err); } }); @@ -48,38 +57,171 @@ module.exports = function(verb, base, env) { */ verb.config - .map('helpers') + .map('helpers', function(val) { + console.log('helpers >', val); + }) .map('asyncHelpers'); /** - * Templates + * Template options and settings */ verb.config - .map('sections', function(val) { - console.log(val) + .map('toc', function(val) { + verb.option('toc', val); }) .map('create', function(val) { verb.visit('create', val); + }); + + /** + * Load templates + */ + + verb.config + .map('sections', function(val) { + verb.set('cache.sections', sections(val, verb)); }) .map('includes', views('includes')) .map('layouts', views('layouts')) .map('badges', views('badges')) .map('docs', views('docs')); + function views(name) { + return function(options) { + verb[name](options); + }; + } + /** - * Middleware + * Helpers */ - // verb.config - // .map('onLoad', function(fn) { - // verb.onLoad(fn); - // }); + verb.config.map('helpers', function(helpers) { + var obj = verb.get('env.argv.helpers'); + var cwd = verb.get('env.user.cwd'); + var opts = {}; + + if (typeof helpers === 'string') { + helpers = [helpers]; + } + + if (Array.isArray(helpers)) { + helpers = helpers.reduce(function(acc, helper) { + if (typeof helper === 'string') { + acc[helper] = {}; + } else { + throw new Error('helper format not supported, implement me!'); + } + return acc; + }, {}); + } + + for (var key in helpers) { + var name = path.basename(key, path.extname(key)); + var helper = helpers[key]; + + try { + if (name === 'index') { + var dir = path.resolve(cwd); + var fp = utils.tryResolve(dir); + if (fs.existsSync(fp)) { + opts = helper; + helper = utils.tryRequire(fp); + name = path.basename(dir); + } + } + } catch (err) { + throw err; + } + + if (typeof helper === 'string') { + // use `name` + // resolve `helper` + + } else if (utils.isObject(helper)) { + opts = helper; + helper = utils.loadModule(opts.path || name); + name = opts.name || name; + } + + if (typeof helper !== 'function') { + throw new Error('cannot load helper: ' + helper); + } + + verb.helper(name, opts, helper); + } + }); /** - * Plugins + * Middleware */ + verb.config.map('use', function(middleware) { + var obj = verb.get('env.argv.middleware'); + var cwd = verb.get('env.user.cwd'); + var opts = {}; + + if (typeof middleware === 'string') { + middleware = [middleware]; + } + + if (Array.isArray(middleware)) { + middleware = middleware.reduce(function(acc, val) { + if (typeof val === 'string') { + acc[val] = {}; + } else { + throw new Error('format not supported, implement me!'); + } + return acc; + }, {}); + } + + for (var key in middleware) { + var name = path.basename(key, path.extname(key)); + var val = middleware[key]; + + if (name === 'index') { + var dir = path.resolve(cwd); + var fp = utils.tryResolve(dir); + if (fs.existsSync(fp)) { + opts = val; + val = utils.tryRequire(fp); + name = path.basename(dir); + } + } + + if (typeof val === 'string') { + throw new Error('format not supported, implement me!'); + + } else if (utils.isObject(val)) { + opts = val; + var fp = utils.resolveModule(opts.path || name); + if (opts.path) { + fp = path.resolve(path.dirname(fp), opts.path); + } + val = utils.tryRequire(fp); + name = opts.name || name; + } + + if (typeof val !== 'function') { + throw new Error('cannot load middleware: ' + val); + } + + // get the regex pattern to use + var pattern = opts.pattern ? new RegExp(opts.pattern) : /./; + + // get the method name (onLoad, preWrite, preRender, etc) + var method = opts.method || 'onLoad'; + if (!(method in verb)) { + throw new Error('verb does not have a middleware handler for ' + method); + } + + // load the middleware + verb[method](pattern, val(verb.options)); + } + }); + verb.config.map('plugins', function(plugins) { var obj = verb.get('env.argv.plugins'); var cwd = verb.get('env.user.cwd'); @@ -91,7 +233,11 @@ module.exports = function(verb, base, env) { if (Array.isArray(plugins)) { plugins = plugins.reduce(function(acc, plugin) { - acc[plugin] = {}; + if (typeof plugin === 'string') { + acc[plugin] = {}; + } else { + throw new Error('plugin format not supported, implement me!'); + } return acc; }, {}); } @@ -100,14 +246,18 @@ module.exports = function(verb, base, env) { var name = path.basename(key, path.extname(key)); var plugin = plugins[key]; - if (name === 'index') { - var dir = path.resolve(cwd); - var fp = utils.tryResolve(dir); - if (fs.existsSync(fp)) { - opts = plugin; - plugin = utils.tryRequire(fp); - name = path.basename(dir); + try { + if (name === 'index') { + var dir = path.resolve(cwd); + var fp = utils.tryResolve(dir); + if (fs.existsSync(fp)) { + opts = plugin; + plugin = utils.tryRequire(fp); + name = path.basename(dir); + } } + } catch (err) { + throw err; } if (typeof plugin === 'string') { @@ -116,96 +266,15 @@ module.exports = function(verb, base, env) { } else if (utils.isObject(plugin)) { opts = plugin; - plugin = utils.loadModule(name); + plugin = utils.loadModule(opts.path || name); name = opts.name || name; } if (typeof plugin !== 'function') { throw new Error('cannot load plugin: ' + plugin); } + verb.plugin(name, opts, plugin); } }); - - // helper for loading views - function views(name) { - return function(options) { - verb[name](options); - }; - } }; - -// function loadPlugin(verb, name, plugin) { -// if (typeof plugin === 'function') { -// verb.plugin(name, {}, plugin); -// return true; -// } -// var fn; -// if (typeof plugin === 'string') { -// fn = tryRequire(plugin, cwd, verb.options); -// verb.plugin(name, {}, fn); -// return true; -// } -// if (utils.isObject(plugin)) { -// fn = tryRequire(key, cwd, verb.options); -// verb.plugin(name, plugin, fn); -// return true; -// } -// return false; -// } - -function content(val) { - return typeof val === 'string' - ? { content: val } - : val; -} - -/** - * Try to require the given module - * or file path. - */ - -function tryRequire(name, cwd, options) { - options = options || {}; - var attempts = [name], fp; - - try { - return require(name); - } catch (err) {} - - name = utils.resolveDir(name); - try { - return require(name); - } catch (err) {} - - try { - fp = path.resolve(name); - attempts.push(fp); - return require(fp); - } catch (err) {} - - try { - fp = path.resolve(utils.resolveDir(cwd), name); - attempts.push(fp); - return require(fp); - } catch (err) {} - - if (options.verbose !== true) { - return; - } - - var msg = utils.red('[base-pipeline] cannot find plugin at: \n') - + format(attempts) - + utils.colors.yellow(' check your configuration to ensure plugin paths are\n' - + ' correct (package.json config, options, etc) \n'); - - throw new Error(msg); -} - -function format(arr) { - var res = ''; - arr.forEach(function(ele) { - res += utils.colors.red(' ✖ ') + '\'' + ele + '\'' + '\n'; - }); - return res; -} From f9a1de171a437e802ed22b4b79e9d2fee85907b7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:44:35 -0500 Subject: [PATCH 106/282] expand package.json data before merging into context --- lib/expand.js | 59 +++++++++++++++++++++++++++++++++++-------- lib/runner/context.js | 7 +++++ lib/runner/data.js | 54 ++++++++++----------------------------- lib/runner/preload.js | 35 +++++++++++++++++-------- 4 files changed, 94 insertions(+), 61 deletions(-) create mode 100644 lib/runner/context.js diff --git a/lib/expand.js b/lib/expand.js index f2bc6be7..dc71b426 100644 --- a/lib/expand.js +++ b/lib/expand.js @@ -4,18 +4,57 @@ var utils = require('./utils'); /** * Adds get/set methods to verb env + * + * @param {Object} `verb` Instance of verb + * @param {Object} `config` Verb config data from package.json */ -module.exports = function(options) { - return function(verb) { - var pkg = verb.get('env.user.pkg'); - if (!pkg) return; +module.exports = function(verb, config) { + var pkg = verb.get('env.user.pkg'); + if (!pkg) return; - var exp = utils.extend({}, pkg); - if (typeof pkg.author === 'string') { - exp.author = utils.parseAuthor(pkg.author); - } + var data = utils.extend({}, pkg); + if (typeof pkg.author === 'string') { + data.author = utils.parseAuthor(pkg.author); + } + + data.author = formatAuthor(verb, data.author); - verb.base.set('cache.expanded', exp); - }; + var res = utils.defaults(config, data); + res.license = formatLicense(res, verb.options); + return res; }; + +/** + * Format license + */ + +function formatLicense(pkg, options) { + options = options || {}; + if (typeof options.license === 'string') { + return options.license; + } + var str = pkg.license; + if (Array.isArray(pkg.licenses)) { + str = pkg.licenses[0]; + } + if (typeof str === 'undefined') { + return ''; + } + return 'Released under the ' + str + ' license.'; +} + +function formatAuthor(app, author) { + var config = app.store.config; + author = author || {}; + + if (!author.username && author.url) { + if (config.has('author.data.username')) { + author.username = config.get('data.author.username'); + } else if (/github\.com/.test(author.url)) { + var username = author.url.split('github.com').pop(); + author.username = username.replace(/^\W+|\W+$/g, ''); + } + } + return author; +} diff --git a/lib/runner/context.js b/lib/runner/context.js new file mode 100644 index 00000000..94516581 --- /dev/null +++ b/lib/runner/context.js @@ -0,0 +1,7 @@ +'use strict'; + +var data = require('./data'); + +module.exports = function(verb, base, env) { + data(verb, base, env); +}; diff --git a/lib/runner/data.js b/lib/runner/data.js index d66b89d6..e220d28a 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -1,8 +1,10 @@ 'use strict'; +var expandPkg = require('../expand'); var utils = require('../utils'); module.exports = function(verb, base, env) { + verb.data({year: new Date().getFullYear()}); verb.data({ runner: { name: 'verb', @@ -10,51 +12,23 @@ module.exports = function(verb, base, env) { }, verb: { reflinks: [], - related: { - list: [] - } + related: {list: []}, + sections: {} } }); }; -module.exports.updateData = function(app, base, env) { - app.questions.enqueue('author', 'name', 'description'); - var pkg = env.user.pkg; +module.exports.updateData = function(verb) { + verb.questions.enqueue('author', 'name', 'description'); + + var pkg = verb._pkg; var config = pkg.verb || {}; - app.option(config.options || {}); + // load package.json data and user options onto `verb.cache.data` + verb.option(config.options || {}); + verb.data(pkg); - // load package.json data and user options onto `app.cache.data` - app.data({options: app.options}); - app.data(pkg); - app.data(app.base.get('cache.expanded') || {}); - var config = app.get('cache.data.verb.data'); - if (config) { - var obj = utils.tableize(config); - for (var key in obj) { - var val = obj[key]; - utils.set(app.cache.data, key, obj[key]); - } - } - app.data({license: formatLicense(pkg, app.options)}); + var config = verb.pkg.get('verb.data') || {}; + config = utils.defaults(config, verb.cache.data); + return expandPkg(verb, config); }; - -/** - * Format license - */ - -function formatLicense(pkg, options) { - options = options || {}; - if (typeof options.license === 'string') { - return options.license; - } - var str = pkg.license; - if (Array.isArray(pkg.licenses)) { - str = pkg.licenses[0]; - } - if (typeof str === 'undefined') { - return ''; - } - return 'Released under the ' + str + ' license.'; -} - diff --git a/lib/runner/preload.js b/lib/runner/preload.js index 013ed083..b1d97b0e 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -1,34 +1,47 @@ 'use strict'; +var pkg = require('../pkg'); var utils = require('../utils'); var config = require('../config'); -var expand = require('../expand'); var templates = require('./templates'); var defaults = require('./defaults'); +var context = require('./context'); var cli = require('./cli'); module.exports = function preload(verb, base, env) { - verb.use(utils.ask()); + verb.use(utils.ask({storeName: 'verb'})); verb.use(utils.loader()); defaults(verb, base, env); verb.use(templates()); + context(verb, base, env); config(verb, base, env); cli(verb, base, env); - verb.use(utils.list('apps', { - method: 'app' - })); + verb.use(utils.list('apps', {method: 'app'})); verb.on('register', function(name, app) { - var toc = verb.get('env.user.pkg.verb.toc'); - if (typeof toc === 'undefined') { - verb.set('env.user.pkg.verb.toc', {insert: true}); - } - + context(verb, base, env); defaults(app, base, env); app.use(templates()); - app.use(expand()); + }); + + verb.questions.on('ask', function(key, question, answers) { + if (!verb.pkg.get('description') && key === 'description') { + question.options.skip = true; + } + + // console.log(question.isAnswered()) + // if (verb.store.has(key) && question) { + // verb.questions.setData(key, verb.store.get(key)); + // } + }); + + verb.questions.on('answer', function(key, answer) { + // console.log(arguments) + // if (/author\./.test(key)) { + // // verb.store.set() + // } }); }; From b191dac4346afd2c336500a4db85d2e37ec7cc2e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:45:02 -0500 Subject: [PATCH 107/282] ensure that values are cast to the desired type --- lib/updater.js | 92 ++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/lib/updater.js b/lib/updater.js index 69ed480d..d61ac1c6 100644 --- a/lib/updater.js +++ b/lib/updater.js @@ -3,35 +3,25 @@ var utils = require('./utils'); module.exports = function(options) { - return function(verb) { - verb.define('update', function(config, obj) { - var table = utils.tableize(obj); + options = options || {}; - for (var key in table) { - updateProperty(config, key, table[key]); - } - return config; - }); - - // utils.define(verb, 'update', function(pkg, args) { - // if (!pkg) return; - - // var config = utils.mapper(schema) - // .map('set', function(val) { - // update(pkg, 'set', val); - // }) - // .map('del', function(val) { - // update(pkg, 'del', val); - // }) - // .map('data', function(val) { - // update(pkg, 'data', val); - // }); - - // config.process(args); - // }); + return function(verb) { + verb.define('update', update, options.schema); }; }; +function update(config, obj, schema) { + var table = utils.tableize(obj); + for (var key in table) { + if (schema && schema.has(key)) { + updateProperty(config, key, table[key]); + } else { + updateProperty(config, key, table[key]); + } + } + return config; +} + function updateProperty(config, prop, val) { config = config || {}; @@ -43,13 +33,23 @@ function updateProperty(config, prop, val) { return config; } + if (prop === 'del') { + utils.del(config, val); + return config; + } + + // If defined in `cast`, the value will be + // cast to a specified type + var obj = cast(prop, val); + prop = obj.prop; + val = obj.val; + var current = utils.get(config, prop); if (typeof current === 'undefined') { utils.set(config, prop, val); return config; } - // replace existing primitive with whatever `val` is if (utils.isPrimitive(current)) { utils.set(config, prop, val); @@ -80,27 +80,23 @@ function updateProperty(config, prop, val) { } } -/** - * Adds get/set methods to verb env - */ - -// module.exports = function(options) { -// return function(verb) { -// utils.define(verb, 'update', function(pkg, args) { -// update() -// // if (!pkg) return; - -// // var schema = new Schema(pkg, options); -// // // console.log(schema) -// // var config = utils.mapper(schema) -// // .map('set') -// // .map('del'); - -// // config.process(args); -// }); -// }; -// }; +function cast(key, val) { + if (val === true) { + if (/^(plugins|helpers)\./.test(key)) { + var segs = key.split('.'); + key = segs[0]; + val = [segs[1]]; + } + } -// function updater(pkg) { + if (typeof val === 'string') { + if (/helpers|plugins|reflinks|related/.test(key)) { + val = [val]; + } + } -// } + return { + prop: key, + val: val + }; +} From 667416d13a6f6de82c5d6f0c81b9be8aaad27485 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:45:37 -0500 Subject: [PATCH 108/282] remove `data` from `defaults.js` --- lib/runner/defaults.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/runner/defaults.js b/lib/runner/defaults.js index 9b573a92..dd56dd13 100644 --- a/lib/runner/defaults.js +++ b/lib/runner/defaults.js @@ -3,11 +3,9 @@ var middleware = require('./middleware'); var questions = require('./questions'); var helpers = require('./helpers'); -var data = require('./data'); module.exports = function(verb, base, env) { helpers(verb, base, env); middleware(verb, base, env); questions(verb, base, env); - data(verb, base, env); }; From db4ef6fab08cc30c0461386c6ec0697972b0f985 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:46:04 -0500 Subject: [PATCH 109/282] create `sections` partials, rename `libFiles` to `jsFiles` --- lib/runner/create.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/runner/create.js b/lib/runner/create.js index 9c177c71..96d3a502 100644 --- a/lib/runner/create.js +++ b/lib/runner/create.js @@ -7,20 +7,29 @@ var path = require('path'); */ module.exports = function(options) { - return function(verb) { - create(verb, 'docs'); - create(verb, 'libFiles'); - create(verb, 'badges', 'partial'); - create(verb, 'includes', 'partial'); - create(verb, 'layouts', 'layout'); + return function(app) { + create(app, 'docs'); + create(app, 'jsFiles'); + create(app, 'badges', 'partial'); + create(app, 'includes', 'partial'); + create(app, 'sections', 'partial'); + create(app, 'layouts', 'layout'); }; }; -function create(verb, name, type) { - // only create the collection if it doesn't already exist - if (verb[name]) return; +/** + * Create a view collection if it doesn't already exist. + * + * @param {String} `app` Verb instance ("app", since it's also compatible with assemble and other `base` apps) + * @param {String} `name` + * @param {String} `type` + * @return {String} + */ + +function create(app, name, type) { + if (app[name]) return; - verb.create(name, { + app.create(name, { engine: 'text', viewType: [type || 'renderable'], renameKey: function(key) { From 345055372c4261710f2501de840cbd96f657a9ff Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:46:26 -0500 Subject: [PATCH 110/282] adds several utils for merging/flattening data --- lib/utils.js | 125 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 600987fa..bb8b3946 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,14 +31,12 @@ require('base-store', 'store'); require('base-list', 'list'); // misc -require('opn'); -require('async'); -require('expand'); -require('array-unique', 'unique'); require('arr-flatten', 'flatten'); +require('array-unique', 'unique'); +require('async'); require('define-property', 'define'); +require('expand'); require('extend-shallow', 'extend'); -require('mixin-deep', 'merge'); require('get-value', 'get'); require('global-modules', 'gm'); require('has-glob'); @@ -48,13 +46,15 @@ require('isobject', 'isObject'); require('kind-of', 'typeOf'); require('map-config', 'mapper'); require('matched', 'glob'); +require('mixin-deep', 'merge'); require('object-visit', 'visit'); +require('opn'); require('resolve'); require('resolve-dir'); require('set-value', 'set'); require('stream-exhaust', 'exhaust'); require('through2', 'through'); -require('tableize'); +require('unset-value', 'del'); // cli require('success-symbol'); @@ -70,11 +70,46 @@ require('parse-author'); require = fn; +utils.defaults = function(a, b) { + a = utils.tableize(a || {}); + b = utils.tableize(b || {}); + + var akeys = Object.keys(a); + var bkeys = []; + var res = {}; + + for (var key in b) { + bkeys.push(key); + var orig = utils.get(a, key); + var val = utils.get(b, key); + + if (key.indexOf('.') > -1) { + var seg = key.split('.').shift(); + if (bkeys.indexOf(seg) === -1) { + bkeys.push(seg); + } + } + + if (val && !orig) { + utils.set(res, key, val); + } else { + utils.set(res, key, orig || val); + } + } + + akeys.forEach(function(key) { + if (bkeys.indexOf(key) === -1) { + utils.set(res, key, utils.get(a, key)); + } + }); + return res; +}; + utils.expandConfig = function(config, data) { var expand, res; try { expand = utils.expand(config); - data = utils.extend({}, config, data); + data = utils.merge({}, config, data); res = expand(config, data); } catch (err) { return config; @@ -128,9 +163,28 @@ utils.globFiles = function(patterns, options) { * @return {String} */ -utils.timestamp = function(msg) { +utils.timestamp = function(msg, stamp) { var time = '[' + utils.colors.gray(utils.stamp('HH:mm:ss', new Date())) + ']'; - return console.log(time, msg, utils.colors.green(utils.successSymbol)); + console.log(time, msg); +}; + +/** + * Get a home-relative filepath + */ + +utils.homeRelative = function(fp) { + fp = path.relative(utils.resolveDir('~/'), fp); + return utils.colors.green('~/' + fp); +}; + +/** + * Green checkmark + * + * @return {String} + */ + +utils.success = function() { + return utils.colors.green(utils.successSymbol); }; /** @@ -206,6 +260,59 @@ utils.loadModule = function(name, cwd) { return main ? utils.tryRequire(main) : null; }; +/** + * Modified from the `tableize` lib, which replaces + * dashes with underscores, and we don't want that behavior. + * Tableize `obj` by flattening and normalizing the keys. + * + * @param {Object} obj + * @return {Object} + * @api public + */ + +utils.tableize = function tableize(obj, opts) { + var ret = {}; + opts = opts || {}; + type(ret, obj, '', opts); + return ret; +}; + +/** + * Type `obj` recursively. + * + * @param {Object} schema + * @param {Object} obj + * @param {String} prefix + * @api private + */ + +function type(schema, obj, prefix, opts) { + Object.keys(obj).forEach(function(key){ + var val = obj[key]; + + key = prefix + key; + if (opts.lowercase) key = key.toLowerCase(); + + if (isObject(val)) { + type(schema, val, key + '.', opts); + } else { + schema[key] = val; + } + }); +} + +/** + * Check if `val` is an object. + * + * @param {Mixed} val + * @return {Boolean} + * @api private + */ + +function isObject(val) { + return '[object Object]' == Object.prototype.toString.call(val); +} + /** * Expose `utils` modules */ From e9a56c08d66eec95287e2c1679e775f91424c62e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:46:50 -0500 Subject: [PATCH 111/282] cli improvements --- cli.js | 51 +++++++++--- index.js | 30 +++++-- lib/runner/cli.js | 89 +++++++++----------- lib/{schema.js => schema/index.js} | 128 +++++++++++++---------------- verbfile.js | 44 +++++----- 5 files changed, 186 insertions(+), 156 deletions(-) rename lib/{schema.js => schema/index.js} (65%) diff --git a/cli.js b/cli.js index 1e2b43ca..b5e00beb 100755 --- a/cli.js +++ b/cli.js @@ -2,13 +2,14 @@ var fs = require('fs'); var path = require('path'); +var async = require('async'); var gm = require('global-modules'); -var processArgv = require('base-argv').processArgv(); var minimist = require('minimist'); var defaults = require('./lib/runner/defaults'); var preload = require('./lib/runner/preload'); var data = require('./lib/runner/data'); var utils = require('./lib/utils'); +var pkg = require('./lib/pkg'); var colors = utils.colors; var Verb = require('./'); var create = Verb.create; @@ -48,13 +49,26 @@ Verb.mixin(utils.runner('verb', 'app', preload)); var verb = Verb.getConfig('verbfile.js', __dirname); +/** + * Support `--emit` for debugging + * + * Example: + * $ --emit data + */ + +if (args.emit && typeof args.emit === 'string') { + verb.on(args.emit, console.log.bind(console)); +} + // get `verb` property from package.json, if it exists var pkg = verb.get('env.user.pkg'); -var userConfig = pkg.verb; +var verbConfig = pkg.verb; -if (userConfig) { - var config = utils.expandConfig(userConfig, pkg); +if (verbConfig) { + var config = utils.expandConfig(verbConfig, pkg); + verb._pkg = config; verb.config.process(config); + verb.set('cache.config', config); verb.emit('config-loaded'); } @@ -62,21 +76,17 @@ if (userConfig) { * Resolve user config files, eg. `verbfile.js`. */ -verb.resolve('default', {pattern: 'verbfile.js', cwd: __dirname}); +// verb.resolve('default', {pattern: 'verbfile.js', cwd: __dirname}); verb.resolve('global', {pattern: 'verb-*/verbfile.js', cwd: gm}); /** - * Run apps and tasks + * Run verb "apps" and tasks */ verb.cli.map('apps', function(tasks) { // ensure this is run after other configuration is complete setImmediate(function() { - if (verb.enabled('generate.init')) { - - } - if (verb.enabled('tasks.display')) { console.log(colors.gray(' Verb apps and registered tasks:')); verb.displayTasks(); @@ -87,7 +97,7 @@ verb.cli.map('apps', function(tasks) { if (verb.enabled('tasks.choose')) { verb.chooseTasks(function(err, results) { if (err) throw err; - run([results.apps]); + run(utils.arrayify(results.apps)); }); return; } @@ -104,15 +114,29 @@ verb.cli.map('apps', function(tasks) { run(tasks); function run(tasks) { - data.updateData(verb, verb.base, verb.env); + verb.data(data.updateData(verb)); + + if (args.init) { + verb.questions.enqueue('author', {force: true}); + console.log('fix me! I should output all author questions!'); + } + // ask queued questions verb.ask(function(err, answers) { if (err) return console.error(err); + var fp = utils.homeRelative(verb.get('env.config.path')); + utils.timestamp('using verbfile ' + fp); + + // emit the answers + verb.emit('answers', answers); + + // update context for templates verb.data(answers); + // run apps and/or tasks verb.runApps(tasks, function(err) { if (err) return console.error(err); - utils.timestamp('done'); + utils.timestamp('finished ' + utils.success()); process.exit(0); }); }); @@ -125,3 +149,4 @@ verb.cli.map('apps', function(tasks) { */ verb.cli.processArgv(args); + diff --git a/index.js b/index.js index 6e036ede..be6a57f4 100644 --- a/index.js +++ b/index.js @@ -3,8 +3,8 @@ var path = require('path'); var async = require('async'); var Base = require('assemble-core'); -var expand = require('./lib/expand'); var utils = require('./lib/utils'); +var pkg = require('./lib/pkg'); var env = require('./lib/env'); /** @@ -52,13 +52,9 @@ function create(preload) { this.use(utils.middleware(opts)) .use(utils.pipeline(opts)) .use(utils.store()) - .use(expand()) + .use(pkg()) .use(env()); - this.engine(['md', 'text'], require('engine-base'), { - delims: ['{%', '%}'] - }); - this.initVerb(this); } @@ -73,6 +69,12 @@ function create(preload) { */ Verb.prototype.initVerb = function(app) { + this.store.create('config'); + + this.engine(['md', 'text'], require('engine-base'), { + delims: ['{%', '%}'] + }); + if (typeof preload === 'function') { preload.call(app, app, this.base, this.env); } @@ -178,6 +180,22 @@ function create(preload) { }.bind(this), cb); }; + /** + * Get the `base` instance + */ + + Object.defineProperty(Verb.prototype, '_pkg', { + configurable: true, + get: function(val) { + this.cache.pkg = val; + }, + get: function() { + var user = this.env.user || {}; + var pkg = user.pkg || {}; + return (this.cache.pkg = pkg); + } + }); + /** * Get the `base` instance */ diff --git a/lib/runner/cli.js b/lib/runner/cli.js index 569c0d12..aee7faa9 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -1,68 +1,63 @@ 'use strict'; -var path = require('path'); +var commands = require('./commands'); var utils = require('../utils'); -module.exports = function(verb, base, env) { - if (!verb.cli) { - throw new Error('the base-cli plugin should be registered on every instance'); - } +/** + * Verb CLI + * + * Custom extensions to the built-in mappings + * provided by the `base-cli` plugin. + */ - // verb.store.on('set', function(key, val) { - // console.log('set >', key, val); - // }); +module.exports = function(verb) { + + /** + * Help and information-related + */ verb.cli .map('init', function(fp) { - // console.log('cli > init (implement me!)'); - verb.option('questions.force', true); - }) - .map('help', function(fp) { - console.log('cli > help (implement me!)'); - // do help stuff - // verb.enable('help'); - process.exit(0); - }) - .map('ask', function(key) { - if (key === true) { - verb.enable('questions.init'); - } else { - verb.questions.enqueue(key); - verb.option('questions.init', key); - } - }) - .map('open', function(name) { - if (name === 'answers') { - var dest = verb.get('questions.dest'); - if (dest) { - console.log('opening answers data directory >', dest); - utils.opn(dest); - process.exit(0); - } - } else if (name === 'store') { - var dir = path.dirname(verb.get('store.path')); - if (dir) { - console.log('opening store data directory >', dir); - utils.opn(dir); - process.exit(0); - } - } - }) - .map('config', function(val) { - verb.config.process({update: val}); + console.log('cli > init (implement me!)'); + verb.set('questions.options.forceAll', true); }) + .map('help', commands.help(verb)) + .map('show', commands.show(verb)) + .map('open', commands.open(verb)) .map('diff', function(val) { verb.option('diff', val); }) + + /** + * Options, settings and context related + */ + + verb.cli + .map('ask', commands.ask(verb)) .map('cwd', function(val) { verb.option('cwd', val); }) + .map('save', function(val) { + verb.store.config.set(val); + val = utils.tableize(val); + console.log('saved > "%j" %s', val, 'in global config store.'); + }) .map('data', function(val) { verb.data(val); }) + .map('option', function(val) { + verb.option(val); + }) + .map('config', function(val) { + verb.config.process({ + update: val + }); + }); + /** + * Task-related + */ - // task-related verb.cli .map('choose', function(key) { if (key === true) { @@ -73,8 +68,6 @@ module.exports = function(verb, base, env) { if (key === true) { verb.enable('tasks.display'); } - }) + }); - verb.define('commands', verb.cli.keys); }; - diff --git a/lib/schema.js b/lib/schema/index.js similarity index 65% rename from lib/schema.js rename to lib/schema/index.js index 866d2737..59b36f40 100644 --- a/lib/schema.js +++ b/lib/schema/index.js @@ -3,31 +3,6 @@ var tableize = require('tableize'); var utils = require('./utils'); -function Field(type, options) { - if (utils.isObject(type)) { - options = type; - type = options.type || options.types; - } - options = options || {}; - this.types = typeof type === 'string' - ? type.split(/\W/) - : type; - - for (var key in options) { - this[key] = options[key]; - } - if (!options.hasOwnProperty('optional')) { - this.optional = true; - } - if (!options.hasOwnProperty('required')) { - this.required = false; - } -} - -Field.prototype.validate = function(val) { - return this.types.indexOf(utils.typeOf(val)) > -1; -}; - function Schema(options) { this.options = options || {}; this.errors = []; @@ -90,38 +65,35 @@ Schema.prototype.validate = function(context) { return this.errors; }; -var schema = new Schema(); - -schema.field('data', ['object']); -schema.field('options', ['object']); -schema.field('plugins', ['array', 'object']); -schema.field('reflinks', ['array', 'object']); -schema.field('reflinks.list', ['array']); -schema.field('related', ['array', 'object']); -schema.field('reflinks.list', ['array'], { - validate: function(val) { - return Array.isArray(val); +function Field(type, options) { + if (utils.isObject(type)) { + options = type; + type = options.type || options.types; } -}); - -// console.log(schema.isValid('reflinks.list', '')) -// console.log(schema.isValid('whatever')) + options = options || {}; + this.types = typeof type === 'string' + ? type.split(/\W/) + : type; -var pkg = { - verb: { - reflinks: 'foo', - related: { - list: ['foo', 'bar', 'baz'] - }, + for (var key in options) { + this[key] = options[key]; } -}; + if (!options.hasOwnProperty('optional')) { + this.optional = true; + } + if (!options.hasOwnProperty('required')) { + this.required = false; + } +} -var results = schema.validate(pkg.verb); -// console.log(results) +Field.prototype.validate = function(val) { + return this.types.indexOf(utils.typeOf(val)) > -1; +}; function article(types) { if (typeof types === 'string' || types.length === 1) { - return (/^[aeiou]/.test(types) ? 'an ' : 'a ') + types; + var prefix = /^[aeiou]/.test(String(types)) ? 'an ' : 'a '; + return prefix + types; } return types.map(function(type) { return article(type); @@ -134,24 +106,42 @@ function article(types) { module.exports = Schema; -Schema.prototype.validate = function(prop, val) { - var target = utils.get(schema, prop); - if (typeof target === 'undefined') { - return val; - } - if (typeof target.fn === 'function') { - val = target.fn(val, target); - } - if (typeof target.validate === 'function') { - target.validate(val); - } - if (!target.type) return val; - var types = target.type.split(/\W/); - if (types.indexOf(utils.typeOf(val)) > -1) { - val = JSON.stringify(val); - throw new TypeError(prop + ' expects "' + val + '" to be ' + article(types)); - } - return val; -}; +/** + * Adds get/set methods to verb env + */ + +// utils.define(verb, 'update', function(pkg, args) { +// if (!pkg) return; + +// var config = utils.mapper(schema) +// .map('set', function(val) { +// update(pkg, 'set', val); +// }) +// .map('del', function(val) { +// update(pkg, 'del', val); +// }) +// .map('data', function(val) { +// update(pkg, 'data', val); +// }); + +// config.process(args); +// }); + +// module.exports = function(options) { +// return function(verb) { +// utils.define(verb, 'update', function(pkg, args) { +// update() +// // if (!pkg) return; + +// // var schema = new Schema(pkg, options); +// // // console.log(schema) +// // var config = utils.mapper(schema) +// // .map('set') +// // .map('del'); + +// // config.process(args); +// }); +// }; +// }; diff --git a/verbfile.js b/verbfile.js index 1968e3e5..1a86c9b6 100644 --- a/verbfile.js +++ b/verbfile.js @@ -4,30 +4,16 @@ var path = require('path'); var utils = require('./lib/utils'); var data = require('./lib/runner/data'); var argv = require('minimist')(process.argv.slice(2), { - alias: { - v: 'verbose' - } + alias: {v: 'verbose'} }); /** - * !HEADS UP: this is not what a normal verfile.js would look like. + * HEADS UP: this is not what a normal verfile.js would look like. * We're just trying to break everyting right now, so this has a * lot of stuff in it. */ module.exports = function(verb, base, env) { - verb.task('a', function(cb) { - console.log('verb base > a'); - cb(); - }); - verb.task('b', function(cb) { - console.log('verb base > b'); - cb(); - }); - verb.task('c', function(cb) { - console.log('verb base > c'); - cb(); - }); /** * Readme task @@ -39,22 +25,28 @@ module.exports = function(verb, base, env) { // ask pre-configured questions, but only if // they don't have answers yet verb.toStream('docs', function(key, view) { - return key === '.verb'; + if (view.dest === 'readme.md') { + // custom `sections` from package.json `verb` config + var sections = verb.get('cache.sections'); + if (sections) view.content = sections; + return true; + } }) .pipe(handle(verb, 'onStream')) .on('error', cb) .pipe(verb.renderFile('text')) .on('error', handleError(verb)) .pipe(verb.pipeline(plugins)) - .on('error', cb) .pipe(handle(verb, 'preWrite')) .pipe(verb.dest(rename())) - .on('error', cb) .pipe(utils.exhaust(handle(verb, 'postWrite'))) - .on('error', cb) .on('finish', cb); }); + /** + * Re-write package.json with any user-defined config updates + */ + verb.task('package', function(cb) { verb.toStream('docs', function(key, view) { return view.basename === 'package.json'; @@ -66,9 +58,21 @@ module.exports = function(verb, base, env) { .on('finish', cb); }); + /** + * Default tasks + */ + verb.task('default', ['readme', 'package']); }; +/** + * Handle a middleware stage in the pipeline + * + * @param {Object} `app` + * @param {Object} `stage` + * @return {Object} + */ + function handle(app, stage) { return utils.through.obj(function(file, enc, next) { if (file.isNull()) return next(); From faeccc94452dc9ec160bf516318e9a473d90005a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:47:44 -0500 Subject: [PATCH 112/282] adds `pkg` plugin, for getting/setting values on package.json --- lib/pkg.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/pkg.js diff --git a/lib/pkg.js b/lib/pkg.js new file mode 100644 index 00000000..b893a80e --- /dev/null +++ b/lib/pkg.js @@ -0,0 +1,28 @@ +'use strict'; + +var utils = require('./utils'); + +/** + * Adds get/set methods to verb env + */ + +module.exports = function(config) { + return function(verb) { + verb.mixin('pkg', new Pkg(verb, '_pkg.verb')); + }; +}; + +function Pkg(config, prop) { + this.config = config || {}; + this.prop = prop; +} + +Pkg.prototype.set = function(key, value) { + utils.set(this.config, [this.prop, key], value); + return this; +}; + +Pkg.prototype.get = function(key) { + return utils.get(this.config, [this.prop, key]); +}; + From d5e78e8e7f3d94144724a607039152fba2a0680c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 22 Dec 2015 18:48:19 -0500 Subject: [PATCH 113/282] adds support for `sections` --- lib/runner/sections.js | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 lib/runner/sections.js diff --git a/lib/runner/sections.js b/lib/runner/sections.js new file mode 100644 index 00000000..9f2f84c0 --- /dev/null +++ b/lib/runner/sections.js @@ -0,0 +1,54 @@ +'use strict'; + +var utils = require('../utils'); + +module.exports = function(val, verb) { + return createSections(val); +}; + +function createSections(val, builtins) { + if (Array.isArray(val)) { + return sectionsArray(val, builtins); + } + if (utils.isObject(val)) { + return sectionsObject(val, builtins); + } +} + +function createSection(val, builtins) { + // do stuff to normalize section + return formatSection(val); +} + +function formatSection(section) { + var content = '\n'; + + if (section.prefix) { + content += section.prefix; + } else if (section.level) { + content += new Array(section.level).join('#'); + } + + if (content !== '\n') content += ' '; + content += section.heading || ''; + content += '\n\n'; + content += section.content || ''; + return content; +} + +function sectionsArray(arr, builtins) { + var len = arr.length, i = -1; + var sections = ''; + while (++i < len) { + sections += createSection(arr[i], builtins); + } + return sections.trim(); +} + +function sectionsObject(obj, builtins) { + var sections = ''; + for (var key in val) { + sections += createSection(val[key], builtins); + } + return sections.trim(); +} From bc3cfbaeb7b920eff0419656ae3965707c9dcc47 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 12:01:10 -0500 Subject: [PATCH 114/282] lint --- .eslintrc | 125 ---------------------- .eslintrc.json | 278 +++++++++++++++++++++++++++++++++++++++++++++++++ cli.js | 3 +- 3 files changed, 279 insertions(+), 127 deletions(-) delete mode 100644 .eslintrc create mode 100644 .eslintrc.json diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 7b5d047f..00000000 --- a/.eslintrc +++ /dev/null @@ -1,125 +0,0 @@ -{ - "ecmaFeatures": { - "modules": true, - "experimentalObjectRestSpread": true - }, - - "env": { - "browser": false, - "es6": true, - "node": true, - "mocha": true - }, - - "globals": { - "document": false, - "navigator": false, - "window": false - }, - - "rules": { - "accessor-pairs": 2, - "arrow-spacing": [2, { "before": true, "after": true }], - "block-spacing": [2, "always"], - "brace-style": [2, "1tbs", { "allowSingleLine": true }], - "comma-dangle": [2, "never"], - "comma-spacing": [2, { "before": false, "after": true }], - "comma-style": [2, "last"], - "constructor-super": 2, - "curly": [2, "multi-line"], - "dot-location": [2, "property"], - "eol-last": 2, - "eqeqeq": [2, "allow-null"], - "generator-star-spacing": [2, { "before": true, "after": true }], - "handle-callback-err": [2, "^(err|error)$" ], - "indent": [2, 2, { "SwitchCase": 1 }], - "key-spacing": [2, { "beforeColon": false, "afterColon": true }], - "new-cap": [2, { "newIsCap": true, "capIsNew": false }], - "new-parens": 2, - "no-array-constructor": 2, - "no-caller": 2, - "no-class-assign": 2, - "no-cond-assign": 2, - "no-const-assign": 2, - "no-control-regex": 2, - "no-debugger": 2, - "no-delete-var": 2, - "no-dupe-args": 2, - "no-dupe-class-members": 2, - "no-dupe-keys": 2, - "no-duplicate-case": 2, - "no-empty-character-class": 2, - "no-empty-label": 2, - "no-eval": 2, - "no-ex-assign": 2, - "no-extend-native": 2, - "no-extra-bind": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": [2, "functions"], - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-func-assign": 2, - "no-implied-eval": 2, - "no-inner-declarations": [2, "functions"], - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-iterator": 2, - "no-label-var": 2, - "no-labels": 2, - "no-lone-blocks": 2, - "no-mixed-spaces-and-tabs": 2, - "no-multi-spaces": 2, - "no-multi-str": 2, - "no-multiple-empty-lines": [2, { "max": 1 }], - "no-native-reassign": 2, - "no-negated-in-lhs": 2, - "no-new": 2, - "no-new-func": 2, - "no-new-object": 2, - "no-new-require": 2, - "no-new-wrappers": 2, - "no-obj-calls": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-proto": 0, - "no-redeclare": 2, - "no-regex-spaces": 2, - "no-return-assign": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-shadow-restricted-names": 2, - "no-spaced-func": 2, - "no-sparse-arrays": 2, - "no-this-before-super": 2, - "no-throw-literal": 2, - "no-trailing-spaces": 0, - "no-undef": 2, - "no-undef-init": 2, - "no-unexpected-multiline": 2, - "no-unneeded-ternary": [2, { "defaultAssignment": false }], - "no-unreachable": 2, - "no-unused-vars": [2, { "vars": "all", "args": "none" }], - "no-useless-call": 0, - "no-with": 2, - "one-var": [0, { "initialized": "never" }], - "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], - "padded-blocks": [0, "never"], - "quotes": [2, "single", "avoid-escape"], - "radix": 2, - "semi": [2, "always"], - "semi-spacing": [2, { "before": false, "after": true }], - "space-after-keywords": [2, "always"], - "space-before-blocks": [2, "always"], - "space-before-function-paren": [2, "never"], - "space-before-keywords": [2, "always"], - "space-in-parens": [2, "never"], - "space-infix-ops": 2, - "space-return-throw-case": 2, - "space-unary-ops": [2, { "words": true, "nonwords": false }], - "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], - "use-isnan": 2, - "valid-typeof": 2, - "wrap-iife": [2, "any"], - "yoda": [2, "never"] - } -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..cc6a8679 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,278 @@ +{ + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true + }, + "env": { + "browser": false, + "es6": true, + "node": true, + "mocha": true + }, + "globals": { + "document": false, + "navigator": false, + "window": false + }, + "rules": { + "accessor-pairs": 2, + "arrow-spacing": [ + 2, + { + "before": true, + "after": true + } + ], + "block-spacing": [ + 2, + "always" + ], + "brace-style": [ + 2, + "1tbs", + { + "allowSingleLine": true + } + ], + "comma-dangle": [ + 2, + "never" + ], + "comma-spacing": [ + 2, + { + "before": false, + "after": true + } + ], + "comma-style": [ + 2, + "last" + ], + "constructor-super": 2, + "curly": [ + 2, + "multi-line" + ], + "dot-location": [ + 2, + "property" + ], + "eol-last": 2, + "eqeqeq": [ + 2, + "allow-null" + ], + "generator-star-spacing": [ + 2, + { + "before": true, + "after": true + } + ], + "handle-callback-err": [ + 2, + "^(err|error)$" + ], + "indent": [ + 2, + 2, + { + "SwitchCase": 1 + } + ], + "key-spacing": [ + 2, + { + "beforeColon": false, + "afterColon": true + } + ], + "new-cap": [ + 2, + { + "newIsCap": true, + "capIsNew": false + } + ], + "new-parens": 2, + "no-array-constructor": 2, + "no-caller": 2, + "no-class-assign": 2, + "no-cond-assign": 2, + "no-const-assign": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-dupe-args": 2, + "no-dupe-class-members": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty-character-class": 2, + "no-empty-label": 2, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": [ + 2, + "functions" + ], + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implied-eval": 2, + "no-inner-declarations": [ + 2, + "functions" + ], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [ + 2, + { + "max": 1 + } + ], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-proto": 0, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-return-assign": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 0, + "no-undef": 2, + "no-undef-init": 2, + "no-unexpected-multiline": 2, + "no-unneeded-ternary": [ + 2, + { + "defaultAssignment": false + } + ], + "no-unreachable": 2, + "no-unused-vars": [ + 2, + { + "vars": "all", + "args": "none" + } + ], + "no-useless-call": 0, + "no-with": 2, + "one-var": [ + 0, + { + "initialized": "never" + } + ], + "operator-linebreak": [ + 0, + "after", + { + "overrides": { + "?": "before", + ":": "before" + } + } + ], + "padded-blocks": [ + 0, + "never" + ], + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "radix": 2, + "semi": [ + 2, + "always" + ], + "semi-spacing": [ + 2, + { + "before": false, + "after": true + } + ], + "space-after-keywords": [ + 2, + "always" + ], + "space-before-blocks": [ + 2, + "always" + ], + "space-before-function-paren": [ + 2, + "never" + ], + "space-before-keywords": [ + 2, + "always" + ], + "space-in-parens": [ + 2, + "never" + ], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [ + 2, + { + "words": true, + "nonwords": false + } + ], + "spaced-comment": [ + 0, + "always", + { + "markers": [ + "global", + "globals", + "eslint", + "eslint-disable", + "*package", + "!", + "," + ] + } + ], + "use-isnan": 2, + "valid-typeof": 2, + "wrap-iife": [ + 2, + "any" + ], + "yoda": [ + 2, + "never" + ] + } +} diff --git a/cli.js b/cli.js index b5e00beb..ebe80058 100755 --- a/cli.js +++ b/cli.js @@ -76,7 +76,6 @@ if (verbConfig) { * Resolve user config files, eg. `verbfile.js`. */ -// verb.resolve('default', {pattern: 'verbfile.js', cwd: __dirname}); verb.resolve('global', {pattern: 'verb-*/verbfile.js', cwd: gm}); /** @@ -148,5 +147,5 @@ verb.cli.map('apps', function(tasks) { * Process args */ -verb.cli.processArgv(args); +verb.cli.process(args); From 73f6ea9ec930d2b82b53700951b7804d30d185e7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 12:01:35 -0500 Subject: [PATCH 115/282] start working on sections, modularize code to start moving it out --- lib/runner/middleware/examples.js | 29 ++++ lib/runner/middleware/index.js | 1 + lib/schema/index.js | 147 ------------------ .../sections.js => sections/create.js} | 6 +- lib/sections/helper.js | 13 ++ lib/sections/index.js | 1 + lib/sections/load.js | 28 ++++ .../sections.js => sections/templates.js} | 9 ++ 8 files changed, 84 insertions(+), 150 deletions(-) create mode 100644 lib/runner/middleware/examples.js create mode 100644 lib/runner/middleware/index.js delete mode 100644 lib/schema/index.js rename lib/{runner/sections.js => sections/create.js} (90%) create mode 100644 lib/sections/helper.js create mode 100644 lib/sections/index.js create mode 100644 lib/sections/load.js rename lib/{templates/sections.js => sections/templates.js} (69%) diff --git a/lib/runner/middleware/examples.js b/lib/runner/middleware/examples.js new file mode 100644 index 00000000..fbe1c9fa --- /dev/null +++ b/lib/runner/middleware/examples.js @@ -0,0 +1,29 @@ +'use strict'; + +var extract = require('gfm-code-blocks'); + +/** + * Detect the layout to use + */ + +module.exports = function(app) { + + return function(view, next) { + var examples = {}; + + var str = view.content; + extract(str).forEach(function(block) { + block.orig = block.block; + var m = /^\/\/\s*example(\.[^\n]+)([\s\S]+)/.exec(block.code); + if (!m) return next(); + var name = m[1]; + examples[name] = examples[name] || []; + view.content = view.content.split(block.block).join(''); + block.block = '```js\n' + m[2] + '\n```\n'; + examples[name].push(block); + }); + + app.set('cache.data.examples', examples); + next(); + }; +}; diff --git a/lib/runner/middleware/index.js b/lib/runner/middleware/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/runner/middleware/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/schema/index.js b/lib/schema/index.js deleted file mode 100644 index 59b36f40..00000000 --- a/lib/schema/index.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict'; - -var tableize = require('tableize'); -var utils = require('./utils'); - -function Schema(options) { - this.options = options || {}; - this.errors = []; - this.fields = {}; - - if (this.options.fields) { - for (var key in this.options.fields) { - this.fields[key] = this.options.fields[key]; - } - } -} - -Schema.prototype.field = function(name, type, options) { - return (this.fields[name] = new Field(type, options)); -}; - -Schema.prototype.get = function(name, prop) { - return utils.get(this.fields, name, prop); -}; - -Schema.prototype.isValid = function(name, val) { - var field = this.get(name); - if (this.options.knownOnly === true && typeof field === 'undefined') { - return false; - } - if (typeof field === 'undefined') { - return true; - } - return field.validate(val); -}; - -Schema.prototype.isOptional = function(name) { - return this.get(name, 'optional'); -}; - -Schema.prototype.isRequired = function(name) { - return this.get(name, 'required'); -}; - -Schema.prototype.error = function(method, msg) { - this.errors.push({method: method, msg: msg}); - return this; -}; - -Schema.prototype.validate = function(context) { - if (!utils.isObject(context)) { - this.error('schema.validate', 'invalid context object'); - } else { - var ctx = tableize(context); - for (var key in ctx) { - var field = ctx[key]; - var isValid = this.isValid(key, field); - if (!isValid) { - var types = this.get(key, 'types'); - var val = JSON.stringify(field); - this.error(key, 'expected "' + field + '" to be ' + article(types)); - } - } - } - return this.errors; -}; - -function Field(type, options) { - if (utils.isObject(type)) { - options = type; - type = options.type || options.types; - } - options = options || {}; - this.types = typeof type === 'string' - ? type.split(/\W/) - : type; - - for (var key in options) { - this[key] = options[key]; - } - if (!options.hasOwnProperty('optional')) { - this.optional = true; - } - if (!options.hasOwnProperty('required')) { - this.required = false; - } -} - -Field.prototype.validate = function(val) { - return this.types.indexOf(utils.typeOf(val)) > -1; -}; - -function article(types) { - if (typeof types === 'string' || types.length === 1) { - var prefix = /^[aeiou]/.test(String(types)) ? 'an ' : 'a '; - return prefix + types; - } - return types.map(function(type) { - return article(type); - }).join(' or '); -} - -/** - * Expose `Schema` - */ - -module.exports = Schema; - - -/** - * Adds get/set methods to verb env - */ - -// utils.define(verb, 'update', function(pkg, args) { -// if (!pkg) return; - -// var config = utils.mapper(schema) -// .map('set', function(val) { -// update(pkg, 'set', val); -// }) -// .map('del', function(val) { -// update(pkg, 'del', val); -// }) -// .map('data', function(val) { -// update(pkg, 'data', val); -// }); - -// config.process(args); -// }); - -// module.exports = function(options) { -// return function(verb) { -// utils.define(verb, 'update', function(pkg, args) { -// update() -// // if (!pkg) return; - -// // var schema = new Schema(pkg, options); -// // // console.log(schema) -// // var config = utils.mapper(schema) -// // .map('set') -// // .map('del'); - -// // config.process(args); -// }); -// }; -// }; - diff --git a/lib/runner/sections.js b/lib/sections/create.js similarity index 90% rename from lib/runner/sections.js rename to lib/sections/create.js index 9f2f84c0..056e195e 100644 --- a/lib/runner/sections.js +++ b/lib/sections/create.js @@ -1,6 +1,6 @@ 'use strict'; -var utils = require('../utils'); +var utils = require('../../utils'); module.exports = function(val, verb) { return createSections(val); @@ -47,8 +47,8 @@ function sectionsArray(arr, builtins) { function sectionsObject(obj, builtins) { var sections = ''; - for (var key in val) { - sections += createSection(val[key], builtins); + for (var key in obj) { + sections += createSection(obj[key], builtins); } return sections.trim(); } diff --git a/lib/sections/helper.js b/lib/sections/helper.js new file mode 100644 index 00000000..affd1e5d --- /dev/null +++ b/lib/sections/helper.js @@ -0,0 +1,13 @@ +'use strict'; + +var utils = require('../../utils'); + +module.exports = function(config) { + config = config || {}; + + return function(name, context) { + var section = this.app.sections.getView(name); + + return ''; + }; +}; diff --git a/lib/sections/index.js b/lib/sections/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/sections/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/sections/load.js b/lib/sections/load.js new file mode 100644 index 00000000..5fab3f67 --- /dev/null +++ b/lib/sections/load.js @@ -0,0 +1,28 @@ +'use strict'; + +var sections = require('./templates'); + +module.exports = function(app, base, env) { + for (var key in sections) { + if (sections.hasOwnProperty(key)) { + var section = sections[key]; + + // call validation function, if defined on the template + if (base && env && typeof section.validate === 'function') { + if (!section.validate(app, base, env)) { + continue; + } + } + + // load the template + app.section(key, {content: buildContent(section)}); + } + } +} + +function buildContent(section) { + var str = /^\w/.test(section.heading) ? '## ' : ''; + str += section.heading + '\n\n'; + str += section.content + '\n'; + return str; +} diff --git a/lib/templates/sections.js b/lib/sections/templates.js similarity index 69% rename from lib/templates/sections.js rename to lib/sections/templates.js index 8e49a543..8700a127 100644 --- a/lib/templates/sections.js +++ b/lib/sections/templates.js @@ -1,5 +1,7 @@ 'use strict'; +var fs = require('fs'); + module.exports = { apidocs: { heading: 'API', @@ -13,6 +15,13 @@ module.exports = { heading: 'Running tests', content: '{%= include("tests") %}', }, + coverage: { + heading: 'Coverage summary', + content: 'As of version {%= version %}\n\n```\n{%= coverage(\'coverage/summary.txt\') %}\n```\n', + validate: function(verb, base, env) { + return fs.existsSync(env.user.cwd + '/coverage'); + } + }, contributing: { heading: 'Contributing', content: '{%= include("contributing") %}', From 40c3693a62691f68b055522343330a8a480ee3dc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 12:02:20 -0500 Subject: [PATCH 116/282] get `pkg` working --- lib/pkg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pkg.js b/lib/pkg.js index b893a80e..fbbb8c89 100644 --- a/lib/pkg.js +++ b/lib/pkg.js @@ -8,7 +8,7 @@ var utils = require('./utils'); module.exports = function(config) { return function(verb) { - verb.mixin('pkg', new Pkg(verb, '_pkg.verb')); + verb.mixin('pkg', new Pkg(verb, '_pkg')); }; }; From 26404b67314a17bb69e468e35057753f946498b9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 12:03:10 -0500 Subject: [PATCH 117/282] another attempt to get TOC middleware working correctly also adds examples middleware, to add examples to the comments context --- lib/runner/middleware.js | 56 ++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 01f2150a..859a1a08 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -1,5 +1,6 @@ 'use strict'; +var middleware = require('./middleware/'); var utils = require('../utils'); var falsey = require('falsey'); var diff = require('base-diff'); @@ -23,14 +24,15 @@ module.exports = function(app, base, env) { next(); }); + + app.onLoad(/\.md/, middleware.examples(app)); + app.onLoad(/\.md/, function(view, next) { - view.options.foo = 'bar' - if (hasToc(view.content)) { + if (hasToc(view)) { // don't render or insert a TOC view.options.toc = view.options.toc || {}; view.options.toc.render = true; view.options.toc.insert = true; - // don't break toc helpers or variables verb.data.toc = ''; } next(); @@ -62,9 +64,15 @@ module.exports = function(app, base, env) { next(); }); + /** + * Determine layout + */ app.preLayout(/\.md/, function(view, next) { - if (view.isType('partial')) return next(); + if (view.isType('partial')) { + next(); + return; + } var layout = app.pkg.get('verb.layout') if (utils.isObject(layout)) { @@ -74,11 +82,8 @@ module.exports = function(app, base, env) { if (typeof layout !== 'undefined') { view.layout = layout; - return next(); - } - - if (typeof view.layout === 'string' || isFalsey(view)) { - return next(); + next(); + return; } next(); }); @@ -89,6 +94,14 @@ module.exports = function(app, base, env) { view.dest = 'package.json'; next(); }); + + app.preWrite(/\.md$/, function(view, next) { + if (!view.actions.has('toc') && view.data.toc) { + view.content = insertToc(view.content, view.data.toc); + view.actions.set('toc'); + } + next(); + }); }; function renderToc(app, stage, append) { @@ -108,8 +121,22 @@ function renderToc(app, stage, append) { }; } -function hasToc(str) { - return str.indexOf('') !== -1; +function hasToc(view) { + if (view.get('options.toc.inserted') === true) { + return true; + } + if (view.content.indexOf('') !== -1) { + return true; + } +} + +function tocInjected(view) { + if (view.get('options.toc.inserted') === true) { + return true; + } + if (view.content.indexOf('') !== -1) { + return true; + } } function stripToc(str) { @@ -118,6 +145,13 @@ function stripToc(str) { return str; } +function insertToc(str, toc) { + if (!toc) return; + str = str.split('').join(toc); + str = str.split('').join(toc); + return str; +} + function isDisabled(opts, view, prop) { return isFalsey(utils.get(opts, prop)) || isFalsey(utils.get(view.options, prop)) From 67e9ea632d4571cd440eb43661ea556bc19f25b8 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 12:03:47 -0500 Subject: [PATCH 118/282] adds `actions` object --- docs/src/content/actions.md | 6 ++++++ index.js | 2 ++ lib/actions.js | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 docs/src/content/actions.md create mode 100644 lib/actions.js diff --git a/docs/src/content/actions.md b/docs/src/content/actions.md new file mode 100644 index 00000000..2995e257 --- /dev/null +++ b/docs/src/content/actions.md @@ -0,0 +1,6 @@ +# Actions + +## Table of Contents + +- `toc.isInserted` +- `toc.isRendered` diff --git a/index.js b/index.js index be6a57f4..b41cd21a 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ var path = require('path'); var async = require('async'); var Base = require('assemble-core'); +var actions = require('./lib/actions'); var utils = require('./lib/utils'); var pkg = require('./lib/pkg'); var env = require('./lib/env'); @@ -52,6 +53,7 @@ function create(preload) { this.use(utils.middleware(opts)) .use(utils.pipeline(opts)) .use(utils.store()) + .use(actions()) .use(pkg()) .use(env()); diff --git a/lib/actions.js b/lib/actions.js new file mode 100644 index 00000000..b7bd4665 --- /dev/null +++ b/lib/actions.js @@ -0,0 +1,37 @@ +'use strict'; + +var utils = require('./utils'); + +/** + * Adds an `action` object with `get` and `set` methods + * to `app`, `collection` and `view` + */ + +module.exports = function() { + return function plugin(app) { + if (!app.has('cache.actions')) { + app.set('cache.actions', {}); + } + + app.mixin('actions', new Actions(app.cache.actions)); + return plugin; + }; +}; + +function Actions() {} + +Actions.prototype.set = function(key, val) { + if (typeof key === 'string' && typeof val === 'undefined') { + val = true; + } + utils.set(this, key, val); + return this; +}; + +Actions.prototype.has = function(key) { + return utils.has(this, key); +}; + +Actions.prototype.get = function(key) { + return utils.get(this, key); +}; From d3ba8482be29ee0c276b9fbc2547d85e728c6d4e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 12:04:31 -0500 Subject: [PATCH 119/282] adds `resolve` and `exists` helpers --- lib/runner/helpers.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 0b7f4398..038e6e29 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -6,6 +6,22 @@ module.exports = function(verb, base, env) { var apidocs = require('helper-apidocs'); var issue = require('helper-issue'); + verb.helper('resolve', function (name) { + var base = path.resolve(process.cwd(), 'node_modules', name); + var pkg = require(path.join(base, 'package.json')); + var cwd = path.join(base, pkg.main); + + var res = {}; + res.pkg = pkg; + res.cwd = path.relative(process.cwd(), cwd); + res.dest = pkg.homepage; + return res; + }); + + verb.helper('exists', function(fp) { + return fs.existsSync(fp); + }); + verb.helper('log', function(msg) { console.log.apply(console, arguments); }); From a3f3f1a2fa9a7786f7837a2142af121944c874bd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 22:02:52 -0500 Subject: [PATCH 120/282] start adding section logic --- lib/config.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/config.js b/lib/config.js index 8636a995..94b09a26 100644 --- a/lib/config.js +++ b/lib/config.js @@ -2,7 +2,7 @@ var fs = require('fs'); var path = require('path'); -var sections = require('./runner/sections'); +var sections = require('./sections'); var updater = require('./updater'); var utils = require('./utils'); @@ -34,7 +34,6 @@ module.exports = function(verb, base, env) { verb.option(val); }) .map('update', function(val) { - verb.emit('config.update', val); env = env || verb.env; var file = verb.docs.getView('package.json'); @@ -44,11 +43,12 @@ module.exports = function(verb, base, env) { if (!pkg) return; pkg.verb = pkg.verb || {}; + try { file.json.verb = verb.update(pkg.verb, val); verb.set('cache.config', file.json.verb); } catch (err) { - throw new Error('config', 'cannot update pkg.verb', err); + throw new Error('config cannot update pkg.verb' + err); } }); @@ -80,10 +80,26 @@ module.exports = function(verb, base, env) { verb.config .map('sections', function(val) { - verb.set('cache.sections', sections(val, verb)); + verb.set('cache.sections', sections.create(val, verb)); }) .map('includes', views('includes')) .map('layouts', views('layouts')) + .map('layout', function(val) { + if (utils.isObject(val)) { + if (val.name) { + var readme = verb.docs.getView('readme.md'); + readme.layout = val.name; + } + if (val.sections) { + for (var key in val.sections) { + if (val.sections.hasOwnProperty(key)) { + var section = val.sections[key]; + + } + } + } + } + }) .map('badges', views('badges')) .map('docs', views('docs')); @@ -149,7 +165,8 @@ module.exports = function(verb, base, env) { throw new Error('cannot load helper: ' + helper); } - verb.helper(name, opts, helper); + name = name.slice(name.lastIndexOf('-') + 1); + verb.helper(name, helper); } }); From c90268a558d7ef394105f81c501d5e88dc995206 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 22:48:35 -0500 Subject: [PATCH 121/282] add base and env to args for `templates` util --- lib/runner/preload.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/runner/preload.js b/lib/runner/preload.js index b1d97b0e..d2ecb82b 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -1,6 +1,5 @@ 'use strict'; -var pkg = require('../pkg'); var utils = require('../utils'); var config = require('../config'); var templates = require('./templates'); @@ -13,7 +12,7 @@ module.exports = function preload(verb, base, env) { verb.use(utils.loader()); defaults(verb, base, env); - verb.use(templates()); + verb.use(templates(base, env)); context(verb, base, env); config(verb, base, env); @@ -24,7 +23,7 @@ module.exports = function preload(verb, base, env) { verb.on('register', function(name, app) { context(verb, base, env); defaults(app, base, env); - app.use(templates()); + app.use(templates(base, env)); }); verb.questions.on('ask', function(key, question, answers) { From e1805ff53a85a6cc67156a3872e626a1e6732bb3 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 26 Dec 2015 22:49:00 -0500 Subject: [PATCH 122/282] clean up questions junk --- lib/runner/preload.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/runner/preload.js b/lib/runner/preload.js index d2ecb82b..0272dacb 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -30,17 +30,5 @@ module.exports = function preload(verb, base, env) { if (!verb.pkg.get('description') && key === 'description') { question.options.skip = true; } - - // console.log(question.isAnswered()) - // if (verb.store.has(key) && question) { - // verb.questions.setData(key, verb.store.get(key)); - // } - }); - - verb.questions.on('answer', function(key, answer) { - // console.log(arguments) - // if (/author\./.test(key)) { - // // verb.store.set() - // } }); }; From 7c455ca345f62f4ce83cd1b38beee66feb1246eb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:25:42 -0500 Subject: [PATCH 123/282] update/move templates --- lib/sections/create.js | 2 +- lib/sections/helper.js | 19 ++++++-- lib/sections/load.js | 11 +++-- {lib/templates => templates}/badges.js | 0 templates/gh-issue.tmpl | 10 ++++ {lib/templates => templates}/includes.js | 32 +++++++++---- templates/includes/banner.tmpl | 6 +++ templates/layouts/basic.md | 31 +++++++++++++ templates/layouts/default.md | 40 ++++++++++++++++ templates/layouts/default2.hbs | 46 +++++++++++++++++++ templates/layouts/min.md | 21 +++++++++ templates/readme/header.md | 9 ++++ templates/readme/min.md | 14 ++++++ .../templates.js => templates/sections.js | 25 +++++++++- 14 files changed, 245 insertions(+), 21 deletions(-) rename {lib/templates => templates}/badges.js (100%) create mode 100644 templates/gh-issue.tmpl rename {lib/templates => templates}/includes.js (66%) create mode 100644 templates/includes/banner.tmpl create mode 100644 templates/layouts/basic.md create mode 100644 templates/layouts/default.md create mode 100644 templates/layouts/default2.hbs create mode 100644 templates/layouts/min.md create mode 100644 templates/readme/header.md create mode 100644 templates/readme/min.md rename lib/sections/templates.js => templates/sections.js (61%) diff --git a/lib/sections/create.js b/lib/sections/create.js index 056e195e..d4258f32 100644 --- a/lib/sections/create.js +++ b/lib/sections/create.js @@ -1,6 +1,6 @@ 'use strict'; -var utils = require('../../utils'); +var utils = require('../utils'); module.exports = function(val, verb) { return createSections(val); diff --git a/lib/sections/helper.js b/lib/sections/helper.js index affd1e5d..5acada30 100644 --- a/lib/sections/helper.js +++ b/lib/sections/helper.js @@ -1,12 +1,23 @@ 'use strict'; -var utils = require('../../utils'); +var utils = require('../utils'); +var load = require('./load'); -module.exports = function(config) { +module.exports = function(app, config) { config = config || {}; + var loaded = false; - return function(name, context) { - var section = this.app.sections.getView(name); + return function(name, options) { + var app = this.app; + + if (!loaded) { + loaded = true; + load(app); + } + + var context = utils.merge({}, config, this.context); + var section = app.verb_sections.getView(name); + // console.log(section) return ''; }; diff --git a/lib/sections/load.js b/lib/sections/load.js index 5fab3f67..a8c1cea1 100644 --- a/lib/sections/load.js +++ b/lib/sections/load.js @@ -1,21 +1,22 @@ 'use strict'; -var sections = require('./templates'); -module.exports = function(app, base, env) { +module.exports = function(app) { + var sections = require('../../templates/sections'); + for (var key in sections) { if (sections.hasOwnProperty(key)) { var section = sections[key]; // call validation function, if defined on the template - if (base && env && typeof section.validate === 'function') { - if (!section.validate(app, base, env)) { + if (typeof section.validate === 'function') { + if (!section.validate(app)) { continue; } } // load the template - app.section(key, {content: buildContent(section)}); + app.verb_section(key, {content: buildContent(section)}); } } } diff --git a/lib/templates/badges.js b/templates/badges.js similarity index 100% rename from lib/templates/badges.js rename to templates/badges.js diff --git a/templates/gh-issue.tmpl b/templates/gh-issue.tmpl new file mode 100644 index 00000000..9e3e7be4 --- /dev/null +++ b/templates/gh-issue.tmpl @@ -0,0 +1,10 @@ +Closing this issue since Assemble has been completely refactored from the ground up and released as v0.6.0. + +However, if this is related to grunt-assemble, and the issue or feature request is still valid, please [use this link](https://github.com/assemble/grunt-assemble/issues/new?body=Moved%20from%20assemble%2Fassemble%23issuenumber&title=) to create a new issue on the grunt-assemble repository. + +Also, please be sure to include: + +* The version of `assemble` you are using. +* Your `assemblefile.js` (This can be in a gist) +* The commandline output. (Screenshot or gist is fine) +* The expected result versus what's actually happening diff --git a/lib/templates/includes.js b/templates/includes.js similarity index 66% rename from lib/templates/includes.js rename to templates/includes.js index 7e279765..54c82ab3 100644 --- a/lib/templates/includes.js +++ b/templates/includes.js @@ -1,16 +1,30 @@ 'use strict'; -var install = [ - 'Install with [npm](https://www.npmjs.com/)', - '', - '```sh', - '$ npm i {%= name %}{%= save === true ? " --save" : "" %}', - '```' -].join('\n'); module.exports = { - 'install-npm.md': install, - 'install.md': install, + 'install-npm.md': [ + 'Install with [npm](https://www.npmjs.com/):', + '', + '```sh', + '$ npm i {%= name %}{%= ((typeof save !== "undefined" && save === true) ? " --save" : "") %}', + '```' + ].join('\n'), + + 'install.md': [ + 'Install with [npm](https://www.npmjs.com/):', + '', + '```sh', + '$ npm i {%= name %}', + '```' + ].join('\n'), + + 'install-dev.md': [ + 'Install as a `devDependency` with [npm](https://www.npmjs.com/):', + '', + '```sh', + '$ npm i {%= name %} -D', + '```' + ].join('\n'), 'install-bower.md': [ 'Install with [bower](http://bower.io/)', diff --git a/templates/includes/banner.tmpl b/templates/includes/banner.tmpl new file mode 100644 index 00000000..d173957e --- /dev/null +++ b/templates/includes/banner.tmpl @@ -0,0 +1,6 @@ +/*! + * {%= name %} {https://github.com/{%= author.username %}/{%= name %} + * + * Copyright (c) {%= year %}, {%= author.name %}. + * Licensed under the MIT License. + */ \ No newline at end of file diff --git a/templates/layouts/basic.md b/templates/layouts/basic.md new file mode 100644 index 00000000..cd5d5290 --- /dev/null +++ b/templates/layouts/basic.md @@ -0,0 +1,31 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Basic layout +--- +# {%= name %} {%= badge('npm') %} {%= badge('travis') %} + +> {%= description %} + +## Install +{%= include('install-npm', {save: true}) %} + +## Usage +{% body %} + +## Running tests +{%= include("tests") %} + +## Contributing +{%= include("contributing") %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/templates/layouts/default.md b/templates/layouts/default.md new file mode 100644 index 00000000..1705af3c --- /dev/null +++ b/templates/layouts/default.md @@ -0,0 +1,40 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Default layout +--- +# {%= name %} {%= badge('npm') %} {%= badge('travis') %} + +> {%= description %} + + + +{% if (verb.sections.install !== false) { %} +## Install +{%= include('install-npm', {save: true}) %} +{% } %} + +{% body %} + +{% if (verb.related && verb.related.list && verb.related.list.length) { %} +## Related projects +{%= verb.related.description || '' %} +{%= related(verb.related.list) %} +{% } %} + +## Running tests +{%= include("tests") %} + +## Contributing +{%= include("contributing") %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/templates/layouts/default2.hbs b/templates/layouts/default2.hbs new file mode 100644 index 00000000..161b92c5 --- /dev/null +++ b/templates/layouts/default2.hbs @@ -0,0 +1,46 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Default layout +--- +# {{name}} {{badge "npm"}} {{badge "travis"}} + +> {{description}} + + + +{{#if verb.sections.install}} +## Install +{{include "install-npm" save="true"}} +{{/if}} + +{% body %} + +{{#if obj.sections.api.path}} +## API +{{apidocs obj.sections.api.path}} +{{/if}} + +{{#if (verb.related && verb.related.list && verb.related.list.length) {}} +## Related projects +{{verb.related.description}} +{{related verb.related.list}} +{{/if}} + +## Running tests +{{include "tests"}} + +## Contributing +{{include "contributing"}} + +## Author +{{include "author"}} + +## License +{{copyright linkify="true"}} +{{license}} + +*** + +{{include "footer"}} + diff --git a/templates/layouts/min.md b/templates/layouts/min.md new file mode 100644 index 00000000..46a559d4 --- /dev/null +++ b/templates/layouts/min.md @@ -0,0 +1,21 @@ +--- +verb_docs: + tags: ['template', 'built-in', '.verb.md', 'layout'] + title: Minimal layout +--- +# {%= name %} + +> {%= description %} + +{% body %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/templates/readme/header.md b/templates/readme/header.md new file mode 100644 index 00000000..1c40ad93 --- /dev/null +++ b/templates/readme/header.md @@ -0,0 +1,9 @@ +# {%= name %} {%= badge('npm') %} {%= badge('travis') %} + +> {%= description %} + +{% if(verb.sections) { %} +{% for(var key in verb.sections) { %} +{%= sections(key, verb.sections[key]) %} +{% } %} +{% } %} \ No newline at end of file diff --git a/templates/readme/min.md b/templates/readme/min.md new file mode 100644 index 00000000..a8f75669 --- /dev/null +++ b/templates/readme/min.md @@ -0,0 +1,14 @@ +# {%= name %} + +> {%= description %} + +## Author +{%= include("author") %} + +## License +{%= copyright({linkify: true}) %} +{%= license %} + +*** + +{%= include("footer") %} diff --git a/lib/sections/templates.js b/templates/sections.js similarity index 61% rename from lib/sections/templates.js rename to templates/sections.js index 8700a127..f4add9f9 100644 --- a/lib/sections/templates.js +++ b/templates/sections.js @@ -3,38 +3,59 @@ var fs = require('fs'); module.exports = { + title: { + level: 1, + heading: '{%= name %} {%= badge(\'npm\') %} {%= badge(\'travis\') %}', + content: '> {%= description %}\n\n', + }, + install: { + level: 2, + heading: 'Install', + content: '{%= include(\'install-npm\', {save: true}) %}', + }, apidocs: { + level: 2, heading: 'API', content: '{%= section("apidocs") %}', }, related: { + level: 2, heading: 'Related projects', content: '{%= related(verb.related.list) %}', }, tests: { + level: 2, heading: 'Running tests', content: '{%= include("tests") %}', }, coverage: { + level: 2, heading: 'Coverage summary', content: 'As of version {%= version %}\n\n```\n{%= coverage(\'coverage/summary.txt\') %}\n```\n', - validate: function(verb, base, env) { - return fs.existsSync(env.user.cwd + '/coverage'); + validate: function(app) { + if (app && app.env) { + return fs.existsSync(app.env.user.cwd + '/coverage'); + } + return true; } }, contributing: { + level: 2, heading: 'Contributing', content: '{%= include("contributing") %}', }, author: { + level: 2, heading: 'Author', content: '{%= include("author") %}', }, license: { + level: 2, heading: 'License', content: '{%= copyright() %}\n{%= license %}' }, footer: { + level: 2, heading: '***', content: '{%= include("footer") %}' } From 2615acd7387652282bb1d6090915cafe34fc674b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:26:39 -0500 Subject: [PATCH 124/282] move some middleware functions into files, clean up --- lib/runner/commands/help.js | 22 ++--- lib/runner/middleware.js | 142 ++++++------------------------ lib/runner/middleware/reflinks.js | 23 +++++ lib/runner/middleware/toc.js | 60 +++++++++++++ 4 files changed, 119 insertions(+), 128 deletions(-) create mode 100644 lib/runner/middleware/reflinks.js create mode 100644 lib/runner/middleware/toc.js diff --git a/lib/runner/commands/help.js b/lib/runner/commands/help.js index 6af7bd46..9a7c55ba 100644 --- a/lib/runner/commands/help.js +++ b/lib/runner/commands/help.js @@ -21,57 +21,57 @@ function help() { init: { description: 'Force initialization questions to be re-asked.', example: '', - short: 'i', + short: 'i' }, help: { description: '', example: '', - short: 'h', + short: 'h' }, show: { description: '', example: '', - short: null, + short: null }, ask: { description: '', example: '', - short: null, + short: null }, open: { description: '', example: '', - short: 'o', + short: 'o' }, config: { description: '', example: '', - short: 'c', + short: 'c' }, diff: { description: '', example: '', - short: null, + short: null }, cwd: { description: '', example: '', - short: null, + short: null }, data: { description: '', example: '', - short: 'd', + short: 'd' }, choose: { description: '', example: '', - short: null, + short: null }, tasks: { description: '', example: '', - short: null, + short: null } }, footer: '' diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 859a1a08..45abeceb 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -1,19 +1,10 @@ 'use strict'; +var diff = require('base-diff'); var middleware = require('./middleware/'); var utils = require('../utils'); -var falsey = require('falsey'); -var diff = require('base-diff'); module.exports = function(app, base, env) { - // `view` plugin that add a `isType` method to views - app.use(function fn(view) { - if (!view.isView) return fn; - - view.define('isType', function(type) { - return this.options.viewType.indexOf(type) !== -1; - }); - }); /** * Readme dest path @@ -24,145 +15,62 @@ module.exports = function(app, base, env) { next(); }); - - app.onLoad(/\.md/, middleware.examples(app)); - - app.onLoad(/\.md/, function(view, next) { - if (hasToc(view)) { - // don't render or insert a TOC - view.options.toc = view.options.toc || {}; - view.options.toc.render = true; - view.options.toc.insert = true; - verb.data.toc = ''; - } - next(); - }); - /** * Table of contents > adds `file.data.toc` property */ - var append = '\n\n_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_'; - app.preRender(/\.md/, renderToc(app, 'preRender', append)); + var append = '_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_'; + + app.postLayout(/\.md/, middleware.toc.create(app, append)); + app.preWrite(/\.md/, middleware.toc.inject(app, append)); + app.onLoad(/\.md/, middleware.reflinks(app)); + app.onLoad(/\.md/, middleware.examples(app)); + + // /** + // * Merge options onto `view.data` + // */ app.preRender(/./, function(view, next) { - view.data.options = view.options; + view.data.options = utils.merge({}, view.data.options, view.options); next(); }); - /** - * Diff files (currently just testing readme) - */ + // /** + // * Diff files (currently just testing readme) + // */ - app.preRender(/./, diff.view('diffLines')); - app.postRender(/./, function(view, next) { - var diff = app.option('diff'); - if (diff && diff === true || diff === view.stem) { - view.diff(); - } - next(); - }); + // // app.preRender(/./, diff.view('diffLines')); + // // app.postRender(/./, function(view, next) { + // // var diff = app.option('diff'); + // // if (diff && diff === true || diff === view.stem) { + // // view.diff(); + // // } + // // next(); + // // }); /** * Determine layout */ app.preLayout(/\.md/, function(view, next) { - if (view.isType('partial')) { + if (utils.isType(view, 'partial')) { next(); return; } - - var layout = app.pkg.get('verb.layout') + var layout = app.pkg.get('verb.layout'); if (utils.isObject(layout)) { var opts = layout; layout = layout.name; } - if (typeof layout !== 'undefined') { view.layout = layout; - next(); - return; } next(); }); - app.postLayout(/./, renderToc(app, 'postLayout', append)); - app.preWrite(/package\.json$/, function(view, next) { view.dest = 'package.json'; next(); }); - - app.preWrite(/\.md$/, function(view, next) { - if (!view.actions.has('toc') && view.data.toc) { - view.content = insertToc(view.content, view.data.toc); - view.actions.set('toc'); - } - next(); - }); }; - -function renderToc(app, stage, append) { - return function(view, next) { - var opts = utils.merge({append: append, toc: {}}, app.options); - if (view.isType('partial') || isDisabled(opts, view, 'toc')) { - view.content = stripToc(view.content); - next(); - return; - } - - var toc = utils.toc(app, opts); - - // ensure the TOC isn't injected until after formatting - view.options.toc = { insert: false }; - toc(view, next); - }; -} - -function hasToc(view) { - if (view.get('options.toc.inserted') === true) { - return true; - } - if (view.content.indexOf('') !== -1) { - return true; - } -} - -function tocInjected(view) { - if (view.get('options.toc.inserted') === true) { - return true; - } - if (view.content.indexOf('') !== -1) { - return true; - } -} - -function stripToc(str) { - str = str.split('').join(''); - str = str.split('').join(''); - return str; -} - -function insertToc(str, toc) { - if (!toc) return; - str = str.split('').join(toc); - str = str.split('').join(toc); - return str; -} - -function isDisabled(opts, view, prop) { - return isFalsey(utils.get(opts, prop)) - || isFalsey(utils.get(view.options, prop)) - || isFalsey(utils.get(view.data, prop)) -} - -function isFalsey(val) { - if (typeof val === 'undefined') { - return false; - } - if (!utils.isObject(val)) { - return falsey(val); - } -} diff --git a/lib/runner/middleware/reflinks.js b/lib/runner/middleware/reflinks.js new file mode 100644 index 00000000..ea73fa79 --- /dev/null +++ b/lib/runner/middleware/reflinks.js @@ -0,0 +1,23 @@ +'use strict'; + +var utils = require('../../utils'); + +/** + * Add a reflinks helper to + */ + +module.exports = function(verb) { + return function(view, next) { + var arr = verb.pkg.get('verb.reflinks'); + + if (Array.isArray(arr)) { + var reflinks = utils.reflinks({verbose: true}); + + reflinks(arr, function(err, res) { + if (err) return next(err); + file.content += '\n\n' + res; + next(); + }); + } + }; +}; diff --git a/lib/runner/middleware/toc.js b/lib/runner/middleware/toc.js new file mode 100644 index 00000000..84cdaec1 --- /dev/null +++ b/lib/runner/middleware/toc.js @@ -0,0 +1,60 @@ +'use strict'; + +var toc = require('markdown-toc'); +var utils = require('../../utils'); + +/** + * Add a TOC to the context of a view + */ + +exports.create = function(app, append) { + return function(view, next) { + view.options.toc = view.options.toc || {}; + var opts = utils.merge({append: append}, app.options, view.options); + + if (view.isType('partial') || opts.toc === false) { + next(); + return; + } + + view.options.toc.hasMarker = hasMarker(view); + if (view.options.toc.hasMarker && !view.data.toc) { + view.data.toc = toc(view.content).content; + } + next(); + }; +}; + +exports.inject = function(app, append) { + return function(view, next) { + var str = view.data.toc; + + if (!str || view.options.toc.hasMarker === false) { + return next(); + } + + if (append) { + str += '\n\n' + append; + } + + view.content = injectToc(view.content, str); + next(); + }; +}; + +function hasMarker(view) { + return view.content.indexOf('') !== -1; +} + +function stripToc(str) { + str = str.split('').join(''); + str = str.split('').join(''); + return str; +} + +function injectToc(str, toc) { + if (!toc) return str; + str = str.split('').join(toc); + str = str.split('').join(toc); + return str; +} From 0968545e97a65f94ee7f82148bf5df01c0c91067 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:27:20 -0500 Subject: [PATCH 125/282] externalize loader function --- lib/config.js | 213 ++++++++------------------------------------------ lib/loader.js | 79 +++++++++++++++++++ 2 files changed, 110 insertions(+), 182 deletions(-) create mode 100644 lib/loader.js diff --git a/lib/config.js b/lib/config.js index 94b09a26..6d639042 100644 --- a/lib/config.js +++ b/lib/config.js @@ -4,6 +4,7 @@ var fs = require('fs'); var path = require('path'); var sections = require('./sections'); var updater = require('./updater'); +var loader = require('./loader'); var utils = require('./utils'); module.exports = function(verb, base, env) { @@ -52,16 +53,6 @@ module.exports = function(verb, base, env) { } }); - /** - * Helpers - */ - - verb.config - .map('helpers', function(val) { - console.log('helpers >', val); - }) - .map('asyncHelpers'); - /** * Template options and settings */ @@ -110,188 +101,46 @@ module.exports = function(verb, base, env) { } /** - * Helpers + * Load helpers from verb config in `package.json` */ - verb.config.map('helpers', function(helpers) { - var obj = verb.get('env.argv.helpers'); - var cwd = verb.get('env.user.cwd'); - var opts = {}; - - if (typeof helpers === 'string') { - helpers = [helpers]; - } - - if (Array.isArray(helpers)) { - helpers = helpers.reduce(function(acc, helper) { - if (typeof helper === 'string') { - acc[helper] = {}; - } else { - throw new Error('helper format not supported, implement me!'); - } - return acc; - }, {}); - } - - for (var key in helpers) { - var name = path.basename(key, path.extname(key)); - var helper = helpers[key]; - - try { - if (name === 'index') { - var dir = path.resolve(cwd); - var fp = utils.tryResolve(dir); - if (fs.existsSync(fp)) { - opts = helper; - helper = utils.tryRequire(fp); - name = path.basename(dir); - } - } - } catch (err) { - throw err; - } - - if (typeof helper === 'string') { - // use `name` - // resolve `helper` - - } else if (utils.isObject(helper)) { - opts = helper; - helper = utils.loadModule(opts.path || name); - name = opts.name || name; - } - - if (typeof helper !== 'function') { - throw new Error('cannot load helper: ' + helper); - } - - name = name.slice(name.lastIndexOf('-') + 1); - verb.helper(name, helper); - } - }); + verb.config.map('helpers', loader('helpers', verb, function(name, opts, fn) { + name = name.slice(name.lastIndexOf('-') + 1); + verb.helper(name, fn); + })); /** - * Middleware + * Load asyncHelpers from verb config in `package.json` */ - verb.config.map('use', function(middleware) { - var obj = verb.get('env.argv.middleware'); - var cwd = verb.get('env.user.cwd'); - var opts = {}; - - if (typeof middleware === 'string') { - middleware = [middleware]; - } - - if (Array.isArray(middleware)) { - middleware = middleware.reduce(function(acc, val) { - if (typeof val === 'string') { - acc[val] = {}; - } else { - throw new Error('format not supported, implement me!'); - } - return acc; - }, {}); - } - - for (var key in middleware) { - var name = path.basename(key, path.extname(key)); - var val = middleware[key]; - - if (name === 'index') { - var dir = path.resolve(cwd); - var fp = utils.tryResolve(dir); - if (fs.existsSync(fp)) { - opts = val; - val = utils.tryRequire(fp); - name = path.basename(dir); - } - } - - if (typeof val === 'string') { - throw new Error('format not supported, implement me!'); - - } else if (utils.isObject(val)) { - opts = val; - var fp = utils.resolveModule(opts.path || name); - if (opts.path) { - fp = path.resolve(path.dirname(fp), opts.path); - } - val = utils.tryRequire(fp); - name = opts.name || name; - } - - if (typeof val !== 'function') { - throw new Error('cannot load middleware: ' + val); - } + verb.config.map('asyncHelpers', loader('asyncHelpers', verb, function(name, opts, fn) { + name = name.slice(name.lastIndexOf('-') + 1); + verb.asyncHelper(name, fn); + })); - // get the regex pattern to use - var pattern = opts.pattern ? new RegExp(opts.pattern) : /./; - - // get the method name (onLoad, preWrite, preRender, etc) - var method = opts.method || 'onLoad'; - if (!(method in verb)) { - throw new Error('verb does not have a middleware handler for ' + method); - } - - // load the middleware - verb[method](pattern, val(verb.options)); - } - }); - - verb.config.map('plugins', function(plugins) { - var obj = verb.get('env.argv.plugins'); - var cwd = verb.get('env.user.cwd'); - var opts = {}; + /** + * Load middleware from verb config in `package.json` + */ - if (typeof plugins === 'string') { - plugins = [plugins]; - } + verb.config.map('use', loader('middleware', verb, function(name, opts, fn) { + // get the regex pattern to use + var pattern = opts.pattern ? new RegExp(opts.pattern) : /./; - if (Array.isArray(plugins)) { - plugins = plugins.reduce(function(acc, plugin) { - if (typeof plugin === 'string') { - acc[plugin] = {}; - } else { - throw new Error('plugin format not supported, implement me!'); - } - return acc; - }, {}); + // get the method name (onLoad, preWrite, preRender, etc) + var method = opts.method || 'onLoad'; + if (!(method in verb)) { + throw new Error('verb does not have handler: "' + method + '"'); } + // register the middleware + verb[method](pattern, fn(verb.options)); + })); - for (var key in plugins) { - var name = path.basename(key, path.extname(key)); - var plugin = plugins[key]; - - try { - if (name === 'index') { - var dir = path.resolve(cwd); - var fp = utils.tryResolve(dir); - if (fs.existsSync(fp)) { - opts = plugin; - plugin = utils.tryRequire(fp); - name = path.basename(dir); - } - } - } catch (err) { - throw err; - } - - if (typeof plugin === 'string') { - // use `name` - // resolve `plugin` - - } else if (utils.isObject(plugin)) { - opts = plugin; - plugin = utils.loadModule(opts.path || name); - name = opts.name || name; - } - - if (typeof plugin !== 'function') { - throw new Error('cannot load plugin: ' + plugin); - } + /** + * Load plugins from verb config in `package.json` + */ - verb.plugin(name, opts, plugin); - } - }); + verb.config.map('plugins', loader('plugins', verb, function(name, opts, fn) { + verb.plugin(name, opts, fn); + })); }; + diff --git a/lib/loader.js b/lib/loader.js new file mode 100644 index 00000000..bc297ed6 --- /dev/null +++ b/lib/loader.js @@ -0,0 +1,79 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var utils = require('./utils'); + +module.exports = function loader(prop, app, fn) { + if (typeof prop !== 'string') { + throw new TypeError('expected the first argument to be a string'); + } + + return function(config) { + var obj = app.get('env.argv.' + prop); + var cwd = app.get('env.user.cwd'); + var opts = {}; + + if (typeof config === 'string') { + config = [config]; + + } else if (Array.isArray(config)) { + config = config.reduce(function(acc, val) { + if (typeof val === 'string') { + acc[val] = {}; + } else { + throw new TypeError('type not supported, implement me!'); + } + return acc; + }, {}); + } + + for (var key in config) { + var name = path.basename(key, path.extname(key)); + var val = config[key]; + + try { + if (name === 'index') { + var dir = path.resolve(cwd); + var fp = utils.tryResolve(dir); + if (fs.existsSync(fp)) { + opts = val; + val = utils.tryRequire(fp); + name = path.basename(dir); + } + } + } catch (err) { + throw err; + } + + if (typeof val === 'string') { + throw new TypeError('type not supported. implement me!'); + } + + if (utils.isObject(val)) { + opts = val; + name = opts.name || name; + var filepath = opts.path || key || name; + val = utils.loadModule(filepath, cwd); + if (val === null) { + var fp = path.resolve(cwd, filepath); + throw new Error('cannot resolve ' + prop + ' at path: ' + fp); + } + } + + opts = opts || {}; + if (typeof name !== 'string') { + throw new TypeError('expected ' + name + ' to be a string'); + } + if (typeof val !== 'function') { + var msg = 'expected ' + val + + ' to be a function. Cannot load ' + + prop + ' ' + JSON.stringify(arguments); + throw new Error(msg); + } + + name = name.replace(/^[-\W]*(verb|helper|middleware|plugin)?[-\W]*/, ''); + fn(name, opts, val); + } + }; +}; From 5ecbd74efebfe190f3f5a5a3dcb509597eb1e5dc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:29:19 -0500 Subject: [PATCH 126/282] don't use loader for built-in templates --- lib/runner/preload.js | 12 +--- lib/runner/templates.js | 120 ++++++++++++++++++++++++++-------------- 2 files changed, 82 insertions(+), 50 deletions(-) diff --git a/lib/runner/preload.js b/lib/runner/preload.js index 0272dacb..f363d6ac 100644 --- a/lib/runner/preload.js +++ b/lib/runner/preload.js @@ -8,23 +8,17 @@ var context = require('./context'); var cli = require('./cli'); module.exports = function preload(verb, base, env) { - verb.use(utils.ask({storeName: 'verb'})); + verb.use(utils.ask({ storeName: 'verb' })); verb.use(utils.loader()); defaults(verb, base, env); - verb.use(templates(base, env)); + templates(verb, base, env); context(verb, base, env); config(verb, base, env); cli(verb, base, env); - verb.use(utils.list('apps', {method: 'app'})); - - verb.on('register', function(name, app) { - context(verb, base, env); - defaults(app, base, env); - app.use(templates(base, env)); - }); + verb.use(utils.list('apps', { method: 'app' })); verb.questions.on('ask', function(key, question, answers) { if (!verb.pkg.get('description') && key === 'description') { diff --git a/lib/runner/templates.js b/lib/runner/templates.js index dd921425..45372315 100644 --- a/lib/runner/templates.js +++ b/lib/runner/templates.js @@ -3,57 +3,95 @@ var fs = require('fs'); var path = require('path'); var create = require('./create'); +var utils = require('../utils'); var loaded, views = {}; -module.exports = function(options) { - var includes = require('../templates/includes'); - var badges = require('../templates/badges'); - - return function(verb) { - verb.use(create(options)); - - verb.on('config-loaded', function() { - verb.includes(includes); - verb.badges(badges); - - // get the user's CWD - var userDir = path.join.bind(path, process.cwd()); - - // load lib files, for API documentation - verb.jsFiles(['*.js', 'lib/*.js'], {cwd: userDir()}); - - // load root files, including `.verb.dm` - verb.docs('*', {cwd: userDir(), dot: true}); - - // load templates from `docs` directory - verb.includes('*.md', {cwd: userDir('docs/includes')}); - verb.layouts('*.md', {cwd: userDir('docs/layouts')}); - verb.docs('*.md', {cwd: userDir('docs')}); - - // load `verb` built-in templates onto the instance - var collections = loadDefaults(verb); - if (collections) { - for (var key in collections) { - var collection = collections[key]; - for (var name in collection) { - if (!verb.views[key][name]) { - verb.views[key][name] = collection[name]; - } - } +module.exports = function(verb, base, env) { + var includes = require('../../templates/includes'); + var badges = require('../../templates/badges'); + + verb.use(create()); + for (var key in includes) { + var view = includes[key]; + verb.includes.addView(key, { content: view }); + } + + verb.includes.addViews(includes); + verb.badges.addViews(badges); + + // get the user's CWD + var userDir = path.resolve.bind(path, process.cwd()); + + // load lib files, for API documentation + verb.jsFiles(['*.js', 'lib/*.js'], {cwd: userDir()}); + + // load root files, including `.verb.dm` + verb.docs('*', { + ignore: ['*.sublime*', '.DS_Store'], + cwd: userDir(), + dot: true + }); + + if (fs.existsSync(userDir('docs'))) { + // load templates from `docs` directory + verb.includes('*.md', {cwd: userDir('docs/includes')}); + verb.layouts('*.md', {cwd: userDir('docs/layouts')}); + verb.docs('*.md', {cwd: userDir('docs')}); + } + + // load `verb` built-in templates onto the instance + var collections = loadDefaults(verb); + if (collections) { + for (var key in collections) { + var collection = collections[key]; + for (var name in collection) { + if (!verb.views[key][name]) { + var view = collection[name]; + verb[key](view.path, view); } } - }); - }; + } + } }; function loadDefaults(app) { if (loaded) return views; loaded = true; - var docs = path.join.bind(path, __dirname, '../../docs/src/readme'); - app.includes('*.md', {cwd: docs('includes')}); - app.layouts('*.md', {cwd: docs('layouts')}); - app.docs('*.md', {cwd: docs()}); + var docs = path.resolve.bind(path, __dirname, '../../templates'); + var includesDir = docs('includes'); + var includes = utils.glob.sync('*.md', {cwd: includesDir}); + includes.forEach(function(fp) { + fp = path.resolve(includesDir, fp); + app.includes.addView(fp, { + path: fp, + contents: fs.readFileSync(fp) + }); + }); + + var layoutsDir = docs('layouts'); + var layouts = utils.glob.sync('*.md', {cwd: layoutsDir}); + layouts.forEach(function(fp) { + fp = path.resolve(layoutsDir, fp); + app.layouts.addView(fp, { + path: fp, + contents: fs.readFileSync(fp) + }); + }); + + var readmeDir = docs('readme'); + var docs = utils.glob.sync('*.md', {cwd: readmeDir}); + docs.forEach(function(fp) { + fp = path.resolve(readmeDir, fp); + app.docs.addView(fp, { + path: fp, + contents: fs.readFileSync(fp) + }); + }); + + // app.includes('*.md', {cwd: docs('includes')}); + // app.layouts('*.md', {cwd: docs('layouts')}); + // app.docs('*.md', {cwd: docs('readme')}); // cache views views.includes = app.views.includes; From 27911cca303ddbe0c7b207dd401f1fbe01152500 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:29:32 -0500 Subject: [PATCH 127/282] lint --- lib/pkg.js | 2 +- lib/updater.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pkg.js b/lib/pkg.js index fbbb8c89..7010b72d 100644 --- a/lib/pkg.js +++ b/lib/pkg.js @@ -6,7 +6,7 @@ var utils = require('./utils'); * Adds get/set methods to verb env */ -module.exports = function(config) { +module.exports = function() { return function(verb) { verb.mixin('pkg', new Pkg(verb, '_pkg')); }; diff --git a/lib/updater.js b/lib/updater.js index d61ac1c6..397b7355 100644 --- a/lib/updater.js +++ b/lib/updater.js @@ -4,7 +4,6 @@ var utils = require('./utils'); module.exports = function(options) { options = options || {}; - return function(verb) { verb.define('update', update, options.schema); }; From e1963b5fcc3dce7c302e5d4bce8356c1ecb07ce4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:30:58 -0500 Subject: [PATCH 128/282] CLI utils --- lib/utils.js | 62 +++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index bb8b3946..82d6f187 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -40,6 +40,7 @@ require('extend-shallow', 'extend'); require('get-value', 'get'); require('global-modules', 'gm'); require('has-glob'); +require('has-value', 'has'); require('is-primitive'); require('is-valid-glob'); require('isobject', 'isObject'); @@ -70,6 +71,28 @@ require('parse-author'); require = fn; +/** + * Green checkmark + * + * @return {String} + */ + +utils.success = function() { + return utils.colors.green(utils.successSymbol); +}; + +/** + * Create a formatted timestamp + * + * @param {String} msg + * @return {String} + */ + +utils.timestamp = function(msg, stamp) { + var time = '[' + utils.colors.gray(utils.stamp('HH:mm:ss', new Date())) + ']'; + console.log(time, msg); +}; + utils.defaults = function(a, b) { a = utils.tableize(a || {}); b = utils.tableize(b || {}); @@ -156,18 +179,6 @@ utils.globFiles = function(patterns, options) { return utils.glob.sync(patterns, opts); }; -/** - * Create a formatted timestamp - * - * @param {String} msg - * @return {String} - */ - -utils.timestamp = function(msg, stamp) { - var time = '[' + utils.colors.gray(utils.stamp('HH:mm:ss', new Date())) + ']'; - console.log(time, msg); -}; - /** * Get a home-relative filepath */ @@ -177,16 +188,6 @@ utils.homeRelative = function(fp) { return utils.colors.green('~/' + fp); }; -/** - * Green checkmark - * - * @return {String} - */ - -utils.success = function() { - return utils.colors.green(utils.successSymbol); -}; - /** * Try to read a directory */ @@ -241,7 +242,6 @@ utils.resolveModule = function(name, cwd) { var fp = utils.tryResolve(cwd); if (fp) return fp; } - return utils.tryResolve(name, cwd) || utils.tryResolve(name, utils.gm) || utils.tryResolve(name); @@ -287,13 +287,13 @@ utils.tableize = function tableize(obj, opts) { */ function type(schema, obj, prefix, opts) { - Object.keys(obj).forEach(function(key){ + Object.keys(obj).forEach(function(key) { var val = obj[key]; key = prefix + key; if (opts.lowercase) key = key.toLowerCase(); - if (isObject(val)) { + if (utils.isObject(val)) { type(schema, val, key + '.', opts); } else { schema[key] = val; @@ -301,18 +301,6 @@ function type(schema, obj, prefix, opts) { }); } -/** - * Check if `val` is an object. - * - * @param {Mixed} val - * @return {Boolean} - * @api private - */ - -function isObject(val) { - return '[object Object]' == Object.prototype.toString.call(val); -} - /** * Expose `utils` modules */ From 61b4eae53ff7141c5218ecf7683176ae9ba018c9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:31:17 -0500 Subject: [PATCH 129/282] namespace `sections` collection --- lib/runner/create.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/runner/create.js b/lib/runner/create.js index 96d3a502..36433b9b 100644 --- a/lib/runner/create.js +++ b/lib/runner/create.js @@ -12,8 +12,8 @@ module.exports = function(options) { create(app, 'jsFiles'); create(app, 'badges', 'partial'); create(app, 'includes', 'partial'); - create(app, 'sections', 'partial'); create(app, 'layouts', 'layout'); + create(app, 'verb_sections', 'partial'); }; }; @@ -28,10 +28,11 @@ module.exports = function(options) { function create(app, name, type) { if (app[name]) return; + type = type || 'renderable'; - app.create(name, { + return app.create(name, { engine: 'text', - viewType: [type || 'renderable'], + viewType: type, renameKey: function(key) { return path.basename(key, path.extname(key)); } From 27445087db9130abf8a0bbeae599f79a228f842e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:31:55 -0500 Subject: [PATCH 130/282] adds `read` and `example` helpers --- lib/runner/helpers.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 038e6e29..4479f648 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -1,8 +1,11 @@ 'use strict'; +var fs = require('fs'); +var path = require('path'); var utils = require('../utils'); module.exports = function(verb, base, env) { + var section = require('../sections/helper'); var apidocs = require('helper-apidocs'); var issue = require('helper-issue'); @@ -46,6 +49,13 @@ module.exports = function(verb, base, env) { return issue(opts); }); + verb.helper('example', function(str, name) { + return example(str, name).trim(); + }); + + verb.helper('read', function(fp) { + return fs.readFileSync(fp, 'utf8'); + }); verb.helper('apidocs', apidocs({delims: ['{%', '%}']})); verb.helper('copyright', require('helper-copyright')({ linkify: true @@ -54,3 +64,25 @@ module.exports = function(verb, base, env) { verb.asyncHelper('related', utils.related({verbose: true})); verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); }; + + +/** + * Create a code example from the contents of the specified + * JavaScript file. + * + * ```js + * {%%= example("foo", {name: "my-module"}) %} + * ``` + * + * @param {String} `fp` The path of the file to include. + * @param {String} `options` + * @option {String} `name` Replace `./` in `require('./')` with the given name. + * @return {String} + */ + +function example(str, name) { + if (typeof str !== 'string') { + throw new TypeError('example-helper expects a string.'); + } + return str.replace(/\((['"])\.\/\1\)/g, '(\'' + name + '\')'); +} From a13c3197dbee2484bf95e1f982700ae378216ebc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:32:11 -0500 Subject: [PATCH 131/282] re-add diff middleware --- lib/runner/middleware.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 45abeceb..08393dd7 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -36,25 +36,25 @@ module.exports = function(app, base, env) { next(); }); - // /** - // * Diff files (currently just testing readme) - // */ + /** + * Diff files (currently just testing readme) + */ - // // app.preRender(/./, diff.view('diffLines')); - // // app.postRender(/./, function(view, next) { - // // var diff = app.option('diff'); - // // if (diff && diff === true || diff === view.stem) { - // // view.diff(); - // // } - // // next(); - // // }); + app.preRender(/./, diff.view('diffLines')); + app.postRender(/./, function(view, next) { + var diff = app.option('diff'); + if (diff && diff === true || diff === view.stem) { + view.diff(); + } + next(); + }); /** * Determine layout */ app.preLayout(/\.md/, function(view, next) { - if (utils.isType(view, 'partial')) { + if (view.isType('partial')) { next(); return; } From 72ac92a0d56100002a45ade9a241449f043fc4ed Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:32:39 -0500 Subject: [PATCH 132/282] clean up data-expansion logic --- lib/runner/data.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/runner/data.js b/lib/runner/data.js index e220d28a..660be929 100644 --- a/lib/runner/data.js +++ b/lib/runner/data.js @@ -22,13 +22,12 @@ module.exports.updateData = function(verb) { verb.questions.enqueue('author', 'name', 'description'); var pkg = verb._pkg; - var config = pkg.verb || {}; + var config = verb.pkg.get('verb') || {}; - // load package.json data and user options onto `verb.cache.data` verb.option(config.options || {}); verb.data(pkg); + verb.data(verb.pkg.get('verb.data') || {}); - var config = verb.pkg.get('verb.data') || {}; - config = utils.defaults(config, verb.cache.data); - return expandPkg(verb, config); + var res = utils.defaults(pkg, verb.cache.data); + return expandPkg(verb, res); }; From 3f85c94a946dc81e3057a09facd2b270768582cd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 01:36:57 -0500 Subject: [PATCH 133/282] lint --- cli.js | 24 +++++++++++------------- index.js | 12 ++++++++---- lib/config.js | 5 ++--- lib/runner/helpers.js | 4 +--- lib/runner/middleware/reflinks.js | 2 +- lib/sections/helper.js | 1 - lib/sections/load.js | 1 - verbfile.js | 5 +---- 8 files changed, 24 insertions(+), 30 deletions(-) diff --git a/cli.js b/cli.js index ebe80058..bf6415b5 100755 --- a/cli.js +++ b/cli.js @@ -1,15 +1,10 @@ #!/usr/bin/env node -var fs = require('fs'); -var path = require('path'); -var async = require('async'); var gm = require('global-modules'); var minimist = require('minimist'); -var defaults = require('./lib/runner/defaults'); var preload = require('./lib/runner/preload'); var data = require('./lib/runner/data'); var utils = require('./lib/utils'); -var pkg = require('./lib/pkg'); var colors = utils.colors; var Verb = require('./'); var create = Verb.create; @@ -61,12 +56,14 @@ if (args.emit && typeof args.emit === 'string') { } // get `verb` property from package.json, if it exists -var pkg = verb.get('env.user.pkg'); -var verbConfig = pkg.verb; +var userPkg = verb.get('env.user.pkg'); +if (userPkg) { + verb.set('cache.pkg', userPkg); + var verbConfig = userPkg.verb; +} -if (verbConfig) { - var config = utils.expandConfig(verbConfig, pkg); - verb._pkg = config; +if (userPkg && verbConfig) { + var config = utils.expandConfig(verbConfig, userPkg); verb.config.process(config); verb.set('cache.config', config); verb.emit('config-loaded'); @@ -103,7 +100,9 @@ verb.cli.map('apps', function(tasks) { if (!tasks.length) { if (verb.tasks.hasOwnProperty('default')) { - tasks = [{base: ['default']}]; + var task = {}; + task[verb.env.user.alias] = ['default']; + tasks = [task]; } else { console.log(' no default task is defined.'); utils.timestamp('done'); @@ -134,7 +133,7 @@ verb.cli.map('apps', function(tasks) { // run apps and/or tasks verb.runApps(tasks, function(err) { - if (err) return console.error(err); + if (err) throw err; utils.timestamp('finished ' + utils.success()); process.exit(0); }); @@ -148,4 +147,3 @@ verb.cli.map('apps', function(tasks) { */ verb.cli.process(args); - diff --git a/index.js b/index.js index b41cd21a..618dd038 100644 --- a/index.js +++ b/index.js @@ -73,6 +73,7 @@ function create(preload) { Verb.prototype.initVerb = function(app) { this.store.create('config'); + this.engine('hbs', require('engine-handlebars')); this.engine(['md', 'text'], require('engine-base'), { delims: ['{%', '%}'] }); @@ -188,12 +189,15 @@ function create(preload) { Object.defineProperty(Verb.prototype, '_pkg', { configurable: true, - get: function(val) { - this.cache.pkg = val; + set: function(pkg) { + this.cache.pkg = pkg; }, get: function() { - var user = this.env.user || {}; - var pkg = user.pkg || {}; + if (this.cache.pkg) { + return this.cache.pkg; + } + var pkg = this.cache.pkg + || this.get('env.user.pkg'); return (this.cache.pkg = pkg); } }); diff --git a/lib/config.js b/lib/config.js index 6d639042..c48645dd 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,7 +1,5 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); var sections = require('./sections'); var updater = require('./updater'); var loader = require('./loader'); @@ -81,11 +79,12 @@ module.exports = function(verb, base, env) { var readme = verb.docs.getView('readme.md'); readme.layout = val.name; } + if (val.sections) { for (var key in val.sections) { if (val.sections.hasOwnProperty(key)) { var section = val.sections[key]; - + throw new Error('sections are not setup yet. implement me!'); } } } diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 4479f648..e35ea710 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -5,11 +5,10 @@ var path = require('path'); var utils = require('../utils'); module.exports = function(verb, base, env) { - var section = require('../sections/helper'); var apidocs = require('helper-apidocs'); var issue = require('helper-issue'); - verb.helper('resolve', function (name) { + verb.helper('resolve', function(name) { var base = path.resolve(process.cwd(), 'node_modules', name); var pkg = require(path.join(base, 'package.json')); var cwd = path.join(base, pkg.main); @@ -65,7 +64,6 @@ module.exports = function(verb, base, env) { verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); }; - /** * Create a code example from the contents of the specified * JavaScript file. diff --git a/lib/runner/middleware/reflinks.js b/lib/runner/middleware/reflinks.js index ea73fa79..829f10cb 100644 --- a/lib/runner/middleware/reflinks.js +++ b/lib/runner/middleware/reflinks.js @@ -15,7 +15,7 @@ module.exports = function(verb) { reflinks(arr, function(err, res) { if (err) return next(err); - file.content += '\n\n' + res; + view.content += '\n\n' + res; next(); }); } diff --git a/lib/sections/helper.js b/lib/sections/helper.js index 5acada30..3f9835fc 100644 --- a/lib/sections/helper.js +++ b/lib/sections/helper.js @@ -18,7 +18,6 @@ module.exports = function(app, config) { var context = utils.merge({}, config, this.context); var section = app.verb_sections.getView(name); // console.log(section) - return ''; }; }; diff --git a/lib/sections/load.js b/lib/sections/load.js index a8c1cea1..01ca959d 100644 --- a/lib/sections/load.js +++ b/lib/sections/load.js @@ -1,6 +1,5 @@ 'use strict'; - module.exports = function(app) { var sections = require('../../templates/sections'); diff --git a/verbfile.js b/verbfile.js index 1a86c9b6..dcf1556e 100644 --- a/verbfile.js +++ b/verbfile.js @@ -2,10 +2,6 @@ var path = require('path'); var utils = require('./lib/utils'); -var data = require('./lib/runner/data'); -var argv = require('minimist')(process.argv.slice(2), { - alias: {v: 'verbose'} -}); /** * HEADS UP: this is not what a normal verfile.js would look like. @@ -35,6 +31,7 @@ module.exports = function(verb, base, env) { .pipe(handle(verb, 'onStream')) .on('error', cb) .pipe(verb.renderFile('text')) + .on('error', cb) .on('error', handleError(verb)) .pipe(verb.pipeline(plugins)) .pipe(handle(verb, 'preWrite')) From 8a69042f7485ca751b578cc17336196f8501bed0 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 02:08:29 -0500 Subject: [PATCH 134/282] adds verb `sections` --- lib/config.js | 2 +- lib/runner/helpers.js | 43 ++++++ lib/runner/middleware.js | 12 +- package.json | 55 +++++--- readme.md | 261 +++-------------------------------- templates/includes.js | 7 + templates/layouts/default.md | 23 +-- 7 files changed, 115 insertions(+), 288 deletions(-) diff --git a/lib/config.js b/lib/config.js index c48645dd..7bd20650 100644 --- a/lib/config.js +++ b/lib/config.js @@ -84,7 +84,7 @@ module.exports = function(verb, base, env) { for (var key in val.sections) { if (val.sections.hasOwnProperty(key)) { var section = val.sections[key]; - throw new Error('sections are not setup yet. implement me!'); + verb.option('section.' + key, section); } } } diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index e35ea710..65aa51b5 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -24,6 +24,49 @@ module.exports = function(verb, base, env) { return fs.existsSync(fp); }); + verb.asyncHelper('section', function(title, name, locals, cb) { + if (typeof locals === 'function') { + cb = locals; + locals = {}; + } + + var opts = utils.merge({}, verb.options, this.context.view.options); + var view = this.app.includes.getView(name); + + if (!view) { + cb(new Error('section helper cannot find include: ' + name)); + return; + } + + var section = opts.section[name]; + if (section === false) { + cb(null, ''); + return; + } + + var ctx = {}; + ctx = utils.merge({}, ctx, this.context.view.data); + ctx = utils.merge({}, ctx, this.context); + ctx = utils.merge({}, ctx, view.locals, view.data); + ctx = utils.merge({}, ctx, locals); + + if (utils.isObject(section)) { + ctx = utils.merge({}, ctx, section); + } + + view.render(ctx, function(err, res) { + if (err) return cb(err); + var str = ''; + if (title.charAt(0) !== '#') { + str = '## ' + title + '\n'; + } else { + str = title + '\n'; + } + str += res.content; + cb(null, str); + }); + }); + verb.helper('log', function(msg) { console.log.apply(console, arguments); }); diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 08393dd7..b29f6dbc 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -11,6 +11,7 @@ module.exports = function(app, base, env) { */ app.onLoad(/\.verb\.md/, function(view, next) { + view.options.section = view.options.section || {}; view.path = view.dest = 'readme.md'; next(); }); @@ -27,9 +28,14 @@ module.exports = function(app, base, env) { app.onLoad(/\.md/, middleware.reflinks(app)); app.onLoad(/\.md/, middleware.examples(app)); - // /** - // * Merge options onto `view.data` - // */ + /** + * Merge options onto `view.data` + */ + + app.preLayout(/\.md$/, function(view, next) { + view.data.options = utils.merge({}, app.options, view.data.options, view.options); + next(); + }); app.preRender(/./, function(view, next) { view.data.options = utils.merge({}, view.data.options, view.options); diff --git a/package.json b/package.json index 73cfdca9..4319ad8a 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ }, "license": "MIT", "files": [ - "lib/", "cli.js", - "verbfile.js", - "index.js" + "index.js", + "lib/", + "verbfile.js" ], "main": "index.js", "engines": { @@ -30,28 +30,33 @@ "ansi-colors": "^0.1.0", "arr-flatten": "^1.0.1", "array-unique": "^0.2.1", - "assemble-core": "^0.6.0", + "assemble-core": "^0.8.0", "assemble-loader": "^0.2.4", "base-argv": "^0.3.0", "base-diff": "^0.1.0", "base-list": "^0.1.4", "base-pipeline": "^0.1.4", - "base-questions": "^0.2.1", - "base-runner": "^0.4.3", - "base-store": "^0.3.0", + "base-questions": "^0.2.2", + "base-runner": "^0.4.4", + "base-store": "^0.3.1", "common-middleware": "^0.2.1", "composer-runtimes": "^0.7.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", + "engine-handlebars": "^0.8.0", "expand": "^0.4.0", + "export-files": "^2.1.0", "extend-shallow": "^2.0.1", - "falsey": "^0.2.1", + "falsey": "^0.3.0", "get-value": "^2.0.2", + "gfm-code-blocks": "^0.3.0", "global-modules": "^0.2.0", "has-glob": "^0.1.1", + "has-value": "^0.3.0", "helper-apidocs": "^0.5.0", "helper-copyright": "^2.0.0", "helper-date": "^0.2.2", + "helper-issue": "^0.2.0", "helper-reflinks": "^3.0.1", "helper-related": "^0.12.1", "is-primitive": "^2.0.0", @@ -60,6 +65,7 @@ "kind-of": "^3.0.2", "lazy-cache": "^1.0.2", "map-config": "^0.3.0", + "markdown-toc": "^0.12.2", "matched": "^0.3.2", "minimist": "^1.2.0", "mixin-deep": "^1.1.3", @@ -71,9 +77,10 @@ "set-value": "^0.3.2", "stream-exhaust": "^1.0.1", "success-symbol": "^0.1.0", - "tableize": "^0.1.0", - "template-toc": "^0.5.0", - "time-stamp": "^0.1.3" + "template-toc": "^0.6.2", + "time-stamp": "^0.1.3", + "unset-value": "^0.1.1", + "word-wrap": "^1.1.0" }, "devDependencies": { "async": "^1.5.0", @@ -83,12 +90,11 @@ "consolidate": "^0.13.1", "coveralls": "^2.11.4", "data-store": "^0.12.0", - "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", "graceful-fs": "^4.1.2", "gulp": "^3.9.0", "gulp-eslint": "^1.1.1", - "gulp-format-md": "^0.1.0", + "gulp-format-md": "^0.1.4", "gulp-istanbul": "^0.10.3", "gulp-mocha": "^2.2.0", "is-buffer": "^1.1.0", @@ -123,26 +129,31 @@ "verb": { "related": { "list": [ - "base-methods", + "assemble", "assemble-core", - "resolve-modules", - "base-runner", + "base-methods", "base-resolver", - "assemble" + "base-runner", + "resolve-modules" ] }, - "layout": "full", + "layout": { + "name": "default", + "sections": { + "install-npm": false + } + }, "plugins": [ "gulp-format-md" ], "reflinks": [ - "verb", "assemble", - "base-methods", "assemble-core", + "base-methods", "gulp", - "vinyl", - "scaffold" + "scaffold", + "verb", + "vinyl" ] } } diff --git a/readme.md b/readme.md index 6204ea98..031b8aff 100644 --- a/readme.md +++ b/readme.md @@ -9,32 +9,17 @@ + [help](#help) + [init](#init) + [diff](#diff) - * [Run apps](#run-apps) * [Run tasks](#run-tasks) - * [Run sub-apps](#run-sub-apps) - * [Run a app's tasks](#run-a-app-s-tasks) - * [Run a sub-app's tasks](#run-a-sub-app-s-tasks) - [API](#api) - * [.getConfig](#getconfig) - * [.getTask](#gettask) - * [.addApp](#addapp) - * [.hasApp](#hasapp) - * [.getApp](#getapp) - * [.extendApp](#extendapp) - * [.invoke](#invoke) -- [Authoring apps](#authoring-apps) - * [App naming conventions](#app-naming-conventions) +- [Apps](#apps) - [TODO](#todo) +- [License](#license) -_(TOC generated by [verb](https://github.com/verbose/verb))_ +_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ ## Install -To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. - -**Install verb** - -## Install +To get started, you'll first need to install `verb` globally using [npm][]: Install globally with [npm](https://www.npmjs.com/) @@ -42,22 +27,6 @@ Install globally with [npm](https://www.npmjs.com/) $ npm i -g verb ``` -**Install a "verb app"** - -If you aren't familiar with verb, just take the `node` app for a test drive: - -```sh -$ npm i -g verb-node -``` - -**Run a "verb app"** - -If everything installed correctly, you should now be able to verb a new project with the following command (make sure you run the command from an empty directory!): - -```sh -$ verb node -``` - *** ## Usage @@ -108,20 +77,6 @@ $ verb --diff _(gray is unchanged, red is deleted, green is new)_ -### Run apps - -```sh -$ verb [options] -``` - -**Example** - -Run app `abc` - -```sh -$ verb abc -``` - ### Run tasks To run a task on the `base` app, just pass the name of the task to run. @@ -140,63 +95,9 @@ Run task `bar`: $ verb bar ``` -### Run sub-apps - -> Sub-apps are normal apps that are called from (or registered by) other apps. - -Dot-notation is used for getting and runing sub-apps. - -```sh -$ verb . [options] -``` - -**Examples** - -Run sub-app `b` on app `a`: - -```sh -$ verb a.b [options] -``` - -Run sub-app `c`: - -```sh -$ verb a.b.c [options] -``` - -And so on... - -### Run a app's tasks - -```sh -$ verb : [options] -``` - -**Example** - -Run task `bar` on app `foo`. - -```sh -$ verb foo:bar -``` - -### Run a sub-app's tasks - -```sh -$ verb .: [options] -``` - -**Example** - -Run task `foo` on sub.app `a.b.c`. - -```sh -$ verb a.b.c:foo -``` - ## API -### [create](index.js#L25) +### [create](index.js#L26) Create a customized `Verb` constructor that calls the given `fn` when an instance is created. @@ -214,7 +115,7 @@ var Verb = create(function(verb) { var verb = new Verb(); ``` -### [Verb](index.js#L38) +### [Verb](index.js#L39) Create an instance of `Verb` with the given `options` @@ -229,7 +130,7 @@ var Verb = require('verb'); var verb = new Verb(options); ``` -### [.process](index.js#L97) +### [.process](index.js#L102) Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options`. This allows plugin pipelines to be programmatically built-up and dynamically changed on-the-fly. @@ -238,7 +139,7 @@ Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options * `files` **{Object}** * `options` **{Object}** * `cb` **{Function}** -* `returns` **{Stream}**: Returns a [vinyl](http://github.com/gulpjs/vinyl) src stream +* `returns` **{Stream}**: Returns a [vinyl][] src stream **Example** @@ -246,7 +147,7 @@ Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options verb.process({src: ['a.txt', 'b.txt']}, options); ``` -### [.each](index.js#L121) +### [.each](index.js#L126) Verb `files` configurations in parallel. @@ -263,7 +164,7 @@ verb.each(files, function(err) { }); ``` -### [.eachSeries](index.js#L143) +### [.eachSeries](index.js#L148) Verb `files` configurations in series. @@ -280,9 +181,9 @@ verb.eachSeries(files, function(err) { }); ``` -### [.scaffold](index.js#L175) +### [.scaffold](index.js#L180) -Verb files from a declarative [scaffold](https://github.com/jonschlinkert/scaffold) configuration. +Verb files from a declarative [scaffold][] configuration. **Params** @@ -308,146 +209,20 @@ verb.scaffold(scaffold, function(err) { }); ``` -### .getConfig - -Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. - -Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. - -**Params** - -* `filename` **{String}**: Then name of the config file to lookup. -* `returns` **{Object}**: Returns the "base" instance. - -**Example** - -```js -var verb = Verb.getConfig('verbfile.js'); -``` - -### .getTask - -Get task `name` from the `verb.tasks` object. - -**Params** - -* `name` **{String}** -* `returns` **{Object}** - -**Example** - -```js -verb.getTask('abc'); - -// get a task from app `foo` -verb.getTask('foo:abc'); - -// get a task from sub-app `foo.bar` -verb.getTask('foo.bar:abc'); -``` - -### .addApp - -Alias for `register`. Adds a `app` with the given `name` to the `verb.apps` object. - -**Params** - -* `name` **{String}**: The name of the config object to register -* `config` **{Object|Function}**: The config object or function - -**Example** - -```js -base.addApp('foo', function(app, base, env) { - // `app` is a `Verb` instance created for the app - // `base` is a "shared" instance that provides access to all loaded apps - // `env` is a configuration/environment object with details about the app, - // user cwd, etc -}); -``` - -### .hasApp - -Return true if app `name` is registered. Dot-notation may be used to check for [sub-apps](#sub-apps). - -**Params** - -* `name` **{String}** -* `returns` **{Boolean}** - -**Example** - -```js -base.hasApp('foo.bar.baz'); -``` - -### .getApp - -Return app `name` is registered. Dot-notation may be used to get [sub-apps](#sub-apps). - -**Params** - -* `name` **{String}** -* `returns` **{Boolean}** - -**Example** - -```js -base.getApp('foo'); -// or -base.getApp('foo.bar.baz'); -``` - -### .extendApp - -Extend an app. - -**Params** - -* `app` **{Object}** -* `returns` **{Object}**: Returns the instance for chaining. - -**Example** - -```js -var foo = base.getApp('foo'); -foo.extendApp(app); -``` - -### .invoke - -Invoke app `fn` with the given `base` instance. - -**Params** - -* `fn` **{Function}**: The app function. -* `app` **{Object}**: The "base" instance to use with the app. -* `returns` **{Object}** - -**Example** - -```js -verb.invoke(app.fn, app); -``` - -## Authoring apps +## Apps _(TODO)_ -### App naming conventions - -Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. - ## TODO * [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! * [x] support sub-apps (to any level of nesting) -* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) +* [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] * [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI * [x] support _instance plugins_ that allow you to easily add functionality and features to verb * [x] support any template engine * [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) +* [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) * [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) * [x] 820+ unit tests * [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) @@ -460,7 +235,7 @@ Use `verb-` as the prefix, followed by any words of your choosing to describe th * [assemble](https://www.npmjs.com/package/assemble): Static site generator for Grunt.js, Yeoman and Node.js. Used by Zurb Foundation, Zurb Ink, H5BP/Effeckt,… [more](https://www.npmjs.com/package/assemble) | [homepage](http://assemble.io) * [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) -* [base-methods](https://www.npmjs.com/package/base-methods): Starter for creating a node.js application with a handful of common methods, like `set`, `get`,… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) +* [base-methods](https://www.npmjs.com/package/base-methods): base-methods is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) * [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) * [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) * [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) @@ -486,9 +261,9 @@ Pull requests and stars are always welcome. For bugs and feature requests, [plea ## License -Copyright © 2015 [Jon Schlinkert](https://github.com/jonschlinkert) +Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert) Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on December 14, 2015._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on January 03, 2016._ \ No newline at end of file diff --git a/templates/includes.js b/templates/includes.js index 54c82ab3..da25f8e5 100644 --- a/templates/includes.js +++ b/templates/includes.js @@ -2,6 +2,13 @@ module.exports = { + 'related-list': [ + '{% if (verb.related && verb.related.list && verb.related.list.length) { %}', + '{%= verb.related.description || "" %} ', + '{%= related(verb.related.list) %} ', + '{% } %}', + ].join('\n'), + 'install-npm.md': [ 'Install with [npm](https://www.npmjs.com/):', '', diff --git a/templates/layouts/default.md b/templates/layouts/default.md index 1705af3c..f4074a2a 100644 --- a/templates/layouts/default.md +++ b/templates/layouts/default.md @@ -9,27 +9,12 @@ verb_docs: -{% if (verb.sections.install !== false) { %} -## Install -{%= include('install-npm', {save: true}) %} -{% } %} - {% body %} -{% if (verb.related && verb.related.list && verb.related.list.length) { %} -## Related projects -{%= verb.related.description || '' %} -{%= related(verb.related.list) %} -{% } %} - -## Running tests -{%= include("tests") %} - -## Contributing -{%= include("contributing") %} - -## Author -{%= include("author") %} +{%= section("Related projects", "related-list") %} +{%= section("Running tests", "tests") %} +{%= section("Contributing", "contributing") %} +{%= section("Author", "author") %} ## License {%= copyright({linkify: true}) %} From acded2ac4809f4913b70be47464a759c1a5a599e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 02:08:37 -0500 Subject: [PATCH 135/282] docs --- docs/recipes/3-ways-to-disable-toc.md | 48 ++++++++ docs/recipes/auto-load-middleware.md | 47 ++++++++ docs/recipes/inspecting-the-context.md | 63 ++++++++++ docs/recipes/merging-context.md | 60 ++++++++++ docs/recipes/render-a-list.md | 152 +++++++++++++++++++++++++ docs/src/{ => content}/argv.md | 0 docs/src/content/built-in-templates.md | 18 +++ docs/src/content/cli.md | 82 +++++++++++++ docs/src/content/collections.md | 83 ++++++++++++++ docs/src/content/context.md | 15 +++ docs/src/content/features.md | 26 +++++ docs/src/content/fs.md | 10 ++ docs/src/content/middleware.md | 4 + docs/src/content/naming-conventions.md | 13 +++ docs/src/content/plugins.md | 55 ++++----- docs/src/content/settings.md | 80 +++++++++++++ docs/src/content/tasks.md | 1 + docs/src/content/templates.md | 2 + docs/src/content/terminology.md | 14 +++ docs/src/content/verb.config.md | 22 ++++ docs/src/content/verb.options.md | 65 +++++++++++ docs/src/readme/layouts/basic.md | 31 ----- docs/src/readme/layouts/default.md | 50 -------- docs/src/readme/layouts/min.md | 21 ---- docs/src/readme/min.md | 14 --- docs/src/readme/verb.md | 0 26 files changed, 824 insertions(+), 152 deletions(-) create mode 100644 docs/recipes/3-ways-to-disable-toc.md create mode 100644 docs/recipes/auto-load-middleware.md create mode 100644 docs/recipes/inspecting-the-context.md create mode 100644 docs/recipes/merging-context.md create mode 100644 docs/recipes/render-a-list.md rename docs/src/{ => content}/argv.md (100%) create mode 100644 docs/src/content/built-in-templates.md create mode 100644 docs/src/content/cli.md create mode 100644 docs/src/content/collections.md create mode 100644 docs/src/content/context.md create mode 100644 docs/src/content/features.md create mode 100644 docs/src/content/fs.md create mode 100644 docs/src/content/naming-conventions.md create mode 100644 docs/src/content/settings.md create mode 100644 docs/src/content/tasks.md create mode 100644 docs/src/content/templates.md create mode 100644 docs/src/content/terminology.md create mode 100644 docs/src/content/verb.config.md create mode 100644 docs/src/content/verb.options.md delete mode 100644 docs/src/readme/layouts/basic.md delete mode 100644 docs/src/readme/layouts/default.md delete mode 100644 docs/src/readme/layouts/min.md delete mode 100644 docs/src/readme/min.md delete mode 100644 docs/src/readme/verb.md diff --git a/docs/recipes/3-ways-to-disable-toc.md b/docs/recipes/3-ways-to-disable-toc.md new file mode 100644 index 00000000..c6310868 --- /dev/null +++ b/docs/recipes/3-ways-to-disable-toc.md @@ -0,0 +1,48 @@ +# Disable Verb's built-in TOC + +A couple of Verb's built-in layouts include a `` tag, which is used as a placeholder to drop the automatically generated Table of Contents into your document. + +Here are three ways to disable the TOC: + +**1. Front-matter** + +Add a section of YAML front-matter to your document with a `toc` property. + +```markdown +--- +toc: false +--- + +## Usage + +Some really useful information, that you can use - because it's so useful. + +``` + +Verb uses [falsey][] to check values for just a few different properties, including `toc`. So any value that the falsey lib evaluates as `falsey` will disable the `toc`. + +**2: {{name}} config** + +Disable the TOC by defining it on an `options` property in your [local verb config](./settings.md): + +```json +{ + "name": "my-project", + "verb": { + "layout": "default", + "options": { + "toc": false + } + } +} +``` + +**3: {{name}}.option()** + +Use the `option` API to disable the TOC: + +```js +{{name}}.option('toc', false); +``` + +[falsey]: https://github.com/jonschlinkert/falsey \ No newline at end of file diff --git a/docs/recipes/auto-load-middleware.md b/docs/recipes/auto-load-middleware.md new file mode 100644 index 00000000..305048d6 --- /dev/null +++ b/docs/recipes/auto-load-middleware.md @@ -0,0 +1,47 @@ +# Load middleware from Verb config + +The `verb` property in package.json can be used to define configuration options for verb. This recipe shows how to define a middleware and related options. + +## Middleware method + +On the `verb` object in `package.json`, add a property where: + +- the key is the name of the middleware method to run +- the value is either an options object, or an array of "options" objects (if multiple middleware are to be run) + +**Example** + +This would tell verb to add an `onLoad` middleware (but would fail since we haven't defined options yet): + +```json +{ + "name": "foo", + + "verb": { + "onLoad": {} + } +} +``` + +## Middleware options + +Now we need to tell verb a few things about the middleware: + +- `path`: Where is the middleware? Whether it's a module dependency in `node_modules` or a local file, define this value the way you would define a require statement. +- `pattern`: the regex pattern to use for matching `view.path` +- `alias`: optionally specify an alias to use if, for example, you want to pass options to the middleware by the alias etc. + +```json +{ + "name": "foo", + + "verb": { + "onLoad": [ + { + "path": "parser-front-matter", + "pattern": "readme\\.md$" + } + ] + } +} +``` \ No newline at end of file diff --git a/docs/recipes/inspecting-the-context.md b/docs/recipes/inspecting-the-context.md new file mode 100644 index 00000000..f2975c04 --- /dev/null +++ b/docs/recipes/inspecting-the-context.md @@ -0,0 +1,63 @@ +# Inspecting the context + +Generally, "inspecting the context" means that we're looking at the object that will be used for rendering a template or templates. + +**Where can we see the context?** + +The context object is created in-memory at render time, and to inspect it we need to see the object _as a template is being rendered_ (not before or after), which means there is really only one place to do it: inside a helper function. + +**Create a helper** + +A simple `log` helper can be used to show any object we pass to it in the console. Add the following to your `{{configfile}}.js`: + +```js +app.helper('log', function(context) { + console.log(context); +}); +``` +Next, add the following to the template you want to inspect: + +```handlebars +{{log .}} +``` +Handlebars uses `.` as an alias for `this`. You can replace the `.` with any variable you want to inspect. + +**Other objects** + +Inside the helper, there are a number of different objects we can inspect, depending on how we're building up the context. Here is a quick overview: + +```js +app.helper('log', function(context) { + console.log(arguments); + console.log(context); // the object passed to the helper + console.log(context.hash); // hash arguments, like `foo="bar"` + + console.log(this); // handlebars context + console.log(this.options); // assemble `options` + console.log(this.context); // context of the current "view" + console.log(this.app); // assemble instance +}); +``` + +## Debugging + +**Detective work** + +Sometimes it takes a little more work to figure out what's happening. In addition to inspecting the context at render time, we can get a better perspective on what's happening if we also: + +- **inspect pre-render data**: inspect the objects that will be used to create the context **before** passing the object to the `render` method +- **diff content**: diff the generated content **after** the view is rendered (assuming the view makes it this far and is actually being rendered) +- **inspect post-render data**: inspect the `view.data` object post-render to see what, if anything, has been changed + +```js +// before +// - inspect `locals` +// - inspect `view.data` +// - inspect `app.cache.data` +app.render(view, locals, function(err, res) { + + // after + // - inspect `res.data` + console.log(res.data); +}); +``` \ No newline at end of file diff --git a/docs/recipes/merging-context.md b/docs/recipes/merging-context.md new file mode 100644 index 00000000..87c43f55 --- /dev/null +++ b/docs/recipes/merging-context.md @@ -0,0 +1,60 @@ +# Merging context + +> This recipe shows different strategies for merging the context object before rendering, based on which data should be given preferential treatment. + +Learn more about [context](./terminology#context) + +## Middleware + +If you need control over how context is merged on a file-by-file basis, one solution is to create a custom `preRender` middleware. + +**Example** + +Let's say you have a view (any template. can be a partial, page, layout, etc.) with the following contents: + +```hbs +--- +title: Foo +--- + +This is {{title}} +``` + +**Pre-render middleware** + +Try the following + +```js +var merge = require('mixin-deep'); +var app = assemble(); + +// merge data onto `app.cache.data` +app.data({title: 'Site'}); + +// pre-render middleware, called right before the engine renders +app.preRender(/\.hbs$/, function(view, next) { + console.log(view.data); + //=> { title: 'Foo' } + console.log(app.cache.data); + //=> { title: 'Site' } + + file.data = merge({}, app.cache.data, file.data); + next(); +}); +``` + +To create a re-usable middleware: + +```js +function mergeContext(app, locals) { + return function(view, next) { + file.data = merge({}, app.cache.data, file.data, locals); + next(); + }; +} + +// usage +app.preRender(/\.hbs$/, mergeContext(app, {title: 'Foo'})); +app.preRender(/\.txt$/, mergeContext(app, {title: 'Bar'})); +app.preRender(/\.ejs$/, mergeContext(app, {title: 'Baz'})); +``` \ No newline at end of file diff --git a/docs/recipes/render-a-list.md b/docs/recipes/render-a-list.md new file mode 100644 index 00000000..401f45e2 --- /dev/null +++ b/docs/recipes/render-a-list.md @@ -0,0 +1,152 @@ +# Rendering a list + +> This recipe shows how to render a list from a view collection + +## Preparation + +First, create an `assemblefile.js`. The rest of this recipe assumes that you have the following defined: + +```js +var assemble = require('assemble'); +var app = assemble(); +``` + +**Add a "log" helper** + +Next, let's add a `log` helper that we can use for inspecting the context and debugging: + +```js +app.helper('log', function(context) { + console.log(context); +}); +``` + +_(Learn more about [inspecting the context](./inspecting-the-context.md))_ + +## Create a collection + +We need a collection to use for our list, so let's create a `pages` collection: + +```js +app.create('pages'); +``` + +### Add pages to the collection + +```js +app.page('foo', 'this is foo'); +app.page('bar', 'this is bar'); +app.page('baz', 'this is baz'); +``` + +## Build the context + +Before we can render a list, we need to expose the + +```js +/** + * Middleware + * + * Add the `pages` collection to `view.data`, + * which exposes it to the context for rendering + */ + +app.preRender(/./, function(view, next) { + view.data.pages = app.views.pages; + next(); +}); + +/** + * Task for rendering "site" + */ + +app.task('site', function() { + app.pages('src/pages/**/*.hbs'); + app.partials('src/partials/*.hbs'); + app.layouts('src/layouts/*.hbs'); + + // use the `toStream` method instead of `src` + // so that all pages are available at render time + return app.toStream('pages') + .pipe(app.renderFile()) + .pipe(extname()) + .pipe(app.dest('_build')); +}); +``` + +Layout: `default.hbs` + +```handlebars + + + + + {{ title }} + + + {% body %} + + {{> list }} + + +``` + +Partial: `list.hbs` (or you could add this inline in the layout) + + +```handlebars +{{#each pages}} +{{log .}} +{{@key}} +{{/each}} +``` + +## Inspecting the context + + +Since views are vinyl files, you'll need to inspect them to see what's available to use. To make this easier, you might also try adding a helper to see what's on the context: + +**Example** + +Create a helper, arbitrarily named `ctx` (for context) or whatever you want, and add it to your `assemblefile.js`: + +```js +app.helper('ctx', function(context) { + console.log(arguments); + console.log(context); // the object passed to the helper + + console.log(this); // handlebars context + console.log(this.options); // assemble `options` + console.log(this.context); // context of the current "view" + console.log(this.app); // assemble instance +}); +``` + +Then use the `ctx` helper inside the `{{#each}}` loop: + +```handlebars +{{#each pages}} + {{ctx .}} +{{/each}} +``` + +And try it outside the loop: + +```handlebars +{{ctx .}} +``` + + +## FAQ + +**What does "render a list" mean?** + +When a template is rendered, placeholder variables are replaced with actual values from the "context" object. Sometimes it's necessary to render a list of something, like pages, posts, related links, and so on. To do so, the _context_ must contain an object (or array) representation of the list we wish to render. + +**Why isn't my list rendering?** + +A common mistake is trying to render a list of _something_ before all items in that list have actually been loaded onto the context. + +For example, if you're using `app.src()` (or `gulp.src()`) to read in a glob of "pages" (or posts, etc.), and you try to generate a list of pages, you might be wondering why the list isn't rendering. This is because, when using `.src()`, files are rendered one-by-one, so the entire context (of all pages) is not yet available until the last page has already been rendered. + +The good news is that this is easily solved by building up the list in the flush function of a plugin, or by _not using `app.src()` to load pages_. Instead, we can use the `.toStream()` method. The latter is what this recipe shows. diff --git a/docs/src/argv.md b/docs/src/content/argv.md similarity index 100% rename from docs/src/argv.md rename to docs/src/content/argv.md diff --git a/docs/src/content/built-in-templates.md b/docs/src/content/built-in-templates.md new file mode 100644 index 00000000..7d97258d --- /dev/null +++ b/docs/src/content/built-in-templates.md @@ -0,0 +1,18 @@ +--- +title: Built-in templates +--- + + + +Verb ships with a small collection of built-in templates. Each was chosen based on usage patterns and experience with hundreds of projects that use verb. + +## .verb.md + + +## layouts + + +## includes + + +Want something different? We love to [hear your feedback]({%= bugs.url %})! \ No newline at end of file diff --git a/docs/src/content/cli.md b/docs/src/content/cli.md new file mode 100644 index 00000000..e0ff52c8 --- /dev/null +++ b/docs/src/content/cli.md @@ -0,0 +1,82 @@ +--- +title: <%= upper(name) %> CLI +description: "Using the <%= name %> command line." +related: ["terminology", "tasks", "features"] +tags: ['config', 'cli', 'command line', 'options', 'flags'] +--- + +**FAQ** + +- object paths (dot notation) may be used for most command line arguments +- In cases where dots should not be expanded to an object, you may escape the dot with a single backslash + + +## Summary + +``` +todo: generate/display CLI help here +``` + +## option + + +Set options to be used in plugins and middleware. + +```sh +$ verb --option=: +# or +$ verb --option= +``` + +- `key` The property to set +- `value`: the value of `key`. If no value is given, the value will be set to `true` + + +**Example** + +Omit the Table of Contents when generating a document from a built-in verb template: + +```sh +$ verb --option=toc:false +``` + +## config + +Persist project-specific `config` values to the `verb` object in `package.json`. These values will override config settings defined anywhere else (accept for those passed via command line): + +```sh +$ verb --config= +``` + +### config examples + +**Set the layout to use** + +Use a verb [built-in layout](./built-in-templates.md#layouts) with `.verb.md` every time it's rendered: + +```sh +$ verb --config=layout:default +``` + +**Disable Table of Contents** + +Disable the Table of Contents for all templates: + +```sh +$ verb --config=option.toc:false +``` + +Disable the Table of Contents for a specific template: + +```sh +$ verb --config=disable.toc:"foo\.md" +# remember to escape dots in filepaths! +``` + +Disable the Table of Contents for multiple templates: + +_(TODO: implement me)_ + +```sh +$ verb --config=disable.toc:"foo\.md,bar\.md,baz\.md" +``` diff --git a/docs/src/content/collections.md b/docs/src/content/collections.md new file mode 100644 index 00000000..8e89927b --- /dev/null +++ b/docs/src/content/collections.md @@ -0,0 +1,83 @@ +# Collections + +> Collections are first-class citizens in Assemble, with three different collection types to choose from. This document describes each collection type and provides the information you need to start using them. + +- `create` +- `collection` +- `list` + +## View collections + +**What is a "view collection"?** + +A view collection has all of the features of a "generic" {{appname}} collection, along with special features and methods that are specific to managing views (templates). + +**Special features** + +View collections have methods getting, setting and finding views, as well as assigning "view types" to individual views (Learn more about [view types](./view-types.md)). + +**Methods** + +- `setView` +- `getView` +- `viewType` +- `isType` + +## Create + +The `create` method is used for adding custom "view collections" to {{appname}}. A few things happen when the method is used: + +- + +- This exposed `page` and `pages` methods on {{appname}} ({{appname}} automatically detects inflections - plural and singular forms) +- A `pages` + + +## Collections types + +Assemble 0.6.0 supports **3 different collection types**: + +| **Collection type** | **unit** | **storage object** | **description** | +| --- | --- | --- | ---| +| **collections** | `item` | `items` | generic collections, with methods for setting and getting `items` | +| **view collections** | `view` | `views` | Methods for working with template collections, like pages, posts, layouts, partials, etc. | +| **lists** | `item` | `items` | Stored as an array, has methods for getting, setting, [sorting](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L359), [grouping](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L333). etc | + +_(sidenote: views and items are also [vinyl](https://github.com/gulpjs/vinyl) files)_ + +Since "generic" collections are mostly used internally, from here forward "collection" will usually refer to "view collection" unless noted otherwise. + +## View collections + +View collections have methods for adding, finding and getting views, such as: + +- `.getView` +- `.setView` + +Views (templates) are stored on the `views` object of a collection, allowing views to be looked up by key. + +Keys can be customized and renamed using a `renameKey` function passed on the options of the collection, or to rename all keys in all collections, you may pass a `renameKey` function on the {{appname}} options. + +Ultimately this gives you full control over how views are named and how lookups are done. + +## Lists + +Lists are similar to collections but instead of storing an object of views, `items` are stored as an array. + + +**Nice to know** + +- You can create [lists from collections][lists-from-collections] +- You can create [collections from lists][collections-from-lists] +- Lists are useful for doing things like: + * [pagination][], + * [sorting](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L359), and + * [grouping](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L333) + +_(the above links go to [templates](https://github.com/jonschlinkert/templates), which is the underlying library that provides {{appname}} with methods for managing views, collections, rendering, engines, and so on. This lib could be used to create your own static site generator if you need something different)_ + +Last, keep in mind that {{appname}} is highly pluggable, so you can extend it to do whatever you need (plugins can be used on {{appname}} itself, a collection, or even a specific view). Let us know if you want to do something that you think ought to be in {{appname}} itself and we can discuss ways to implement it. + +[lists-from-collections]: https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L77-L83 +[collections-from-lists]: https://github.com/jonschlinkert/templates/blob/master/lib/views.js#L75-L81 +[pagination]: https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L393 diff --git a/docs/src/content/context.md b/docs/src/content/context.md new file mode 100644 index 00000000..40bcaa52 --- /dev/null +++ b/docs/src/content/context.md @@ -0,0 +1,15 @@ +# Context + +> When templates are rendered, "context" is the data object used by the rendering engine to resolve variables in template strings to their actual values. + +Typically, the _context object_ is created dynamically at render time by merging multiple source objects together (before being passed to the rendering engine). Thus, it only exists in memory. + +For example, data from [front-matter][] is often merged with `locals` to create the context object. + +**How context is created** + +When a template is rendered a context object is created and passed to the rendering engine for that template. This object is created any or all of the following data sources, depending on options or customizations: + +- **dynamic data**: data that is dynamically generated at runtime or only exists at certain points during runtime, like pagination info, destination file paths or URLs, permalinks, navigation variables, an so on. +- **locals**: an object that is passed to the `render` method +- **globals**: an object that is intended to be universally made available to all templates, and is created from some "globally" stored data object. {{upper name}} Stores global data on `{{name}}.cache.data`. See the [data][] docs for more details. \ No newline at end of file diff --git a/docs/src/content/features.md b/docs/src/content/features.md new file mode 100644 index 00000000..04cd85a6 --- /dev/null +++ b/docs/src/content/features.md @@ -0,0 +1,26 @@ +# Features + +## Fast + +Assemble is fast. Depending on setup, plugins, customizations and other factors, a small site with 10-15 pages can be generated in ~300 ms. + +## Flexible + +Assemble gives you complete control over how and where you build files. If you've used [gulp][], you'll already be familiar with Assembles file-sytem API. + +## Template collections + +Assemble has rich support for template collections +- assemble supports template "types": `renderable`, `layout` and `partial`. Defined when a collection is created, types add special behavior and decorate methods related to each type onto the collection and its views (e.g. `partials` will be passed to the rendering engine _as partials_, helpers are created for getting partials, etc). Collections can use one or more type. +- assemble supports instance plugins (like metalsmith), so you can add do anything you want to an instance +- assemble collections can use plugins +- assemble views (templates) can use plugins (assemble views are vinyl files) +- assemble supports pipeline plugins (like gulp), so you can do in-stream transformations on files (views) + +## Template engines + +- assemble allows you to use any template engine, and allows you to use more than one template engine during the same build. + +## Middleware and routes + +- assemble supports middleware, similar to express. diff --git a/docs/src/content/fs.md b/docs/src/content/fs.md new file mode 100644 index 00000000..d3b36007 --- /dev/null +++ b/docs/src/content/fs.md @@ -0,0 +1,10 @@ +# File system + +_(TODO)_ + +**Example** + +```js +app.src('*.hbs') + .pipe(app.dest('site/')) +``` \ No newline at end of file diff --git a/docs/src/content/middleware.md b/docs/src/content/middleware.md index b7160297..2878369c 100644 --- a/docs/src/content/middleware.md +++ b/docs/src/content/middleware.md @@ -7,6 +7,10 @@ related: ['en-route'] reflinks: ['en-route'] --- +While plugins and middleware are both used to "extend" {{name}}, they serve very different purposes, are used in completely different ways, and have access to different objects at runtime. + +## What is middleware? + (Some verbiage was borrowed from [express's middleware docs][express]) Middleware functions are functions that have access to the `file` object (or in {{name}}'s case, the `view` object), and a callback function that represents the `next` middleware in the application’s build cycle. diff --git a/docs/src/content/naming-conventions.md b/docs/src/content/naming-conventions.md new file mode 100644 index 00000000..ea4c909b --- /dev/null +++ b/docs/src/content/naming-conventions.md @@ -0,0 +1,13 @@ +# Naming conventions + + +- `name` +- `configfile`: `assemblefile`, `verfile`, `generator` +- `appname` +- `varname` +- `method` +- `ctor` + +- `singular` +- `plural` + diff --git a/docs/src/content/plugins.md b/docs/src/content/plugins.md index 33dbbc1a..a4530434 100644 --- a/docs/src/content/plugins.md +++ b/docs/src/content/plugins.md @@ -1,32 +1,27 @@ -> How do you differentiate between assemble and metalsmith +--- +name: verb +title: Plugins +engine: hbs +description: "" +related: ['assemble-core'] +reflinks: [] +--- -There is not much similar between the two. The examples show how to build projects using a similar style to metalsmith, but that's about it. +## Plugin types -- assemble has rich support for template collections -- assemble supports template "types": renderable, layout and partial - defined when a collection is created, to add special behavior and methods related to the type; and collections can use one or more type. -- assemble does not limit how and where you build files -- assemble supports instance plugins (like metalsmith), so you can augment an instance -- assemble supports pipeline plugins (like gulp) -- assemble collections can use plugins -- assemble views (templates) can use plugins (assemble views are vinyl files) -- assemble supports any template engine, and allows you to use more than one template engine during the same build. -- assemble supports middleware, similar to express. (plugins and middleware serve very different purposes, and are used in completely different ways. more on this below) - -## Plugins - -- **instance plugins**: Instance plugins are registered with the `.use()` method and are called immediately upon instantiation. The only parameter exposed to an instance plugin is the instance of `app` (assemble), `collection`, or `view`. -- **pipeline plugins**: Pipeline plugins are registered with `.pipe()` and are used on vinyl `file` objects in a stream (note that all assemble "views" are instances of vinyl files) +- **instance plugins**: Instance plugins are registered with the `.use()` method and are called immediately upon instantiation. The only parameter exposed to an instance plugin is the instance of `app` ({{name}}), `collection`, or `view`. +- **pipeline plugins**: Pipeline plugins are registered with `.pipe()` and are used on vinyl `file` objects in a stream (note that all {{name}} "views" are instances of vinyl files) ### Instance plugins **Example instance plugin** ```js -var assemble = require('assemble'); -var app = assemble(); +var {{name}} = require('{{name}}'); +var app = {{name}}(); app.use(function(app) { - // do stuff to app or `this` (the assemble instance) + // do stuff to app or `this`, which is the {{name}} instance }); ``` @@ -37,12 +32,12 @@ Collections themselves are like mini-application instances, and collection plugi **Example collection plugin** ```js -var assemble = require('assemble'); -var app = assemble(); +var {{name}} = require('{{name}}'); +var app = {{name}}(); // register a plugin to be used on all collections app.use(function(app) { - // do stuff to app or `this` (the assemble instance) + // do stuff to app or `this` (the {{name}} instance) // return a function to be use used as a collection plugin return function(collection) { @@ -74,8 +69,8 @@ app.create('posts') **Example view plugin** ```js -var assemble = require('assemble'); -var app = assemble(); +var {{name}} = require('{{name}}'); +var app = {{name}}(); // register a plugin to be used on all views, from all collections app.use(function(app) { @@ -126,15 +121,3 @@ Here are just a few examples - `pagination` - `groups` and `lists` - `sorting` - -## Middleware - -(this description was inspired by express's middleware description) - -Middleware functions are functions that have access to the `file` object (or in assemble's case, the `view` object), and a callback function that represents the `next` middleware in the application’s build cycle. - -Middleware functions can perform the following tasks: - -- Execute any code. -- Make changes to the `file` object. -- Call the `next` middleware function in the stack. \ No newline at end of file diff --git a/docs/src/content/settings.md b/docs/src/content/settings.md new file mode 100644 index 00000000..e6d49c5e --- /dev/null +++ b/docs/src/content/settings.md @@ -0,0 +1,80 @@ +--- +title: Settings +related: ['./recipes/3-ways-to-disable-toc'] +--- + +There are multiple ways to define and manage configuration settings in {{name}}. + +- `cli`: command line arguments +- `options`: in-memory options cache +- `data`: in-memory data cache +- `store`: global config store +- `config`: local {{name}} config, defined on the `{{name}}` property in package.json. +- `locals`: template locals, typically passed to the `render` method +- `helpers`: options can be passed as arguments to helpers +- `answers`: answer store +- `view.data`: front-matter +- `view.options`: + +**Why aren't these merged onto one big object?** + +For a couple of reasons: + +1. These objects aren't all available at the same time. For example, command line options might be used to determine which templates to render. We can use front-matter from templates once they're loaded, but not before. +1. Separation of concerns. This allows users to decide order of preference for conflicting options defined in more than one place. + + +## Local config + +Configuration values may be stored on a project-by-project basis by adding a `verb` object to the project's package.json. + +**Example** + +```json +{ + "name": "my-project", + "verb": { + "layout": "default", + "plugins": ["gulp-format-md"] + } +} +``` + +## Global config store + +**API** + +Persist a global configuration value that may be used on any project: + +```js +{{name}}.store.set('foo', 'bar'); +``` + +Get a global configuration value: + +```js +{{name}}.store.get('foo'); +//=> 'bar' +``` + +**CLI** + +Set a global config value via command line: + +```sh +$ verb --set=foo:bar +``` + +Show the value in the command line: + +```sh +$ verb --get=foo +# 'bar' +``` + +Or get the value programmatically: + +```js +{{name}}.store.get('foo'); +//=> 'bar' +``` \ No newline at end of file diff --git a/docs/src/content/tasks.md b/docs/src/content/tasks.md new file mode 100644 index 00000000..6ec576e5 --- /dev/null +++ b/docs/src/content/tasks.md @@ -0,0 +1 @@ +# Tasks \ No newline at end of file diff --git a/docs/src/content/templates.md b/docs/src/content/templates.md new file mode 100644 index 00000000..686dc811 --- /dev/null +++ b/docs/src/content/templates.md @@ -0,0 +1,2 @@ +# Templates + diff --git a/docs/src/content/terminology.md b/docs/src/content/terminology.md new file mode 100644 index 00000000..08e48d39 --- /dev/null +++ b/docs/src/content/terminology.md @@ -0,0 +1,14 @@ +# Terminology + +This document provides summary descriptions of common terms used in {{name}}. Further information may be found by following the links on some of the terms. + + +## Data + +- data +- `globals` +- `locals`: The data object passed to a rendering engine is commonly referred to as `locals` (as opposed to `globals`) +- [context](/context) +- front-matter: + + diff --git a/docs/src/content/verb.config.md b/docs/src/content/verb.config.md new file mode 100644 index 00000000..94647394 --- /dev/null +++ b/docs/src/content/verb.config.md @@ -0,0 +1,22 @@ +--- +title: <%= upper(name) %> Configuration +--- + +### package.json + +> setting options in package.json + +If your package.json file has a `{{name}}` property with an `options` object, {{upper name}} will use it to extend the `{{name}}.options` object. + +**Example** + +```json +{ + "name": "my-project", + "description": "It's awesome, seriously.", + + "{{name}}": { + "options": {} + } +} +``` diff --git a/docs/src/content/verb.options.md b/docs/src/content/verb.options.md new file mode 100644 index 00000000..1363dd41 --- /dev/null +++ b/docs/src/content/verb.options.md @@ -0,0 +1,65 @@ +--- +name: Verb Options +descriptions: Currently supported options that may defined on the `verb.options` object. +--- + +To become familiarized with how to get and set options with verb, see the [Options API][options-api] documentation. + +### options.toc + +Generate a markdown table of contents using [markdown-toc][]. + +Type: `boolean` | `object` + +Default: `undefined` + +**Object** + +When defined as an object, the following sub-options may be defined: + +* `render` + - If `true`, a toc string will be added to the `view.data.toc` property on any views that have a `` tag defined. + - If `false`, all `` tags will simply be stripped, and no TOC will be created. This allows the tag to be added to a document, and rendering and insertion to be controlled programmatically. +* `insert` + - If `true`, the Table of Contents value stored in `view.data.toc` will be inserted into the document by a built-in `postLayout` middleware. + - If `false`, the middleware will simply ignore the `view.data.toc` value and continue. + +_(Note that since `view.data.toc` is a value on the context, TOCs may also/alternatively be inserted via helper or template variable)_. + +**Boolean** + +When `options.toc` is either true or false, the option is converted to an object before being passed to [template-toc][], and both the `render` and `insert` options will be defined using the given value. + +For example, `{toc: false}` normalizes to `{toc: {insert: false, render: false}}`. + +**Examples** + +As a boolean: + +```js +var verb = require('verb'); +var app = verb(); + +app.option('toc', true); +// or "app.enable('toc');" +``` + +As an object: + +```js +app.option({toc: {insert: false, render: true}}); +``` + +**view.options** + +Note that, as with most options, `toc` options may also be defined on `view.options`: + +```js +var app = verb(); + +app.create('pages'); +app.pages.addView('foo', {content: 'bar', options: {toc: false}}); +``` + +[options-api]: ./options-api.md +[markdown-toc]: https://github.com/jonschlinkert/markdown-toc \ No newline at end of file diff --git a/docs/src/readme/layouts/basic.md b/docs/src/readme/layouts/basic.md deleted file mode 100644 index cd5d5290..00000000 --- a/docs/src/readme/layouts/basic.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Basic layout ---- -# {%= name %} {%= badge('npm') %} {%= badge('travis') %} - -> {%= description %} - -## Install -{%= include('install-npm', {save: true}) %} - -## Usage -{% body %} - -## Running tests -{%= include("tests") %} - -## Contributing -{%= include("contributing") %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} diff --git a/docs/src/readme/layouts/default.md b/docs/src/readme/layouts/default.md deleted file mode 100644 index c46a4d93..00000000 --- a/docs/src/readme/layouts/default.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Default layout ---- -# {%= name %} {%= badge('npm') %} {%= badge('travis') %} - -> {%= description %} - - - -{%= section("install") %} - -{% if (verb.sections.install !== false) { %} -## Install -{%= include('install-npm', {save: true}) %} -{% } %} - -{% body %} - -{% if (verb.related && verb.related.list && verb.related.list.length) { %} -## Related projects -{%= verb.related.description || '' %} -{%= related(verb.related.list) %} -{% } %} - -## Running tests -{%= include("tests") %} - -## Contributing -{%= include("contributing") %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} - -{% if (verb.reflinks) { %} -{% if (Array.isArray(verb.reflinks) && verb.reflinks.length) { %} -{%= reflinks(verb.reflinks) %} -{% } else if (verb.reflinks.list && verb.reflinks.list.length) { %} -{%= reflinks(verb.reflinks.list) %} -{% } %} -{% } %} diff --git a/docs/src/readme/layouts/min.md b/docs/src/readme/layouts/min.md deleted file mode 100644 index 46a559d4..00000000 --- a/docs/src/readme/layouts/min.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Minimal layout ---- -# {%= name %} - -> {%= description %} - -{% body %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} diff --git a/docs/src/readme/min.md b/docs/src/readme/min.md deleted file mode 100644 index a8f75669..00000000 --- a/docs/src/readme/min.md +++ /dev/null @@ -1,14 +0,0 @@ -# {%= name %} - -> {%= description %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} diff --git a/docs/src/readme/verb.md b/docs/src/readme/verb.md deleted file mode 100644 index e69de29b..00000000 From a620f1df74ed6179e48052e0026fd00b5718b13a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 02:08:58 -0500 Subject: [PATCH 136/282] update readme template to use `sections` --- .verb.md | 221 +------------------------------------------------------ 1 file changed, 2 insertions(+), 219 deletions(-) diff --git a/.verb.md b/.verb.md index 48426f6b..82b6609b 100644 --- a/.verb.md +++ b/.verb.md @@ -1,30 +1,9 @@ - - ## Install -To get started, you'll first need to install `verb` globally using [npm][], along with any apps you'd like to run. - -**Install verb** -## Install +To get started, you'll first need to install `verb` globally using [npm][]: {%= include("install-global") %} -**Install a "verb app"** - -If you aren't familiar with verb, just take the `node` app for a test drive: - -```sh -$ npm i -g verb-node -``` - -**Run a "verb app"** - -If everything installed correctly, you should now be able to verb a new project with the following command (make sure you run the command from an empty directory!): - -```sh -$ verb node -``` - *** ## Usage @@ -75,20 +54,6 @@ $ verb --diff _(gray is unchanged, red is deleted, green is new)_ -### Run apps - -```sh -$ verb [options] -``` - -**Example** - -Run app `abc` - -```sh -$ verb abc -``` - ### Run tasks To run a task on the `base` app, just pass the name of the task to run. @@ -107,194 +72,12 @@ Run task `bar`: $ verb bar ``` -### Run sub-apps - -> Sub-apps are normal apps that are called from (or registered by) other apps. - -Dot-notation is used for getting and runing sub-apps. - -```sh -$ verb . [options] -``` - -**Examples** - -Run sub-app `b` on app `a`: - -```sh -$ verb a.b [options] -``` - -Run sub-app `c`: - -```sh -$ verb a.b.c [options] -``` - -And so on... - - -### Run a app's tasks - -```sh -$ verb : [options] -``` - -**Example** - -Run task `bar` on app `foo`. - -```sh -$ verb foo:bar -``` - -### Run a sub-app's tasks - -```sh -$ verb .: [options] -``` - -**Example** - -Run task `foo` on sub.app `a.b.c`. - -```sh -$ verb a.b.c:foo -``` - ## API {%= apidocs("index.js") %} -### .getConfig - -Static method that first tries to get the `verbfile.js` in the root of the current project, then if not found, falls back to the default `verbfile.js` in this project. - -Once resolved, the verbfile will be loaded and used to create the "base" instance of verb. All other instances will be stored on the base instance's `apps` object. - -**Params** - -* `filename` **{String}**: Then name of the config file to lookup. -* `returns` **{Object}**: Returns the "base" instance. - -**Example** - -```js -var verb = Verb.getConfig('verbfile.js'); -``` - -### .getTask - -Get task `name` from the `verb.tasks` object. - -**Params** - -* `name` **{String}** -* `returns` **{Object}** - -**Example** - -```js -verb.getTask('abc'); - -// get a task from app `foo` -verb.getTask('foo:abc'); - -// get a task from sub-app `foo.bar` -verb.getTask('foo.bar:abc'); -``` - -### .addApp - -Alias for `register`. Adds a `app` with the given `name` to the `verb.apps` object. - -**Params** - -* `name` **{String}**: The name of the config object to register -* `config` **{Object|Function}**: The config object or function - -**Example** - -```js -base.addApp('foo', function(app, base, env) { - // `app` is a `Verb` instance created for the app - // `base` is a "shared" instance that provides access to all loaded apps - // `env` is a configuration/environment object with details about the app, - // user cwd, etc -}); -``` - -### .hasApp - -Return true if app `name` is registered. Dot-notation may be used to check for [sub-apps](#sub-apps). - -**Params** - -* `name` **{String}** -* `returns` **{Boolean}** - -**Example** - -```js -base.hasApp('foo.bar.baz'); -``` - -### .getApp - -Return app `name` is registered. Dot-notation may be used to get [sub-apps](#sub-apps). - -**Params** - -* `name` **{String}** -* `returns` **{Boolean}** - -**Example** - -```js -base.getApp('foo'); -// or -base.getApp('foo.bar.baz'); -``` - -### .extendApp - -Extend an app. - -**Params** - -* `app` **{Object}** -* `returns` **{Object}**: Returns the instance for chaining. - -**Example** - -```js -var foo = base.getApp('foo'); -foo.extendApp(app); -``` - -### .invoke - -Invoke app `fn` with the given `base` instance. - -**Params** - -* `fn` **{Function}**: The app function. -* `app` **{Object}**: The "base" instance to use with the app. -* `returns` **{Object}** - -**Example** - -```js -verb.invoke(app.fn, app); -``` - -## Authoring apps - +## Apps _(TODO)_ -### App naming conventions - -Use `verb-` as the prefix, followed by any words of your choosing to describe the purpose of the app. - ## TODO - [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! From b185bc36c8e99dfbf1b268f77f4d51daf3dab1d2 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 02:12:58 -0500 Subject: [PATCH 137/282] remove unused deps --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 4319ad8a..b17b4a92 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "array-unique": "^0.2.1", "assemble-core": "^0.8.0", "assemble-loader": "^0.2.4", - "base-argv": "^0.3.0", "base-diff": "^0.1.0", "base-list": "^0.1.4", "base-pipeline": "^0.1.4", @@ -47,7 +46,6 @@ "expand": "^0.4.0", "export-files": "^2.1.0", "extend-shallow": "^2.0.1", - "falsey": "^0.3.0", "get-value": "^2.0.2", "gfm-code-blocks": "^0.3.0", "global-modules": "^0.2.0", @@ -156,4 +154,4 @@ "vinyl" ] } -} +} \ No newline at end of file From e814587a80c6e2120907eedfa18d9b4de6e107c3 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Jan 2016 02:18:48 -0500 Subject: [PATCH 138/282] clean up docs --- index.js | 15 +++++++-------- package.json | 2 +- readme.md | 35 +++++++++-------------------------- 3 files changed, 17 insertions(+), 35 deletions(-) diff --git a/index.js b/index.js index 618dd038..3b9159d1 100644 --- a/index.js +++ b/index.js @@ -20,7 +20,6 @@ var env = require('./lib/env'); * ``` * @param {Function} `fn` * @return {Function} - * @api public */ function create(preload) { @@ -84,14 +83,13 @@ function create(preload) { }; /** - * Similar to [copy](#copy) but calls a plugin `pipeline` if passed - * on the `options`. This allows plugin pipelines to be programmatically - * built-up and dynamically changed on-the-fly. + * Similar to [copy](#copy) but calls a plugin `pipeline` if defined globally + * or on the `.process` method `options`. This allows plugin pipelines to + * be programmatically built-up and dynamically changed on-the-fly. * * ```js * verb.process({src: ['a.txt', 'b.txt']}, options); * ``` - * * @param {Object} `files` * @param {Object} `options` * @param {Function} `cb` @@ -137,7 +135,8 @@ function create(preload) { * * ```js * verb.eachSeries(files, function(err) { - * if (err) console.log(err); + * if (err) throw err; + * console.log('done!'); * }); * ``` * @param {Object} `config` @@ -173,7 +172,7 @@ function create(preload) { * }); * ``` * @param {Object} `scaffold` Scaffold configuration - * @param {Function} `cb` Callback function + * @param {Function} `cb` Callback function that exposes `err`, called after the scaffold is generated. * @api public */ @@ -184,7 +183,7 @@ function create(preload) { }; /** - * Get the `base` instance + * Get the package.json from the current working directory. */ Object.defineProperty(Verb.prototype, '_pkg', { diff --git a/package.json b/package.json index b17b4a92..2cea4064 100644 --- a/package.json +++ b/package.json @@ -154,4 +154,4 @@ "vinyl" ] } -} \ No newline at end of file +} diff --git a/readme.md b/readme.md index 031b8aff..99800280 100644 --- a/readme.md +++ b/readme.md @@ -97,25 +97,7 @@ $ verb bar ## API -### [create](index.js#L26) - -Create a customized `Verb` constructor that calls the given `fn` when an instance is created. - -**Params** - -* `fn` **{Function}** -* `returns` **{Function}** - -**Example** - -```js -var Verb = create(function(verb) { - // add stuff to `verb` -}); -var verb = new Verb(); -``` - -### [Verb](index.js#L39) +### [Verb](index.js#L38) Create an instance of `Verb` with the given `options` @@ -130,9 +112,9 @@ var Verb = require('verb'); var verb = new Verb(options); ``` -### [.process](index.js#L102) +### [.process](index.js#L100) -Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options`. This allows plugin pipelines to be programmatically built-up and dynamically changed on-the-fly. +Similar to [copy](#copy) but calls a plugin `pipeline` if defined globally or on the `.process` method `options`. This allows plugin pipelines to be programmatically built-up and dynamically changed on-the-fly. **Params** @@ -147,7 +129,7 @@ Similar to [copy](#copy) but calls a plugin `pipeline` if passed on the `options verb.process({src: ['a.txt', 'b.txt']}, options); ``` -### [.each](index.js#L126) +### [.each](index.js#L124) Verb `files` configurations in parallel. @@ -164,7 +146,7 @@ verb.each(files, function(err) { }); ``` -### [.eachSeries](index.js#L148) +### [.eachSeries](index.js#L147) Verb `files` configurations in series. @@ -177,18 +159,19 @@ Verb `files` configurations in series. ```js verb.eachSeries(files, function(err) { - if (err) console.log(err); + if (err) throw err; + console.log('done!'); }); ``` -### [.scaffold](index.js#L180) +### [.scaffold](index.js#L179) Verb files from a declarative [scaffold][] configuration. **Params** * `scaffold` **{Object}**: Scaffold configuration -* `cb` **{Function}**: Callback function +* `cb` **{Function}**: Callback function that exposes `err`, called after the scaffold is generated. **Example** From 08490574b78dcc6e5a86d0d0dd7aa17b51718119 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 11 Jan 2016 13:30:06 -0500 Subject: [PATCH 139/282] CLI middleware updates --- .verb.md | 12 ++++++ cli.js | 6 ++- examples/schema.js | 58 +++++++++++++++++++++++++++ lib/config.js | 5 +++ lib/loader.js | 7 ++-- lib/runner/cli.js | 2 +- lib/runner/commands/ask.js | 2 +- lib/runner/commands/show.js | 1 - lib/runner/helpers.js | 65 +++++++++++++++++++++++++++++++ lib/runner/middleware.js | 49 ++++++++++++++++++----- lib/runner/middleware/error.js | 13 +++++++ lib/runner/middleware/reflinks.js | 22 +++++++++-- lib/runner/middleware/toc.js | 10 +++-- lib/updater.js | 7 +--- lib/utils.js | 63 ++++++++++++++++++++++++++---- package.json | 10 +++-- readme.md | 19 +++++++-- templates/badges.js | 4 +- templates/layouts/default.md | 2 +- verbfile.js | 28 +------------ 20 files changed, 311 insertions(+), 74 deletions(-) create mode 100644 examples/schema.js create mode 100644 lib/runner/middleware/error.js diff --git a/.verb.md b/.verb.md index 82b6609b..9838d984 100644 --- a/.verb.md +++ b/.verb.md @@ -1,3 +1,9 @@ +See the [release history](#release-history) for updates, news about new features, or to stay on top of breaking changes. + +Also consider installing [update][]. Update works as a companion to verb by automatically updating your `.verb.md` and other templates, based on your preferences. Update is completely customizable and runs on plugins! + +{%= gt(npm(), 10000, name, name) %} + ## Install To get started, you'll first need to install `verb` globally using [npm][]: @@ -95,3 +101,9 @@ _(TODO)_ - [ ] User help (e.g. when the user does `verb help` or just `verb`) - [ ] API docs - [ ] App guidelines and conventions + +## Release history + +**v0.9.0** + +- `license()` helper has been removed in favor of storing the `license` variable on the context as a string. To update your `.verb.md`, just change `{%%= license() %}` to `{%%= license %}`. \ No newline at end of file diff --git a/cli.js b/cli.js index bf6415b5..5e710b71 100755 --- a/cli.js +++ b/cli.js @@ -133,7 +133,11 @@ verb.cli.map('apps', function(tasks) { // run apps and/or tasks verb.runApps(tasks, function(err) { - if (err) throw err; + if (err) { + utils.handleError(verb)(err); + process.exit(1); + } + utils.timestamp('finished ' + utils.success()); process.exit(0); }); diff --git a/examples/schema.js b/examples/schema.js new file mode 100644 index 00000000..82731abf --- /dev/null +++ b/examples/schema.js @@ -0,0 +1,58 @@ +'use strict'; + +var Schema = require('../lib/schema'); +var schema = new Schema(); + +schema.field('data', ['object']); +schema.field('options', ['object']); +schema.field('plugins', ['array', 'object']); + +schema.field('related', ['array', 'object'], { + sort: function(a, b) { + return a > b; + } +}); + +schema.field('reflinks', ['array', 'object'], { + validate: function(val, key, config) { + + } +}); + +schema.field('reflinks.list', ['array'], { + validate: function(val, key, config) { + return Array.isArray(val); + }, + sort: ['foo', 'bar', 'baz'] +}); + +schema.field('example', ['array'], { + normalize: function(val, key, config) { + return Array.isArray(val); + }, + validate: function(val, key, config) { + return true; + }, + validate: function(val, key, config) { + return new Status('foo') + }, + sort: ['foo', 'bar', 'baz'], + sort: function() { + + } +}); + +// console.log(schema.isValid('reflinks.list', '')) +// console.log(schema.isValid('whatever')) + +var pkg = { + verb: { + reflinks: 'foo', + related: { + list: ['foo', 'bar', 'baz'] + }, + } +}; + +var results = schema.validate(pkg.verb); +// console.log(results) diff --git a/lib/config.js b/lib/config.js index 7bd20650..8ac3e141 100644 --- a/lib/config.js +++ b/lib/config.js @@ -77,6 +77,11 @@ module.exports = function(verb, base, env) { if (utils.isObject(val)) { if (val.name) { var readme = verb.docs.getView('readme.md'); + if (!readme) readme = verb.docs.getView('.verb'); + if (!readme) { + throw new Error('cannot find .verb.md'); + } + readme.layout = val.name; } diff --git a/lib/loader.js b/lib/loader.js index bc297ed6..378283b4 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -13,6 +13,7 @@ module.exports = function loader(prop, app, fn) { var obj = app.get('env.argv.' + prop); var cwd = app.get('env.user.cwd'); var opts = {}; + var fp; if (typeof config === 'string') { config = [config]; @@ -35,7 +36,7 @@ module.exports = function loader(prop, app, fn) { try { if (name === 'index') { var dir = path.resolve(cwd); - var fp = utils.tryResolve(dir); + fp = utils.tryResolve(dir); if (fs.existsSync(fp)) { opts = val; val = utils.tryRequire(fp); @@ -56,7 +57,7 @@ module.exports = function loader(prop, app, fn) { var filepath = opts.path || key || name; val = utils.loadModule(filepath, cwd); if (val === null) { - var fp = path.resolve(cwd, filepath); + fp = path.resolve(cwd, filepath); throw new Error('cannot resolve ' + prop + ' at path: ' + fp); } } @@ -72,7 +73,7 @@ module.exports = function loader(prop, app, fn) { throw new Error(msg); } - name = name.replace(/^[-\W]*(verb|helper|middleware|plugin)?[-\W]*/, ''); + name = name.replace(/^[-\W]*(verb|helper|middleware|plugin)*[-\W]*/, ''); fn(name, opts, val); } }; diff --git a/lib/runner/cli.js b/lib/runner/cli.js index aee7faa9..d683372f 100644 --- a/lib/runner/cli.js +++ b/lib/runner/cli.js @@ -26,7 +26,7 @@ module.exports = function(verb) { .map('open', commands.open(verb)) .map('diff', function(val) { verb.option('diff', val); - }) + }); /** * Options, settings and context related diff --git a/lib/runner/commands/ask.js b/lib/runner/commands/ask.js index f3b34b07..1be2a854 100644 --- a/lib/runner/commands/ask.js +++ b/lib/runner/commands/ask.js @@ -19,5 +19,5 @@ module.exports = function(app) { var keys = utils.arrayify(val); app.questions.enqueue(keys); app.option('questions.init', keys); - } + }; }; diff --git a/lib/runner/commands/show.js b/lib/runner/commands/show.js index 9760404c..b70841d1 100644 --- a/lib/runner/commands/show.js +++ b/lib/runner/commands/show.js @@ -1,6 +1,5 @@ 'use strict'; -var path = require('path'); var utils = require('../../utils'); module.exports = function(app) { diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js index 65aa51b5..6b6123ad 100644 --- a/lib/runner/helpers.js +++ b/lib/runner/helpers.js @@ -2,6 +2,7 @@ var fs = require('fs'); var path = require('path'); +var resolve = require('resolve'); var utils = require('../utils'); module.exports = function(verb, base, env) { @@ -44,6 +45,8 @@ module.exports = function(verb, base, env) { return; } + // title = section.title || title; + var ctx = {}; ctx = utils.merge({}, ctx, this.context.view.data); ctx = utils.merge({}, ctx, this.context); @@ -99,12 +102,74 @@ module.exports = function(verb, base, env) { return fs.readFileSync(fp, 'utf8'); }); verb.helper('apidocs', apidocs({delims: ['{%', '%}']})); + verb.helper('depdocs', function(name, filename, context) { + if (typeof filename !== 'string') { + context = filename; + filename = null; + } + + var main = resolve.sync(name, {basedir: process.cwd()}); + var fp = main; + var pkg = {}; + + if (filename) { + var dir = path.dirname(main); + pkg = require(path.join(dir, 'package.json')); + fp = path.resolve(dir, filename); + } + + this.app.docs.addView(fp, { + dest: utils.toGithub(pkg.repository, filename), + content: fs.readFileSync(fp) + }); + + var fn = apidocs({delims: ['{%', '%}']}); + return fn.call(this, fp, context); + }); + verb.helper('copyright', require('helper-copyright')({ linkify: true })); verb.asyncHelper('related', utils.related({verbose: true})); verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); + + verb.helper('gt', function(a, b, fn, efn) { + console.log(arguments); + if (a > b) { + return fn; + } + return efn; + }); + + verb.asyncHelper('npm', function(cb) { + var request = require('request'); + var JSONStream = require('JSONStream'); + + var url = 'https://api.npmjs.org/downloads/range/'; + url += '1900-01-01'; + url += ':' + '3000-01-01'; + url += '/' + this.context.name; + var res = []; + + return request(url) + .on('error', console.error) + .pipe(JSONStream.parse('downloads.*')) + .on('data', function(data) { + res.push(data); + }) + .on('end', function() { + // do stuff with res + var num = res.reduce(function(acc, obj) { + return acc += obj.downloads; + }, 0); + // console.log(num) + + cb(null, num); + }); + + // 'https://api.npmjs.org/downloads/range/2015-11-01:2015-12-01/micromatch' + }); }; /** diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index b29f6dbc..3706823b 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -1,10 +1,17 @@ 'use strict'; var diff = require('base-diff'); +var mdu = require('middleware-utils'); var middleware = require('./middleware/'); var utils = require('../utils'); module.exports = function(app, base, env) { + /** + * Table of contents > adds `file.data.toc` property + */ + + var append = '_(TOC generated by [verb](https://github.com/verbose/verb) ' + + 'using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_'; /** * Readme dest path @@ -16,17 +23,39 @@ module.exports = function(app, base, env) { next(); }); - /** - * Table of contents > adds `file.data.toc` property - */ - - var append = '_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_'; + // extend comments with code examples + app.onLoad(/\.md/, middleware.examples(app)); - app.postLayout(/\.md/, middleware.toc.create(app, append)); - app.preWrite(/\.md/, middleware.toc.inject(app, append)); + // add toc to `view.data` + app.postLayout(/\.md/, mdu.series([ + middleware.toc.create(app, append), + // middleware.toc.inject(app, append), + ]), middleware.error('postLayout')); + + // format markdown between TOC-creation and TOC-injection + app.preRender(/\.md/, mdu.series([ + middleware.toc.create(app, append), + ]), middleware.error('preRender')); + + // format markdown between TOC-creation and TOC-injection + // app.postRender(/\.md/, mdu.series([ + // middleware.toc.inject(app, append), + // ]), middleware.error('postRender')); + + // format markdown between TOC-creation and TOC-injection + app.preWrite(/./, mdu.series([ + middleware.toc.inject(app, append), + ]), middleware.error('preWrite')); + + app.preWrite(/./, function(view, next) { + view.content = view.content.replace(/\)[\s\n]+\[!\[/g, ') [!['); + next(); + }); - app.onLoad(/\.md/, middleware.reflinks(app)); - app.onLoad(/\.md/, middleware.examples(app)); + // add reflinks + app.postRender(/(default|readme|\.verb)/, mdu.series([ + middleware.reflinks(app), + ]), middleware.error('preWrite')); /** * Merge options onto `view.data` @@ -49,7 +78,7 @@ module.exports = function(app, base, env) { app.preRender(/./, diff.view('diffLines')); app.postRender(/./, function(view, next) { var diff = app.option('diff'); - if (diff && diff === true || diff === view.stem) { + if (diff === true || diff === view.stem) { view.diff(); } next(); diff --git a/lib/runner/middleware/error.js b/lib/runner/middleware/error.js new file mode 100644 index 00000000..832a7497 --- /dev/null +++ b/lib/runner/middleware/error.js @@ -0,0 +1,13 @@ +'use strict'; + +/** + * Add a reflinks helper to + */ + +module.exports = function(method) { + return function(err, view, next) { + if (!err) return next(); + console.error(method + ' ' + err.message); + next(); + }; +}; diff --git a/lib/runner/middleware/reflinks.js b/lib/runner/middleware/reflinks.js index 829f10cb..cda4a50f 100644 --- a/lib/runner/middleware/reflinks.js +++ b/lib/runner/middleware/reflinks.js @@ -7,17 +7,33 @@ var utils = require('../../utils'); */ module.exports = function(verb) { + return addReflinks(verb, { verbose: true }); +}; + +function addReflinks(app, opts) { + var cache = {}; + return function(view, next) { - var arr = verb.pkg.get('verb.reflinks'); + if (!view.isType('renderable') || cache[view.path]) { + return next(); + } + var arr = app.pkg.get('verb.reflinks'); if (Array.isArray(arr)) { - var reflinks = utils.reflinks({verbose: true}); + var reflinks = utils.reflinks(opts); reflinks(arr, function(err, res) { if (err) return next(err); + cache[view.path] = true; + // only show logging message once + opts.verbose = false; view.content += '\n\n' + res; next(); }); + } else { + next(); } }; -}; +} + +module.exports.addReflinks = addReflinks; diff --git a/lib/runner/middleware/toc.js b/lib/runner/middleware/toc.js index 84cdaec1..0dfc7363 100644 --- a/lib/runner/middleware/toc.js +++ b/lib/runner/middleware/toc.js @@ -11,8 +11,7 @@ exports.create = function(app, append) { return function(view, next) { view.options.toc = view.options.toc || {}; var opts = utils.merge({append: append}, app.options, view.options); - - if (view.isType('partial') || opts.toc === false) { + if (view.isType('partial') || opts.toc === false || view.data.toc) { next(); return; } @@ -21,6 +20,7 @@ exports.create = function(app, append) { if (view.options.toc.hasMarker && !view.data.toc) { view.data.toc = toc(view.content).content; } + next(); }; }; @@ -28,7 +28,6 @@ exports.create = function(app, append) { exports.inject = function(app, append) { return function(view, next) { var str = view.data.toc; - if (!str || view.options.toc.hasMarker === false) { return next(); } @@ -43,7 +42,7 @@ exports.inject = function(app, append) { }; function hasMarker(view) { - return view.content.indexOf('') !== -1; + return /').join(toc); str = str.split('').join(toc); + + // remove escaped HTML comments + str = str.split(' Powerful, highly pluggable and easy-to-use documentation generator with an expressive API. Use any docs parser or template engine, supports gulp plugins, middleware... @@ -13,10 +13,17 @@ - [API](#api) - [Apps](#apps) - [TODO](#todo) +- [Release history](#release-history) - [License](#license) _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ +See the [release history](#release-history) for updates, news about new features, or to stay on top of breaking changes. + +Also consider installing [update][]. Update works as a companion to verb by automatically updating your `.verb.md` and other templates, based on your preferences. Update is completely customizable and runs on plugins! + +verb + ## Install To get started, you'll first need to install `verb` globally using [npm][]: @@ -214,9 +221,15 @@ _(TODO)_ * [ ] API docs * [ ] App guidelines and conventions +## Release history + +**v0.9.0** + +* `license()` helper has been removed in favor of storing the `license` variable on the context as a string. To update your `.verb.md`, just change `{%= license() %}` to `{%= license %}`. + ## Related projects -* [assemble](https://www.npmjs.com/package/assemble): Static site generator for Grunt.js, Yeoman and Node.js. Used by Zurb Foundation, Zurb Ink, H5BP/Effeckt,… [more](https://www.npmjs.com/package/assemble) | [homepage](http://assemble.io) +* [assemble](https://www.npmjs.com/package/assemble): Assemble is a powerful, extendable and easy to use static site generator for node.js. Used… [more](https://www.npmjs.com/package/assemble) | [homepage](https://github.com/assemble/assemble) * [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) * [base-methods](https://www.npmjs.com/package/base-methods): base-methods is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) * [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) @@ -249,4 +262,4 @@ Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on January 03, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on January 08, 2016._ \ No newline at end of file diff --git a/templates/badges.js b/templates/badges.js index 1f022177..c04380de 100644 --- a/templates/badges.js +++ b/templates/badges.js @@ -1,8 +1,8 @@ 'use strict'; module.exports = { - travis: '[![Build Status](https://img.shields.io/travis/{%= author.username %}/{%= name %}.svg)](https://travis-ci.org/{%= author.username %}/{%= name %})', + travis: '[![Build Status](https://img.shields.io/travis/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})', fury: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', npm: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', - coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= author.username %}/{%= name %}.svg)](https://coveralls.io/r/{%= author.username %}/{%= name %})' + coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= repository %}.svg)](https://coveralls.io/r/{%= repository %})' }; diff --git a/templates/layouts/default.md b/templates/layouts/default.md index f4074a2a..037f3e8d 100644 --- a/templates/layouts/default.md +++ b/templates/layouts/default.md @@ -17,7 +17,7 @@ verb_docs: {%= section("Author", "author") %} ## License -{%= copyright({linkify: true}) %} +{%= copyright({linkify: true}) %} {%= license %} *** diff --git a/verbfile.js b/verbfile.js index dcf1556e..e269a4d6 100644 --- a/verbfile.js +++ b/verbfile.js @@ -32,7 +32,7 @@ module.exports = function(verb, base, env) { .on('error', cb) .pipe(verb.renderFile('text')) .on('error', cb) - .on('error', handleError(verb)) + .on('error', utils.handleError(verb)) .pipe(verb.pipeline(plugins)) .pipe(handle(verb, 'preWrite')) .pipe(verb.dest(rename())) @@ -94,29 +94,3 @@ function rename(dest) { return file.base; }; } - -/** - * Handle render errors - */ - -function handleError(app) { - return function(err, cb) { - var m = /(\w+) is not a function/.exec(err.message); - var msg = ''; - if (m) { - msg = err.message + ': "' + m[1] - + '()" is defined as a helper\n' - + 'in `.verb.md`, but "' + m[1] - + '" is defined on verb.cache.data as a "' - + typeof app.cache.data[m[1]] + '"'; - } - if (app.options.verbose) { - console.log(msg); - console.log(err.stack); - } else { - console.log(err.message); - console.log(msg); - } - process.exit(1); - }; -} From 0ba65686df408c034b3466008eebbedcd3bd8aa5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 11 Jan 2016 18:51:49 -0500 Subject: [PATCH 140/282] fix error messages in applyLayout tests --- test/app.applyLayout.js | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js index 6fdf3902..a1f35509 100644 --- a/test/app.applyLayout.js +++ b/test/app.applyLayout.js @@ -12,72 +12,72 @@ var page = { } }; -describe('helpers', function () { - describe('rendering', function () { - beforeEach(function () { +describe('helpers', function() { + describe('rendering', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('layout', {viewType: 'layout'}); app.create('page'); }); - it('should throw an error when a layout cannot be found:', function (done) { + it('should throw an error when a layout cannot be found:', function(cb) { app.layout('fofof.tmpl', {content: '..'}); app.page('a.tmpl', page) - .render(function (err) { - assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); - done(); + .render(function(err) { + assert(/layouts/.test(err.message)); + cb(); }); }); - it('should emit an error when a layout cannot be found:', function (done) { + it('should emit an error when a layout cannot be found:', function(cb) { app.layout('fofof.tmpl', {content: '..'}); - app.on('error', function (err) { - assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); - done(); + app.on('error', function(err) { + assert(/layouts/.test(err.message)); + cb(); }); app.page('a.tmpl', page) - .render(function () { + .render(function() { }); }); - it('should throw an error - layout defined but no layouts registered:', function (done) { + it('should throw an error - layout defined but no layouts registered:', function(cb) { app.page('a.tmpl', page) - .render(function (err) { - assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); - done(); + .render(function(err) { + assert(/layouts/.test(err.message)); + cb(); }); }); - it('should emit an error - layout defined but no layouts registered:', function (done) { - app.on('error', function (err) { - assert(err.message === 'Templates#layouts no layouts are registered, but one is defined: default.tmpl'); - done(); + it('should emit an error - layout defined but no layouts registered:', function(cb) { + app.on('error', function(err) { + assert(/layouts/.test(err.message)); + cb(); }); app.page('a.tmpl', page) - .render(function () { + .render(function() { }); }); - it('should wrap a view with a layout (view.render):', function (done) { + it('should wrap a view with a layout (view.render):', function(cb) { app.layout('default.tmpl', {content: 'before {% body %} after'}); app.page('a.tmpl', page) - .render(function (err) { - if (err) return done(err); - done(); + .render(function(err) { + if (err) return cb(err); + cb(); }); }); - it('should wrap a view with a layout (app.render):', function (done) { + it('should wrap a view with a layout (app.render):', function(cb) { app.layout('default.tmpl', {content: 'before {% body %} after'}); app.page('a.tmpl', page); var view = app.pages.getView('a.tmpl'); - app.render(view, function (err, res) { - if (err) return done(err); - assert(res.contents.toString() === 'before Halle after'); - done(); + app.render(view, function(err, res) { + if (err) return cb(err); + assert.equal(res.contents.toString(), 'before Halle after'); + cb(); }); }); }); From 777ec5452ac99803b8124e71e052183982824ae8 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 11 Jan 2016 18:52:03 -0500 Subject: [PATCH 141/282] clean up toc middleware --- lib/runner/middleware.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js index 3706823b..ddd96698 100644 --- a/lib/runner/middleware.js +++ b/lib/runner/middleware.js @@ -26,21 +26,10 @@ module.exports = function(app, base, env) { // extend comments with code examples app.onLoad(/\.md/, middleware.examples(app)); - // add toc to `view.data` - app.postLayout(/\.md/, mdu.series([ - middleware.toc.create(app, append), - // middleware.toc.inject(app, append), - ]), middleware.error('postLayout')); - // format markdown between TOC-creation and TOC-injection - app.preRender(/\.md/, mdu.series([ + app.postRender(/\.md/, mdu.series([ middleware.toc.create(app, append), - ]), middleware.error('preRender')); - - // format markdown between TOC-creation and TOC-injection - // app.postRender(/\.md/, mdu.series([ - // middleware.toc.inject(app, append), - // ]), middleware.error('postRender')); + ]), middleware.error('postRender')); // format markdown between TOC-creation and TOC-injection app.preWrite(/./, mdu.series([ From 55ac9de7a30afee69e510495395391ab648a1927 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 11 Jan 2016 18:54:54 -0500 Subject: [PATCH 142/282] generate docs --- .verb.md | 2 -- readme.md | 22 +++++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.verb.md b/.verb.md index 9838d984..9fbfff69 100644 --- a/.verb.md +++ b/.verb.md @@ -2,8 +2,6 @@ See the [release history](#release-history) for updates, news about new features Also consider installing [update][]. Update works as a companion to verb by automatically updating your `.verb.md` and other templates, based on your preferences. Update is completely customizable and runs on plugins! -{%= gt(npm(), 10000, name, name) %} - ## Install To get started, you'll first need to install `verb` globally using [npm][]: diff --git a/readme.md b/readme.md index 9f23e936..a017fbf6 100644 --- a/readme.md +++ b/readme.md @@ -11,9 +11,18 @@ + [diff](#diff) * [Run tasks](#run-tasks) - [API](#api) + * [[Verb](index.js#L38)](#-verb--indexjs-l38-) + * [[.process](index.js#L100)](#-process--indexjs-l100-) + * [[.each](index.js#L124)](#-each--indexjs-l124-) + * [[.eachSeries](index.js#L147)](#-eachseries--indexjs-l147-) + * [[.scaffold](index.js#L179)](#-scaffold--indexjs-l179-) - [Apps](#apps) - [TODO](#todo) - [Release history](#release-history) +- [Related projects](#related-projects) +- [Running tests](#running-tests) +- [Contributing](#contributing) +- [Author](#author) - [License](#license) _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ @@ -22,8 +31,6 @@ See the [release history](#release-history) for updates, news about new features Also consider installing [update][]. Update works as a companion to verb by automatically updating your `.verb.md` and other templates, based on your preferences. Update is completely customizable and runs on plugins! -verb - ## Install To get started, you'll first need to install `verb` globally using [npm][]: @@ -128,7 +135,7 @@ Similar to [copy](#copy) but calls a plugin `pipeline` if defined globally or on * `files` **{Object}** * `options` **{Object}** * `cb` **{Function}** -* `returns` **{Stream}**: Returns a [vinyl][] src stream +* `returns` **{Stream}**: Returns a [vinyl](http://github.com/gulpjs/vinyl) src stream **Example** @@ -173,7 +180,7 @@ verb.eachSeries(files, function(err) { ### [.scaffold](index.js#L179) -Verb files from a declarative [scaffold][] configuration. +Verb files from a declarative [scaffold](https://github.com/jonschlinkert/scaffold) configuration. **Params** @@ -207,12 +214,12 @@ _(TODO)_ * [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! * [x] support sub-apps (to any level of nesting) -* [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] +* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) * [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI * [x] support _instance plugins_ that allow you to easily add functionality and features to verb * [x] support any template engine * [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -* [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) +* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) * [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) * [x] 820+ unit tests * [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) @@ -258,8 +265,9 @@ Pull requests and stars are always welcome. For bugs and feature requests, [plea ## License Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert) + Released under the MIT license. *** -_This file was generated by [verb](https://github.com/verbose/verb) on January 08, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb) on January 11, 2016._ \ No newline at end of file From 56dd630a7c50bcfc8a26b82d20c28fd8999f1d45 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 27 Jan 2016 18:21:06 -0500 Subject: [PATCH 143/282] lint --- .travis.yml | 1 + LICENSE | 2 +- test/helpers.js | 8 ++++---- test/list.js | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6caa76a6..09768f0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ sudo: false language: node_js node_js: - "stable" + - "5" - "4" - "0.12" - "0.10" diff --git a/LICENSE b/LICENSE index 65f90aca..1e49edf8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015, Jon Schlinkert. +Copyright (c) 2015-2016, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/helpers.js b/test/helpers.js index b8388fb2..33398d54 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -271,7 +271,7 @@ describe('async helpers', function () { }); describe('built-in helpers:', function () { - describe('automatically verbd helpers for default view types:', function () { + describe('automatically created helpers for default view types:', function () { beforeEach(function () { app = new App({rethrow: false}); app.engine('md', require('engine-base')); @@ -352,12 +352,12 @@ describe('built-in helpers:', function () { }); }); - it('should return an empty string when the partial is missing.', function (cb) { + it('should return throw an error when a partial is missing.', function (cb) { app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("def.md", { name: "CCC" }) %> bar'}); app.render('xyz.md', {name: 'DDD'}, function (err, res) { - if (err) return cb(err); - res.content.should.equal('foo bar'); + assert(err); + assert.equal(err.message, 'helper "partial" cannot find "def.md"'); cb(); }); }); diff --git a/test/list.js b/test/list.js index 0dc23ca1..45e30412 100644 --- a/test/list.js +++ b/test/list.js @@ -160,7 +160,7 @@ describe('list', function () { var a = list.getItem('a'); list.removeItem(a); assert(list.items.length === 2); - var c = list.getItem(c); + var c = list.getItem('c'); list.removeItem(c); assert(list.items[0].key === 'b'); }); From c22276cf3f5d6a667d67154700c4d8ae5a4146b4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 6 Feb 2016 00:14:11 -0500 Subject: [PATCH 144/282] refactor --- .gitignore | 22 +- .travis.yml | 2 - .verb.md | 102 +--- bin/verb.js | 35 ++ cli.js | 153 ------ examples/fixtures/test.md | 1 - examples/fixtures/test.txt | 1 - examples/scaffold.js | 37 -- examples/schema.js | 58 --- foo.js | 10 + gulpfile.js | 17 +- index.js | 302 ++++-------- lib/actions.js | 37 -- lib/config.js | 204 +++----- lib/config/engines.js | 12 + lib/config/helpers.js | 17 + lib/{runner/commands => config}/index.js | 0 lib/config/options.js | 7 + lib/config/plugins.js | 16 + lib/config/update.js | 7 + lib/env.js | 28 -- lib/errors.js | 9 + lib/expand.js | 60 --- lib/generators/compose-example.js | 29 ++ lib/generators/default.js | 12 + .../generators/templates/.verb.md | 15 +- lib/generators/templates/verbfile.js | 10 + lib/generators/verbfile.js | 31 ++ lib/generators/verbfile2.js | 48 ++ lib/generators/verbfiles.js | 15 + lib/generators/verbfiles2.js | 45 ++ lib/generators/verbmd.js | 31 ++ lib/generators/verbmd2.js | 32 ++ lib/generators/welcome.js | 40 ++ lib/loader.js | 80 --- lib/pkg.js | 28 -- lib/plugins.js | 23 + lib/runner/cli.js | 73 --- lib/runner/commands/ask.js | 23 - lib/runner/commands/help.js | 106 ---- lib/runner/commands/open.js | 26 - lib/runner/commands/show.js | 21 - lib/runner/context.js | 7 - lib/runner/create.js | 40 -- lib/runner/data.js | 33 -- lib/runner/defaults.js | 11 - lib/runner/helpers.js | 194 -------- lib/runner/middleware.js | 100 ---- lib/runner/middleware/error.js | 13 - lib/runner/middleware/examples.js | 29 -- lib/runner/middleware/reflinks.js | 39 -- lib/runner/middleware/toc.js | 62 --- lib/runner/preload.js | 28 -- lib/runner/questions.js | 7 - lib/runner/templates.js | 101 ---- lib/schema.js | 194 ++++++++ lib/sections/create.js | 54 -- lib/sections/helper.js | 23 - lib/sections/index.js | 1 - lib/sections/load.js | 28 -- lib/settings.js | 31 ++ lib/settings/configfile.js | 25 + lib/{runner/middleware => settings}/index.js | 0 lib/settings/opts.js | 5 + lib/settings/pkg.js | 34 ++ lib/updater.js | 96 ---- lib/utils.js | 378 +++----------- package.json | 164 ++----- readme.md | 273 ----------- templates/badges.js | 8 - templates/gh-issue.tmpl | 10 - templates/includes.js | 70 --- templates/includes/banner.tmpl | 6 - templates/layouts/default.md | 25 - templates/layouts/default2.hbs | 46 -- templates/layouts/min.md | 21 - templates/readme/header.md | 9 - templates/readme/min.md | 14 - templates/sections.js | 62 --- test/app.extendWith.js | 78 +++ test/app.generate.js | 462 ++++++++++++++++++ test/app.generateEach.js | 444 +++++++++++++++++ test/app.generator.js | 164 +++++++ test/app.getGenerator.js | 97 ++++ test/app.hasGenerator.js | 43 ++ test/app.invoke.js | 399 +++++++++++++++ test/app.register.js | 249 ++++++++++ test/app.resolve.js | 70 +++ test/app.task.js | 135 +++-- test/app.tasks.js | 46 ++ test/app.toAlias.js | 30 ++ test/cache.js | 37 ++ test/env.js | 73 +++ test/fixtures/generators/a/.gitignore | 15 + test/fixtures/generators/a/package.json | 7 + test/fixtures/generators/a/post.hbs | 5 + test/fixtures/generators/a/verbfile.js | 10 + test/fixtures/generators/b/.gitignore | 15 + test/fixtures/generators/b/index.js | 12 + test/fixtures/generators/b/package.json | 7 + test/fixtures/generators/c/.gitignore | 15 + test/fixtures/generators/c/package.json | 7 + test/fixtures/generators/c/verbfile.js | 39 ++ test/fixtures/generators/e/.gitignore | 15 + test/fixtures/generators/e/package.json | 7 + test/fixtures/generators/e/templates/post.hbs | 5 + test/fixtures/generators/e/verbfile.js | 43 ++ test/fixtures/generators/qux/.gitignore | 15 + test/fixtures/generators/qux/package.json | 7 + test/fixtures/generators/qux/verbfile.js | 11 + test/fixtures/generators2.js | 41 ++ test/fixtures/not-exposed.js | 8 + test/fixtures/one/verbfile.js | 19 + test/fixtures/three/four/five/verbfile.js | 14 + test/fixtures/three/four/verbfile.js | 14 + test/fixtures/three/verbfile.js | 14 + test/fixtures/two/verbfile.js | 21 + test/fixtures/verbfile.js | 49 ++ test/generate.js | 35 ++ verbfile.js | 96 ---- 120 files changed, 3720 insertions(+), 3219 deletions(-) create mode 100755 bin/verb.js delete mode 100755 cli.js delete mode 100644 examples/fixtures/test.md delete mode 100644 examples/fixtures/test.txt delete mode 100644 examples/scaffold.js delete mode 100644 examples/schema.js create mode 100644 foo.js delete mode 100644 lib/actions.js create mode 100644 lib/config/engines.js create mode 100644 lib/config/helpers.js rename lib/{runner/commands => config}/index.js (100%) create mode 100644 lib/config/options.js create mode 100644 lib/config/plugins.js create mode 100644 lib/config/update.js delete mode 100644 lib/env.js create mode 100644 lib/errors.js delete mode 100644 lib/expand.js create mode 100644 lib/generators/compose-example.js create mode 100644 lib/generators/default.js rename templates/layouts/basic.md => lib/generators/templates/.verb.md (72%) create mode 100644 lib/generators/templates/verbfile.js create mode 100644 lib/generators/verbfile.js create mode 100644 lib/generators/verbfile2.js create mode 100644 lib/generators/verbfiles.js create mode 100644 lib/generators/verbfiles2.js create mode 100644 lib/generators/verbmd.js create mode 100644 lib/generators/verbmd2.js create mode 100644 lib/generators/welcome.js delete mode 100644 lib/loader.js delete mode 100644 lib/pkg.js create mode 100644 lib/plugins.js delete mode 100644 lib/runner/cli.js delete mode 100644 lib/runner/commands/ask.js delete mode 100644 lib/runner/commands/help.js delete mode 100644 lib/runner/commands/open.js delete mode 100644 lib/runner/commands/show.js delete mode 100644 lib/runner/context.js delete mode 100644 lib/runner/create.js delete mode 100644 lib/runner/data.js delete mode 100644 lib/runner/defaults.js delete mode 100644 lib/runner/helpers.js delete mode 100644 lib/runner/middleware.js delete mode 100644 lib/runner/middleware/error.js delete mode 100644 lib/runner/middleware/examples.js delete mode 100644 lib/runner/middleware/reflinks.js delete mode 100644 lib/runner/middleware/toc.js delete mode 100644 lib/runner/preload.js delete mode 100644 lib/runner/questions.js delete mode 100644 lib/runner/templates.js create mode 100644 lib/schema.js delete mode 100644 lib/sections/create.js delete mode 100644 lib/sections/helper.js delete mode 100644 lib/sections/index.js delete mode 100644 lib/sections/load.js create mode 100644 lib/settings.js create mode 100644 lib/settings/configfile.js rename lib/{runner/middleware => settings}/index.js (100%) create mode 100644 lib/settings/opts.js create mode 100644 lib/settings/pkg.js delete mode 100644 lib/updater.js delete mode 100644 readme.md delete mode 100644 templates/badges.js delete mode 100644 templates/gh-issue.tmpl delete mode 100644 templates/includes.js delete mode 100644 templates/includes/banner.tmpl delete mode 100644 templates/layouts/default.md delete mode 100644 templates/layouts/default2.hbs delete mode 100644 templates/layouts/min.md delete mode 100644 templates/readme/header.md delete mode 100644 templates/readme/min.md delete mode 100644 templates/sections.js create mode 100644 test/app.extendWith.js create mode 100644 test/app.generate.js create mode 100644 test/app.generateEach.js create mode 100644 test/app.generator.js create mode 100644 test/app.getGenerator.js create mode 100644 test/app.hasGenerator.js create mode 100644 test/app.invoke.js create mode 100644 test/app.register.js create mode 100644 test/app.resolve.js create mode 100644 test/app.tasks.js create mode 100644 test/app.toAlias.js create mode 100644 test/cache.js create mode 100644 test/env.js create mode 100644 test/fixtures/generators/a/.gitignore create mode 100644 test/fixtures/generators/a/package.json create mode 100644 test/fixtures/generators/a/post.hbs create mode 100644 test/fixtures/generators/a/verbfile.js create mode 100644 test/fixtures/generators/b/.gitignore create mode 100644 test/fixtures/generators/b/index.js create mode 100644 test/fixtures/generators/b/package.json create mode 100644 test/fixtures/generators/c/.gitignore create mode 100644 test/fixtures/generators/c/package.json create mode 100644 test/fixtures/generators/c/verbfile.js create mode 100644 test/fixtures/generators/e/.gitignore create mode 100644 test/fixtures/generators/e/package.json create mode 100644 test/fixtures/generators/e/templates/post.hbs create mode 100644 test/fixtures/generators/e/verbfile.js create mode 100644 test/fixtures/generators/qux/.gitignore create mode 100644 test/fixtures/generators/qux/package.json create mode 100644 test/fixtures/generators/qux/verbfile.js create mode 100644 test/fixtures/generators2.js create mode 100644 test/fixtures/not-exposed.js create mode 100644 test/fixtures/one/verbfile.js create mode 100644 test/fixtures/three/four/five/verbfile.js create mode 100644 test/fixtures/three/four/verbfile.js create mode 100644 test/fixtures/three/verbfile.js create mode 100644 test/fixtures/two/verbfile.js create mode 100644 test/fixtures/verbfile.js create mode 100644 test/generate.js delete mode 100644 verbfile.js diff --git a/.gitignore b/.gitignore index 80a228ca..79881544 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,21 @@ +# always ignore files *.DS_Store *.sublime-* -_gh_pages -bower_components + +# test related, or directories generated by tests +test/actual +actual +coverage + +# npm node_modules npm-debug.log -actual -test/actual + +# misc +_gh_pages +benchmark +bower_components +vendor temp tmp TODO.md -vendor -.idea -benchmark -coverage diff --git a/.travis.yml b/.travis.yml index 09768f0d..d6e658ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ sudo: false language: node_js node_js: - "stable" - - "5" - - "4" - "0.12" - "0.10" matrix: diff --git a/.verb.md b/.verb.md index 9fbfff69..76241280 100644 --- a/.verb.md +++ b/.verb.md @@ -1,107 +1,31 @@ -See the [release history](#release-history) for updates, news about new features, or to stay on top of breaking changes. - -Also consider installing [update][]. Update works as a companion to verb by automatically updating your `.verb.md` and other templates, based on your preferences. Update is completely customizable and runs on plugins! - -## Install - -To get started, you'll first need to install `verb` globally using [npm][]: - -{%= include("install-global") %} - -*** - ## Usage -```sh -$ verb [args] -``` - -## CLI - -_(WIP)_ - -### Commands - -#### help - -_(TODO)_ - -Get started with Verb. - ```js -$ verb help +var verb = require('{%= name %}'); ``` -#### init +## verbfile -_(TODO)_ +Your project's `verbfile.js` must either export an instance of Verb, or a function. -Get started with Verb. +**Instance** ```js -$ verb init -``` - -Upon running `init`, verb will prompt you for answers to the following questions: - -#### diff +var verb = require('verb'); +var app = verb(); -Show a diff of a file's contents, pre- and post-render. - -```js -$ verb --diff +module.exports = app; ``` -**Example diff** - -screen shot 2015-12-14 at 1 50 55 am - -_(gray is unchanged, red is deleted, green is new)_ - -### Run tasks +**Function** -To run a task on the `base` app, just pass the name of the task to run. - -```sh -$ verb [options] +```js +module.exports = function(verb) { + // "private" verb instance is created for this verbfile +}; ``` -Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. - -**Example** - -Run task `bar`: - -```sh -$ verb bar -``` +Also, when a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). ## API {%= apidocs("index.js") %} - -## Apps -_(TODO)_ - -## TODO - -- [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! -- [x] support sub-apps (to any level of nesting) -- [x] support streams, tasks, and plugins compatible with both [gulp][] and [assemble][assemble-core] -- [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -- [x] support _instance plugins_ that allow you to easily add functionality and features to verb -- [x] support any template engine -- [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -- [x] support templates as [vinyl][] files, simple to use template collections and lists (for pagination, sorting, groups etc) -- [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -- [x] 820+ unit tests -- [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -- [ ] CLI docs (started) -- [ ] User help (e.g. when the user does `verb help` or just `verb`) -- [ ] API docs -- [ ] App guidelines and conventions - -## Release history - -**v0.9.0** - -- `license()` helper has been removed in favor of storing the `license` variable on the context as a string. To update your `.verb.md`, just change `{%%= license() %}` to `{%%= license %}`. \ No newline at end of file diff --git a/bin/verb.js b/bin/verb.js new file mode 100755 index 00000000..171401b7 --- /dev/null +++ b/bin/verb.js @@ -0,0 +1,35 @@ +#!/usr/bin/env node + +var runtimes = require('base-runtimes'); +var Verb = require('..'); +var verb = new Verb(); + +// register default "fallback" generator +verb.register('fallback', require('../lib/generators/default')); +verb.register('verbfile', require('../lib/generators/verbfile')); +verb.register('verbmd', require('../lib/generators/verbmd')); + +// run generator and/or tasks +verb.runner('verbfile.js', function(err, argv, app) { + if (err) { + console.log(err.message); + process.exit(1); + } + + this.use(runtimes(config)); + // var app = verb.getGenerator('default'); + var config = this.loadSettings(argv); + this.set('cache.config', config); + + this.config.process(config, function(err) { + if (err) throw err; + + + app.cli.process(argv, function(err) { + if (err) throw err; + + verb.emit('done'); + process.exit(0); + }); + }); +}); diff --git a/cli.js b/cli.js deleted file mode 100755 index 5e710b71..00000000 --- a/cli.js +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env node - -var gm = require('global-modules'); -var minimist = require('minimist'); -var preload = require('./lib/runner/preload'); -var data = require('./lib/runner/data'); -var utils = require('./lib/utils'); -var colors = utils.colors; -var Verb = require('./'); -var create = Verb.create; - -// parse argv -var args = minimist(process.argv.slice(2), { - alias: {verbose: 'v'} -}); - -/** - * Use Verb's static `create` method to pre-load CLI-specific - * plugins and middleware onto each instance created. In essence - * this creates a custom `Verb` constructor . - */ - -Verb = create(function(app, base, env) { - return preload(app, base, env || app.env); -}); - -/** - * Register `runner` mixin with `Verb`, wich pre-loads - * CLI-specific methods onto our custom constructor. - * - * We also pass the `runner` function so that any user- - * defined `Verb` constructors will also be pre-loaded - * with our CLI plugins and middeware. - */ - -Verb.mixin(utils.runner('verb', 'app', preload)); - -/** - * Get the `base` instance of verb to use for - * registering all other instances. This will either - * be local to the user (e.g. `node_modules/verb`) - * or a globally installed module - */ - -var verb = Verb.getConfig('verbfile.js', __dirname); - -/** - * Support `--emit` for debugging - * - * Example: - * $ --emit data - */ - -if (args.emit && typeof args.emit === 'string') { - verb.on(args.emit, console.log.bind(console)); -} - -// get `verb` property from package.json, if it exists -var userPkg = verb.get('env.user.pkg'); -if (userPkg) { - verb.set('cache.pkg', userPkg); - var verbConfig = userPkg.verb; -} - -if (userPkg && verbConfig) { - var config = utils.expandConfig(verbConfig, userPkg); - verb.config.process(config); - verb.set('cache.config', config); - verb.emit('config-loaded'); -} - -/** - * Resolve user config files, eg. `verbfile.js`. - */ - -verb.resolve('global', {pattern: 'verb-*/verbfile.js', cwd: gm}); - -/** - * Run verb "apps" and tasks - */ - -verb.cli.map('apps', function(tasks) { - - // ensure this is run after other configuration is complete - setImmediate(function() { - if (verb.enabled('tasks.display')) { - console.log(colors.gray(' Verb apps and registered tasks:')); - verb.displayTasks(); - utils.timestamp('done'); - return; - } - - if (verb.enabled('tasks.choose')) { - verb.chooseTasks(function(err, results) { - if (err) throw err; - run(utils.arrayify(results.apps)); - }); - return; - } - - if (!tasks.length) { - if (verb.tasks.hasOwnProperty('default')) { - var task = {}; - task[verb.env.user.alias] = ['default']; - tasks = [task]; - } else { - console.log(' no default task is defined.'); - utils.timestamp('done'); - return; - } - } - - run(tasks); - function run(tasks) { - verb.data(data.updateData(verb)); - - if (args.init) { - verb.questions.enqueue('author', {force: true}); - console.log('fix me! I should output all author questions!'); - } - - // ask queued questions - verb.ask(function(err, answers) { - if (err) return console.error(err); - var fp = utils.homeRelative(verb.get('env.config.path')); - utils.timestamp('using verbfile ' + fp); - - // emit the answers - verb.emit('answers', answers); - - // update context for templates - verb.data(answers); - - // run apps and/or tasks - verb.runApps(tasks, function(err) { - if (err) { - utils.handleError(verb)(err); - process.exit(1); - } - - utils.timestamp('finished ' + utils.success()); - process.exit(0); - }); - }); - } - }); -}); - -/** - * Process args - */ - -verb.cli.process(args); diff --git a/examples/fixtures/test.md b/examples/fixtures/test.md deleted file mode 100644 index 36f1f1b5..00000000 --- a/examples/fixtures/test.md +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/examples/fixtures/test.txt b/examples/fixtures/test.txt deleted file mode 100644 index 5b88ae15..00000000 --- a/examples/fixtures/test.txt +++ /dev/null @@ -1 +0,0 @@ -test file! \ No newline at end of file diff --git a/examples/scaffold.js b/examples/scaffold.js deleted file mode 100644 index 5c24dc85..00000000 --- a/examples/scaffold.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var Verb = require('..'); -var verb = new Verb(); - -var Scaffold = require('scaffold'); -var scaffold = new Scaffold({ - a: { - options: { - cwd: 'examples/fixtures', - destBase: 'two', - }, - data: {name: 'Jon'}, - files: [ - {src: '*.txt', dest: 'a'}, - {src: '*.txt', dest: 'b'}, - {src: '*.txt', dest: 'c'}, - {src: '*.md', dest: 'md', data: {name: 'Jon'}}, - ] - }, - b: { - cwd: 'examples/fixtures', - destBase: 'one', - data: {name: 'Brian'}, - files: [ - {src: '*.txt', dest: 'a'}, - {src: '*.txt', dest: 'b'}, - {src: '*.txt', dest: 'c'}, - {src: '*.md', dest: 'md', data: {name: 'Brian'}}, - ] - } -}); - -verb.scaffold(scaffold, function(err) { - if (err) return console.log(err); - console.log('done!'); -}); diff --git a/examples/schema.js b/examples/schema.js deleted file mode 100644 index 82731abf..00000000 --- a/examples/schema.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var Schema = require('../lib/schema'); -var schema = new Schema(); - -schema.field('data', ['object']); -schema.field('options', ['object']); -schema.field('plugins', ['array', 'object']); - -schema.field('related', ['array', 'object'], { - sort: function(a, b) { - return a > b; - } -}); - -schema.field('reflinks', ['array', 'object'], { - validate: function(val, key, config) { - - } -}); - -schema.field('reflinks.list', ['array'], { - validate: function(val, key, config) { - return Array.isArray(val); - }, - sort: ['foo', 'bar', 'baz'] -}); - -schema.field('example', ['array'], { - normalize: function(val, key, config) { - return Array.isArray(val); - }, - validate: function(val, key, config) { - return true; - }, - validate: function(val, key, config) { - return new Status('foo') - }, - sort: ['foo', 'bar', 'baz'], - sort: function() { - - } -}); - -// console.log(schema.isValid('reflinks.list', '')) -// console.log(schema.isValid('whatever')) - -var pkg = { - verb: { - reflinks: 'foo', - related: { - list: ['foo', 'bar', 'baz'] - }, - } -}; - -var results = schema.validate(pkg.verb); -// console.log(results) diff --git a/foo.js b/foo.js new file mode 100644 index 00000000..fc23c4fa --- /dev/null +++ b/foo.js @@ -0,0 +1,10 @@ +'use strict'; + +var Verb = require('./'); +var verb = new Verb(); + +var opts = { dot: true }; + +verb.conflicts('lib/generators/templates/*', opts, function(err, views) { + console.log(views) +}); diff --git a/gulpfile.js b/gulpfile.js index 7d0c54ab..aba1612d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,23 +5,22 @@ var mocha = require('gulp-mocha'); var istanbul = require('gulp-istanbul'); var eslint = require('gulp-eslint'); -var lint = ['index.js', 'lib/*.js', 'test/*.js']; - -gulp.task('coverage', function () { - return gulp.src(lint) - .pipe(istanbul()) +gulp.task('coverage', function() { + return gulp.src(['index.js', 'lib/*.js']) + .pipe(istanbul({includeUntested: true})) .pipe(istanbul.hookRequire()); }); -gulp.task('mocha', ['coverage'], function () { +gulp.task('mocha', ['coverage'], function() { return gulp.src('test/*.js') - .pipe(mocha({reporter: 'spec'})) + .pipe(mocha()) .pipe(istanbul.writeReports()); }); -gulp.task('eslint', function () { - return gulp.src(lint) +gulp.task('eslint', function() { + return gulp.src(['*.js', 'lib/*.js', 'test/*.js']) .pipe(eslint()) + .pipe(eslint.format()); }); gulp.task('default', ['mocha', 'eslint']); diff --git a/index.js b/index.js index 3b9159d1..9bd8596c 100644 --- a/index.js +++ b/index.js @@ -1,242 +1,120 @@ +/*! + * verb + * + * Copyright (c) 2016, Jon Schlinkert. + * Licensed under the MIT License. + */ + 'use strict'; -var path = require('path'); +var fs = require('fs'); var async = require('async'); -var Base = require('assemble-core'); -var actions = require('./lib/actions'); +var debug = require('debug')('verb'); +var Assemble = require('assemble-core'); +var settings = require('./lib/settings'); +var plugins = require('./lib/plugins'); +var config = require('./lib/config'); var utils = require('./lib/utils'); -var pkg = require('./lib/pkg'); -var env = require('./lib/env'); /** - * Create a customized `Verb` constructor that calls the given - * `fn` when an instance is created. + * Create an instance of `Verb` with the given `options` * * ```js - * var Verb = create(function(verb) { - * // add stuff to `verb` - * }); + * var Verb = require('verb'); * var verb = new Verb(); * ``` - * @param {Function} `fn` - * @return {Function} + * @param {Object} `options` Settings to initialize with. + * @api public */ -function create(preload) { - - /** - * Create an instance of `Verb` with the given `options` - * - * ```js - * var Verb = require('verb'); - * var verb = new Verb(options); - * ``` - * @param {Object} `options` Configuration options to initialize with. - * @api public - */ - - function Verb(options) { - if (!(this instanceof Verb)) { - return new Verb(options); - } - - this.env = {}; - Base.apply(this, arguments); - this.isVerb = true; - this.isApp = true; - this.apps = {}; - - var opts = this.options; - this.name = opts.name || 'verb'; - - this.use(utils.middleware(opts)) - .use(utils.pipeline(opts)) - .use(utils.store()) - .use(actions()) - .use(pkg()) - .use(env()); - - this.initVerb(this); +function Verb(options) { + if (!(this instanceof Verb)) { + return new Verb(options); } + this.options = utils.extend({}, this.options, options); + Assemble.call(this, options); + this.is('Verb'); + this.initVerb(this.options); +} - /** - * Inherit assemble-core - */ - - Base.extend(Verb); - - /** - * Initialize verb - */ +/** + * Extend `Assemble` + */ - Verb.prototype.initVerb = function(app) { - this.store.create('config'); +Assemble.extend(Verb); - this.engine('hbs', require('engine-handlebars')); - this.engine(['md', 'text'], require('engine-base'), { - delims: ['{%', '%}'] - }); +/** + * Initialize verb defaults + */ - if (typeof preload === 'function') { - preload.call(app, app, this.base, this.env); - } - }; - - /** - * Similar to [copy](#copy) but calls a plugin `pipeline` if defined globally - * or on the `.process` method `options`. This allows plugin pipelines to - * be programmatically built-up and dynamically changed on-the-fly. - * - * ```js - * verb.process({src: ['a.txt', 'b.txt']}, options); - * ``` - * @param {Object} `files` - * @param {Object} `options` - * @param {Function} `cb` - * @return {Stream} Returns a [vinyl][] src stream - * @api public - */ - - Verb.prototype.process = function(files, options) { - options = options || {}; - files.options = files.options || {}; - var pipeline = files.options.pipeline || options.pipeline; - var opts = utils.extend({}, this.options, files.options, options); - - return this.src(files.src, opts) - .pipe(this.pipeline(pipeline, opts)) - .pipe(this.dest(files.dest, opts)); - }; - - /** - * Verb `files` configurations in parallel. - * - * ```js - * verb.each(files, function(err) { - * if (err) console.log(err); - * }); - * ``` - * @param {Object} `config` - * @param {Function} `cb` - * @api public - */ - - Verb.prototype.each = function(config, cb) { - async.each(config.files, function(files, next) { - this.process(files, files.options) - .on('error', next) - .on('end', next); - }.bind(this), cb); - return this; - }; - - /** - * Verb `files` configurations in series. - * - * ```js - * verb.eachSeries(files, function(err) { - * if (err) throw err; - * console.log('done!'); - * }); - * ``` - * @param {Object} `config` - * @param {Function} `cb` - * @api public - */ - - Verb.prototype.eachSeries = function(config, cb) { - async.eachSeries(config.files, function(files, next) { - this.process(files, files.options) - .on('error', next) - .on('end', next); - }.bind(this), cb); - }; - - /** - * Verb files from a declarative [scaffold][] configuration. - * - * ```js - * var Scaffold = require('scaffold'); - * var scaffold = new Scaffold({ - * options: {cwd: 'source'}, - * posts: { - * src: ['content/*.md'] - * }, - * pages: { - * src: ['templates/*.hbs'] - * } - * }); - * - * verb.scaffold(scaffold, function(err) { - * if (err) console.log(err); - * }); - * ``` - * @param {Object} `scaffold` Scaffold configuration - * @param {Function} `cb` Callback function that exposes `err`, called after the scaffold is generated. - * @api public - */ - - Verb.prototype.scaffold = function(scaffold, cb) { - async.eachOf(scaffold, function(target, name, next) { - this.each(target, next); - }.bind(this), cb); - }; - - /** - * Get the package.json from the current working directory. - */ - - Object.defineProperty(Verb.prototype, '_pkg', { - configurable: true, - set: function(pkg) { - this.cache.pkg = pkg; - }, - get: function() { - if (this.cache.pkg) { - return this.cache.pkg; - } - var pkg = this.cache.pkg - || this.get('env.user.pkg'); - return (this.cache.pkg = pkg); - } - }); +Verb.prototype.initVerb = function(opts) { + this.configfile = opts.configfile || 'verbfile.js'; + this.prefix = opts.prefix || 'verb-generate'; - /** - * Get the `base` instance - */ + this.data({runner: require('./package')}); + this.initPlugins(); - Object.defineProperty(Verb.prototype, 'base', { - configurable: true, - get: function() { - return this.parent ? this.parent.base : this; - } + this.create('files'); + this.create('docs'); + this.define('util', utils); + this.define('lazyCreate', function(name, opts) { + if (!this[name]) this.create(name, opts); }); - - return Verb; -} +}; /** - * Expose `Verb` with our baseline defaults + * Initialize verb plugins */ -module.exports = create(function(verb) { - function renameKey(key) { - return path.basename(key, path.extname(key)); +Verb.prototype.initPlugins = function() { + this.use(plugins.generators({prefix: 'verb-generate'})); + this.use(plugins.pipeline()); + this.use(plugins.loader()); + this.use(plugins.runner()); + this.use(plugins.rename({replace: true})); + this.use(plugins.ask()); + this.use(settings()); + this.use(config()); +}; + +Verb.prototype.conflicts = function(patterns, options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; } - verb.create('docs', { renameKey: renameKey }); - verb.create('includes', { - renameKey: renameKey, - viewType: ['partial'] - }); - verb.create('layouts', { - renameKey: renameKey, - viewType: ['layout'] + + this.overwrite = false; + var app = this; + + // function overwrite(filename, cb) { + // var msg = filename + ' already exists. Do you want to overwrite it?'; + // app.questions.set('conflict', msg, { save: false }); + + // app.ask('conflict', function(err, answers) { + + // }); + // } + + var views = this.collection(); + + utils.glob(patterns, options, function(err, files) { + if (err) return cb(err); + + async.each(files, function(fp, next) { + views.addView(fp); + next(); + }, function(err) { + if (err) { + cb(err); + } else { + cb(null, views); + } + }); }); -}); +}; /** - * Expose `create` to allow user to instantiate - * Verb with their own defaults + * Expose `Verb` */ -module.exports.create = create; +module.exports = Verb; diff --git a/lib/actions.js b/lib/actions.js deleted file mode 100644 index b7bd4665..00000000 --- a/lib/actions.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -/** - * Adds an `action` object with `get` and `set` methods - * to `app`, `collection` and `view` - */ - -module.exports = function() { - return function plugin(app) { - if (!app.has('cache.actions')) { - app.set('cache.actions', {}); - } - - app.mixin('actions', new Actions(app.cache.actions)); - return plugin; - }; -}; - -function Actions() {} - -Actions.prototype.set = function(key, val) { - if (typeof key === 'string' && typeof val === 'undefined') { - val = true; - } - utils.set(this, key, val); - return this; -}; - -Actions.prototype.has = function(key) { - return utils.has(this, key); -}; - -Actions.prototype.get = function(key) { - return utils.get(this, key); -}; diff --git a/lib/config.js b/lib/config.js index 8ac3e141..c3d16b3b 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,150 +1,76 @@ 'use strict'; -var sections = require('./sections'); -var updater = require('./updater'); -var loader = require('./loader'); -var utils = require('./utils'); - -module.exports = function(verb, base, env) { - verb.use(updater()); - - /** - * Update verb config (`verb` object in package.json) - * - * * `--init`: force verb to init a project - * * `--data`: add or extend verb config data to be used in templates - * * `--update`: this mapping is setup to be called by the cli `--config` - * command, and is used to extend, add, delete or otherwise update - * any given property on the config object - */ - - verb.config - .map('init', function(val) { - if (val === true) { - verb.enable('init'); - } - }) - .map('options', function(options) { - for (var key in options) { - var val = options[key]; - } - }) - .map('option', function(val) { - verb.option(val); - }) - .map('update', function(val) { - env = env || verb.env; - - var file = verb.docs.getView('package.json'); - if (!file || !file.json) return; - - var pkg = env.user.pkg; - if (!pkg) return; - - pkg.verb = pkg.verb || {}; - - try { - file.json.verb = verb.update(pkg.verb, val); - verb.set('cache.config', file.json.verb); - } catch (err) { - throw new Error('config cannot update pkg.verb' + err); - } - }); - - /** - * Template options and settings - */ - - verb.config - .map('toc', function(val) { - verb.option('toc', val); - }) - .map('create', function(val) { - verb.visit('create', val); - }); - - /** - * Load templates - */ - - verb.config - .map('sections', function(val) { - verb.set('cache.sections', sections.create(val, verb)); - }) - .map('includes', views('includes')) - .map('layouts', views('layouts')) - .map('layout', function(val) { - if (utils.isObject(val)) { - if (val.name) { - var readme = verb.docs.getView('readme.md'); - if (!readme) readme = verb.docs.getView('.verb'); - if (!readme) { - throw new Error('cannot find .verb.md'); - } - - readme.layout = val.name; - } - - if (val.sections) { - for (var key in val.sections) { - if (val.sections.hasOwnProperty(key)) { - var section = val.sections[key]; - verb.option('section.' + key, section); - } - } - } - } - }) - .map('badges', views('badges')) - .map('docs', views('docs')); +var path = require('path'); +var typeOf = require('kind-of'); +var merge = require('mixin-deep'); +var config = require('./config/'); +var plugins = require('./plugins'); + +module.exports = function(options) { + return function(app) { + this.use(plugins.config()); + + this.config + .map('data') + // .map('create') + // .map('templates') + .map('reflinks', function(val) { + val = Array.isArray(val) ? val : [val]; + // this.pkg.set('verb.reflinks', val); + }) + .map('options', config.options(this)) + .map('option', 'options') + .map('plugins', config.plugins(this)) + .map('helpers', config.helpers(this)) + .map('engines', config.engines(this)) + }; +}; - function views(name) { - return function(options) { - verb[name](options); - }; +function normalize(val, key, options, schema) { + var res = {}; + switch(typeOf(val)) { + case 'array': + res = configArray(val, key, options, schema); + break; + case 'object': + res = configObject(val, key, options, schema); + break; + case 'string': + res = configString(val, key, options, schema); + break; } + return res; +} - /** - * Load helpers from verb config in `package.json` - */ - - verb.config.map('helpers', loader('helpers', verb, function(name, opts, fn) { - name = name.slice(name.lastIndexOf('-') + 1); - verb.helper(name, fn); - })); +function configString(val, key, options, schema) { + var res = {}; + res[val] = { key: val }; + return configObject(res, key, options, schema); +} - /** - * Load asyncHelpers from verb config in `package.json` - */ +function configObject(obj, key, options, schema) { + var res = {}; - verb.config.map('asyncHelpers', loader('asyncHelpers', verb, function(name, opts, fn) { - name = name.slice(name.lastIndexOf('-') + 1); - verb.asyncHelper(name, fn); - })); + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + var val = obj[prop]; - /** - * Load middleware from verb config in `package.json` - */ + if (typeof val === 'string') { + val = { name: val }; + } - verb.config.map('use', loader('middleware', verb, function(name, opts, fn) { - // get the regex pattern to use - var pattern = opts.pattern ? new RegExp(opts.pattern) : /./; - // get the method name (onLoad, preWrite, preRender, etc) - var method = opts.method || 'onLoad'; - if (!(method in verb)) { - throw new Error('verb does not have handler: "' + method + '"'); } - // register the middleware - verb[method](pattern, fn(verb.options)); - })); - - /** - * Load plugins from verb config in `package.json` - */ - - verb.config.map('plugins', loader('plugins', verb, function(name, opts, fn) { - verb.plugin(name, opts, fn); - })); -}; - + } + return res; +} + +function configArray(val, key, options, schema) { + var len = val.length; + var idx = -1; + var res = {}; + while (++idx < len) { + merge(res, normalize(val[idx], key, options, schema)); + } + return res; +} diff --git a/lib/config/engines.js b/lib/config/engines.js new file mode 100644 index 00000000..ff930bd5 --- /dev/null +++ b/lib/config/engines.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function(app) { + return function(engines) { + for (var key in engines) { + if (engines.hasOwnProperty(key)) { + var val = engines[key]; + this.engine(key, val.fn, val.options); + } + } + }; +}; diff --git a/lib/config/helpers.js b/lib/config/helpers.js new file mode 100644 index 00000000..2f8de881 --- /dev/null +++ b/lib/config/helpers.js @@ -0,0 +1,17 @@ +'use strict'; + +module.exports = function(app) { + return function(helpers) { + for (var key in helpers) { + if (helpers.hasOwnProperty(key)) { + var val = helpers[key]; + + if (val.async = true) { + this.asyncHelper(key, val.fn); + } else { + this.helper(key, val.fn); + } + } + } + }; +}; diff --git a/lib/runner/commands/index.js b/lib/config/index.js similarity index 100% rename from lib/runner/commands/index.js rename to lib/config/index.js diff --git a/lib/config/options.js b/lib/config/options.js new file mode 100644 index 00000000..3bfc8ee3 --- /dev/null +++ b/lib/config/options.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = function(app) { + return function(val) { + this.option(val); + }; +}; diff --git a/lib/config/plugins.js b/lib/config/plugins.js new file mode 100644 index 00000000..8aa7185e --- /dev/null +++ b/lib/config/plugins.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * Load pipeline plugins + */ + +module.exports = function(app) { + return function(plugins) { + for (var key in plugins) { + if (plugins.hasOwnProperty(key)) { + var val = plugins[key]; + this.plugin(key, val.options, val.fn); + } + } + }; +}; diff --git a/lib/config/update.js b/lib/config/update.js new file mode 100644 index 00000000..9477c7ec --- /dev/null +++ b/lib/config/update.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = function(app) { + return function(val) { + console.log('implement me!'); + }; +}; diff --git a/lib/env.js b/lib/env.js deleted file mode 100644 index a97247d6..00000000 --- a/lib/env.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -/** - * Adds get/set methods to verb env - */ - -module.exports = function(options) { - return function(verb) { - if (!verb.env) { - verb.env = { - config: {}, - module: {}, - user: {} - }; - } - - utils.define(verb.env, 'set', function(key, value) { - utils.set(this, key, value); - return this; - }); - - utils.define(verb.env, 'get', function(key) { - return utils.get(this, key); - }); - }; -}; diff --git a/lib/errors.js b/lib/errors.js new file mode 100644 index 00000000..10067e94 --- /dev/null +++ b/lib/errors.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = function(options) { + return function(app) { + if (this.isRegistered('base-errors')) return; + // to do + this.errors = this.errrors || []; + }; +} diff --git a/lib/expand.js b/lib/expand.js deleted file mode 100644 index dc71b426..00000000 --- a/lib/expand.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -/** - * Adds get/set methods to verb env - * - * @param {Object} `verb` Instance of verb - * @param {Object} `config` Verb config data from package.json - */ - -module.exports = function(verb, config) { - var pkg = verb.get('env.user.pkg'); - if (!pkg) return; - - var data = utils.extend({}, pkg); - if (typeof pkg.author === 'string') { - data.author = utils.parseAuthor(pkg.author); - } - - data.author = formatAuthor(verb, data.author); - - var res = utils.defaults(config, data); - res.license = formatLicense(res, verb.options); - return res; -}; - -/** - * Format license - */ - -function formatLicense(pkg, options) { - options = options || {}; - if (typeof options.license === 'string') { - return options.license; - } - var str = pkg.license; - if (Array.isArray(pkg.licenses)) { - str = pkg.licenses[0]; - } - if (typeof str === 'undefined') { - return ''; - } - return 'Released under the ' + str + ' license.'; -} - -function formatAuthor(app, author) { - var config = app.store.config; - author = author || {}; - - if (!author.username && author.url) { - if (config.has('author.data.username')) { - author.username = config.get('data.author.username'); - } else if (/github\.com/.test(author.url)) { - var username = author.url.split('github.com').pop(); - author.username = username.replace(/^\W+|\W+$/g, ''); - } - } - return author; -} diff --git a/lib/generators/compose-example.js b/lib/generators/compose-example.js new file mode 100644 index 00000000..50423182 --- /dev/null +++ b/lib/generators/compose-example.js @@ -0,0 +1,29 @@ +// 'use strict'; + +// var combined = require('stream-combiner'); +// var filter = require('filter-views'); + +// module.exports = function(verb, base) { +// var toStream = combine(verb, ['foo', 'bar', 'baz']); + +// verb.task('files', function() { +// return toStream('docs', filter('*.qux')) +// .pipe(verb.dest('.')) +// }); +// } + +// function combine(app, generators) { +// return function(names, filter) { +// app.compose(generators).views(names, filter); +// var streams = []; + +// if (typeof names === 'string') { +// names = [names]; +// } + +// names.forEach(function(name) { +// streams.push(app.toStream(name)); +// }); +// return combined(streams); +// }; +// } diff --git a/lib/generators/default.js b/lib/generators/default.js new file mode 100644 index 00000000..8c47dade --- /dev/null +++ b/lib/generators/default.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function(verb) { + verb.register('welcome', require('./welcome')); + verb.register('verbfiles', require('./verbfiles')); + verb.register('verbfile', require('./verbfile')); + verb.register('verbmd', require('./verbmd')); + + verb.task('default', function(cb) { + verb.generate('verbfiles', cb); + }); +}; diff --git a/templates/layouts/basic.md b/lib/generators/templates/.verb.md similarity index 72% rename from templates/layouts/basic.md rename to lib/generators/templates/.verb.md index cd5d5290..87cfe7bf 100644 --- a/templates/layouts/basic.md +++ b/lib/generators/templates/.verb.md @@ -1,8 +1,3 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Basic layout ---- # {%= name %} {%= badge('npm') %} {%= badge('travis') %} > {%= description %} @@ -11,11 +6,17 @@ verb_docs: {%= include('install-npm', {save: true}) %} ## Usage -{% body %} + +```js +var {%= alias %} = require('{%= name %}'); +``` ## Running tests {%= include("tests") %} +## Related projects +{%= related(verb.related.list) %} + ## Contributing {%= include("contributing") %} @@ -29,3 +30,5 @@ verb_docs: *** {%= include("footer") %} + +{%= reflinks(verb.reflinks) %} diff --git a/lib/generators/templates/verbfile.js b/lib/generators/templates/verbfile.js new file mode 100644 index 00000000..fff2aa10 --- /dev/null +++ b/lib/generators/templates/verbfile.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function(verb) { + verb.doc('.verb.md'); + + verb.task('default', function(cb) { + console.log('verbfile > default task'); + cb(); + }); +}; diff --git a/lib/generators/verbfile.js b/lib/generators/verbfile.js new file mode 100644 index 00000000..675498b9 --- /dev/null +++ b/lib/generators/verbfile.js @@ -0,0 +1,31 @@ +'use strict'; + +var utils = require('../utils'); + +module.exports = function(verb) { + + verb.task('verbfile', function(cb) { + var opts = { cwd: verb.cwd, filename: 'verbfile.js' }; + utils.conflict(verb, opts, function(err, conflict) { + if (err) { + cb(err); + return; + } + + if (conflict === true) { + console.log('keeping existing verbfile.js'); + cb(); + return; + } + + verb.src('templates/verbfile.js', {cwd: __dirname}) + .pipe(verb.dest(verb.cwd)) + .on('end', function() { + console.log('created verbfile.js'); + cb(); + }); + }); + }); + + verb.task('default', ['verbfile']); +}; diff --git a/lib/generators/verbfile2.js b/lib/generators/verbfile2.js new file mode 100644 index 00000000..01983d4e --- /dev/null +++ b/lib/generators/verbfile2.js @@ -0,0 +1,48 @@ +'use strict'; + +var path = require('path'); +var isYes = require('is-affirmative'); +var utils = require('../utils'); + +module.exports = function(verb) { + verb.task('verbfile', function(cb) { + ask(verb, 'verbfile.js', cb); + }); + + verb.task('verbmd', function(cb) { + ask(verb, '.verb.md', cb); + }); + + verb.task('default', ['verbmd', 'verbfile']); +}; + +function ask(app, filename, cb) { + if (utils.exists(filename)) return cb(); + + app.questions + .set('verbfiles', 'Can\'t find a verbfile.js or .verb.md, want to add one?', {save: false}) + .set('choose', 'Which file(s) would you like to add?', {save: false}); + + app.ask('verbfiles', function(err, answers) { + if (err) return cb(err); + + if (isYes(answers.verbfiles)) { + app.choices('choose', ['verbfile.js', '.verb.md'], function(err, choices) { + if (err) return cb(err); + + app.src(choices.choose[0], {cwd: path.resolve(__dirname, 'templates')}) + .pipe(app.dest(app.cwd)) + .on('end', cb); + }); + + } else { + cb(); + } + }); +} + +function filter(name) { + return function(key, file) { + return file.basename === name; + }; +} diff --git a/lib/generators/verbfiles.js b/lib/generators/verbfiles.js new file mode 100644 index 00000000..ba1c5c64 --- /dev/null +++ b/lib/generators/verbfiles.js @@ -0,0 +1,15 @@ +'use strict'; + +var path = require('path'); +var utils = require('../utils'); + +module.exports = function(verb) { + verb.register('verbfile', require('./verbfile')); + verb.register('verbmd', require('./verbmd')); + + verb.task('verbfiles', function(cb) { + utils.ask(verb, '.verb.md', cb); + }); + + verb.task('default', ['verbfiles']); +}; diff --git a/lib/generators/verbfiles2.js b/lib/generators/verbfiles2.js new file mode 100644 index 00000000..2dc7f8f5 --- /dev/null +++ b/lib/generators/verbfiles2.js @@ -0,0 +1,45 @@ +'use strict'; + +var path = require('path'); +var isYes = require('is-affirmative'); +var utils = require('../utils'); + +module.exports = function(verb) { + verb.task('verbfile', function(cb) { + ask(verb, 'verbfile.js', cb); + }); + + verb.task('default', ['verbfile']); +}; + +function ask(app, filename, cb) { + console.log(filename) + if (utils.exists(filename)) return cb(); + + app.questions + .set('verbfiles', 'Can\'t find a verbfile.js or .verb.md, want to add one?', {save: false}) + .set('choose', 'Which file(s) would you like to add?', {save: false}); + + app.ask('verbfiles', function(err, answers) { + if (err) return cb(err); + + if (isYes(answers.verbfiles)) { + app.choices('choose', ['verbfile.js', '.verb.md'], function(err, choices) { + if (err) return cb(err); + + app.src(choices.choose[0], {cwd: path.resolve(__dirname, 'templates')}) + .pipe(app.dest(app.cwd)) + .on('end', cb); + }); + + } else { + cb(); + } + }); +} + +function filter(name) { + return function(key, file) { + return file.basename === name; + }; +} diff --git a/lib/generators/verbmd.js b/lib/generators/verbmd.js new file mode 100644 index 00000000..5b7c2cb0 --- /dev/null +++ b/lib/generators/verbmd.js @@ -0,0 +1,31 @@ +'use strict'; + +var utils = require('../utils'); + +module.exports = function(verb) { + + verb.task('verbmd', function(cb) { + var opts = { cwd: verb.cwd, filename: '.verb.md' }; + utils.conflict(verb, opts, function(err, conflict) { + if (err) { + cb(err); + return; + } + + if (conflict === true) { + console.log('keeping existing .verb.md'); + cb(); + return; + } + + verb.src('templates/.verb.md', {cwd: __dirname}) + .pipe(verb.dest(verb.cwd)) + .on('end', function() { + console.log('created .verb.md'); + cb(); + }); + }); + }); + + verb.task('default', ['verbmd']); +}; diff --git a/lib/generators/verbmd2.js b/lib/generators/verbmd2.js new file mode 100644 index 00000000..e07ef4e3 --- /dev/null +++ b/lib/generators/verbmd2.js @@ -0,0 +1,32 @@ +'use strict'; + +var isYes = require('is-affirmative'); + +module.exports = function(verb) { + verb.task('default', function(cb) { + verb.questions + .set('verbmd', 'Can\'t find a .verb.md, want to add one?', { + save: false + }); + + verb.ask('verbmd', function(err, answers) { + if (err) { + cb(err); + return; + } + + if (!isYes(answers.verbmd)) { + cb(); + return; + } + + verb.src('templates/.verb.md', {cwd: __dirname}) + .pipe(verb.dest(verb.cwd)) + .on('end', function() { + console.log('created verbfile.js'); + cb(); + }); + + }); + }); +}; diff --git a/lib/generators/welcome.js b/lib/generators/welcome.js new file mode 100644 index 00000000..5d186807 --- /dev/null +++ b/lib/generators/welcome.js @@ -0,0 +1,40 @@ +'use strict'; + +var debug = require('debug')('verb:generator'); +var isYes = require('is-affirmative'); + +module.exports = function(app) { + app.questions + .set('tasks', { + message: 'Would you like to choose generators or tasks to run automatically when you run verb?', + force: true + }) + .set('run.before', { + message: 'Enter the names of the generators and tasks to run:', + type: 'input', + force: true + }) + .set('run.after', { + message: 'Enter the names of the generators and tasks to run after `end` is emitted:', + type: 'input', + force: true + }); + + app.task('default', function(cb) { + app.ask('tasks', function(err, answers) { + if (isYes(answers.tasks)) { + app.ask('run', function(err, answers) { + answers.run.before = answers.run.before.split(' '); + answers.run.after = answers.run.after.split(' '); + var res = answers.run; + console.log('implement me!'); + console.log(res) + cb(); + }); + } else { + console.log('aborted'); + cb(); + } + }); + }); +}; diff --git a/lib/loader.js b/lib/loader.js deleted file mode 100644 index 378283b4..00000000 --- a/lib/loader.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var utils = require('./utils'); - -module.exports = function loader(prop, app, fn) { - if (typeof prop !== 'string') { - throw new TypeError('expected the first argument to be a string'); - } - - return function(config) { - var obj = app.get('env.argv.' + prop); - var cwd = app.get('env.user.cwd'); - var opts = {}; - var fp; - - if (typeof config === 'string') { - config = [config]; - - } else if (Array.isArray(config)) { - config = config.reduce(function(acc, val) { - if (typeof val === 'string') { - acc[val] = {}; - } else { - throw new TypeError('type not supported, implement me!'); - } - return acc; - }, {}); - } - - for (var key in config) { - var name = path.basename(key, path.extname(key)); - var val = config[key]; - - try { - if (name === 'index') { - var dir = path.resolve(cwd); - fp = utils.tryResolve(dir); - if (fs.existsSync(fp)) { - opts = val; - val = utils.tryRequire(fp); - name = path.basename(dir); - } - } - } catch (err) { - throw err; - } - - if (typeof val === 'string') { - throw new TypeError('type not supported. implement me!'); - } - - if (utils.isObject(val)) { - opts = val; - name = opts.name || name; - var filepath = opts.path || key || name; - val = utils.loadModule(filepath, cwd); - if (val === null) { - fp = path.resolve(cwd, filepath); - throw new Error('cannot resolve ' + prop + ' at path: ' + fp); - } - } - - opts = opts || {}; - if (typeof name !== 'string') { - throw new TypeError('expected ' + name + ' to be a string'); - } - if (typeof val !== 'function') { - var msg = 'expected ' + val - + ' to be a function. Cannot load ' - + prop + ' ' + JSON.stringify(arguments); - throw new Error(msg); - } - - name = name.replace(/^[-\W]*(verb|helper|middleware|plugin)*[-\W]*/, ''); - fn(name, opts, val); - } - }; -}; diff --git a/lib/pkg.js b/lib/pkg.js deleted file mode 100644 index 7010b72d..00000000 --- a/lib/pkg.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -/** - * Adds get/set methods to verb env - */ - -module.exports = function() { - return function(verb) { - verb.mixin('pkg', new Pkg(verb, '_pkg')); - }; -}; - -function Pkg(config, prop) { - this.config = config || {}; - this.prop = prop; -} - -Pkg.prototype.set = function(key, value) { - utils.set(this.config, [this.prop, key], value); - return this; -}; - -Pkg.prototype.get = function(key) { - return utils.get(this.config, [this.prop, key]); -}; - diff --git a/lib/plugins.js b/lib/plugins.js new file mode 100644 index 00000000..e018c9ac --- /dev/null +++ b/lib/plugins.js @@ -0,0 +1,23 @@ +'use strict'; + +var plugins = require('lazy-cache')(require); +var fn = require; +require = plugins; + +/** + * Plugins + */ + +require('assemble-loader', 'loader'); +require('base-config', 'config'); +require('base-fs-rename', 'rename'); +require('base-generators', 'generators'); +require('base-runner', 'runner'); +require('base-pipeline', 'pipeline'); +require('base-questions', 'ask'); + +/** + * Expose plugins + */ + +module.exports = plugins; diff --git a/lib/runner/cli.js b/lib/runner/cli.js deleted file mode 100644 index d683372f..00000000 --- a/lib/runner/cli.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -var commands = require('./commands'); -var utils = require('../utils'); - -/** - * Verb CLI - * - * Custom extensions to the built-in mappings - * provided by the `base-cli` plugin. - */ - -module.exports = function(verb) { - - /** - * Help and information-related - */ - - verb.cli - .map('init', function(fp) { - console.log('cli > init (implement me!)'); - verb.set('questions.options.forceAll', true); - }) - .map('help', commands.help(verb)) - .map('show', commands.show(verb)) - .map('open', commands.open(verb)) - .map('diff', function(val) { - verb.option('diff', val); - }); - - /** - * Options, settings and context related - */ - - verb.cli - .map('ask', commands.ask(verb)) - .map('cwd', function(val) { - verb.option('cwd', val); - }) - .map('save', function(val) { - verb.store.config.set(val); - val = utils.tableize(val); - console.log('saved > "%j" %s', val, 'in global config store.'); - }) - .map('data', function(val) { - verb.data(val); - }) - .map('option', function(val) { - verb.option(val); - }) - .map('config', function(val) { - verb.config.process({ - update: val - }); - }); - - /** - * Task-related - */ - - verb.cli - .map('choose', function(key) { - if (key === true) { - verb.enable('tasks.choose'); - } - }) - .map('tasks', function(key) { - if (key === true) { - verb.enable('tasks.display'); - } - }); - -}; diff --git a/lib/runner/commands/ask.js b/lib/runner/commands/ask.js deleted file mode 100644 index 1be2a854..00000000 --- a/lib/runner/commands/ask.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var utils = require('../../utils'); - -module.exports = function(app) { - return function(val) { - if (val === true) { - app.enable('questions.init'); - return; - } - - if (utils.isObject(val)) { - var keys = Object.keys(utils.tableize(val)); - app.questions.enqueue(keys); - app.option('questions.init', keys); - return; - } - - var keys = utils.arrayify(val); - app.questions.enqueue(keys); - app.option('questions.init', keys); - }; -}; diff --git a/lib/runner/commands/help.js b/lib/runner/commands/help.js deleted file mode 100644 index 9a7c55ba..00000000 --- a/lib/runner/commands/help.js +++ /dev/null @@ -1,106 +0,0 @@ -'use strict'; - -var wrap = require('word-wrap'); - -module.exports = function(app) { - return function(key) { - // var commands = help(); - // console.log(commands.options[key]); - // process.exit(0); - }; -}; - -/** - * Create `help` documentation - */ - -function help() { - return { - heading: '', - options: { - init: { - description: 'Force initialization questions to be re-asked.', - example: '', - short: 'i' - }, - help: { - description: '', - example: '', - short: 'h' - }, - show: { - description: '', - example: '', - short: null - }, - ask: { - description: '', - example: '', - short: null - }, - open: { - description: '', - example: '', - short: 'o' - }, - config: { - description: '', - example: '', - short: 'c' - }, - diff: { - description: '', - example: '', - short: null - }, - cwd: { - description: '', - example: '', - short: null - }, - data: { - description: '', - example: '', - short: 'd' - }, - choose: { - description: '', - example: '', - short: null - }, - tasks: { - description: '', - example: '', - short: null - } - }, - footer: '' - }; -} - -function format(obj) { - var heading = obj.heading || ''; - var optsList = ''; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - var val = obj[key]; - - optsList += toFlag(key, val.short); - optsList += wrap(val.description); - } - } - - return heading + '\n' - + optsList + '\n' - + obj.footer || ''; -} - -function toFlag(key, short) { - return shortKey(short) + '--' + key + ' '; -} - -function shortKey(sh) { - return sh ? ('-' + sh + ', ') : ' '; -} - -// console.log(format(help())) diff --git a/lib/runner/commands/open.js b/lib/runner/commands/open.js deleted file mode 100644 index 71cc0402..00000000 --- a/lib/runner/commands/open.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('../../utils'); - -module.exports = function(app) { - return function(val) { - if (val === 'answers') { - var dest = app.get('questions.dest'); - if (dest) { - console.log('opening answers data directory >', '"' + dest + '"'); - utils.opn(dest); - process.exit(0); - } - } - - if (val === 'store') { - var dir = path.dirname(app.get('store.path')); - if (dir) { - console.log('opening store data directory >', '"' + dir + '"'); - utils.opn(dir); - process.exit(0); - } - } - }; -}; diff --git a/lib/runner/commands/show.js b/lib/runner/commands/show.js deleted file mode 100644 index b70841d1..00000000 --- a/lib/runner/commands/show.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -var utils = require('../../utils'); - -module.exports = function(app) { - return function(key) { - if (utils.isObject(key)) { - key = utils.tableize(key); - } - - if (key === 'answers') { - app.on('answers', console.log); - return; - } - - if (key === 'commands') { - console.log(app.commands.sort()); - return; - } - }; -}; diff --git a/lib/runner/context.js b/lib/runner/context.js deleted file mode 100644 index 94516581..00000000 --- a/lib/runner/context.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -var data = require('./data'); - -module.exports = function(verb, base, env) { - data(verb, base, env); -}; diff --git a/lib/runner/create.js b/lib/runner/create.js deleted file mode 100644 index 36433b9b..00000000 --- a/lib/runner/create.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var path = require('path'); - -/** - * Create default template collections - */ - -module.exports = function(options) { - return function(app) { - create(app, 'docs'); - create(app, 'jsFiles'); - create(app, 'badges', 'partial'); - create(app, 'includes', 'partial'); - create(app, 'layouts', 'layout'); - create(app, 'verb_sections', 'partial'); - }; -}; - -/** - * Create a view collection if it doesn't already exist. - * - * @param {String} `app` Verb instance ("app", since it's also compatible with assemble and other `base` apps) - * @param {String} `name` - * @param {String} `type` - * @return {String} - */ - -function create(app, name, type) { - if (app[name]) return; - type = type || 'renderable'; - - return app.create(name, { - engine: 'text', - viewType: type, - renameKey: function(key) { - return path.basename(key, path.extname(key)); - } - }); -} diff --git a/lib/runner/data.js b/lib/runner/data.js deleted file mode 100644 index 660be929..00000000 --- a/lib/runner/data.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -var expandPkg = require('../expand'); -var utils = require('../utils'); - -module.exports = function(verb, base, env) { - verb.data({year: new Date().getFullYear()}); - verb.data({ - runner: { - name: 'verb', - url: 'https://github.com/verbose/verb' - }, - verb: { - reflinks: [], - related: {list: []}, - sections: {} - } - }); -}; - -module.exports.updateData = function(verb) { - verb.questions.enqueue('author', 'name', 'description'); - - var pkg = verb._pkg; - var config = verb.pkg.get('verb') || {}; - - verb.option(config.options || {}); - verb.data(pkg); - verb.data(verb.pkg.get('verb.data') || {}); - - var res = utils.defaults(pkg, verb.cache.data); - return expandPkg(verb, res); -}; diff --git a/lib/runner/defaults.js b/lib/runner/defaults.js deleted file mode 100644 index dd56dd13..00000000 --- a/lib/runner/defaults.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -var middleware = require('./middleware'); -var questions = require('./questions'); -var helpers = require('./helpers'); - -module.exports = function(verb, base, env) { - helpers(verb, base, env); - middleware(verb, base, env); - questions(verb, base, env); -}; diff --git a/lib/runner/helpers.js b/lib/runner/helpers.js deleted file mode 100644 index 6b6123ad..00000000 --- a/lib/runner/helpers.js +++ /dev/null @@ -1,194 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var resolve = require('resolve'); -var utils = require('../utils'); - -module.exports = function(verb, base, env) { - var apidocs = require('helper-apidocs'); - var issue = require('helper-issue'); - - verb.helper('resolve', function(name) { - var base = path.resolve(process.cwd(), 'node_modules', name); - var pkg = require(path.join(base, 'package.json')); - var cwd = path.join(base, pkg.main); - - var res = {}; - res.pkg = pkg; - res.cwd = path.relative(process.cwd(), cwd); - res.dest = pkg.homepage; - return res; - }); - - verb.helper('exists', function(fp) { - return fs.existsSync(fp); - }); - - verb.asyncHelper('section', function(title, name, locals, cb) { - if (typeof locals === 'function') { - cb = locals; - locals = {}; - } - - var opts = utils.merge({}, verb.options, this.context.view.options); - var view = this.app.includes.getView(name); - - if (!view) { - cb(new Error('section helper cannot find include: ' + name)); - return; - } - - var section = opts.section[name]; - if (section === false) { - cb(null, ''); - return; - } - - // title = section.title || title; - - var ctx = {}; - ctx = utils.merge({}, ctx, this.context.view.data); - ctx = utils.merge({}, ctx, this.context); - ctx = utils.merge({}, ctx, view.locals, view.data); - ctx = utils.merge({}, ctx, locals); - - if (utils.isObject(section)) { - ctx = utils.merge({}, ctx, section); - } - - view.render(ctx, function(err, res) { - if (err) return cb(err); - var str = ''; - if (title.charAt(0) !== '#') { - str = '## ' + title + '\n'; - } else { - str = title + '\n'; - } - str += res.content; - cb(null, str); - }); - }); - - verb.helper('log', function(msg) { - console.log.apply(console, arguments); - }); - - verb.helper('require', function(name) { - return utils.tryRequire(name); - }); - - verb.helper('get', function(key) { - return utils.get(this.context, key); - }); - - verb.helper('shield', function(type) { - return 'https://img.shields.io/' + type + '/{%= author.username %}/{%= name %}.svg'; - }); - - verb.helper('date', require('helper-date')); - verb.helper('issue', function(options) { - var opts = utils.extend({}, options); - opts.owner = opts.owner || utils.get(this, 'context.author.username'); - opts.repo = this.context.name; - return issue(opts); - }); - - verb.helper('example', function(str, name) { - return example(str, name).trim(); - }); - - verb.helper('read', function(fp) { - return fs.readFileSync(fp, 'utf8'); - }); - verb.helper('apidocs', apidocs({delims: ['{%', '%}']})); - verb.helper('depdocs', function(name, filename, context) { - if (typeof filename !== 'string') { - context = filename; - filename = null; - } - - var main = resolve.sync(name, {basedir: process.cwd()}); - var fp = main; - var pkg = {}; - - if (filename) { - var dir = path.dirname(main); - pkg = require(path.join(dir, 'package.json')); - fp = path.resolve(dir, filename); - } - - this.app.docs.addView(fp, { - dest: utils.toGithub(pkg.repository, filename), - content: fs.readFileSync(fp) - }); - - var fn = apidocs({delims: ['{%', '%}']}); - return fn.call(this, fp, context); - }); - - verb.helper('copyright', require('helper-copyright')({ - linkify: true - })); - - verb.asyncHelper('related', utils.related({verbose: true})); - verb.asyncHelper('reflinks', utils.reflinks({verbose: true})); - - verb.helper('gt', function(a, b, fn, efn) { - console.log(arguments); - if (a > b) { - return fn; - } - return efn; - }); - - verb.asyncHelper('npm', function(cb) { - var request = require('request'); - var JSONStream = require('JSONStream'); - - var url = 'https://api.npmjs.org/downloads/range/'; - url += '1900-01-01'; - url += ':' + '3000-01-01'; - url += '/' + this.context.name; - var res = []; - - return request(url) - .on('error', console.error) - .pipe(JSONStream.parse('downloads.*')) - .on('data', function(data) { - res.push(data); - }) - .on('end', function() { - // do stuff with res - var num = res.reduce(function(acc, obj) { - return acc += obj.downloads; - }, 0); - // console.log(num) - - cb(null, num); - }); - - // 'https://api.npmjs.org/downloads/range/2015-11-01:2015-12-01/micromatch' - }); -}; - -/** - * Create a code example from the contents of the specified - * JavaScript file. - * - * ```js - * {%%= example("foo", {name: "my-module"}) %} - * ``` - * - * @param {String} `fp` The path of the file to include. - * @param {String} `options` - * @option {String} `name` Replace `./` in `require('./')` with the given name. - * @return {String} - */ - -function example(str, name) { - if (typeof str !== 'string') { - throw new TypeError('example-helper expects a string.'); - } - return str.replace(/\((['"])\.\/\1\)/g, '(\'' + name + '\')'); -} diff --git a/lib/runner/middleware.js b/lib/runner/middleware.js deleted file mode 100644 index ddd96698..00000000 --- a/lib/runner/middleware.js +++ /dev/null @@ -1,100 +0,0 @@ -'use strict'; - -var diff = require('base-diff'); -var mdu = require('middleware-utils'); -var middleware = require('./middleware/'); -var utils = require('../utils'); - -module.exports = function(app, base, env) { - /** - * Table of contents > adds `file.data.toc` property - */ - - var append = '_(TOC generated by [verb](https://github.com/verbose/verb) ' - + 'using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_'; - - /** - * Readme dest path - */ - - app.onLoad(/\.verb\.md/, function(view, next) { - view.options.section = view.options.section || {}; - view.path = view.dest = 'readme.md'; - next(); - }); - - // extend comments with code examples - app.onLoad(/\.md/, middleware.examples(app)); - - // format markdown between TOC-creation and TOC-injection - app.postRender(/\.md/, mdu.series([ - middleware.toc.create(app, append), - ]), middleware.error('postRender')); - - // format markdown between TOC-creation and TOC-injection - app.preWrite(/./, mdu.series([ - middleware.toc.inject(app, append), - ]), middleware.error('preWrite')); - - app.preWrite(/./, function(view, next) { - view.content = view.content.replace(/\)[\s\n]+\[!\[/g, ') [!['); - next(); - }); - - // add reflinks - app.postRender(/(default|readme|\.verb)/, mdu.series([ - middleware.reflinks(app), - ]), middleware.error('preWrite')); - - /** - * Merge options onto `view.data` - */ - - app.preLayout(/\.md$/, function(view, next) { - view.data.options = utils.merge({}, app.options, view.data.options, view.options); - next(); - }); - - app.preRender(/./, function(view, next) { - view.data.options = utils.merge({}, view.data.options, view.options); - next(); - }); - - /** - * Diff files (currently just testing readme) - */ - - app.preRender(/./, diff.view('diffLines')); - app.postRender(/./, function(view, next) { - var diff = app.option('diff'); - if (diff === true || diff === view.stem) { - view.diff(); - } - next(); - }); - - /** - * Determine layout - */ - - app.preLayout(/\.md/, function(view, next) { - if (view.isType('partial')) { - next(); - return; - } - var layout = app.pkg.get('verb.layout'); - if (utils.isObject(layout)) { - var opts = layout; - layout = layout.name; - } - if (typeof layout !== 'undefined') { - view.layout = layout; - } - next(); - }); - - app.preWrite(/package\.json$/, function(view, next) { - view.dest = 'package.json'; - next(); - }); -}; diff --git a/lib/runner/middleware/error.js b/lib/runner/middleware/error.js deleted file mode 100644 index 832a7497..00000000 --- a/lib/runner/middleware/error.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -/** - * Add a reflinks helper to - */ - -module.exports = function(method) { - return function(err, view, next) { - if (!err) return next(); - console.error(method + ' ' + err.message); - next(); - }; -}; diff --git a/lib/runner/middleware/examples.js b/lib/runner/middleware/examples.js deleted file mode 100644 index fbe1c9fa..00000000 --- a/lib/runner/middleware/examples.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -var extract = require('gfm-code-blocks'); - -/** - * Detect the layout to use - */ - -module.exports = function(app) { - - return function(view, next) { - var examples = {}; - - var str = view.content; - extract(str).forEach(function(block) { - block.orig = block.block; - var m = /^\/\/\s*example(\.[^\n]+)([\s\S]+)/.exec(block.code); - if (!m) return next(); - var name = m[1]; - examples[name] = examples[name] || []; - view.content = view.content.split(block.block).join(''); - block.block = '```js\n' + m[2] + '\n```\n'; - examples[name].push(block); - }); - - app.set('cache.data.examples', examples); - next(); - }; -}; diff --git a/lib/runner/middleware/reflinks.js b/lib/runner/middleware/reflinks.js deleted file mode 100644 index cda4a50f..00000000 --- a/lib/runner/middleware/reflinks.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -var utils = require('../../utils'); - -/** - * Add a reflinks helper to - */ - -module.exports = function(verb) { - return addReflinks(verb, { verbose: true }); -}; - -function addReflinks(app, opts) { - var cache = {}; - - return function(view, next) { - if (!view.isType('renderable') || cache[view.path]) { - return next(); - } - - var arr = app.pkg.get('verb.reflinks'); - if (Array.isArray(arr)) { - var reflinks = utils.reflinks(opts); - - reflinks(arr, function(err, res) { - if (err) return next(err); - cache[view.path] = true; - // only show logging message once - opts.verbose = false; - view.content += '\n\n' + res; - next(); - }); - } else { - next(); - } - }; -} - -module.exports.addReflinks = addReflinks; diff --git a/lib/runner/middleware/toc.js b/lib/runner/middleware/toc.js deleted file mode 100644 index 0dfc7363..00000000 --- a/lib/runner/middleware/toc.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -var toc = require('markdown-toc'); -var utils = require('../../utils'); - -/** - * Add a TOC to the context of a view - */ - -exports.create = function(app, append) { - return function(view, next) { - view.options.toc = view.options.toc || {}; - var opts = utils.merge({append: append}, app.options, view.options); - if (view.isType('partial') || opts.toc === false || view.data.toc) { - next(); - return; - } - - view.options.toc.hasMarker = hasMarker(view); - if (view.options.toc.hasMarker && !view.data.toc) { - view.data.toc = toc(view.content).content; - } - - next(); - }; -}; - -exports.inject = function(app, append) { - return function(view, next) { - var str = view.data.toc; - if (!str || view.options.toc.hasMarker === false) { - return next(); - } - - if (append) { - str += '\n\n' + append; - } - - view.content = injectToc(view.content, str); - next(); - }; -}; - -function hasMarker(view) { - return /').join(''); - str = str.split('').join(''); - return str; -} - -function injectToc(str, toc) { - if (!toc) return str; - str = str.split('').join(toc); - str = str.split('').join(toc); - - // remove escaped HTML comments - str = str.split(' -1) { - var seg = key.split('.').shift(); - if (bkeys.indexOf(seg) === -1) { - bkeys.push(seg); - } - } - - if (val && !orig) { - utils.set(res, key, val); - } else { - utils.set(res, key, orig || val); - } - } - - akeys.forEach(function(key) { - if (bkeys.indexOf(key) === -1) { - utils.set(res, key, utils.get(a, key)); - } - }); - return res; -}; - -utils.expandConfig = function(config, data) { - var expand, res; - try { - expand = utils.expand(config); - data = utils.merge({}, config, data); - res = expand(config, data); - } catch (err) { - return config; - } - - function format(obj) { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - var val = obj[key]; - if (Array.isArray(val)) { - obj[key] = utils.unique(utils.flatten(val)).sort(); - } else if (utils.isObject(val)) { - obj[key] = format(val); - } else { - obj[key] = val; - } - } - } - return obj; +utils.exists = function(filepath) { + if (typeof filepath === 'undefined') { + return false; } - return format(res); + var val = utils.tryOpen(path.resolve(filepath), 'r'); + return typeof val === 'number'; }; -/** - * Cast val to an array. - */ - utils.arrayify = function(val) { - if (typeof val === 'undefined' || val === null || val === '') { - return []; - } - return Array.isArray(val) ? val : [val]; + return val ? (Array.isArray(val) ? val : [val]) : []; }; -/** - * Convenience method for loading files. - */ - -utils.globFiles = function(patterns, options) { - var opts = utils.extend({dot: true}, options); - opts.cwd = opts.cwd || process.cwd(); - opts.ignore = ['**/.DS_Store', '**/.git']; - opts.realpath = true; - return utils.glob.sync(patterns, opts); +utils.renameKey = function(key, view) { + return view ? view.filename : path.basename(key, path.extname(key)); }; -utils.toRepo = function(gh) { - if (utils.isObject(gh)) { - gh = gh.url; +utils.conflict = function(app, options, cb) { + if (typeof cb !== 'function') { + throw new TypeError('expected a callback function'); } - if (!/https:/.test(gh)) { - return gh; + options = options || {}; + if (typeof options.filename !== 'string') { + throw new TypeError('expected options.filename to be a string'); } - var parsed = utils.parseGithubUrl(gh); - return parsed.repopath; -}; - -utils.toGithub = function(repo, filepath) { - return 'https://github.com/' - + utils.toRepo(repo) + '/blob/master/' - + filepath; -}; - -/** - * Try to read a directory - */ - -utils.tryReaddir = function(dir) { - try { - return fs.readdirSync(dir); - } catch (err) {} - return []; -}; - -/** - * Try to read a directory - */ - -utils.tryResolve = function(fp, cwd) { - try { - cwd = utils.resolveDir(cwd || process.cwd()); - return utils.resolve.sync(fp, { basedir: cwd }); - } catch (err) {} - return null; -}; - -/** - * Try to require a file - */ - -utils.tryRequire = function(name) { - try { - return require(name); - } catch (err) {} - - try { - return require(path.resolve(name)); - } catch (err) {} - return {}; -}; - -/** - * Resolve module the module to use from the given cwd. - * - * @param {String} `name` - * @return {String|Null} - */ - -utils.resolveModule = function(name, cwd) { - if (typeof name === 'undefined') { - throw new TypeError('expected name to be a string'); + var filename = options.filename; + var fp = path.resolve(options.cwd || process.cwd(), filename); + app.questions.set('conflict', filename + ' exists, want to overwrite it?', {save: false}); + if (utils.exists(fp)) { + app.ask('conflict', function(err, answers) { + if (err) return cb(err); + + cb(null, !utils.isAffirmative(answers.conflict)); + }); + } else { + cb(null, false); } - name = utils.resolveDir(name); - if (cwd && path.basename(cwd) === name) { - var fp = utils.tryResolve(cwd); - if (fp) return fp; - } - return utils.tryResolve(name, cwd) - || utils.tryResolve(name, utils.gm) - || utils.tryResolve(name); }; -/** - * Try to resolve and load a module, either from the given - * cwd or from global npm packages. - * - * @param {String} `name` - * @return {String|Null} - */ - -utils.loadModule = function(name, cwd) { - var main = utils.resolveModule(name, cwd); - return main ? utils.tryRequire(main) : null; -}; - -/** - * Modified from the `tableize` lib, which replaces - * dashes with underscores, and we don't want that behavior. - * Tableize `obj` by flattening and normalizing the keys. - * - * @param {Object} obj - * @return {Object} - * @api public - */ - -utils.tableize = function tableize(obj, opts) { - var ret = {}; - opts = opts || {}; - type(ret, obj, '', opts); - return ret; -}; - -/** - * Type `obj` recursively. - * - * @param {Object} schema - * @param {Object} obj - * @param {String} prefix - * @api private - */ +utils.ask = function(app, filename, cb) { + if (app.disabled('force') && utils.exists(filename)) { + cb(); + return; + } -function type(schema, obj, prefix, opts) { - Object.keys(obj).forEach(function(key) { - var val = obj[key]; + // create a question + app.questions.set('writefile', 'Can\'t find "' + filename + '", want to add one?', { + save: false + }); - key = prefix + key; - if (opts.lowercase) key = key.toLowerCase(); + // ask the question created above + app.ask('writefile', function(err, answers) { + if (err) { + cb(err); + return; + } - if (utils.isObject(val)) { - type(schema, val, key + '.', opts); - } else { - schema[key] = val; + // if the answer is falsey, we're done + if (!utils.isAffirmative(answers.writefile)) { + cb(); + return; } + + // if the answer is "affirmative", write the file + app.src(filename, {cwd: path.resolve(__dirname, 'generators/templates')}) + .pipe(app.dest(app.cwd)) + .on('end', function() { + console.log('created', filename); + cb(); + }); }); -} +}; /** - * Expose `utils` modules + * Expose utils */ module.exports = utils; diff --git a/package.json b/package.json index 83af7681..b0043a57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "verb", - "description": "Powerful, highly pluggable and easy-to-use documentation generator with an expressive API. Use any docs parser or template engine, supports gulp plugins, middleware...", - "version": "0.9.0", + "description": "Documentation generator for GitHub projects.", + "version": "0.1.0", "homepage": "https://github.com/verbose/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", "repository": "verbose/verb", @@ -10,152 +10,66 @@ }, "license": "MIT", "files": [ - "cli.js", - "index.js", - "lib/", - "verbfile.js" + "index.js" ], "main": "index.js", + "bin": { + "verb": "bin/verb.js" + }, "engines": { "node": ">=0.10.0" }, "scripts": { "test": "mocha" }, - "preferGlobal": true, - "bin": { - "verb": "cli.js" - }, "dependencies": { - "JSONStream": "^1.0.7", - "ansi-colors": "^0.1.0", - "arr-flatten": "^1.0.1", - "array-unique": "^0.2.1", - "assemble-core": "^0.8.0", - "assemble-loader": "^0.2.4", - "base-diff": "^0.1.0", - "base-list": "^0.1.4", - "base-pipeline": "^0.1.4", - "base-questions": "^0.2.2", - "base-runner": "^0.4.4", - "base-store": "^0.3.1", - "common-middleware": "^0.2.1", - "composer-runtimes": "^0.7.0", - "define-property": "^0.2.5", - "engine-base": "^0.1.2", - "engine-handlebars": "^0.8.0", - "expand": "^0.4.0", - "export-files": "^2.1.0", + "assemble-core": "^0.11.2", + "assemble-loader": "^0.3.0", + "async": "^1.5.2", + "base-config": "^0.4.0", + "base-fs-rename": "^0.1.0", + "base-generators": "^0.1.4", + "base-pipeline": "^0.2.1", + "base-questions": "^0.3.0", + "base-runner": "^0.5.6", + "base-runtimes": "^0.1.2", + "debug": "^2.2.0", + "export-files": "^2.1.1", "extend-shallow": "^2.0.1", - "get-value": "^2.0.2", - "gfm-code-blocks": "^0.3.0", - "global-modules": "^0.2.0", - "has-glob": "^0.1.1", - "has-value": "^0.3.0", - "helper-apidocs": "^0.5.0", - "helper-copyright": "^2.0.0", - "helper-date": "^0.2.2", - "helper-issue": "^0.2.0", - "helper-reflinks": "^3.0.1", - "helper-related": "^0.12.1", - "is-primitive": "^2.0.0", - "is-valid-glob": "^0.3.0", - "isobject": "^2.0.0", + "is-affirmative": "^0.1.0", "kind-of": "^3.0.2", - "lazy-cache": "^1.0.2", - "map-config": "^0.3.0", - "markdown-toc": "^0.12.2", - "matched": "^0.3.2", - "middleware-utils": "^0.1.4", - "minimist": "^1.2.0", + "lazy-cache": "^1.0.3", + "map-schema": "^0.1.1", + "matched": "^0.4.1", + "merge-settings": "^0.1.0", "mixin-deep": "^1.1.3", - "object-visit": "^0.3.4", - "opn": "^3.0.3", - "parse-author": "^0.2.0", - "parse-github-url": "^0.2.1", - "request": "^2.67.0", - "resolve": "^1.1.6", - "resolve-dir": "^0.1.0", - "set-value": "^0.3.2", - "stream-exhaust": "^1.0.1", - "success-symbol": "^0.1.0", - "template-toc": "^0.6.2", - "time-stamp": "^0.1.3", - "unset-value": "^0.1.1", - "word-wrap": "^1.1.0" + "resolve-glob": "^0.1.8", + "try-open": "^0.1.0" }, "devDependencies": { - "async": "^1.5.0", - "base-config": "^0.3.2", - "base-methods": "^0.6.1", - "buffer-equal": "0.0.1", - "consolidate": "^0.13.1", - "coveralls": "^2.11.4", - "data-store": "^0.12.0", - "event-stream": "^3.3.2", - "graceful-fs": "^4.1.2", + "generate-foo": "^0.1.5", + "generator-util": "^0.2.0", + "global-modules": "^0.2.0", "gulp": "^3.9.0", "gulp-eslint": "^1.1.1", - "gulp-format-md": "^0.1.4", + "gulp-format-md": "*", "gulp-istanbul": "^0.10.3", "gulp-mocha": "^2.2.0", - "is-buffer": "^1.1.0", - "istanbul": "^0.4.1", - "load-pkg": "^3.0.0", + "inflection": "^1.8.0", "mocha": "*", - "parser-front-matter": "^1.3.0", - "resolve-glob": "^0.1.7", - "rimraf": "^2.4.4", - "scaffold": "^0.2.1", - "should": "*", - "sinon": "^1.17.2", - "swig": "^1.4.2", - "through2": "^2.0.0", - "vinyl": "^1.1.0" + "spawn-commands": "^0.3.1" + }, + "keywords": [], + "lintDeps": { + "ignore": [] }, - "keywords": [ - "app", - "boilerplate", - "create", - "init", - "initialize", - "project", - "scaffold", - "template", - "templates", - "verb", - "verbApp", - "webapp", - "yeoman" - ], "verb": { - "related": { - "list": [ - "assemble", - "assemble-core", - "base-methods", - "base-resolver", - "base-runner", - "resolve-modules" - ] - }, - "layout": { - "name": "default", - "sections": { - "install-npm": false - } - }, + "layout": "default", "plugins": [ "gulp-format-md" ], - "reflinks": [ - "assemble", - "assemble-core", - "base-methods", - "gulp", - "scaffold", - "verb", - "vinyl" - ] + "related": { + "list": [] + } } } diff --git a/readme.md b/readme.md deleted file mode 100644 index a017fbf6..00000000 --- a/readme.md +++ /dev/null @@ -1,273 +0,0 @@ -# verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg)](https://travis-ci.org/verbose/verb) - -> Powerful, highly pluggable and easy-to-use documentation generator with an expressive API. Use any docs parser or template engine, supports gulp plugins, middleware... - -- [Install](#install) -- [Usage](#usage) -- [CLI](#cli) - * [Commands](#commands) - + [help](#help) - + [init](#init) - + [diff](#diff) - * [Run tasks](#run-tasks) -- [API](#api) - * [[Verb](index.js#L38)](#-verb--indexjs-l38-) - * [[.process](index.js#L100)](#-process--indexjs-l100-) - * [[.each](index.js#L124)](#-each--indexjs-l124-) - * [[.eachSeries](index.js#L147)](#-eachseries--indexjs-l147-) - * [[.scaffold](index.js#L179)](#-scaffold--indexjs-l179-) -- [Apps](#apps) -- [TODO](#todo) -- [Release history](#release-history) -- [Related projects](#related-projects) -- [Running tests](#running-tests) -- [Contributing](#contributing) -- [Author](#author) -- [License](#license) - -_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ - -See the [release history](#release-history) for updates, news about new features, or to stay on top of breaking changes. - -Also consider installing [update][]. Update works as a companion to verb by automatically updating your `.verb.md` and other templates, based on your preferences. Update is completely customizable and runs on plugins! - -## Install - -To get started, you'll first need to install `verb` globally using [npm][]: - -Install globally with [npm](https://www.npmjs.com/) - -```sh -$ npm i -g verb -``` - -*** - -## Usage - -```sh -$ verb [args] -``` - -## CLI - -_(WIP)_ - -### Commands - -#### help - -_(TODO)_ - -Get started with Verb. - -```js -$ verb help -``` - -#### init - -_(TODO)_ - -Get started with Verb. - -```js -$ verb init -``` - -Upon running `init`, verb will prompt you for answers to the following questions: - -#### diff - -Show a diff of a file's contents, pre- and post-render. - -```js -$ verb --diff -``` - -**Example diff** - -screen shot 2015-12-14 at 1 50 55 am - -_(gray is unchanged, red is deleted, green is new)_ - -### Run tasks - -To run a task on the `base` app, just pass the name of the task to run. - -```sh -$ verb [options] -``` - -Unless overridden by the user, the `base` app is the default app that ships with Verb. This app doesn't really "verb" anything, but it will prompt you for a few answers (if you choose), to store data that's commonly needed by templates, like `author.name`, GitHub `username`, etc. - -**Example** - -Run task `bar`: - -```sh -$ verb bar -``` - -## API - -### [Verb](index.js#L38) - -Create an instance of `Verb` with the given `options` - -**Params** - -* `options` **{Object}**: Configuration options to initialize with. - -**Example** - -```js -var Verb = require('verb'); -var verb = new Verb(options); -``` - -### [.process](index.js#L100) - -Similar to [copy](#copy) but calls a plugin `pipeline` if defined globally or on the `.process` method `options`. This allows plugin pipelines to be programmatically built-up and dynamically changed on-the-fly. - -**Params** - -* `files` **{Object}** -* `options` **{Object}** -* `cb` **{Function}** -* `returns` **{Stream}**: Returns a [vinyl](http://github.com/gulpjs/vinyl) src stream - -**Example** - -```js -verb.process({src: ['a.txt', 'b.txt']}, options); -``` - -### [.each](index.js#L124) - -Verb `files` configurations in parallel. - -**Params** - -* `config` **{Object}** -* `cb` **{Function}** - -**Example** - -```js -verb.each(files, function(err) { - if (err) console.log(err); -}); -``` - -### [.eachSeries](index.js#L147) - -Verb `files` configurations in series. - -**Params** - -* `config` **{Object}** -* `cb` **{Function}** - -**Example** - -```js -verb.eachSeries(files, function(err) { - if (err) throw err; - console.log('done!'); -}); -``` - -### [.scaffold](index.js#L179) - -Verb files from a declarative [scaffold](https://github.com/jonschlinkert/scaffold) configuration. - -**Params** - -* `scaffold` **{Object}**: Scaffold configuration -* `cb` **{Function}**: Callback function that exposes `err`, called after the scaffold is generated. - -**Example** - -```js -var Scaffold = require('scaffold'); -var scaffold = new Scaffold({ - options: {cwd: 'source'}, - posts: { - src: ['content/*.md'] - }, - pages: { - src: ['templates/*.hbs'] - } -}); - -verb.scaffold(scaffold, function(err) { - if (err) console.log(err); -}); -``` - -## Apps - -_(TODO)_ - -## TODO - -* [x] publish a fast, composable, highly extendable project app with a user-friendly and expressive API! -* [x] support sub-apps (to any level of nesting) -* [x] support streams, tasks, and plugins compatible with both [gulp](http://gulpjs.com) and [assemble](https://github.com/assemble/assemble-core) -* [x] make it super easy to run specific tasks from any app or sub-app, programmatically or via CLI -* [x] support _instance plugins_ that allow you to easily add functionality and features to verb -* [x] support any template engine -* [x] support using any number of template engines at once, so that different file types can simultaneously be handled by the engine that was registered for that file type -* [x] support templates as [vinyl](http://github.com/gulpjs/vinyl) files, simple to use template collections and lists (for pagination, sorting, groups etc) -* [x] support middleware that can be run on all files or specific files, and at specific points during the _build process_ (like `onLoad`, `preRender`, `postRender`, etc) -* [x] 820+ unit tests -* [ ] create and publish apps (we created a handful of apps that we've been using locally, these will be published shortly) -* [ ] CLI docs (started) -* [ ] User help (e.g. when the user does `verb help` or just `verb`) -* [ ] API docs -* [ ] App guidelines and conventions - -## Release history - -**v0.9.0** - -* `license()` helper has been removed in favor of storing the `license` variable on the context as a string. To update your `.verb.md`, just change `{%= license() %}` to `{%= license %}`. - -## Related projects - -* [assemble](https://www.npmjs.com/package/assemble): Assemble is a powerful, extendable and easy to use static site generator for node.js. Used… [more](https://www.npmjs.com/package/assemble) | [homepage](https://github.com/assemble/assemble) -* [assemble-core](https://www.npmjs.com/package/assemble-core): The core assemble application with no presets or defaults. All configuration is left to the… [more](https://www.npmjs.com/package/assemble-core) | [homepage](https://github.com/assemble/assemble-core) -* [base-methods](https://www.npmjs.com/package/base-methods): base-methods is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base-methods) | [homepage](https://github.com/jonschlinkert/base-methods) -* [base-resolver](https://www.npmjs.com/package/base-resolver): 'base-methods' plugin for resolving and loading globally installed npm modules. | [homepage](https://github.com/jonschlinkert/base-resolver) -* [base-runner](https://www.npmjs.com/package/base-runner): Orchestrate multiple instances of base-methods at once. | [homepage](https://github.com/jonschlinkert/base-runner) -* [resolve-modules](https://www.npmjs.com/package/resolve-modules): Resolves local and global npm modules that match specified patterns, and returns a configuration object… [more](https://www.npmjs.com/package/resolve-modules) | [homepage](https://github.com/jonschlinkert/resolve-modules) - -## Running tests - -Install dev dependencies: - -```sh -$ npm i -d && npm test -``` - -## Contributing - -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). - -## Author - -**Jon Schlinkert** - -* [github/jonschlinkert](https://github.com/jonschlinkert) -* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) - -## License - -Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert) - -Released under the MIT license. - -*** - -_This file was generated by [verb](https://github.com/verbose/verb) on January 11, 2016._ \ No newline at end of file diff --git a/templates/badges.js b/templates/badges.js deleted file mode 100644 index c04380de..00000000 --- a/templates/badges.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -module.exports = { - travis: '[![Build Status](https://img.shields.io/travis/{%= repository %}.svg)](https://travis-ci.org/{%= repository %})', - fury: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', - npm: '[![NPM version](https://img.shields.io/npm/v/{%= name %}.svg)](https://www.npmjs.com/package/{%= name %})', - coveralls: '[![Coverage Status](https://img.shields.io/coveralls/{%= repository %}.svg)](https://coveralls.io/r/{%= repository %})' -}; diff --git a/templates/gh-issue.tmpl b/templates/gh-issue.tmpl deleted file mode 100644 index 9e3e7be4..00000000 --- a/templates/gh-issue.tmpl +++ /dev/null @@ -1,10 +0,0 @@ -Closing this issue since Assemble has been completely refactored from the ground up and released as v0.6.0. - -However, if this is related to grunt-assemble, and the issue or feature request is still valid, please [use this link](https://github.com/assemble/grunt-assemble/issues/new?body=Moved%20from%20assemble%2Fassemble%23issuenumber&title=) to create a new issue on the grunt-assemble repository. - -Also, please be sure to include: - -* The version of `assemble` you are using. -* Your `assemblefile.js` (This can be in a gist) -* The commandline output. (Screenshot or gist is fine) -* The expected result versus what's actually happening diff --git a/templates/includes.js b/templates/includes.js deleted file mode 100644 index da25f8e5..00000000 --- a/templates/includes.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; - - -module.exports = { - 'related-list': [ - '{% if (verb.related && verb.related.list && verb.related.list.length) { %}', - '{%= verb.related.description || "" %} ', - '{%= related(verb.related.list) %} ', - '{% } %}', - ].join('\n'), - - 'install-npm.md': [ - 'Install with [npm](https://www.npmjs.com/):', - '', - '```sh', - '$ npm i {%= name %}{%= ((typeof save !== "undefined" && save === true) ? " --save" : "") %}', - '```' - ].join('\n'), - - 'install.md': [ - 'Install with [npm](https://www.npmjs.com/):', - '', - '```sh', - '$ npm i {%= name %}', - '```' - ].join('\n'), - - 'install-dev.md': [ - 'Install as a `devDependency` with [npm](https://www.npmjs.com/):', - '', - '```sh', - '$ npm i {%= name %} -D', - '```' - ].join('\n'), - - 'install-bower.md': [ - 'Install with [bower](http://bower.io/)', - '', - '```sh', - '$ bower install {%= name %}{%= save === true ? " --save" : "" %}', - '```' - ].join('\n'), - - 'install-global.md': [ - 'Install globally with [npm](https://www.npmjs.com/)', - '', - '```sh', - '$ npm i -g {%= name %}', - '```' - ].join('\n'), - - 'tests.md': [ - 'Install dev dependencies:', - '', - '```sh', - '$ npm i -d && npm test', - '```' - ].join('\n'), - - 'author.md': [ - '**{%= author.name %}**', - '', - '+ [github/{%= author.username %}](https://github.com/{%= author.username %})', - '+ [twitter/{%= author.twitter %}](http://twitter.com/{%= author.twitter %})' - ].join('\n'), - - 'contributing.md': 'Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= author.username %}/{%= name %}/issues/new).', - - 'footer.md': '_This file was generated by [{%= runner.name %}]({%= runner.url %}) on {%= date() %}._' -}; diff --git a/templates/includes/banner.tmpl b/templates/includes/banner.tmpl deleted file mode 100644 index d173957e..00000000 --- a/templates/includes/banner.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * {%= name %} {https://github.com/{%= author.username %}/{%= name %} - * - * Copyright (c) {%= year %}, {%= author.name %}. - * Licensed under the MIT License. - */ \ No newline at end of file diff --git a/templates/layouts/default.md b/templates/layouts/default.md deleted file mode 100644 index 037f3e8d..00000000 --- a/templates/layouts/default.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Default layout ---- -# {%= name %} {%= badge('npm') %} {%= badge('travis') %} - -> {%= description %} - - - -{% body %} - -{%= section("Related projects", "related-list") %} -{%= section("Running tests", "tests") %} -{%= section("Contributing", "contributing") %} -{%= section("Author", "author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} diff --git a/templates/layouts/default2.hbs b/templates/layouts/default2.hbs deleted file mode 100644 index 161b92c5..00000000 --- a/templates/layouts/default2.hbs +++ /dev/null @@ -1,46 +0,0 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Default layout ---- -# {{name}} {{badge "npm"}} {{badge "travis"}} - -> {{description}} - - - -{{#if verb.sections.install}} -## Install -{{include "install-npm" save="true"}} -{{/if}} - -{% body %} - -{{#if obj.sections.api.path}} -## API -{{apidocs obj.sections.api.path}} -{{/if}} - -{{#if (verb.related && verb.related.list && verb.related.list.length) {}} -## Related projects -{{verb.related.description}} -{{related verb.related.list}} -{{/if}} - -## Running tests -{{include "tests"}} - -## Contributing -{{include "contributing"}} - -## Author -{{include "author"}} - -## License -{{copyright linkify="true"}} -{{license}} - -*** - -{{include "footer"}} - diff --git a/templates/layouts/min.md b/templates/layouts/min.md deleted file mode 100644 index 46a559d4..00000000 --- a/templates/layouts/min.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -verb_docs: - tags: ['template', 'built-in', '.verb.md', 'layout'] - title: Minimal layout ---- -# {%= name %} - -> {%= description %} - -{% body %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} diff --git a/templates/readme/header.md b/templates/readme/header.md deleted file mode 100644 index 1c40ad93..00000000 --- a/templates/readme/header.md +++ /dev/null @@ -1,9 +0,0 @@ -# {%= name %} {%= badge('npm') %} {%= badge('travis') %} - -> {%= description %} - -{% if(verb.sections) { %} -{% for(var key in verb.sections) { %} -{%= sections(key, verb.sections[key]) %} -{% } %} -{% } %} \ No newline at end of file diff --git a/templates/readme/min.md b/templates/readme/min.md deleted file mode 100644 index a8f75669..00000000 --- a/templates/readme/min.md +++ /dev/null @@ -1,14 +0,0 @@ -# {%= name %} - -> {%= description %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} diff --git a/templates/sections.js b/templates/sections.js deleted file mode 100644 index f4add9f9..00000000 --- a/templates/sections.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -var fs = require('fs'); - -module.exports = { - title: { - level: 1, - heading: '{%= name %} {%= badge(\'npm\') %} {%= badge(\'travis\') %}', - content: '> {%= description %}\n\n', - }, - install: { - level: 2, - heading: 'Install', - content: '{%= include(\'install-npm\', {save: true}) %}', - }, - apidocs: { - level: 2, - heading: 'API', - content: '{%= section("apidocs") %}', - }, - related: { - level: 2, - heading: 'Related projects', - content: '{%= related(verb.related.list) %}', - }, - tests: { - level: 2, - heading: 'Running tests', - content: '{%= include("tests") %}', - }, - coverage: { - level: 2, - heading: 'Coverage summary', - content: 'As of version {%= version %}\n\n```\n{%= coverage(\'coverage/summary.txt\') %}\n```\n', - validate: function(app) { - if (app && app.env) { - return fs.existsSync(app.env.user.cwd + '/coverage'); - } - return true; - } - }, - contributing: { - level: 2, - heading: 'Contributing', - content: '{%= include("contributing") %}', - }, - author: { - level: 2, - heading: 'Author', - content: '{%= include("author") %}', - }, - license: { - level: 2, - heading: 'License', - content: '{%= copyright() %}\n{%= license %}' - }, - footer: { - level: 2, - heading: '***', - content: '{%= include("footer") %}' - } -}; diff --git a/test/app.extendWith.js b/test/app.extendWith.js new file mode 100644 index 00000000..8b9893ac --- /dev/null +++ b/test/app.extendWith.js @@ -0,0 +1,78 @@ +'use strict'; + +require('mocha'); +require('generate-foo/verbfile.js'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +describe('.extendWith', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should get a named generator', function(cb) { + generate.register('foo', function(app) { + app.extendWith('bar'); + cb(); + }); + + generate.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + + it('should extend a generator with a named generator', function(cb) { + generate.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('bar'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + + it('should extend a generator with an array of generators', function(cb) { + generate.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith(['bar', 'baz', 'qux']); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.register('bar', function(app) { + app.task('a', function() {}); + }); + + generate.register('baz', function(app) { + app.task('b', function() {}); + }); + + generate.register('qux', function(app) { + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); +}); diff --git a/test/app.generate.js b/test/app.generate.js new file mode 100644 index 00000000..f7484bae --- /dev/null +++ b/test/app.generate.js @@ -0,0 +1,462 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Verb = require('..'); +var generate; + +describe('.generate', function() { + beforeEach(function() { + generate = new Verb(); + generate.initVerb({}); + }); + + describe('generators', function(cb) { + it('should throw an error when a generator is not found', function(cb) { + generate.generate('fdsslsllsfjssl', function(err) { + assert(err); + assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + // special case + it('should throw an error when a generator is not found in argv.cwd', function(cb) { + generate.option('cwd', 'foo/bar/baz'); + generate.generate('sflsjljskksl', function(err) { + assert(err); + assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); + cb(); + }); + }); + + it('should throw an error when a stringified task is not found', function(cb) { + generate.register('fdsslsllsfjssl', function() {}); + generate.generate('fdsslsllsfjssl:foo', function(err) { + assert(err); + assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + it('should throw an error when a task is not found', function(cb) { + generate.register('fdsslsllsfjssl', function() {}); + generate.generate('fdsslsllsfjssl', ['foo'], function(err) { + assert(err); + assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + it('should not reformat error messages that are not about invalid tasks', function(cb) { + generate.task('default', function(cb) { + cb(new Error('whatever')); + }); + + generate.generate('default', function(err) { + assert(err); + assert.equal(err.message, 'whatever'); + cb(); + }); + }); + + it('should run a task on the instance', function(cb) { + generate.task('foo', function(next) { + next(); + }); + + generate.generate('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run a task instead of a generator of the same name', function(cb) { + generate.register('foo', function(app) { + app.task('default', function() { + cb(new Error('expected the task to run first')); + }); + }); + + generate.task('foo', function() { + cb(); + }); + + generate.generate('foo', function(err) { + assert(!err); + }); + }); + + it('should run a task on a generator with the same name when specified', function(cb) { + generate.register('foo', function(app) { + app.task('default', function() { + cb(); + }); + }); + + generate.task('foo', function() { + cb(new Error('expected the generator to run')); + }); + + generate.generate('foo:default', function(err) { + assert(!err); + }); + }); + + it('should run the default task on a generator', function(cb) { + generate.register('foo', function(app) { + app.task('default', function(next) { + next(); + }); + }); + + generate.generate('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run an array of tasks on the instance', function(cb) { + var count = 0; + generate.task('a', function(next) { + count++; + next(); + }); + generate.task('b', function(next) { + count++; + next(); + }); + generate.task('c', function(next) { + count++; + next(); + }); + + generate.generate('a,b,c', function(err) { + assert.equal(count, 3); + assert(!err); + cb(); + }); + }); + + it('should run the default task on the default generator', function(cb) { + var count = 0; + generate.register('default', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generate(function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the default task on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generate('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('abc', function(next) { + count++; + next(); + }); + }); + + generate.generate('foo', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('a', function(next) { + count++; + next(); + }); + + app.task('b', function(next) { + count++; + next(); + }); + + app.task('c', function(next) { + count++; + next(); + }); + }); + + generate.generate('foo', 'a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + }); + + describe('sub-generators', function(cb) { + it('should run the default task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.sub', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.bar', ['a', 'b', 'c'], function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + + it('should run an multiple tasks on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.bar', 'a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + }); + + describe('cross-generator', function(cb) { + it('should run a generator from another generator', function(cb) { + var res = ''; + + generate.register('foo', function(app, two) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + res += 'foo > sub > default '; + generate.generate('bar.sub', next); + }); + }); + }); + + generate.register('bar', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + res += 'bar > sub > default '; + next(); + }); + }); + }); + + generate.generate('foo.sub', function(err) { + if (err) return cb(err); + assert.equal(res, 'foo > sub > default bar > sub > default '); + cb(); + }); + }); + + it('should run the specified task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + }); + + describe('events', function(cb) { + it('should emit generate', function(cb) { + generate.on('generate', function() { + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + }); + }); + + it('should expose the generator alias as the first parameter', function(cb) { + generate.on('generate', function(name) { + assert.equal(name, 'sub'); + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + }); + }); + + it('should expose the tasks array as the second parameter', function(cb) { + generate.on('generate', function(name, tasks) { + assert.deepEqual(tasks, ['abc']); + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + }); + }); + }); +}); diff --git a/test/app.generateEach.js b/test/app.generateEach.js new file mode 100644 index 00000000..31157f96 --- /dev/null +++ b/test/app.generateEach.js @@ -0,0 +1,444 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Verb = require('..'); +var generate; + +describe('.generate', function() { + beforeEach(function() { + generate = new Verb(); + generate.initVerb({}); + }); + + describe('generators', function(cb) { + it('should throw an error when a generator is not found', function(cb) { + generate.generateEach('fdsslsllsfjssl', function(err) { + assert(err); + assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + // special case + it('should throw an error when a generator is not found in argv.cwd', function(cb) { + generate.option('cwd', 'foo/bar/baz'); + generate.generateEach('sflsjljskksl', function(err) { + assert(err); + assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); + cb(); + }); + }); + + it('should throw an error when a task is not found', function(cb) { + generate.register('fdsslsllsfjssl', function() {}); + generate.generateEach('fdsslsllsfjssl:foo', function(err) { + assert(err); + assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + it('should run a task on the instance', function(cb) { + generate.task('foo', function(next) { + next(); + }); + + generate.generateEach('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run an array of tasks on the instance', function(cb) { + var count = 0; + generate.task('a', function(next) { + count++; + next(); + }); + generate.task('b', function(next) { + count++; + next(); + }); + generate.task('c', function(next) { + count++; + next(); + }); + + generate.generateEach('a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + assert(!err); + cb(); + }); + }); + + it('should run the default task on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generateEach('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of generators', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.register('bar', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generateEach(['foo', 'bar'], function(err) { + if (err) return cb(err); + assert.equal(count, 2); + cb(); + }); + }); + + it('should run the default task on the default generator', function(cb) { + var count = 0; + generate.register('default', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generateEach(function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('abc', function(next) { + count++; + next(); + }); + }); + + generate.generateEach('foo:abc', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('a', function(next) { + count++; + next(); + }); + + app.task('b', function(next) { + count++; + next(); + }); + + app.task('c', function(next) { + count++; + next(); + }); + }); + + generate.generateEach('foo:a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + }); + + describe('sub-generators', function(cb) { + it('should run the default task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generateEach('foo.sub', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generateEach('foo.sub:abc', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.generateEach('foo.bar:a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + + it('should run an multiple tasks on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.register('qux', function(app) { + app.register('fez', function(fez) { + fez.task('default', function(next) { + count++; + next(); + }); + + fez.task('a', function(next) { + count++; + next(); + }); + + fez.task('b', function(next) { + count++; + next(); + }); + + fez.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.generateEach(['foo.bar:a,b,c', 'qux.fez:a,b,c'], function(err) { + if (err) return cb(err); + assert.equal(count, 6); + cb(); + }); + }); + }); + + describe('cross-generator', function(cb) { + it('should run a generator from another generator', function(cb) { + var res = ''; + + generate.register('foo', function(app, two) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + res += 'foo > sub > default '; + generate.generateEach('bar.sub', next); + }); + }); + }); + + generate.register('bar', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + res += 'bar > sub > default '; + next(); + }); + }); + }); + + generate.generateEach('foo.sub', function(err) { + if (err) return cb(err); + assert.equal(res, 'foo > sub > default bar > sub > default '); + cb(); + }); + }); + + it('should run the specified task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generateEach('foo.sub:abc', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + }); + + describe('events', function(cb) { + it('should emit generate', function(cb) { + generate.on('generate', function() { + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generateEach('foo.sub:abc', function(err) { + if (err) return cb(err); + }); + }); + + it('should expose the generator alias as the first parameter', function(cb) { + generate.on('generate', function(name) { + assert.equal(name, 'sub'); + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generateEach('foo.sub:abc', function(err) { + if (err) return cb(err); + }); + }); + + it('should expose the tasks array as the second parameter', function(cb) { + generate.on('generate', function(name, tasks) { + assert.deepEqual(tasks, ['abc']); + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generateEach('foo.sub:abc', function(err) { + if (err) return cb(err); + }); + }); + }); +}); diff --git a/test/app.generator.js b/test/app.generator.js new file mode 100644 index 00000000..1e899a26 --- /dev/null +++ b/test/app.generator.js @@ -0,0 +1,164 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var Verb = require('..'); +var verb; + +var fixtures = path.resolve.bind(path, __dirname, 'fixtures'); + +describe('.generator', function() { + beforeEach(function() { + verb = new Verb(); + verb.initVerb({}); + }); + + describe('register > function', function() { + it('should register a generator function by name', function() { + verb.generator('foo', function() {}); + assert(verb.generators.hasOwnProperty('foo')); + }); + + it('should register a generator function by alias', function() { + verb.generator('verb-generate-abc', function() {}); + assert(verb.generators.hasOwnProperty('abc')); + }); + }); + + describe('get > alias', function() { + it('should get a generator by alias', function() { + verb.generator('verb-generate-abc', function() {}); + var abc = verb.generator('abc'); + assert(abc); + assert.equal(typeof abc, 'object'); + }); + }); + + describe('get > name', function() { + it('should get a generator by name', function() { + verb.generator('verb-generate-abc', function() {}); + var abc = verb.generator('verb-generate-abc'); + assert(abc); + assert.equal(typeof abc, 'object'); + }); + }); + + describe('generators', function() { + it('should invoke a registered generator when `getGenerator` is called', function(cb) { + verb.register('foo', function(app) { + app.task('default', function() {}); + cb(); + }); + verb.getGenerator('foo'); + }); + + it('should expose an app\'s generators on app.generators', function(cb) { + verb.register('foo', function(app) { + app.register('a', function() {}); + app.register('b', function() {}); + + app.generators.hasOwnProperty('a'); + app.generators.hasOwnProperty('b'); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should expose all root generators on verb.generators', function(cb) { + verb.register('foo', function(app, b) { + b.generators.hasOwnProperty('foo'); + b.generators.hasOwnProperty('bar'); + b.generators.hasOwnProperty('baz'); + cb(); + }); + + verb.register('bar', function(app, verb) {}); + verb.register('baz', function(app, verb) {}); + verb.getGenerator('foo'); + }); + }); + + describe('cross-generators', function() { + it('should get a generator from another generator', function(cb) { + verb.register('foo', function(app, b) { + var bar = b.getGenerator('bar'); + assert(bar); + cb(); + }); + + verb.register('bar', function(app, verb) {}); + verb.register('baz', function(app, verb) {}); + verb.getGenerator('foo'); + }); + + it('should set options on another generator instance', function(cb) { + verb.generator('foo', function(app) { + app.task('default', function(next) { + assert.equal(app.option('abc'), 'xyz'); + next(); + }); + }); + + verb.generator('bar', function(app, b) { + var foo = b.getGenerator('foo'); + foo.option('abc', 'xyz'); + foo.build(function(err) { + if (err) return cb(err); + cb(); + }); + }); + }); + }); + + describe('generators > filepath', function() { + it('should register a generator function from a file path', function() { + var one = verb.generator('one', fixtures('one/verbfile.js')); + assert(verb.generators.hasOwnProperty('one')); + assert(typeof verb.generators.one === 'object'); + assert.deepEqual(verb.generators.one, one); + }); + + it('should register a Verb instance from a file path', function() { + var two = verb.generator('two', fixtures('two/verbfile.js')); + assert(verb.generators.hasOwnProperty('two')); + assert(typeof verb.generators.two === 'object'); + assert.deepEqual(verb.generators.two, two); + }); + + it('should get a registered generator by name', function() { + var one = verb.generator('one', fixtures('one/verbfile.js')); + assert.deepEqual(verb.generator('one'), one); + }); + }); + + describe('tasks', function() { + it('should expose a generator\'s tasks on app.tasks', function(cb) { + verb.register('foo', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + assert(app.tasks.a); + assert(app.tasks.b); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should get tasks from another generator', function(cb) { + verb.register('foo', function(app, b) { + var baz = b.getGenerator('baz'); + var task = baz.tasks.aaa; + assert(task); + cb(); + }); + + verb.register('bar', function(app, verb) {}); + verb.register('baz', function(app, verb) { + app.task('aaa', function() {}); + }); + verb.getGenerator('foo'); + }); + }); +}); diff --git a/test/app.getGenerator.js b/test/app.getGenerator.js new file mode 100644 index 00000000..7fd2c9fd --- /dev/null +++ b/test/app.getGenerator.js @@ -0,0 +1,97 @@ +'use strict'; + +var path = require('path'); +var assert = require('assert'); +var Verb = require('..'); +var verb; + +var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); + +describe('.generator', function() { + beforeEach(function() { + verb = new Verb(); + }); + + it('should get a generator from the verb instance', function() { + verb.register('abc', function() {}); + var generator = verb.getGenerator('abc'); + assert(generator); + assert.equal(typeof generator, 'object'); + assert.equal(generator.name, 'abc'); + }); + + it('should get a generator from the verb instance from a nested generator', function() { + verb.register('abc', function() {}); + verb.register('xyz', function(app) { + app.register('sub', function(sub) { + var generator = verb.getGenerator('abc'); + assert(generator); + assert.equal(typeof generator, 'object'); + assert.equal(generator.name, 'abc'); + }); + }); + verb.getGenerator('xyz'); + }); + + it('should get a generator from the verb instance using `this`', function() { + verb.register('abc', function() {}); + verb.register('xyz', function(app) { + app.register('sub', function(sub) { + var generator = this.getGenerator('abc'); + assert(generator); + assert.equal(typeof generator, 'object'); + assert.equal(generator.name, 'abc'); + }); + }); + verb.getGenerator('xyz'); + }); + + it('should get a verb generator from "app" from a nested generator', function() { + verb.register('abc', function() {}); + verb.register('xyz', function(app) { + app.register('sub', function(sub) { + var generator = app.getGenerator('abc'); + assert(generator); + assert.equal(typeof generator, 'object'); + assert.equal(generator.name, 'abc'); + }); + }); + verb.getGenerator('xyz'); + }); + + it('should get a nested generator', function() { + verb.register('abc', function(abc) { + abc.register('def', function() {}); + }); + + var generator = verb.getGenerator('abc.def'); + assert(generator); + assert.equal(typeof generator, 'object'); + assert.equal(generator.name, 'def'); + }); + + it('should get a deeply nested generator', function() { + verb.register('abc', function(abc) { + abc.register('def', function(def) { + def.register('ghi', function(ghi) { + ghi.register('jkl', function(jkl) { + jkl.register('mno', function() {}); + }); + }); + }); + }); + + var generator = verb.getGenerator('abc.def.ghi.jkl.mno'); + assert(generator); + assert.equal(typeof generator, 'object'); + assert.equal(generator.name, 'mno'); + }); + + it('should get a generator that was registered by path', function() { + verb.register('a', fixtures('generators/a')); + var generator = verb.getGenerator('a'); + assert(generator); + assert(generator.tasks); + assert(generator.tasks.hasOwnProperty('default')); + }); +}); diff --git a/test/app.hasGenerator.js b/test/app.hasGenerator.js new file mode 100644 index 00000000..bf8dd745 --- /dev/null +++ b/test/app.hasGenerator.js @@ -0,0 +1,43 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +describe('.hasGenerator', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should return true if a generator is registered', function() { + generate.register('foo', function(app) { + }); + assert(generate.hasGenerator('foo')); + }); + + it('should return false if a generator is not registered', function() { + generate.register('foo', function(app) { + app.register('bar', function() {}); + }); + + assert(!generate.hasGenerator('bar')); + }); + + it('should return false if a sub-generator is registered', function() { + generate.register('foo', function(app) { + app.register('bar', function() {}); + }); + + assert(generate.hasGenerator('foo.bar')); + }); + + it('should return false if a sub-generator is not registered', function() { + generate.register('foo', function(app) { + app.register('bar', function() {}); + }); + + assert(!generate.hasGenerator('foo.baz')); + assert(!generate.hasGenerator('foo.bar.baz')); + }); +}); diff --git a/test/app.invoke.js b/test/app.invoke.js new file mode 100644 index 00000000..500c1658 --- /dev/null +++ b/test/app.invoke.js @@ -0,0 +1,399 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var gm = require('global-modules'); +var commands = require('spawn-commands'); +var utils = require('generator-util'); +require('generate-foo/generator.js'); +var Verb = require('..'); +var verb; + +var fixture = path.resolve.bind(path, __dirname, 'fixtures/generators'); +function install(name, cb) { + commands({ + args: ['install', '-g', '--silent', name], + cmd: 'npm' + }, cb); +} + +describe('.invoke', function() { + before(function(cb) { + if (!utils.exists(path.resolve(gm, 'generate-bar'))) { + install('generate-bar', cb); + } else { + cb(); + } + }); + + beforeEach(function() { + verb = new Verb({prefix: 'generate'}); + }); + + describe('invoke generators', function(cb) { + it('should invoke an instance', function(cb) { + verb.register('foo', function(app) { + var bar = app.getGenerator('bar'); + app.invoke(bar); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + verb.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke a named generator', function(cb) { + verb.register('foo', function(app) { + app.invoke('bar'); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + verb.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + verb.getGenerator('foo'); + }); + }); + + describe('extend generators', function(cb) { + it('should extend a generator with a generator invoked by name', function(cb) { + verb.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('bar'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + verb.getGenerator('foo'); + }); + + it('should extend a generator with a generator invoked by alias', function(cb) { + verb.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('qux'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.register('generate-qux', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + verb.getGenerator('foo'); + }); + + it('should extend with a generator invoked by filepath', function(cb) { + verb.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke(fixture('qux')); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should extend with a generator invoked from node_modules by name', function(cb) { + verb.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('generate-foo'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.getGenerator('abc'); + }); + + it('should extend with a generator invoked from node_modules by alias', function(cb) { + verb.prefix = 'generate'; + verb.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('foo'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.getGenerator('abc'); + }); + + it('should extend with a generator invoked from global modules by name', function(cb) { + verb.register('zzz', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + app.invoke('generate-bar'); + + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.getGenerator('zzz'); + }); + + it('should extend with a generator invoked from global modules by alias', function(cb) { + verb.register('zzz', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('bar'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.getGenerator('zzz'); + }); + }); + + describe('sub-generators', function(cb) { + it('should invoke sub-generators', function(cb) { + verb.register('foo', function(app) { + app.register('one', function(app) { + app.task('a', function() {}); + }); + app.register('two', function(app) { + app.task('b', function() {}); + }); + + app.invoke('one'); + app.invoke('two'); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke a sub-generator on the verb instance', function(cb) { + verb.register('foo', function(app) { + app.invoke('bar.sub'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + verb.register('bar', function(app) { + app.register('sub', function(sub) { + sub.task('a', function() {}); + sub.task('b', function() {}); + sub.task('c', function() {}); + }); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke a sub-generator from node_modules by name', function(cb) { + verb.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('xyz'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.register('xyz', function(app) { + app.invoke('generate-foo'); + }); + + verb.getGenerator('abc'); + }); + + it('should invoke a sub-generator from node_modules by alias', function(cb) { + verb.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.invoke('xyz'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + verb.register('xyz', function(app) { + app.invoke('foo'); + }); + + verb.getGenerator('abc'); + }); + + it('should invoke an array of sub-generators', function(cb) { + verb.register('foo', function(app) { + app.register('one', function(app) { + app.task('a', function() {}); + }); + app.register('two', function(app) { + app.task('b', function() {}); + }); + + app.invoke(['one', 'two']); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke sub-generators from sub-generators', function(cb) { + verb.register('foo', function(app) { + app.register('one', function(sub) { + sub.register('a', function(a) { + a.task('a', function() {}); + }); + }); + + app.register('two', function(sub) { + sub.register('a', function(a) { + a.task('b', function() {}); + }); + }); + + app.invoke('one.a'); + app.invoke('two.a'); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke an array of sub-generators from sub-generators', function(cb) { + verb.register('foo', function(app) { + app.register('one', function(sub) { + sub.register('a', function(a) { + a.task('a', function() {}); + }); + }); + + app.register('two', function(sub) { + sub.register('a', function(a) { + a.task('b', function() {}); + }); + }); + + app.invoke(['one.a', 'two.a']); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke sub-generator that invokes another generator', function(cb) { + verb.register('foo', function(app) { + app.invoke('bar'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + verb.register('bar', function(app) { + app.invoke('baz'); + }); + + verb.register('baz', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + verb.getGenerator('foo'); + }); + + it('should invoke sub-generator that invokes another sub-generator', function(cb) { + verb.register('foo', function(app) { + app.invoke('bar.sub'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + verb.register('bar', function(app) { + app.register('sub', function(sub) { + sub.invoke('baz.sub'); + }); + }); + + verb.register('baz', function(app) { + app.register('sub', function(sub) { + sub.task('a', function() {}); + sub.task('b', function() {}); + sub.task('c', function() {}); + }); + }); + + verb.getGenerator('foo'); + }); + }); +}); diff --git a/test/app.register.js b/test/app.register.js new file mode 100644 index 00000000..194d0c6a --- /dev/null +++ b/test/app.register.js @@ -0,0 +1,249 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var Verb = require('..'); +var app; + +var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); + +describe('.register plugin', function() { + it('should register as a plugin', function() { + var app = new Verb(); + assert(app.registered.hasOwnProperty('base-generators')); + }); +}); + +describe('.register', function() { + beforeEach(function() { + app = new Verb(); + app.initVerb({}); + }); + + describe('properties', function() { + it('should expose a configfile getter/setter', function() { + assert.equal(typeof app.configfile, 'string'); + }); + + it('should set configfile to verbfile.js by default', function() { + assert.equal(app.configfile, 'verbfile.js'); + }); + + it('should set configfile', function() { + app.configfile = 'foo.js'; + assert.equal(app.configfile, 'foo.js'); + }); + }); + + describe('function', function() { + it('should get a generator registered as a function', function() { + app.register('foo', function() {}); + var foo = app.getGenerator('foo'); + assert(foo); + assert.equal(foo.env.alias, 'foo'); + }); + + it('should get a task from a generator registered as a function', function() { + app.register('foo', function(foo) { + foo.task('default', function() {}); + }); + var generator = app.getGenerator('foo'); + assert(generator); + assert(generator.tasks); + assert(generator.tasks.hasOwnProperty('default')); + }); + + it('should get a sub-generator from a generator registered as a function', function() { + app.register('foo', function(foo) { + foo.register('bar', function(bar) {}); + }); + var bar = app.getGenerator('foo.bar'); + assert(bar); + assert.equal(bar.env.alias, 'bar'); + }); + + it('should get a sub-generator from a generator registered as a function', function() { + app.register('foo', function(foo) { + foo.register('bar', function(bar) { + bar.task('something', function() {}); + }); + }); + var bar = app.getGenerator('foo.bar'); + assert(bar); + assert(bar.tasks); + assert(bar.tasks.hasOwnProperty('something')); + }); + + it('should get a deeply-nested sub-generator registered as a function', function() { + app.register('foo', function(foo) { + foo.register('bar', function(bar) { + bar.register('baz', function(baz) { + baz.register('qux', function(qux) { + qux.task('qux-one', function() {}); + }); + }); + }); + }); + + var qux = app.getGenerator('foo.bar.baz.qux'); + assert(qux); + assert(qux.tasks); + assert(qux.tasks.hasOwnProperty('qux-one')); + }); + + it('should expose the instance from each generator', function() { + app.register('foo', function(foo) { + foo.register('bar', function(bar) { + bar.register('baz', function(baz) { + baz.register('qux', function(qux) { + qux.task('qux-one', function() {}); + }); + }); + }); + }); + + var qux = app + .getGenerator('foo') + .getGenerator('bar') + .getGenerator('baz') + .getGenerator('qux'); + + assert(qux); + assert(qux.tasks); + assert(qux.tasks.hasOwnProperty('qux-one')); + }); + + it('should fail when the wrong generator name is given', function() { + app.register('foo', function(foo) { + foo.register('bar', function(bar) { + bar.register('baz', function(baz) { + baz.register('qux', function(qux) { + }); + }); + }); + }); + var fez = app.getGenerator('foo.bar.fez'); + assert.equal(typeof fez, 'undefined'); + }); + + it('should expose the `base` instance as the second param', function(cb) { + app.register('foo', function(foo, base) { + assert(app.generators.hasOwnProperty('foo')); + cb(); + }); + app.getGenerator('foo'); + }); + + it('should expose sibling generators on the `base` instance', function(cb) { + app.register('foo', function(foo, base) { + foo.task('abc', function() {}); + }); + app.register('bar', function(bar, base) { + assert(app.generators.hasOwnProperty('foo')); + assert(app.generators.hasOwnProperty('bar')); + cb(); + }); + + app.getGenerator('foo'); + app.getGenerator('bar'); + }); + }); + + describe('alias', function() { + it('should use a custom function to create the alias', function() { + app.option('alias', function(name) { + return name.slice(name.lastIndexOf('-') + 1); + }); + + app.register('base-abc-xyz', function() {}); + assert(app.generators.hasOwnProperty('xyz')); + }); + }); + + describe('path', function() { + it('should register a generator function by name', function() { + app.register('foo', function() {}); + assert(app.generators.hasOwnProperty('foo')); + }); + + it('should register a generator function by alias', function() { + app.register('verb-generate-abc', function() {}); + assert(app.generators.hasOwnProperty('abc')); + }); + + it('should register a generator by dirname', function() { + app.register('a', fixtures('generators/a')); + assert(app.generators.hasOwnProperty('a')); + }); + + it('should register a generator from a configfile filepath', function() { + app.register('verb-generate-abc', fixtures('generators/a/verbfile.js')); + assert(app.generators.hasOwnProperty('abc')); + }); + + it('should throw when a generator does not expose the instance', function(cb) { + try { + app.register('not-exposed', require(fixtures('not-exposed.js'))); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'generator instances must be exposed with module.exports'); + cb(); + } + }); + }); + + describe('instance', function() { + it('should register an instance', function() { + app.register('verb-generate-inst', new Verb()); + assert(app.generators.hasOwnProperty('inst')); + }); + + it('should get a generator that was registered as an instance', function() { + var foo = new Verb(); + foo.task('default', function() {}); + app.register('foo', foo); + assert(app.getGenerator('foo')); + }); + + it('should register multiple instances', function() { + var foo = new Verb(); + var bar = new Verb(); + var baz = new Verb(); + app.register('foo', foo); + app.register('bar', bar); + app.register('baz', baz); + assert(app.getGenerator('foo')); + assert(app.getGenerator('bar')); + assert(app.getGenerator('baz')); + }); + + it('should get tasks from a generator that was registered as an instance', function() { + var foo = new Verb(); + foo.task('default', function() {}); + app.register('foo', foo); + var generator = app.getGenerator('foo'); + assert(generator.tasks); + assert(generator.tasks.hasOwnProperty('default')); + }); + + it('should get sub-generators from a generator registered as an instance', function() { + var foo = new Verb(); + foo.register('bar', function() {}); + app.register('foo', foo); + var generator = app.getGenerator('foo.bar'); + assert(generator); + }); + + it('should get tasks from sub-generators registered as an instance', function() { + var foo = new Verb(); + foo.register('bar', function(bar) { + bar.task('whatever', function() {}); + }); + app.register('foo', foo); + var generator = app.getGenerator('foo.bar'); + assert(generator.tasks); + assert(generator.tasks.hasOwnProperty('whatever')); + }); + }); +}); diff --git a/test/app.resolve.js b/test/app.resolve.js new file mode 100644 index 00000000..10ab74d1 --- /dev/null +++ b/test/app.resolve.js @@ -0,0 +1,70 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var gm = require('global-modules'); +var Verb = require('..'); +var verb; + +var fixtures = path.resolve.bind(path, __dirname, 'fixtures/generators'); + +describe('.resolve', function() { + beforeEach(function() { + verb = new Verb(); + verb.prefix = 'generate'; + }); + + describe('method', function() { + it('should expose a `resolve` method', function() { + assert.equal(typeof verb.resolve, 'function'); + }); + }); + + describe('local', function() { + it('should resolve a local generator path', function() { + var fp = verb.resolve(fixtures('a')); + assert.equal(typeof fp, 'string'); + }); + + it('should resolve a generator path from a cwd', function() { + assert(verb.resolve('a', {cwd: fixtures()})); + }); + + it('should resolve a generator path from a generator name', function() { + assert(verb.resolve('a', {cwd: fixtures()})); + }); + + it('should resolve the path to a local config file', function() { + var fp = verb.resolve('a', {cwd: fixtures()}); + assert.equal(typeof fp, 'string'); + }); + }); + + describe('global', function() { + it('should resolve a global generator path', function() { + var fp = verb.resolve('bar', gm); + assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); + }); + + it('should resolve a global generator path without a cwd', function() { + var fp = verb.resolve('bar'); + assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); + }); + + it('should resolve a global generator by full name', function() { + var fp = verb.resolve('generate-bar'); + assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); + }); + + it('should return undefined when a generator is not found', function() { + var fp = verb.resolve('foo-bar'); + assert.equal(typeof fp, 'undefined'); + }); + + it('should return undefined when a generator is not found at the given cwd', function() { + var actual = verb.resolve('bar', {cwd: fixtures()}); + assert.equal(typeof actual, 'undefined'); + }); + }); +}); diff --git a/test/app.task.js b/test/app.task.js index 40727cab..a34956b0 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -1,94 +1,90 @@ +'use strict'; + +require('mocha'); var assert = require('assert'); -var App = require('..'); -var app; +var Generate = require('..'); +var generate; -describe('task()', function () { - beforeEach(function () { - app = new App(); +describe('.generate', function() { + beforeEach(function() { + generate = new Generate(); }); - it('should register a task', function () { - var fn = function (done) { - done(); + it('should register a task', function() { + var fn = function(cb) { + cb(); }; - app.task('default', fn); - assert.equal(typeof app.tasks.default, 'object'); - assert.equal(app.tasks.default.fn, fn); + generate.task('default', fn); + assert.equal(typeof generate.tasks.default, 'object'); + assert.equal(generate.tasks.default.fn, fn); }); - it('should register a task with an array of dependencies', function () { - app.task('default', ['foo', 'bar'], function (done) { - done(); + it('should register a task with an array of dependencies', function() { + generate.task('default', ['foo', 'bar'], function(cb) { + cb(); }); - assert.equal(typeof app.tasks.default, 'object'); - assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof generate.tasks.default, 'object'); + assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); }); - it('should register a task with a list of strings as dependencies', function () { - app.task('default', 'foo', 'bar', function (done) { - done(); + it('should register a task with a list of strings as dependencies', function() { + generate.task('default', 'foo', 'bar', function(cb) { + cb(); }); - assert.equal(typeof app.tasks.default, 'object'); - assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof generate.tasks.default, 'object'); + assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); }); - it('should run a task', function (done) { + it('should run a task', function(cb) { var count = 0; - app.task('default', function (cb) { + generate.task('default', function(cb) { count++; cb(); }); - app.build('default', function (err) { - if (err) return done(err); + generate.build('default', function(err) { + if (err) return cb(err); assert.equal(count, 1); - done(); + cb(); }); }); - it('should throw an error when a task with unregistered dependencies is run', function (done) { - var count = 0; - app.task('default', ['foo', 'bar'], function (cb) { - count++; + it('should throw an error when a task with unregistered dependencies is run', function(cb) { + generate.task('default', ['foo', 'bar']); + generate.build('default', function(err) { + assert(err); cb(); }); - - app.build('default', function (err) { - if (!err) return done(new Error('Expected an error to be thrown.')); - assert.equal(count, 0); - done(); - }); }); - it('should throw an error when `.build` is called without a callback function.', function () { + it('should throw an error when `.build` is called without a callback function.', function() { try { - app.build('default'); + generate.build('default'); throw new Error('Expected an error to be thrown.'); } catch (err) { } }); - it('should emit task events', function (done) { + it('should emit task events', function(cb) { var events = []; - app.on('task:starting', function (task) { + generate.on('task:starting', function(task) { events.push('starting.' + task.name); }); - app.on('task:finished', function (task) { + generate.on('task:finished', function(task) { events.push('finished.' + task.name); }); - app.on('task:error', function (err, task) { + generate.on('task:error', function(e, task) { events.push('error.' + task.name); }); - - app.task('foo', function (cb) { + generate.task('foo', function(cb) { cb(); }); - app.task('bar', ['foo'], function (cb) { + generate.task('bar', ['foo'], function(cb) { cb(); }); - app.task('default', ['bar']); - app.build('default', function (err) { - if (err) return done(err); + generate.task('default', ['bar']); + generate.build('default', function(err) { + if (err) return cb(err); assert.deepEqual(events, [ 'starting.default', 'starting.bar', @@ -97,60 +93,57 @@ describe('task()', function () { 'finished.bar', 'finished.default' ]); - done(); + cb(); }); }); - it('should emit an error event when an error is passed back in a task', function (done) { - app.on('error', function (err) { + it('should emit an error event when an error is passed back in a task', function(cb) { + generate.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - app.task('default', function (cb) { + generate.task('default', function(cb) { return cb(new Error('This is an error')); }); - app.build('default', function (err) { - if (err) return done(); - done(new Error('Expected an error')); + generate.build('default', function(err) { + if (err) return cb(); + cb(new Error('Expected an error')); }); }); - it('should emit an error event when an error is thrown in a task', function (done) { - var errors = 0; - app.on('error', function (err) { - errors++; + it('should emit an error event when an error is thrown in a task', function(cb) { + generate.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - app.task('default', function (cb) { + generate.task('default', function(cb) { cb(new Error('This is an error')); }); - app.build('default', function (err) { - assert.equal(errors, 1); - if (err) return done(); - done(new Error('Expected an error')); + generate.build('default', function(err) { + assert(err); + cb(); }); }); - it('should run dependencies before running the dependent task.', function (done) { + it('should run dependencies before running the dependent task.', function(cb) { var seq = []; - app.task('foo', function (cb) { + generate.task('foo', function(cb) { seq.push('foo'); cb(); }); - app.task('bar', function (cb) { + generate.task('bar', function(cb) { seq.push('bar'); cb(); }); - app.task('default', ['foo', 'bar'], function (cb) { + generate.task('default', ['foo', 'bar'], function(cb) { seq.push('default'); cb(); }); - app.build('default', function (err) { - if (err) return done(err); + generate.build('default', function(err) { + if (err) return cb(err); assert.deepEqual(seq, ['foo', 'bar', 'default']); - done(); + cb(); }); }); }); diff --git a/test/app.tasks.js b/test/app.tasks.js new file mode 100644 index 00000000..1da39843 --- /dev/null +++ b/test/app.tasks.js @@ -0,0 +1,46 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +describe('.tasks plugin', function() { + it('should register as a plugin', function() { + var generate = new Generate(); + assert(generate.registered.hasOwnProperty('base-generators-tasks')); + }); +}); + +describe('tasks', function() { + beforeEach(function() { + generate = new Generate(); + }); + + describe('hasTask', function() { + it('should return true if a task exists', function() { + generate.task('foo', function() {}); + assert(generate.hasTask('foo')); + }); + + it('should return false if a task does not exist', function() { + generate.task('foo', function() {}); + assert(!generate.hasTask('bar')); + }); + }); + + describe('.stringifyTasks', function() { + it('should create a generator-task string', function() { + assert.equal(generate.stringifyTasks('foo'), 'foo'); + assert.equal(generate.stringifyTasks('foo', function() {}), 'foo'); + assert.equal(generate.stringifyTasks('foo', []), 'foo'); + assert.equal(generate.stringifyTasks('foo:a,b,c'), 'foo:a,b,c'); + assert.equal(generate.stringifyTasks('foo.bar:a,b,c'), 'foo.bar:a,b,c'); + assert.equal(generate.stringifyTasks('foo.bar', 'a,b,c'), 'foo.bar:a,b,c'); + assert.equal(generate.stringifyTasks('foo', 'a,b,c'), 'foo:a,b,c'); + assert.equal(generate.stringifyTasks('foo', ['a', 'b', 'c']), 'foo:a,b,c'); + assert.equal(generate.stringifyTasks(['foo', 'bar'], ['a', 'b']), 'foo.bar:a,b'); + assert.equal(generate.stringifyTasks(['foo', 'bar'], 'a,b,c'), 'foo.bar:a,b,c'); + }); + }); +}); diff --git a/test/app.toAlias.js b/test/app.toAlias.js new file mode 100644 index 00000000..1e77b77f --- /dev/null +++ b/test/app.toAlias.js @@ -0,0 +1,30 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +require('generate-foo/generator.js'); +var Generate = require('..'); +var generate; + +describe('.toAlias', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should not create an alias from a name without a prefix', function() { + assert.equal(generate.toAlias('foo-bar'), 'foo-bar'); + }); + + it('should create an alias using the given prefix', function() { + assert.equal(generate.toAlias('foo-bar', {prefix: 'foo'}), 'bar'); + }); + + it('should create an alias using the given alias function', function() { + var alias = generate.toAlias('one-two-three', { + alias: function(name) { + return name.slice(name.lastIndexOf('-') + 1); + } + }); + assert.equal(alias, 'three'); + }); +}); diff --git a/test/cache.js b/test/cache.js new file mode 100644 index 00000000..9df96c5e --- /dev/null +++ b/test/cache.js @@ -0,0 +1,37 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +describe('cache', function() { + describe('set', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should set an instance', function() { + generate.generators.set('foo', function() {}); + assert(generate.generators.hasOwnProperty('foo')); + }); + + it('should set an instance with a parent instance', function() { + generate.generators.set('foo', function() {}, generate); + assert(generate.generators.hasOwnProperty('foo')); + }); + }); + + describe('get', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should get an instance from app.generators', function() { + generate.generators.set('foo', function() {}, generate); + var foo = generate.generators.get('foo'); + assert(foo); + assert(foo.isGenerator); + }); + }); +}); diff --git a/test/env.js b/test/env.js new file mode 100644 index 00000000..11d24996 --- /dev/null +++ b/test/env.js @@ -0,0 +1,73 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var Verb = require('..'); +var verb; + +var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); + +describe('env', function() { + describe('createEnv', function() { + beforeEach(function() { + verb = new Verb(); + verb.initVerb({}); + }); + + it('should add an env object to the instance', function() { + var fn = function() {}; + verb.createEnv('foo', fn); + assert(verb.env); + }); + + it('should take options as the second arg', function() { + var fn = function() {}; + verb.createEnv('foo', {}, fn); + assert(verb.env); + }); + + it('should prime `env` if it doesn\'t exist', function() { + var fn = function() {}; + delete verb.env; + verb.createEnv('foo', {}, fn); + assert(verb.env); + }); + + it('should add an alias to the env object', function() { + var fn = function() {}; + verb.createEnv('foo', {}, fn); + assert.equal(verb.env.alias, 'foo'); + }); + + it('should use the given prefix', function() { + var fn = function() {}; + verb.prefix = 'generate'; + verb.createEnv('foo', {}, fn); + assert.equal(verb.env.name, 'generate-foo'); + }); + + it('should use `prefix` to add a full name to the env object', function() { + var fn = function() {}; + verb.prefix = 'whatever'; + verb.createEnv('foo', {}, fn); + assert.equal(verb.env.name, 'whatever-foo'); + }); + + it('should try to resolve a path passed as the second arg', function() { + verb.createEnv('foo', 'generate-foo/verbfile.js'); + assert.equal(verb.env.alias, 'foo'); + assert.equal(verb.env.name, 'verb-generate-foo'); + }); + + it('should throw an error when the path is not resolved', function(cb) { + try { + verb.createEnv('foo', fixtures('whatever.js')); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); + cb(); + } + }); + }); +}); diff --git a/test/fixtures/generators/a/.gitignore b/test/fixtures/generators/a/.gitignore new file mode 100644 index 00000000..80a228ca --- /dev/null +++ b/test/fixtures/generators/a/.gitignore @@ -0,0 +1,15 @@ +*.DS_Store +*.sublime-* +_gh_pages +bower_components +node_modules +npm-debug.log +actual +test/actual +temp +tmp +TODO.md +vendor +.idea +benchmark +coverage diff --git a/test/fixtures/generators/a/package.json b/test/fixtures/generators/a/package.json new file mode 100644 index 00000000..36390f08 --- /dev/null +++ b/test/fixtures/generators/a/package.json @@ -0,0 +1,7 @@ +{ + "name": "generator-a", + "private": true, + "version": "0.1.0", + "files": ["index.js"], + "main": "verbfile.js" +} diff --git a/test/fixtures/generators/a/post.hbs b/test/fixtures/generators/a/post.hbs new file mode 100644 index 00000000..b2cb52b9 --- /dev/null +++ b/test/fixtures/generators/a/post.hbs @@ -0,0 +1,5 @@ +--- +title: Post +--- + +This is SOME POST \ No newline at end of file diff --git a/test/fixtures/generators/a/verbfile.js b/test/fixtures/generators/a/verbfile.js new file mode 100644 index 00000000..c3f4d75e --- /dev/null +++ b/test/fixtures/generators/a/verbfile.js @@ -0,0 +1,10 @@ +'use strict'; + +var path = require('path'); + +module.exports = function(app) { + app.task('default', function(cb) { + console.log('fixtures/a > default'); + cb(); + }); +}; diff --git a/test/fixtures/generators/b/.gitignore b/test/fixtures/generators/b/.gitignore new file mode 100644 index 00000000..80a228ca --- /dev/null +++ b/test/fixtures/generators/b/.gitignore @@ -0,0 +1,15 @@ +*.DS_Store +*.sublime-* +_gh_pages +bower_components +node_modules +npm-debug.log +actual +test/actual +temp +tmp +TODO.md +vendor +.idea +benchmark +coverage diff --git a/test/fixtures/generators/b/index.js b/test/fixtures/generators/b/index.js new file mode 100644 index 00000000..0148fb4d --- /dev/null +++ b/test/fixtures/generators/b/index.js @@ -0,0 +1,12 @@ +/*! + * a + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +module.exports = function () { + // do stuff +}; diff --git a/test/fixtures/generators/b/package.json b/test/fixtures/generators/b/package.json new file mode 100644 index 00000000..177dabf0 --- /dev/null +++ b/test/fixtures/generators/b/package.json @@ -0,0 +1,7 @@ +{ + "name": "bbb", + "private": true, + "version": "0.1.0", + "files": ["index.js"], + "main": "verbfile.js" +} diff --git a/test/fixtures/generators/c/.gitignore b/test/fixtures/generators/c/.gitignore new file mode 100644 index 00000000..80a228ca --- /dev/null +++ b/test/fixtures/generators/c/.gitignore @@ -0,0 +1,15 @@ +*.DS_Store +*.sublime-* +_gh_pages +bower_components +node_modules +npm-debug.log +actual +test/actual +temp +tmp +TODO.md +vendor +.idea +benchmark +coverage diff --git a/test/fixtures/generators/c/package.json b/test/fixtures/generators/c/package.json new file mode 100644 index 00000000..1d98e02b --- /dev/null +++ b/test/fixtures/generators/c/package.json @@ -0,0 +1,7 @@ +{ + "name": "a", + "private": true, + "version": "0.1.0", + "files": ["index.js"], + "main": "verbfile.js" +} diff --git a/test/fixtures/generators/c/verbfile.js b/test/fixtures/generators/c/verbfile.js new file mode 100644 index 00000000..2c448ee6 --- /dev/null +++ b/test/fixtures/generators/c/verbfile.js @@ -0,0 +1,39 @@ +'use strict'; + +module.exports = function(app) { + app.task('default', function(cb) { + console.log('c > default'); + cb(); + }); + app.task('c_a', function(cb) { + console.log('c > a'); + cb(); + }); + app.task('c_b', function(cb) { + console.log('c > b'); + cb(); + }); + app.task('c_c', function(cb) { + console.log('c > c'); + cb(); + }); + + app.register('docs', function(docs, base) { + docs.task('default', function(cb) { + console.log('c > x'); + cb(); + }); + docs.task('c_x', function(cb) { + console.log('c > x'); + cb(); + }); + docs.task('c_y', function(cb) { + console.log('c > y'); + cb(); + }); + docs.task('c_z', function(cb) { + console.log('c > z'); + cb(); + }); + }); +}; diff --git a/test/fixtures/generators/e/.gitignore b/test/fixtures/generators/e/.gitignore new file mode 100644 index 00000000..80a228ca --- /dev/null +++ b/test/fixtures/generators/e/.gitignore @@ -0,0 +1,15 @@ +*.DS_Store +*.sublime-* +_gh_pages +bower_components +node_modules +npm-debug.log +actual +test/actual +temp +tmp +TODO.md +vendor +.idea +benchmark +coverage diff --git a/test/fixtures/generators/e/package.json b/test/fixtures/generators/e/package.json new file mode 100644 index 00000000..d8b7e141 --- /dev/null +++ b/test/fixtures/generators/e/package.json @@ -0,0 +1,7 @@ +{ + "name": "generator-e", + "private": true, + "version": "0.1.0", + "files": ["index.js"], + "main": "verbfile.js" +} diff --git a/test/fixtures/generators/e/templates/post.hbs b/test/fixtures/generators/e/templates/post.hbs new file mode 100644 index 00000000..16e08aeb --- /dev/null +++ b/test/fixtures/generators/e/templates/post.hbs @@ -0,0 +1,5 @@ +--- +title: Post +--- + +This is three \ No newline at end of file diff --git a/test/fixtures/generators/e/verbfile.js b/test/fixtures/generators/e/verbfile.js new file mode 100644 index 00000000..fc333ec2 --- /dev/null +++ b/test/fixtures/generators/e/verbfile.js @@ -0,0 +1,43 @@ +'use strict'; + +/** + * testing... + */ + +module.exports = function(app, base, env) { + app.task('default', function(cb) { + console.log('e > default'); + cb(); + }); + app.task('one', function(cb) { + console.log('e > one'); + cb(); + }); + app.task('two', function(cb) { + console.log('e > two'); + cb(); + }); + + // console.log(app) + app.task('three', function(cb) { + base.data({title: 'three'}); + base.build('readme', cb); + }); + + app.register('eee', '../c'); + + app.register('e_docs', function(docs) { + docs.task('e_x', function(cb) { + console.log('e > x'); + cb(); + }); + docs.task('e_y', function(cb) { + console.log('e > y'); + cb(); + }); + docs.task('e_z', function(cb) { + console.log('e > z'); + cb(); + }); + }); +}; diff --git a/test/fixtures/generators/qux/.gitignore b/test/fixtures/generators/qux/.gitignore new file mode 100644 index 00000000..80a228ca --- /dev/null +++ b/test/fixtures/generators/qux/.gitignore @@ -0,0 +1,15 @@ +*.DS_Store +*.sublime-* +_gh_pages +bower_components +node_modules +npm-debug.log +actual +test/actual +temp +tmp +TODO.md +vendor +.idea +benchmark +coverage diff --git a/test/fixtures/generators/qux/package.json b/test/fixtures/generators/qux/package.json new file mode 100644 index 00000000..214aac14 --- /dev/null +++ b/test/fixtures/generators/qux/package.json @@ -0,0 +1,7 @@ +{ + "name": "generator-qux", + "private": true, + "version": "0.1.0", + "files": ["verbfile.js"], + "main": "verbfile.js" +} diff --git a/test/fixtures/generators/qux/verbfile.js b/test/fixtures/generators/qux/verbfile.js new file mode 100644 index 00000000..28979eea --- /dev/null +++ b/test/fixtures/generators/qux/verbfile.js @@ -0,0 +1,11 @@ +'use strict'; + +/** + * testing... + */ + +module.exports = function(app, base, env) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); +}; diff --git a/test/fixtures/generators2.js b/test/fixtures/generators2.js new file mode 100644 index 00000000..e2ddbb04 --- /dev/null +++ b/test/fixtures/generators2.js @@ -0,0 +1,41 @@ +'use strict'; + +var Generate = require('./'); +var app = new Generate({foo: 'bar'}); + +app.register('a', function(a, base) { + a.register('a1', function(aa) { + aa.register('aaa', require('./generators/f/generator.js')); + aa.task('default', function(cb) { + console.log('aa > default'); + cb(); + }); + }); + a.register('a2', function(aa) { + }); +}); + +app.register('b', new Generate()); +app.generators.b.register('bb', function(bb) { + bb.register('bbb', function() {}); + bb.task('default', function(cb) { + console.log('bb > default'); + cb(); + }); +}); + +app.register('c', function(c, base) { + c.register('cc', function(cc, basecc) { + cc.task('default', function(cb) { + console.log('cc > default'); + cb(); + }); + + cc.register('base', require('./generator')); + }); +}); + +app.generate('a.a1.aaa', ['default'], function(err) { + if (err) throw err; + console.log('DONE!!!'); +}); diff --git a/test/fixtures/not-exposed.js b/test/fixtures/not-exposed.js new file mode 100644 index 00000000..065e82e6 --- /dev/null +++ b/test/fixtures/not-exposed.js @@ -0,0 +1,8 @@ +'use strict'; + +var Generate = require('../..'); +var generate = new Generate(); + +generate.register('not-exposed', function(app) { + +}); diff --git a/test/fixtures/one/verbfile.js b/test/fixtures/one/verbfile.js new file mode 100644 index 00000000..546d0048 --- /dev/null +++ b/test/fixtures/one/verbfile.js @@ -0,0 +1,19 @@ +'use strict'; + + +module.exports = function(app, base, env) { + app.task('default', function(cb) { + console.log('one > default'); + cb(); + }); + + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + + app.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/three/four/five/verbfile.js b/test/fixtures/three/four/five/verbfile.js new file mode 100644 index 00000000..61f8bf67 --- /dev/null +++ b/test/fixtures/three/four/five/verbfile.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function(generate, base) { + generate.task('default', function() {}); + generate.task('a', function() {}); + generate.task('b', function() {}); + generate.task('c', function() {}); + + generate.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/three/four/verbfile.js b/test/fixtures/three/four/verbfile.js new file mode 100644 index 00000000..61f8bf67 --- /dev/null +++ b/test/fixtures/three/four/verbfile.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function(generate, base) { + generate.task('default', function() {}); + generate.task('a', function() {}); + generate.task('b', function() {}); + generate.task('c', function() {}); + + generate.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/three/verbfile.js b/test/fixtures/three/verbfile.js new file mode 100644 index 00000000..61f8bf67 --- /dev/null +++ b/test/fixtures/three/verbfile.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function(generate, base) { + generate.task('default', function() {}); + generate.task('a', function() {}); + generate.task('b', function() {}); + generate.task('c', function() {}); + + generate.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); + }); +}; \ No newline at end of file diff --git a/test/fixtures/two/verbfile.js b/test/fixtures/two/verbfile.js new file mode 100644 index 00000000..04c031fc --- /dev/null +++ b/test/fixtures/two/verbfile.js @@ -0,0 +1,21 @@ +'use strict'; + +var Generate = require('../../..'); +var generate = new Generate(); + +generate.task('default', function() {}); +generate.task('a', function() {}); +generate.task('b', function() {}); +generate.task('c', function() {}); + +generate.register('foo', function(app) { + app.task('x', function() {}); + app.task('y', function() {}); + app.task('z', function() {}); +}); + +/** + * Expose this instance of `Generate` + */ + +module.exports = generate; diff --git a/test/fixtures/verbfile.js b/test/fixtures/verbfile.js new file mode 100644 index 00000000..3dbcc451 --- /dev/null +++ b/test/fixtures/verbfile.js @@ -0,0 +1,49 @@ +'use strict'; + +module.exports = function(app) { + app.register('generate-aaa', function(app) { + app.task('default', function(cb) { + console.log('generate > default'); + cb(); + }); + + app.register('sub', function(sub) { + sub.task('default', function(cb) { + console.log('aaa > sub > default'); + cb(); + }); + + sub.register('bbb', function(bbb) { + bbb.task('default', function(cb) { + console.log('aaa > sub > bbb > default'); + cb(); + }); + }); + }); + }); + + app.register('generate-abc', 'test/fixtures/generators/a/generator.js'); + + app.register('generate-bbb', function(app) { + app.task('default', function(cb) { + app.generate('aaa.sub.bbb', 'default', cb); + }); + }); + + app.register('generate-ccc', function(app) { + app.task('default', function(cb) { + app.generate('abc', 'default', cb); + }); + }); + + app.register('generate-ddd', function(app) { + app.task('default', function(cb) { + app.generate('abc.docs', 'x', cb); + }); + }); + + app.generate('aaa.sub', ['default'], function(err) { + if (err) throw err; + console.log('done'); + }); +}; diff --git a/test/generate.js b/test/generate.js new file mode 100644 index 00000000..4bda7952 --- /dev/null +++ b/test/generate.js @@ -0,0 +1,35 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var Verb = require('..'); +var generate; + +describe('generate', function() { + describe('cwd', function() { + beforeEach(function() { + generate = new Verb(); + }); + + it('should get the current working directory', function() { + assert.equal(generate.cwd, process.cwd()); + }); + + it('should set the current working directory', function() { + generate.cwd = 'test/fixtures'; + assert.equal(generate.cwd, path.join(process.cwd(), 'test/fixtures')); + }); + }); + + describe('generator', function() { + beforeEach(function() { + generate = new Verb(); + }); + + it('should register the default generator', function() { + generate.register('default', require('../lib/generators/default')); + assert(generate.hasGenerator('default')); + }); + }); +}); diff --git a/verbfile.js b/verbfile.js deleted file mode 100644 index e269a4d6..00000000 --- a/verbfile.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('./lib/utils'); - -/** - * HEADS UP: this is not what a normal verfile.js would look like. - * We're just trying to break everyting right now, so this has a - * lot of stuff in it. - */ - -module.exports = function(verb, base, env) { - - /** - * Readme task - */ - - verb.task('readme', function(cb) { - var plugins = verb.get('env.argv.plugins') || verb.plugins; - - // ask pre-configured questions, but only if - // they don't have answers yet - verb.toStream('docs', function(key, view) { - if (view.dest === 'readme.md') { - // custom `sections` from package.json `verb` config - var sections = verb.get('cache.sections'); - if (sections) view.content = sections; - return true; - } - }) - .pipe(handle(verb, 'onStream')) - .on('error', cb) - .pipe(verb.renderFile('text')) - .on('error', cb) - .on('error', utils.handleError(verb)) - .pipe(verb.pipeline(plugins)) - .pipe(handle(verb, 'preWrite')) - .pipe(verb.dest(rename())) - .pipe(utils.exhaust(handle(verb, 'postWrite'))) - .on('finish', cb); - }); - - /** - * Re-write package.json with any user-defined config updates - */ - - verb.task('package', function(cb) { - verb.toStream('docs', function(key, view) { - return view.basename === 'package.json'; - }) - .pipe(handle(verb, 'preWrite')) - .pipe(verb.dest(rename())) - .pipe(utils.exhaust(handle(verb, 'postWrite'))) - .on('error', cb) - .on('finish', cb); - }); - - /** - * Default tasks - */ - - verb.task('default', ['readme', 'package']); -}; - -/** - * Handle a middleware stage in the pipeline - * - * @param {Object} `app` - * @param {Object} `stage` - * @return {Object} - */ - -function handle(app, stage) { - return utils.through.obj(function(file, enc, next) { - if (file.isNull()) return next(); - if (typeof app.handle !== 'function') { - return next(null, file); - } - app.handle(stage, file, next); - }); -} - -/** - * Rename template files - */ - -function rename(dest) { - return function(file) { - var fp = file.dest || dest || ''; - file.base = fp ? path.dirname(fp) : file.base; - file.path = fp; - file.basename = file.basename.replace(/^_/, '.'); - file.basename = file.basename.replace(/^\$/, ''); - return file.base; - }; -} From be80665a2e05dfdad489094093841bbaca605c3f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 6 Feb 2016 05:13:46 -0500 Subject: [PATCH 145/282] update unit tests --- test/app.applyLayout.js | 16 +- test/app.cli.js | 23 ++ test/app.collection.compile.js | 18 +- test/app.collection.js | 95 ++--- test/app.collection.render.js | 84 ++--- test/app.compile.js | 18 +- test/app.copy.js | 20 +- test/app.create.js | 154 +++++--- test/app.data.js | 26 +- test/app.dest.js | 212 +++++------ test/app.doc.js | 8 +- test/app.docs.js | 8 +- test/app.engines.js | 99 +++--- test/app.events.js | 96 ++++- test/app.generate.js | 462 +----------------------- test/app.get-set.js | 26 +- test/app.handle.js | 22 +- test/app.handlers.js | 72 ++-- test/app.include.js | 11 +- test/app.includes.js | 11 +- test/app.invoke.js | 6 + test/app.js | 85 ++--- test/app.layout.js | 11 +- test/app.layouts.js | 11 +- test/app.list.compile.js | 12 +- test/app.list.js | 52 ++- test/app.list.render.js | 157 ++++++++ test/app.lookups.js | 58 +-- test/app.mergePartials.js | 106 ++++++ test/app.middleware.js | 28 +- test/app.onLoad.js | 50 +++ test/app.option.js | 10 +- test/app.page.js | 47 +++ test/app.pages.js | 34 ++ test/app.partial.js | 25 ++ test/app.partials.js | 28 ++ test/app.render.js | 120 +++---- test/app.renderFile.js | 82 ++--- test/app.route.js | 36 +- test/app.src.js | 172 +++++---- test/app.store.js | 333 +++++++++++++++++ test/app.symlink.js | 44 +-- test/app.task.js | 87 +++-- test/app.toStream.js | 16 +- test/app.use.js | 94 ++--- test/app.view.compile.js | 14 +- test/app.view.render.js | 48 +-- test/app.viewTypes.js | 54 +++ test/app.views.js | 29 ++ test/app.watch.js | 42 --- test/collection.engines.js | 102 +++--- test/collection.events.js | 10 +- test/collection.getView.js | 34 ++ test/collection.js | 160 +++++---- test/collection.options.js | 10 +- test/collection.render.js | 72 ++-- test/collection.use.js | 66 ++-- test/fixtures/copy/a.txt | 1 + test/fixtures/copy/b.txt | 1 + test/fixtures/copy/c.txt | 1 + test/fixtures/helpers/a.js | 2 +- test/fixtures/helpers/b.js | 2 +- test/fixtures/helpers/c.js | 2 +- test/fixtures/helpers/obj.js | 2 +- test/fixtures/posts/a.hbs | 4 + test/fixtures/posts/b.hbs | 4 + test/fixtures/posts/c.hbs | 4 + test/fixtures/templates/a.tmpl | 2 +- test/fixtures/templates/b.tmpl | 2 +- test/fixtures/templates/c.tmpl | 2 +- test/group.js | 132 ++++--- test/handlers.js | 16 +- test/helpers.js | 632 ++++++++++++++++++++------------- test/is.js | 69 ++++ test/item.js | 354 +++++++++--------- test/layouts.js | 62 ++-- test/list.deleteItem.js | 74 ++++ test/list.getIndex.js | 105 ++++++ test/list.js | 144 ++++---- test/list.render.js | 72 ++-- test/list.use.js | 66 ++-- test/mergePartials.js | 24 +- test/partials.js | 96 ++--- test/questions.js | 8 +- test/renameKey.js | 86 ++--- test/render.js | 57 ++- test/routes.js | 44 +-- test/store.js | 120 +++---- test/support/ignore.js | 2 +- test/support/index.js | 19 +- test/view.content.js | 12 +- test/view.events.js | 10 +- test/view.isType.js | 26 ++ test/view.js | 404 ++++++++++----------- test/view.option.js | 10 +- test/view.render.js | 34 +- test/view.set.js | 23 +- test/view.use.js | 40 ++- test/viewTypes.js | 14 +- test/views.getView.js | 62 ++++ test/views.js | 170 ++++----- test/views.use.js | 66 ++-- 102 files changed, 4036 insertions(+), 2872 deletions(-) create mode 100644 test/app.cli.js create mode 100644 test/app.list.render.js create mode 100644 test/app.mergePartials.js create mode 100644 test/app.onLoad.js create mode 100644 test/app.page.js create mode 100644 test/app.pages.js create mode 100644 test/app.partial.js create mode 100644 test/app.partials.js create mode 100644 test/app.store.js create mode 100644 test/app.viewTypes.js create mode 100644 test/app.views.js delete mode 100644 test/app.watch.js create mode 100644 test/collection.getView.js create mode 100644 test/fixtures/copy/a.txt create mode 100644 test/fixtures/copy/b.txt create mode 100644 test/fixtures/copy/c.txt create mode 100644 test/fixtures/posts/a.hbs create mode 100644 test/fixtures/posts/b.hbs create mode 100644 test/fixtures/posts/c.hbs create mode 100644 test/is.js create mode 100644 test/list.deleteItem.js create mode 100644 test/list.getIndex.js create mode 100644 test/view.isType.js create mode 100644 test/views.getView.js diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js index a1f35509..950d64e7 100644 --- a/test/app.applyLayout.js +++ b/test/app.applyLayout.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -12,12 +14,12 @@ var page = { } }; -describe('helpers', function() { +describe('app.applyLayout', function() { describe('rendering', function() { beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); - app.create('layout', {viewType: 'layout'}); + app.create('layout', { viewType: 'layout' }); app.create('page'); }); @@ -25,7 +27,7 @@ describe('helpers', function() { app.layout('fofof.tmpl', {content: '..'}); app.page('a.tmpl', page) .render(function(err) { - assert(/layouts/.test(err.message)); + assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but cannot be not found (common causes are incorrect glob patterns, renameKey function modifying the key, and typos in search pattern)'); cb(); }); }); @@ -33,7 +35,7 @@ describe('helpers', function() { it('should emit an error when a layout cannot be found:', function(cb) { app.layout('fofof.tmpl', {content: '..'}); app.on('error', function(err) { - assert(/layouts/.test(err.message)); + assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but cannot be not found (common causes are incorrect glob patterns, renameKey function modifying the key, and typos in search pattern)'); cb(); }); @@ -45,14 +47,14 @@ describe('helpers', function() { it('should throw an error - layout defined but no layouts registered:', function(cb) { app.page('a.tmpl', page) .render(function(err) { - assert(/layouts/.test(err.message)); + assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but no layouts are registered'); cb(); }); }); it('should emit an error - layout defined but no layouts registered:', function(cb) { app.on('error', function(err) { - assert(/layouts/.test(err.message)); + assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but no layouts are registered'); cb(); }); app.page('a.tmpl', page) @@ -76,7 +78,7 @@ describe('helpers', function() { var view = app.pages.getView('a.tmpl'); app.render(view, function(err, res) { if (err) return cb(err); - assert.equal(res.contents.toString(), 'before Halle after'); + assert(res.contents.toString() === 'before Halle after'); cb(); }); }); diff --git a/test/app.cli.js b/test/app.cli.js new file mode 100644 index 00000000..b7c1c6c4 --- /dev/null +++ b/test/app.cli.js @@ -0,0 +1,23 @@ +'use strict'; + +require('mocha'); +require('should'); +var path = require('path'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.cli', function() { + beforeEach(function() { + app = new App(); + }); + + describe('app.cli.map', function() { + it('should add a property to app.cli', function() { + app.cli.map('abc', function() {}); + assert.equal(app.cli.keys.pop(), 'abc'); + }); + }); +}); + diff --git a/test/app.collection.compile.js b/test/app.collection.compile.js index 6bd595b7..e6d91831 100644 --- a/test/app.collection.compile.js +++ b/test/app.collection.compile.js @@ -1,3 +1,5 @@ +'use strict'; + require('should'); var assert = require('assert'); var support = require('./support'); @@ -5,12 +7,12 @@ var App = support.resolve(); var Views = App.Views; var views; -describe('compile', function () { - beforeEach(function () { +describe('app.collection.compile', function() { + beforeEach(function() { views = new Views(); }); - it('should throw an error when an engine cannot be found:', function () { + it('should throw an error when an engine cannot be found:', function() { views.addView('foo.bar', {content: '<%= name %>'}); var page = views.getView('foo.bar'); (function() { @@ -18,7 +20,7 @@ describe('compile', function () { }).should.throw('Views#compile cannot find an engine for: .bar'); }); - it('should compile a template:', function () { + it('should compile a template:', function() { views.engine('tmpl', require('engine-base')); views.addView('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); @@ -27,7 +29,7 @@ describe('compile', function () { assert.equal(typeof view.fn, 'function'); }); - it('should compile a template by name:', function () { + it('should compile a template by name:', function() { views.engine('tmpl', require('engine-base')); views.addView('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); @@ -35,16 +37,16 @@ describe('compile', function () { assert.equal(typeof view.fn, 'function'); }); - it('should throw an error when a callback is given:', function () { + it('should throw an error when a callback is given:', function() { views.engine('md', require('engine-base')); views.addView('foo.md', {content: '<%= name %>'}); var page = views.getView('foo.md'); (function() { - views.compile(page, function () {}); + views.compile(page, function() {}); }).should.throw('Views#compile is sync and does not take a callback function'); (function() { - views.compile(page, {}, function () {}); + views.compile(page, {}, function() {}); }).should.throw('Views#compile is sync and does not take a callback function'); }); }); diff --git a/test/app.collection.js b/test/app.collection.js index b96cbaf2..cbcccf5f 100644 --- a/test/app.collection.js +++ b/test/app.collection.js @@ -1,6 +1,9 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); +var path = require('path'); var assert = require('assert'); var define = require('define-property'); var support = require('./support'); @@ -8,37 +11,37 @@ var App = support.resolve(); var Collection = App.Collection; var app; -describe('collection', function () { - describe('method', function () { - beforeEach(function () { +describe('app.collection', function() { + describe('method', function() { + beforeEach(function() { app = new App(); }); - it('should expose the collection method', function () { + it('should expose the collection method', function() { assert(typeof app.collection === 'function'); }); - it('should return a new collection', function () { + it('should return a new collection', function() { var collection = app.collection(); assert(typeof collection === 'object'); }); - it('should have isCollection property', function () { + it('should have isCollection property', function() { var collection = app.collection(); assert(collection.isCollection === true); }); }); - describe('adding views', function () { - beforeEach(function () { + describe('adding views', function() { + beforeEach(function() { app = new App() - .use(function () { - return function () { + .use(function() { + return function() { define(this, 'count', { get: function() { return Object.keys(this.views).length; }, - set: function () { + set: function() { throw new Error('count is a read-only getter and cannot be defined.'); } }); @@ -49,39 +52,40 @@ describe('collection', function () { app.create('pages'); }); - it('should load a view onto the respective collection:', function () { + it('should load a view onto the respective collection:', function() { app.pages('test/fixtures/pages/a.hbs'); - app.views.pages.should.have.property('test/fixtures/pages/a.hbs'); + app.views.pages.should.have.property(path.resolve('test/fixtures/pages/a.hbs')); }); - it('should allow collection methods to be chained:', function () { + it('should allow collection methods to be chained:', function() { app .pages('test/fixtures/pages/a.hbs') .pages('test/fixtures/pages/b.hbs') .pages('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' + path.resolve('test/fixtures/pages/a.hbs'), + path.resolve('test/fixtures/pages/b.hbs'), + path.resolve('test/fixtures/pages/c.hbs') ]); }); - it('should expose the `option` method:', function () { + it('should expose the `option` method:', function() { app.pages.option('foo', 'bar') .pages('test/fixtures/pages/a.hbs') .pages('test/fixtures/pages/b.hbs') .pages('test/fixtures/pages/c.hbs'); app.pages.options.should.have.property('foo', 'bar'); + app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' + path.resolve('test/fixtures/pages/a.hbs'), + path.resolve('test/fixtures/pages/b.hbs'), + path.resolve('test/fixtures/pages/c.hbs') ]); }); - it('should expose the `option` method:', function () { + it('should expose the `option` method:', function() { app.pages.option('foo', 'bar') .pages('test/fixtures/pages/a.hbs') .pages('test/fixtures/pages/b.hbs') @@ -91,12 +95,12 @@ describe('collection', function () { }); }); - describe('addItem', function () { - beforeEach(function () { + describe('addItem', function() { + beforeEach(function() { app = new App(); }); - it('should add items to a collection', function () { + it('should add items to a collection', function() { var pages = app.collection({Collection: Collection}); pages.addItem('foo'); pages.addItem('bar'); @@ -107,7 +111,7 @@ describe('collection', function () { pages.items.hasOwnProperty('baz'); }); - it('should create a collection from an existing collection:', function () { + it('should create a collection from an existing collection:', function() { var pages = app.collection({Collection: Collection}); pages.addItem('foo'); pages.addItem('bar'); @@ -120,55 +124,54 @@ describe('collection', function () { }); }); - describe('rendering views', function () { - beforeEach(function () { + describe('rendering views', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); app.create('pages'); + app.cache.data = {}; }); - it('should render a view with inherited app.render', function (done) { + it('should render a view with inherited app.render', function(cb) { app.page('test/fixtures/templates/a.tmpl') - .use(function (view) { - view.content = fs.readFileSync(view.path); + .use(function(view) { + view.contents = fs.readFileSync(view.path); }) .set('data.name', 'Brian') - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.content === 'Brian'); - done(); + cb(); }); }); }); }); -describe('collection singular method', function () { - describe('create', function () { - beforeEach(function () { +describe('collection singular method', function() { + describe('create', function() { + beforeEach(function() { app = new App(); }); - it('should add a pluralized collection from singular name', function () { + it('should add a pluralized collection from singular name', function() { app.create('page'); assert(typeof app.views.pages === 'object'); }); }); - describe('adding views', function () { - beforeEach(function () { + describe('adding views', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('page'); }); - it('should add a view to the created collection:', function () { + it('should add a view to the created collection:', function() { app.page('test/fixtures/pages/a.hbs'); - assert(typeof app.views.pages['test/fixtures/pages/a.hbs'] === 'object'); + assert(typeof app.views.pages[path.resolve('test/fixtures/pages/a.hbs')] === 'object'); }); - it('should expose the `option` method:', function () { + it('should expose the `option` method:', function() { app.pages.option('foo', 'bar'); app.pages.options.should.have.property('foo', 'bar'); }); diff --git a/test/app.collection.render.js b/test/app.collection.render.js index 135b1347..f2cc639f 100644 --- a/test/app.collection.render.js +++ b/test/app.collection.render.js @@ -9,51 +9,51 @@ var App = support.resolve(); var List = App.List; var pages, app; -describe('render', function () { - describe('rendering', function () { - beforeEach(function () { +describe('app.collection.render', function() { + describe('rendering', function() { + beforeEach(function() { app = App(); pages = app.create('pages'); app.engine('tmpl', require('engine-base')); pages.engine('tmpl', require('engine-base')); }); - it('should throw an error when no callback is given:', function () { + it('should throw an error when no callback is given:', function() { (function() { app.pages.render({}); }).should.throw('Views#render is async and expects a callback function'); }); - it('should throw an error when an engine is not defined:', function (done) { - pages.addView('foo.bar', {content: '<%= name %>'}); + it('should throw an error when an engine is not defined:', function(cb) { + pages.addView('foo.bar', { content: '<%= name %>' }); var page = pages.getView('foo.bar'); app.pages.render(page, function(err) { assert(err.message === 'Views#render cannot find an engine for: .bar'); - done(); + cb(); }); }); - it('should use helpers defined on app to render a view:', function (done) { + it('should use helpers defined on app to render a view:', function(cb) { var locals = {name: 'Halle'}; - app.helper('upper', function (str) { + app.helper('upper', function(str) { return str.toUpperCase(str) + 'app'; }); pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getView('a.tmpl'); - app.render(page, function (err, res) { - if (err) return done(err); + app.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLEapp b'); - done(); + cb(); }); }); - it('should use helpers defined on app to render a view with collection.render:', function (done) { + it('should use helpers defined on app to render a view with collection.render:', function(cb) { var locals = {name: 'Halle'}; - app.helper('upper', function (str) { + app.helper('upper', function(str) { return str.toUpperCase(str) + 'app'; }); @@ -61,63 +61,63 @@ describe('render', function () { pages.helper('upper', app._.helpers.sync.upper); var page = pages.getView('a.tmpl'); - pages.render(page, function (err, res) { - if (err) return done(err); + pages.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLEapp b'); - done(); + cb(); }); }); - it('should use helpers when rendering a view:', function (done) { + it('should use helpers when rendering a view:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function (str) { + pages.helper('upper', function(str) { return str.toUpperCase(str); }); pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getView('a.tmpl'); - pages.render(page, function (err, res) { - if (err) return done(err); + pages.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); - it('should render a template when contents is a buffer:', function (done) { + it('should render a template when contents is a buffer:', function(cb) { pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); var view = pages.getView('a.tmpl'); - pages.render(view, function (err, view) { - if (err) return done(err); + pages.render(view, function(err, view) { + if (err) return cb(err); assert(view.contents.toString() === 'b'); - done(); + cb(); }); }); - it('should render a template when content is a string:', function (done) { + it('should render a template when content is a string:', function(cb) { pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); var view = pages.getView('a.tmpl'); - pages.render(view, function (err, view) { - if (err) return done(err); + pages.render(view, function(err, view) { + if (err) return cb(err); assert(view.contents.toString() === 'b'); - done(); + cb(); }); }); - it('should render a view from its path:', function (done) { + it('should render a view from its path:', function(cb) { pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - pages.render('a.tmpl', function (err, view) { - if (err) return done(err); + pages.render('a.tmpl', function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use a plugin for rendering:', function (done) { + it('should use a plugin for rendering:', function(cb) { pages.engine('tmpl', require('engine-base')); pages.option('engine', 'tmpl'); @@ -131,26 +131,26 @@ describe('render', function () { 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}} }); - pages.use(function (collection) { + pages.use(function(collection) { collection.option('pager', false); - collection.renderEach = function (cb) { + collection.renderEach = function(cb) { var list = new List(collection); - async.map(list.items, function (item, next) { + async.map(list.items, function(item, next) { collection.render(item, next); }, cb); }; }); - pages.renderEach(function (err, items) { - if (err) return done(err); + pages.renderEach(function(err, items) { + if (err) return cb(err); assert(items[0].content === 'aaa'); assert(items[9].content === 'jjj'); assert(items.length === 10); - done(); + cb(); }); }); }); diff --git a/test/app.compile.js b/test/app.compile.js index 423c9f99..2b8d585e 100644 --- a/test/app.compile.js +++ b/test/app.compile.js @@ -1,16 +1,18 @@ +'use strict'; + require('should'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); var app; -describe('compile', function () { - beforeEach(function () { +describe('app.compile', function() { + beforeEach(function() { app = new App(); app.create('page'); }); - it('should throw an error when an engine cannot be found:', function () { + it('should throw an error when an engine cannot be found:', function() { app.page('foo.bar', {content: '<%= name %>'}); var page = app.pages.getView('foo.bar'); (function() { @@ -18,7 +20,7 @@ describe('compile', function () { }).should.throw('Templates#compile cannot find an engine for: .bar'); }); - it('should compile a template:', function () { + it('should compile a template:', function() { app.engine('tmpl', require('engine-base')); app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); @@ -27,7 +29,7 @@ describe('compile', function () { assert.equal(typeof view.fn, 'function'); }); - it('should compile a template by name:', function () { + it('should compile a template by name:', function() { app.engine('tmpl', require('engine-base')); app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); @@ -35,17 +37,17 @@ describe('compile', function () { assert.equal(typeof view.fn, 'function'); }); - it('should throw an error when a callback is given:', function () { + it('should throw an error when a callback is given:', function() { app.engine('md', require('engine-base')); app.page('foo.md', {content: '<%= name %>'}); var page = app.pages.getView('foo.md'); (function() { - app.compile(page, function () { + app.compile(page, function() { }); }).should.throw('Templates#compile is sync and does not take a callback function'); (function() { - app.compile(page, {}, function () { + app.compile(page, {}, function() { }); }).should.throw('Templates#compile is sync and does not take a callback function'); }); diff --git a/test/app.copy.js b/test/app.copy.js index 6df9b1bd..db1ee170 100644 --- a/test/app.copy.js +++ b/test/app.copy.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); var path = require('path'); var assert = require('assert'); @@ -9,23 +11,23 @@ var fixtures = path.join(__dirname, 'fixtures/copy/*.txt'); var actual = path.join(__dirname, 'actual'); describe('copy()', function() { - beforeEach(function (done) { - rimraf(actual, done); + beforeEach(function(cb) { + rimraf(actual, cb); app = new App(); }); - afterEach(function (done) { - rimraf(actual, done); + afterEach(function(cb) { + rimraf(actual, cb); }); - describe('streams', function () { - it('should copy files', function (done) { + describe('streams', function() { + it('should copy files', function(cb) { app.copy(fixtures, path.join(__dirname, 'actual')) - .on('error', done) - .on('data', function (file) { + .on('error', cb) + .on('data', function(file) { assert.equal(typeof file, 'object'); }) - .on('end', done); + .on('end', cb); }); }); }); diff --git a/test/app.create.js b/test/app.create.js index eb096991..eaca750b 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); @@ -7,33 +9,86 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('create', function () { - describe('inflections', function () { - beforeEach(function () { +describe('app.create', function() { + describe('inflections', function() { + beforeEach(function() { app = new App(); }); - it('should expose the create method', function () { + it('should expose the create method', function() { assert(typeof app.create === 'function'); }); - it('should add a collection to `views`', function () { + it('should add a collection to `views`', function() { app.create('pages'); assert(typeof app.views.pages === 'object'); assert(typeof app.pages === 'function'); }); - it('should add a pluralized collection to `views`', function () { + it('should add a pluralized collection to `views`', function() { app.create('page'); assert(typeof app.views.pages === 'object'); assert(typeof app.page === 'function'); }); }); - describe('custom constructors', function () { - beforeEach(function () { + describe('renderable views', function() { + beforeEach(function() { + app = new App(); + app.create('pages'); + app.create('partials', {viewType: 'partial'}); + app.create('layout', {viewType: 'layout'}); + }); + + it('should add renderable views when no type is defined', function() { + app.pages.addView('foo', {content: 'bar'}); + assert(app.views.pages.hasOwnProperty('foo')); + }); + + it('should add view Ctor names to views', function() { + app.pages.addView('foo', {content: 'bar'}); + assert(app.views.pages.foo._name === 'Page'); + }); + + it('should add partial views when partial type is defined', function() { + app.partials.addView('abc', {content: 'xyz'}); + assert(app.views.partials.hasOwnProperty('abc')); + }); + + it('should add layout views when layout type is defined', function() { + app.layouts.addView('foo', {content: 'bar'}); + assert(app.views.layouts.hasOwnProperty('foo')); + }); + + it('should set viewType on renderable views', function() { + app.pages.addView('foo', {content: 'bar'}); + var view = app.pages.getView('foo'); + assert(view.isType('renderable')); + assert(!view.isType('layout')); + assert(!view.isType('partial')); + }); + + it('should set viewType on partial views', function() { + app.partials.addView('foo', {content: 'bar'}); + var view = app.partials.getView('foo'); + assert(view.isType('partial')); + assert(!view.isType('layout')); + assert(!view.isType('renderable')); + }); + + it('should set viewType on layout views', function() { + app.layouts.addView('foo', {content: 'bar'}); + var view = app.layouts.getView('foo'); + assert(view.isType('layout')); + assert(!view.isType('renderable')); + assert(!view.isType('partial')); + }); + }); + + describe('custom constructors', function() { + beforeEach(function() { var Vinyl = require('vinyl'); - Vinyl.prototype.custom = function (key) { + Vinyl.prototype.custom = function(key) { this[key] = 'nonsense'; return this; }; @@ -41,7 +96,7 @@ describe('create', function () { app.create('pages'); }); - it('should create views from key-value pairs:', function () { + it('should create views from key-value pairs:', function() { app.page('a.hbs', {path: 'a.hbs', content: 'a'}); app.page('b.hbs', {path: 'b.hbs', content: 'b'}); app.page('c.hbs', {path: 'c.hbs', content: 'c'}); @@ -51,10 +106,10 @@ describe('create', function () { }); }); - describe('custom instances', function () { - it('should create views from custom `View` and `Views` instance/ctor:', function () { + describe('custom instances', function() { + it('should create views from custom `View` and `Views` instance/ctor:', function() { var Vinyl = require('vinyl'); - Vinyl.prototype.read = function (file) { + Vinyl.prototype.read = function(file) { return fs.readFileSync(file.path); }; @@ -80,16 +135,14 @@ describe('create', function () { }); }); - describe('chaining', function () { - beforeEach(function () { + describe('chaining', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); app.create('page'); }); - it('should create views from key-value pairs:', function () { + it('should create views from key-value pairs:', function() { app.page('a.hbs', {content: 'a'}); app.page('b.hbs', {content: 'b'}); app.page('c.hbs', {content: 'c'}); @@ -97,80 +150,71 @@ describe('create', function () { assert(app.views.pages['a.hbs'].contents.toString() === 'a'); }); - it('should create views from file paths:', function () { + it('should create views from file paths:', function() { app.page('test/fixtures/pages/a.hbs'); app.page('test/fixtures/pages/b.hbs'); app.page('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' + path.resolve('test/fixtures/pages/a.hbs'), + path.resolve('test/fixtures/pages/b.hbs'), + path.resolve('test/fixtures/pages/c.hbs'), ]); }); }); - - describe('instance', function () { - beforeEach(function () { + describe('instance', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); }); - it('should return the collection instance', function () { + it('should return the collection instance', function() { var collection = app.create('pages'); assert(collection instanceof App.Views); - collection.option('renameKey', function (key) { + collection.option('renameKey', function(key) { return path.basename(key); }); collection - .use(function (views) { - views.read = function (name) { + .use(function(views) { + views.read = function(name) { var view = this.getView(name); - if (!view.content) { - view.content = fs.readFileSync(view.path); - } + view.contents = fs.readFileSync(view.path); }; }); collection.addView('test/fixtures/templates/a.tmpl'); collection.read('a.tmpl'); - assert(collection.getView('a.tmpl').content === '{%= name %}'); + assert(collection.getView('a.tmpl').contents.toString() === '<%= name %>'); }); }); - describe('viewType', function () { - beforeEach(function () { + describe('viewType', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); }); - it('should add collection to the given viewType', function () { + it('should add collection to the given viewType', function() { app.create('layout', {viewType: 'layout'}); assert(app.layouts.options.viewType[0] === 'layout'); }); - it('should add a collection to multiple viewTypes', function () { + it('should add a collection to multiple viewTypes', function() { app.create('foo', {viewType: ['layout', 'renderable']}); assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); }); }); - describe('events', function () { - beforeEach(function () { + describe('events', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); }); - it('should emit `create` when a collection is created:', function () { - app.on('create', function (collection) { + it('should emit `create` when a collection is created:', function() { + app.on('create', function(collection) { if (collection.options.plural === 'layouts') { collection.options.foo = 'bar'; } @@ -182,11 +226,11 @@ describe('create', function () { }); }); - describe('collection instantiation', function () { - it('should expose collection instance methods that are created after instantiation on the app collection loader', function () { + describe('collection instantiation', function() { + it('should expose collection instance methods that are created after instantiation on the app collection loader', function() { app.create('pages'); - app.pages.use(function (collection) { - collection.define('foo', function (msg) { + app.pages.use(function(collection) { + collection.define('foo', function(msg) { return 'foo ' + msg; }); }); diff --git a/test/app.data.js b/test/app.data.js index 9cf2cd3d..41808b62 100644 --- a/test/app.data.js +++ b/test/app.data.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var path = require('path'); @@ -6,35 +8,35 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('app.data', function () { - beforeEach(function () { +describe('app.data', function() { + beforeEach(function() { app = new App(); }); - it('should set a key-value pair on cache.data:', function () { + it('should set a key-value pair on cache.data:', function() { app.data('a', 'b'); assert(app.cache.data.a === 'b'); }); - it('should set an object on cache.data:', function () { + it('should set an object on cache.data:', function() { app.data({c: 'd'}); assert(app.cache.data.c === 'd'); }); - it('should load data from a file onto cache.data:', function () { + it('should load data from a file onto cache.data:', function() { app.data('test/fixtures/data/a.json'); assert(app.cache.data.a.one.a === 'aaa'); }); - it('should load a glob of data onto cache.data:', function () { + it('should load a glob of data onto cache.data:', function() { app.data('test/fixtures/data/*.json'); assert(app.cache.data.a.one.a === 'aaa'); assert(app.cache.data.b.two.b === 'bbb'); assert(app.cache.data.c.three.c === 'ccc'); }); - it('should use `namespace` defined on global opts:', function () { - app.option('namespace', function (key) { + it('should use `namespace` defined on global opts:', function() { + app.option('namespace', function(key) { return 'prefix_' + path.basename(key, path.extname(key)); }); app.data('test/fixtures/data/*.json'); @@ -43,9 +45,9 @@ describe('app.data', function () { assert(app.cache.data.prefix_c.three.c === 'ccc'); }); - it('should use `namespace` defined on data opts:', function () { + it('should use `namespace` defined on data opts:', function() { app.data('test/fixtures/data/*.json', { - namespace: function (key) { + namespace: function(key) { return 'prefix_' + path.basename(key, path.extname(key)); } }); @@ -54,9 +56,9 @@ describe('app.data', function () { assert(app.cache.data.prefix_c.three.c === 'ccc'); }); - it('should use `renameKey` defined on data opts:', function () { + it('should use `renameKey` defined on data opts:', function() { app.data('test/fixtures/data/*.json', { - renameKey: function (key) { + renameKey: function(key) { return 'prefix_' + path.basename(key, path.extname(key)); } }); diff --git a/test/app.dest.js b/test/app.dest.js index 1e3d67e8..97fc937e 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -42,29 +42,29 @@ describe('dest stream', function() { beforeEach(wipeOut); afterEach(wipeOut); - it('should explode on invalid folder (empty)', function(done) { + it('should explode on invalid folder (empty)', function(cb) { var stream; try { stream = app.dest(); } catch (err) { assert(err && typeof err === 'object'); should.not.exist(stream); - done(); + cb(); } }); - it('should explode on invalid folder (empty string)', function(done) { + it('should explode on invalid folder (empty string)', function(cb) { var stream; try { stream = app.dest(''); } catch (err) { assert(err && typeof err === 'object'); should.not.exist(stream); - done(); + cb(); } }); - it('should pass through writes with cwd', function(done) { + it('should pass through writes with cwd', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var expectedFile = new File({ @@ -77,7 +77,7 @@ describe('dest stream', function() { var onEnd = function(){ buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); - done(); + cb(); }; var stream = app.dest('./actual/', {cwd: __dirname}); @@ -89,7 +89,7 @@ describe('dest stream', function() { stream.end(); }); - it('should pass through writes with default cwd', function(done) { + it('should pass through writes with default cwd', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var expectedFile = new File({ @@ -102,7 +102,7 @@ describe('dest stream', function() { var onEnd = function(){ buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); - done(); + cb(); }; var stream = app.dest(path.join(__dirname, 'actual/')); @@ -114,7 +114,7 @@ describe('dest stream', function() { stream.end(); }); - it('should not write null files', function(done) { + it('should not write null files', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -135,7 +135,7 @@ describe('dest stream', function() { buffered[0].base.should.equal(expectedBase, 'base should have changed'); buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(false); - done(); + cb(); }; var stream = app.dest('./actual/', {cwd: __dirname}); @@ -147,7 +147,7 @@ describe('dest stream', function() { stream.end(); }); - it('should write buffer files to the right folder with relative cwd', function(done) { + it('should write buffer files to the right folder with relative cwd', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -170,7 +170,7 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); + cb(); }; var stream = app.dest('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); @@ -182,7 +182,7 @@ describe('dest stream', function() { stream.end(); }); - it('should write buffer files to the right folder with function and relative cwd', function(done) { + it('should write buffer files to the right folder with function and relative cwd', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -205,7 +205,7 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); + cb(); }; var stream = app.dest(function(file){ @@ -221,7 +221,7 @@ describe('dest stream', function() { stream.end(); }); - it('should write buffer files to the right folder', function(done) { + it('should write buffer files to the right folder', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -249,7 +249,7 @@ describe('dest stream', function() { fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; var stream = app.dest('./actual/', {cwd: __dirname}); @@ -261,7 +261,7 @@ describe('dest stream', function() { stream.end(); }); - it('should write streaming files to the right folder', function(done) { + it('should write streaming files to the right folder', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -290,7 +290,7 @@ describe('dest stream', function() { fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; var stream = app.dest('./actual/', {cwd: __dirname}); @@ -306,7 +306,7 @@ describe('dest stream', function() { stream.end(); }); - it('should write directories to the right folder', function(done) { + it('should write directories to the right folder', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test'); @@ -336,7 +336,7 @@ describe('dest stream', function() { fs.existsSync(expectedPath).should.equal(true); fs.lstatSync(expectedPath).isDirectory().should.equal(true); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; var stream = app.dest('./actual/', {cwd: __dirname}); @@ -348,7 +348,7 @@ describe('dest stream', function() { stream.end(); }); - it('should allow piping multiple dests in streaming mode', function(done) { + it('should allow piping multiple dests in streaming mode', function(cb) { var inputPath1 = path.join(__dirname, 'actual/multiple-first'); var inputPath2 = path.join(__dirname, 'actual/multiple-second'); var inputBase = path.join(__dirname, 'actual/'); @@ -372,7 +372,7 @@ describe('dest stream', function() { }).once('end', function() { fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); - done(); + cb(); }); var file = new File({ @@ -386,7 +386,7 @@ describe('dest stream', function() { stream1.end(); }); - it('should write new files with the default user mode', function(done) { + it('should write new files with the default user mode', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -405,7 +405,7 @@ describe('dest stream', function() { buffered[0].should.equal(expectedFile); fs.existsSync(expectedPath).should.equal(true); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; chmodSpy.reset(); @@ -419,7 +419,7 @@ describe('dest stream', function() { stream.end(); }); - it('should write new files with the specified mode', function(done) { + it('should write new files with the specified mode', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -438,7 +438,7 @@ describe('dest stream', function() { buffered[0].should.equal(expectedFile); fs.existsSync(expectedPath).should.equal(true); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; chmodSpy.reset(); @@ -452,7 +452,7 @@ describe('dest stream', function() { stream.end(); }); - it('should update file mode to match the vinyl mode', function(done) { + it('should update file mode to match the vinyl mode', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -477,7 +477,7 @@ describe('dest stream', function() { buffered[0].should.equal(expectedFile); fs.existsSync(expectedPath).should.equal(true); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; fs.mkdirSync(expectedBase); @@ -495,7 +495,7 @@ describe('dest stream', function() { stream.end(); }); - it('should use different modes for files and directories', function(done) { + it('should use different modes for files and directories', function(cb) { var inputBase = path.join(__dirname, 'fixtures/vinyl'); var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); var expectedBase = path.join(__dirname, 'actual/wow'); @@ -512,7 +512,7 @@ describe('dest stream', function() { var onEnd = function(){ realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); realMode(buffered[0].stat.mode).should.equal(expectedFileMode); - done(); + cb(); }; var stream = app.dest('./actual/', { @@ -529,7 +529,7 @@ describe('dest stream', function() { stream.end(); }); - it('should change to the specified base as string', function(done) { + it('should change to the specified base as string', function(cb) { var inputBase = path.join(__dirname, 'fixtures/vinyl'); var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); @@ -541,7 +541,7 @@ describe('dest stream', function() { var onEnd = function(){ buffered[0].base.should.equal(inputBase); - done(); + cb(); }; var stream = app.dest('./actual/', { @@ -557,7 +557,7 @@ describe('dest stream', function() { stream.end(); }); - it('should change to the specified base as function', function(done) { + it('should change to the specified base as function', function(cb) { var inputBase = path.join(__dirname, 'fixtures/vinyl'); var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); @@ -569,7 +569,7 @@ describe('dest stream', function() { var onEnd = function() { buffered[0].base.should.equal(inputBase); - done(); + cb(); }; var stream = app.dest('./actual/', { @@ -589,7 +589,7 @@ describe('dest stream', function() { stream.end(); }); - it('should report IO errors', function(done) { + it('should report IO errors', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -614,12 +614,12 @@ describe('dest stream', function() { var stream = app.dest('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.code.should.equal('EACCES'); - done(); + cb(); }); stream.write(expectedFile); }); - it('should report stat errors', function(done) { + it('should report stat errors', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -649,12 +649,12 @@ describe('dest stream', function() { var stream = app.dest('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.message.should.equal('stat error'); - done(); + cb(); }); stream.write(expectedFile); }); - it('should report chmod errors', function(done) { + it('should report chmod errors', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -684,12 +684,12 @@ describe('dest stream', function() { var stream = app.dest('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.message.should.equal('chmod error'); - done(); + cb(); }); stream.write(expectedFile); }); - it('should not chmod a matching file', function(done) { + it('should not chmod a matching file', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -718,7 +718,7 @@ describe('dest stream', function() { expectedCount.should.equal(1); assert(!chmodSpy.called); realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); + cb(); }; fs.mkdirSync(expectedBase); @@ -737,7 +737,7 @@ describe('dest stream', function() { stream.end(); }); - it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { + it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -766,7 +766,7 @@ describe('dest stream', function() { var onEnd = function(){ expectedCount.should.equal(1); assert(!chmodSpy.called); - done(); + cb(); }; fs.mkdirSync(expectedBase); @@ -785,7 +785,7 @@ describe('dest stream', function() { stream.end(); }); - it('should not overwrite files with overwrite option set to false', function(done) { + it('should not overwrite files with overwrite option set to false', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var inputContents = fs.readFileSync(inputPath); @@ -804,7 +804,7 @@ describe('dest stream', function() { var onEnd = function(){ buffered.length.should.equal(1); bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); - done(); + cb(); }; // Write expected file which should not be overwritten @@ -820,7 +820,7 @@ describe('dest stream', function() { stream.end(); }); - it('should overwrite files with overwrite option set to true', function(done) { + it('should overwrite files with overwrite option set to true', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var inputContents = fs.readFileSync(inputPath); @@ -839,7 +839,7 @@ describe('dest stream', function() { var onEnd = function(){ buffered.length.should.equal(1); bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); - done(); + cb(); }; // This should be overwritten @@ -855,7 +855,7 @@ describe('dest stream', function() { stream.end(); }); - it('should create symlinks when the `symlink` attribute is set on the file', function (done) { + it('should create symlinks when the `symlink` attribute is set on the file', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test-create-dir-symlink'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var inputRelativeSymlinkPath = 'wow'; @@ -873,10 +873,10 @@ describe('dest stream', function() { inputFile.symlink = inputRelativeSymlinkPath; var onEnd = function(){ - fs.readlink(buffered[0].path, function () { + fs.readlink(buffered[0].path, function() { buffered[0].symlink.should.equal(inputFile.symlink); buffered[0].path.should.equal(expectedPath); - done(); + cb(); }); }; @@ -889,12 +889,12 @@ describe('dest stream', function() { stream.end(); }); - it('should emit finish event', function(done) { + it('should emit finish event', function(cb) { var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var stream = app.dest('./actual/', {cwd: __dirname}); stream.once('finish', function() { - done(); + cb(); }); var file = new File({ @@ -909,30 +909,30 @@ describe('dest stream', function() { }); describe('dest', function() { - beforeEach(function (done) { - rimraf(actual, done); + beforeEach(function(cb) { + rimraf(actual, cb); app = new App(); }); - afterEach(function (done) { - rimraf(actual, done); + afterEach(function(cb) { + rimraf(actual, cb); }); - describe('streams', function () { - it('should return a stream', function (done) { + describe('streams', function() { + it('should return a stream', function(cb) { var stream = app.dest(path.join(__dirname, 'fixtures/')); should.exist(stream); should.exist(stream.on); - done(); + cb(); }); - it('should return an output stream that writes files', function (done) { + it('should return an output stream that writes files', function(cb) { var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt')); var outstream = app.dest(actual); instream.pipe(outstream); - outstream.on('error', done); - outstream.on('data', function (file) { + outstream.on('error', cb); + outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); @@ -940,23 +940,23 @@ describe('dest', function() { path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); String(file.contents).should.equal('Hello world!'); }); - outstream.on('end', function () { - fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { + outstream.on('end', function() { + fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { should.not.exist(err); should.exist(contents); String(contents).should.equal('Hello world!'); - done(); + cb(); }); }); }); - it('should return an output stream that does not write non-read files', function (done) { + it('should return an output stream that does not write non-read files', function(cb) { var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt'), {read: false}); var outstream = app.dest(actual); instream.pipe(outstream); - outstream.on('error', done); - outstream.on('data', function (file) { + outstream.on('error', cb); + outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); @@ -964,79 +964,79 @@ describe('dest', function() { path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); }); - outstream.on('end', function () { - fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { + outstream.on('end', function() { + fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { should.exist(err); should.not.exist(contents); - done(); + cb(); }); }); }); - it('should return an output stream that writes streaming files', function (done) { + it('should return an output stream that writes streaming files', function(cb) { var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt'), {buffer: false}); var outstream = instream.pipe(app.dest(actual)); - outstream.on('error', done); - outstream.on('data', function (file) { + outstream.on('error', cb); + outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); should.exist(file.contents); path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); }); - outstream.on('end', function () { - fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { + outstream.on('end', function() { + fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { should.not.exist(err); should.exist(contents); String(contents).should.equal('Hello world!'); - done(); + cb(); }); }); }); - it('should return an output stream that writes streaming files to new directories', function (done) { - testWriteDir({}, done); + it('should return an output stream that writes streaming files to new directories', function(cb) { + testWriteDir({}, cb); }); - it('should return an output stream that writes streaming files to new directories (buffer: false)', function (done) { - testWriteDir({buffer: false}, done); + it('should return an output stream that writes streaming files to new directories (buffer: false)', function(cb) { + testWriteDir({buffer: false}, cb); }); - it('should return an output stream that writes streaming files to new directories (read: false)', function (done) { - testWriteDir({read: false}, done); + it('should return an output stream that writes streaming files to new directories (read: false)', function(cb) { + testWriteDir({read: false}, cb); }); - it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function (done) { - testWriteDir({buffer: false, read: false}, done); + it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function(cb) { + testWriteDir({buffer: false, read: false}, cb); }); }); - describe('ext', function () { - beforeEach(function () { + describe('ext', function() { + beforeEach(function() { app = new App(); app.set('ext', '.txt'); }); - afterEach(function () { + afterEach(function() { app.set('ext', '.html'); }); - it('should return a stream', function (done) { + it('should return a stream', function(cb) { var stream = app.dest(path.join(__dirname, 'fixtures/')); should.exist(stream); should.exist(stream.on); - done(); + cb(); }); - it('should return an output stream that writes files', function (done) { + it('should return an output stream that writes files', function(cb) { var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt')); var outstream = app.dest(actual); instream.pipe(outstream); - outstream.on('error', done); - outstream.on('data', function (file) { + outstream.on('error', cb); + outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); @@ -1044,23 +1044,23 @@ describe('dest', function() { path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); String(file.contents).should.equal('Hello world!'); }); - outstream.on('end', function () { - fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { + outstream.on('end', function() { + fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { should.not.exist(err); should.exist(contents); String(contents).should.equal('Hello world!'); - done(); + cb(); }); }); }); - it('should return an output stream that does not write non-read files', function (done) { + it('should return an output stream that does not write non-read files', function(cb) { var instream = app.src(path.join(__dirname, 'fixtures/dest/*.txt'), {read: false}); var outstream = app.dest(actual); instream.pipe(outstream); - outstream.on('error', done); - outstream.on('data', function (file) { + outstream.on('error', cb); + outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); should.exist(file.path); @@ -1068,21 +1068,21 @@ describe('dest', function() { path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); }); - outstream.on('end', function () { - fs.readFile(path.join(actual, 'example.txt'), function (err, contents) { + outstream.on('end', function() { + fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { should.exist(err); should.not.exist(contents); - done(); + cb(); }); }); }); }); - function testWriteDir(srcOptions, done) { + function testWriteDir(srcOptions, cb) { var instream = app.src(path.join(__dirname, 'fixtures/generic'), srcOptions); var outstream = instream.pipe(app.dest(actual)); - outstream.on('error', done); + outstream.on('error', cb); outstream.on('data', function(file) { // data should be re-emitted correctly should.exist(file); @@ -1095,7 +1095,7 @@ describe('dest', function() { /* jshint expr: true */ should(exists).be.ok; /* jshint expr: false */ - done(); + cb(); }); }); } diff --git a/test/app.doc.js b/test/app.doc.js index abef13a8..b42c8673 100644 --- a/test/app.doc.js +++ b/test/app.doc.js @@ -1,16 +1,18 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); var verb = require('..'); var app; -describe('app', function () { +describe('app', function() { beforeEach(function() { app = verb(); }); - describe('add doc', function () { - it('should add docs to `app.views.docs`:', function () { + describe('add doc', function() { + it('should add docs to `app.views.docs`:', function() { app.doc('a.hbs', {path: 'a.hbs', content: 'a'}); app.doc('b.hbs', {path: 'b.hbs', content: 'b'}); app.doc('c.hbs', {path: 'c.hbs', content: 'c'}); diff --git a/test/app.docs.js b/test/app.docs.js index 13c12e0f..be4ae9be 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -1,16 +1,18 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); var verb = require('..'); var app; -describe('app', function () { +describe('app', function() { beforeEach(function() { app = verb(); }); - describe('add docs', function () { - it('should add docs to `app.views.docs`:', function () { + describe('add docs', function() { + it('should add docs to `app.views.docs`:', function() { app.docs({ 'a.hbs': {path: 'a.hbs', content: 'a'}, 'b.hbs': {path: 'b.hbs', content: 'b'}, diff --git a/test/app.engines.js b/test/app.engines.js index 17c06699..562aeeb8 100644 --- a/test/app.engines.js +++ b/test/app.engines.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,23 +7,23 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('engine support', function() { +describe('app.engines', function() { beforeEach(function() { app = new App(); }); - it('should throw an error when engine name is invalid:', function () { - (function () { + it('should throw an error when engine name is invalid:', function() { + (function() { app.engine(null, {}); }).should.throw('expected engine ext to be a string or array.'); }); - it('should register an engine to the given extension', function () { - app.engine('hbs', function () {}); + it('should register an engine to the given extension', function() { + app.engine('hbs', function() {}); assert(typeof app.engines['.hbs'] === 'object'); }); - it('should set an engine with the given extension', function () { + it('should set an engine with the given extension', function() { var hbs = function() {}; hbs.render = function() {}; hbs.renderFile = function() {}; @@ -31,129 +33,128 @@ describe('engine support', function() { assert(app.engines['.hbs'].render); }); - it('should get an engine:', function () { - app.engine('hbs', function () {}); + it('should get an engine:', function() { + app.engine('hbs', function() {}); var hbs = app.engine('hbs'); assert(typeof hbs === 'object'); assert(hbs.hasOwnProperty('render')); assert(hbs.hasOwnProperty('compile')); }); - it('should return undefined if no engine is found:', function () { + it('should return undefined if no engine is found:', function() { var hbs = app.getEngine(); assert.equal(typeof hbs, 'undefined'); }); - it('should register multiple engines to the given extension', function () { - app.engine(['hbs', 'md'], function () {}); + it('should register multiple engines to the given extension', function() { + app.engine(['hbs', 'md'], function() {}); assert(typeof app.engines['.hbs'] === 'object'); assert(typeof app.engines['.md'] === 'object'); }); }); -describe('engines', function () { - beforeEach(function () { +describe('engines', function() { + beforeEach(function() { app = new App(); app.create('pages'); app.pages('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); app.pages('bar.tmpl', {content: 'A <%= letter %> {{ letter }} C'}); }); - it('should register an engine:', function () { - app.engine('a', {render: function () {}}); + it('should register an engine:', function() { + app.engine('a', {render: function() {}}); app.engines.should.have.property('.a'); }); - it('should use custom delimiters:', function (done) { + it('should use custom delimiters:', function(cb) { app.engine('tmpl', require('engine-base'), { delims: ['{{', '}}'] }); - app.render('foo.tmpl', {letter: 'B'}, function (err, res) { - if (err) return done(err); + app.render('foo.tmpl', {letter: 'B'}, function(err, res) { + if (err) return cb(err); res.contents.toString().should.equal('A <%= letter %> B C'); - done(); + cb(); }); }); - it('should override individual delims values:', function (done) { + it('should override individual delims values:', function(cb) { app.engine('tmpl', require('engine-base'), { interpolate: /\{{([^}]+)}}/g, evaluate: /\{{([^}]+)}}/g, escape: /\{{-([^}]+)}}/g }); - app.render('bar.tmpl', {letter: 'B'}, function (err, res) { - if (err) return done(err); + app.render('bar.tmpl', {letter: 'B'}, function(err, res) { + if (err) return cb(err); res.contents.toString().should.equal('A <%= letter %> B C'); - done(); + cb(); }); }); - it('should get an engine:', function () { + it('should get an engine:', function() { app.engine('a', { - render: function () {} + render: function() {} }); var a = app.engine('a'); a.should.have.property('render'); }); }); - -describe('engine selection:', function () { - beforeEach(function (done) { +describe('engine selection:', function() { + beforeEach(function(cb) { app = new App(); app.engine('tmpl', require('engine-base')); app.engine('hbs', require('engine-handlebars')); app.create('pages'); - done(); + cb(); }); - it('should get the engine from file extension:', function (done) { + it('should get the engine from file extension:', function(cb) { app.page('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on the collection:', function (done) { + it('should use the engine defined on the collection:', function(cb) { app.create('posts', {engine: 'hbs'}); app.post('a', {content: '{{a}}', locals: {a: 'b'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on the view:', function (done) { + it('should use the engine defined on the view:', function(cb) { app.create('posts'); app.post('a', {content: '{{a}}', engine: 'hbs', locals: {a: 'b'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on `view.data`:', function (done) { + it('should use the engine defined on `view.data`:', function(cb) { app.create('posts'); app.post('a', {content: '{{a}}', locals: {a: 'b'}, data: {engine: 'hbs'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on render locals:', function (done) { + it('should use the engine defined on render locals:', function(cb) { app.create('posts'); app.post('a', {content: '{{a}}', locals: {a: 'b'}}) - .render({engine: 'hbs'}, function (err, view) { - if (err) return done(err); + .render({engine: 'hbs'}, function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); }); diff --git a/test/app.events.js b/test/app.events.js index 3bf001e1..11b92748 100644 --- a/test/app.events.js +++ b/test/app.events.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,31 +7,31 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('events', function () { - beforeEach(function () { +describe('app.events', function() { + beforeEach(function() { app = new App(); }); - it('should listen for an event:', function () { + it('should listen for an event:', function() { var app = new App(); - app.on('foo', function () {}); + app.on('foo', function() {}); assert(Array.isArray(app._callbacks['$foo'])); }); - it('should emit an event:', function (done) { + it('should emit an event:', function(cb) { var app = new App(); - app.on('foo', function (val) { + app.on('foo', function(val) { assert(val === 'bar'); - done(); + cb(); }); assert(Array.isArray(app._callbacks['$foo'])); app.emit('foo', 'bar'); }); - it('should listen for `view` events:', function () { - var app = new App(); + it('should listen for `view` events:', function() { + app = new App(); - app.on('view', function (view) { + app.on('view', function(view) { view.foo = 'bar'; }); @@ -37,3 +39,77 @@ describe('events', function () { assert(view.foo === 'bar'); }); }); + +describe('onLoad', function() { + beforeEach(function() { + app = new App(); + }); + + describe('app.collection', function() { + it('should emit a `view` event when view is created', function(cb) { + var collection = app.collection(); + + app.on('view', function(view) { + assert(view.path === 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + + it('should emit an onLoad event when view is created', function(cb) { + var collection = app.collection(); + + app.on('onLoad', function(view) { + assert(view.path === 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + }); + + describe('view collections', function() { + it('should emit a view event when view is created', function(cb) { + app.create('posts'); + + app.on('view', function(view) { + assert(view.path === 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + + it('should emit an onLoad event when view is created', function(cb) { + app.create('posts'); + + app.on('onLoad', function(view) { + assert(view.path === 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + }); +}); diff --git a/test/app.generate.js b/test/app.generate.js index f7484bae..fa3d1e1c 100644 --- a/test/app.generate.js +++ b/test/app.generate.js @@ -1,462 +1,22 @@ 'use strict'; require('mocha'); +require('should'); +var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var generate; +var support = require('./support'); +var App = support.resolve(); +var app; -describe('.generate', function() { +describe('app.generate', function() { beforeEach(function() { - generate = new Verb(); - generate.initVerb({}); + app = new App(); }); - describe('generators', function(cb) { - it('should throw an error when a generator is not found', function(cb) { - generate.generate('fdsslsllsfjssl', function(err) { - assert(err); - assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); - cb(); - }); - }); - - // special case - it('should throw an error when a generator is not found in argv.cwd', function(cb) { - generate.option('cwd', 'foo/bar/baz'); - generate.generate('sflsjljskksl', function(err) { - assert(err); - assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); - cb(); - }); - }); - - it('should throw an error when a stringified task is not found', function(cb) { - generate.register('fdsslsllsfjssl', function() {}); - generate.generate('fdsslsllsfjssl:foo', function(err) { - assert(err); - assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); - cb(); - }); - }); - - it('should throw an error when a task is not found', function(cb) { - generate.register('fdsslsllsfjssl', function() {}); - generate.generate('fdsslsllsfjssl', ['foo'], function(err) { - assert(err); - assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); - cb(); - }); - }); - - it('should not reformat error messages that are not about invalid tasks', function(cb) { - generate.task('default', function(cb) { - cb(new Error('whatever')); - }); - - generate.generate('default', function(err) { - assert(err); - assert.equal(err.message, 'whatever'); - cb(); - }); - }); - - it('should run a task on the instance', function(cb) { - generate.task('foo', function(next) { - next(); - }); - - generate.generate('foo', function(err) { - assert(!err); - cb(); - }); - }); - - it('should run a task instead of a generator of the same name', function(cb) { - generate.register('foo', function(app) { - app.task('default', function() { - cb(new Error('expected the task to run first')); - }); - }); - - generate.task('foo', function() { - cb(); - }); - - generate.generate('foo', function(err) { - assert(!err); - }); - }); - - it('should run a task on a generator with the same name when specified', function(cb) { - generate.register('foo', function(app) { - app.task('default', function() { - cb(); - }); - }); - - generate.task('foo', function() { - cb(new Error('expected the generator to run')); - }); - - generate.generate('foo:default', function(err) { - assert(!err); - }); - }); - - it('should run the default task on a generator', function(cb) { - generate.register('foo', function(app) { - app.task('default', function(next) { - next(); - }); - }); - - generate.generate('foo', function(err) { - assert(!err); - cb(); - }); - }); - - it('should run an array of tasks on the instance', function(cb) { - var count = 0; - generate.task('a', function(next) { - count++; - next(); - }); - generate.task('b', function(next) { - count++; - next(); - }); - generate.task('c', function(next) { - count++; - next(); - }); - - generate.generate('a,b,c', function(err) { - assert.equal(count, 3); - assert(!err); - cb(); - }); - }); - - it('should run the default task on the default generator', function(cb) { - var count = 0; - generate.register('default', function(app) { - app.task('default', function(next) { - count++; - next(); - }); - }); - - generate.generate(function(err) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - - it('should run the default task on a registered generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.task('default', function(next) { - count++; - next(); - }); - }); - - generate.generate('foo', function(err) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - - it('should run the specified task on a registered generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.task('default', function(next) { - count++; - next(); - }); - - app.task('abc', function(next) { - count++; - next(); - }); - }); - - generate.generate('foo', ['abc'], function(err) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - - it('should run an array of tasks on a registered generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.task('default', function(next) { - count++; - next(); - }); - - app.task('a', function(next) { - count++; - next(); - }); - - app.task('b', function(next) { - count++; - next(); - }); - - app.task('c', function(next) { - count++; - next(); - }); - }); - - generate.generate('foo', 'a,b,c', function(err) { - if (err) return cb(err); - assert.equal(count, 3); - cb(); - }); - }); - }); - - describe('sub-generators', function(cb) { - it('should run the default task on a registered sub-generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - count++; - next(); - }); - - sub.task('abc', function(next) { - count++; - next(); - }); - }); - }); - - generate.generate('foo.sub', function(err) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - - it('should run the specified task on a registered sub-generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - count++; - next(); - }); - - sub.task('abc', function(next) { - count++; - next(); - }); - }); - }); - - generate.generate('foo.sub', ['abc'], function(err) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - - it('should run an array of tasks on a registered sub-generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.register('bar', function(bar) { - bar.task('default', function(next) { - count++; - next(); - }); - - bar.task('a', function(next) { - count++; - next(); - }); - - bar.task('b', function(next) { - count++; - next(); - }); - - bar.task('c', function(next) { - count++; - next(); - }); - }); - }); - - generate.generate('foo.bar', ['a', 'b', 'c'], function(err) { - if (err) return cb(err); - assert.equal(count, 3); - cb(); - }); - }); - - it('should run an multiple tasks on a registered sub-generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.register('bar', function(bar) { - bar.task('default', function(next) { - count++; - next(); - }); - - bar.task('a', function(next) { - count++; - next(); - }); - - bar.task('b', function(next) { - count++; - next(); - }); - - bar.task('c', function(next) { - count++; - next(); - }); - }); - }); - - generate.generate('foo.bar', 'a,b,c', function(err) { - if (err) return cb(err); - assert.equal(count, 3); - cb(); - }); - }); - }); - - describe('cross-generator', function(cb) { - it('should run a generator from another generator', function(cb) { - var res = ''; - - generate.register('foo', function(app, two) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - res += 'foo > sub > default '; - generate.generate('bar.sub', next); - }); - }); - }); - - generate.register('bar', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - res += 'bar > sub > default '; - next(); - }); - }); - }); - - generate.generate('foo.sub', function(err) { - if (err) return cb(err); - assert.equal(res, 'foo > sub > default bar > sub > default '); - cb(); - }); - }); - - it('should run the specified task on a registered sub-generator', function(cb) { - var count = 0; - generate.register('foo', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - count++; - next(); - }); - - sub.task('abc', function(next) { - count++; - next(); - }); - }); - }); - - generate.generate('foo.sub', ['abc'], function(err) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - }); - - describe('events', function(cb) { - it('should emit generate', function(cb) { - generate.on('generate', function() { - cb(); - }); - - generate.register('foo', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - next(); - }); - - sub.task('abc', function(next) { - next(); - }); - }); - }); - - generate.generate('foo.sub', ['abc'], function(err) { - if (err) return cb(err); - }); - }); - - it('should expose the generator alias as the first parameter', function(cb) { - generate.on('generate', function(name) { - assert.equal(name, 'sub'); - cb(); - }); - - generate.register('foo', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - next(); - }); - - sub.task('abc', function(next) { - next(); - }); - }); - }); - - generate.generate('foo.sub', ['abc'], function(err) { - if (err) return cb(err); - }); - }); - - it('should expose the tasks array as the second parameter', function(cb) { - generate.on('generate', function(name, tasks) { - assert.deepEqual(tasks, ['abc']); - cb(); - }); - - generate.register('foo', function(app) { - app.register('sub', function(sub) { - sub.task('default', function(next) { - next(); - }); - - sub.task('abc', function(next) { - next(); - }); - }); - }); - - generate.generate('foo.sub', ['abc'], function(err) { - if (err) return cb(err); - }); + describe('app.generate', function() { + it('should expose a generate method on app', function() { + // assert.equal(typeof app.generate, 'function'); }); }); }); + diff --git a/test/app.get-set.js b/test/app.get-set.js index 8bee52c7..e60fe6e2 100644 --- a/test/app.get-set.js +++ b/test/app.get-set.js @@ -1,46 +1,48 @@ +'use strict'; + require('should'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); var app; -describe('app.set()', function () { +describe('app.get-set', function() { beforeEach(function() { app = new App(); }); - it('should set a value', function () { + it('should set a value', function() { app.set('a', 'b'); app.get('a').should.equal('b'); }); - it('should set properties on the instance.', function () { + it('should set properties on the instance.', function() { app.set('a', 'b'); app.a.should.equal('b'); }); - it('should allow an object to be set directly.', function () { + it('should allow an object to be set directly.', function() { app.set({x: 'y'}); app.x.should.equal('y'); app.get('x').should.equal('y'); }); - it('should set nested properties on the instance.', function () { + it('should set nested properties on the instance.', function() { app.set('c', {d: 'e'}); app.get('c').d.should.equal('e'); }); - it('should use dot notation to `set` values.', function () { + it('should use dot notation to `set` values.', function() { app.set('h.i', 'j'); app.get('h').should.eql({i: 'j'}); }); - it('should use dot notation to `get` values.', function () { + it('should use dot notation to `get` values.', function() { app.set('h', {i: 'j'}); app.get('h.i').should.equal('j'); }); - it('should return `this` for chaining', function () { + it('should return `this` for chaining', function() { app.set('a', 'b').should.equal(app); app .set('aa', 'bb') @@ -51,21 +53,21 @@ describe('app.set()', function () { app.get('cc').should.equal('dd'); }); - it('should return undefined when not set', function () { + it('should return undefined when not set', function() { app.set('a', undefined).should.equal(app); }); }); -describe('app.get()', function () { +describe('app.get()', function() { beforeEach(function() { app = new App(); }); - it('should return undefined when no set', function () { + it('should return undefined when no set', function() { assert(app.get('a') === undefined); }); - it('should otherwise return the value', function () { + it('should otherwise return the value', function() { app.set('a', 'b'); app.get('a').should.equal('b'); }); diff --git a/test/app.handle.js b/test/app.handle.js index 8ce9902e..4e996da4 100644 --- a/test/app.handle.js +++ b/test/app.handle.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,29 +7,33 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('handler', function () { - beforeEach(function () { +describe('app.handle', function() { + beforeEach(function() { app = new App(); app.create('pages'); app.handlers(['foo']); }); - it('should support custom handle methods:', function (done) { + it('should support custom handle methods:', function(cb) { var page = app.page('foo', {contents: null}); - app.handle('foo', page, function (err, view) { + app.handle('foo', page, function(err, view) { + if (err) return cb(err); + assert(typeof view.path === 'string'); - done(); + cb(); }); }); - it('should not blow up if `options.handled` does not exist:', function (done) { + it('should not blow up if `options.handled` does not exist:', function(cb) { var page = app.page('foo', {contents: null}); delete page.options.handled; - app.handle('foo', page, function (err, view) { + app.handle('foo', page, function(err, view) { + if (err) return cb(err); + assert(typeof view.path === 'string'); - done(); + cb(); }); }); }); diff --git a/test/app.handlers.js b/test/app.handlers.js index 71712f67..2b1c9c20 100644 --- a/test/app.handlers.js +++ b/test/app.handlers.js @@ -1,72 +1,46 @@ +'use strict'; + require('should'); -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); -var resolve = require('resolve-glob'); var support = require('./support'); var App = support.resolve(); var app; -function decorateViews(views) { - var fn = views.decorateView; - views.decorateView = function () { - var view = fn.apply(fn, arguments); - view.read = function () { - if (!this.contents) { - this.contents = fs.readFileSync(this.path); - } - }; - return view; - }; - views.loader = function (pattern) { - var files = resolve.sync(pattern); - return files.reduce(function (acc, fp) { - acc[fp] = {path: fp}; - return acc; - }, {}); - }; - return views; -} - -describe('handlers', function () { - describe('custom handlers', function () { - beforeEach(function () { +describe('app.handlers', function() { + describe('custom handlers', function() { + beforeEach(function() { app = new App(); - app.create('pages') - .use(decorateViews) - .option('renameKey', function (key) { - return path.basename(key); - }); - }); - - it('should add custom middleware handlers:', function () { - app.handler('foo'); - app.router.should.have.property('foo'); - assert.equal(typeof app.router.foo, 'function'); + app.create('page'); }); - it('should add custom middleware handlers:', function () { + it('should add custom middleware handlers:', function() { app.handler('foo'); app.handler('bar'); - app.foo(/./, function (view, next) { + app.pages.use(function() { + return function(view) { + app.handle('foo', view); + app.handle('bar', view); + }; + }); + + app.foo(/a/, function(view, next) { view.one = 'aaa'; next(); }); - app.bar(/./, function (view, next) { + app.bar(/z/, function(view, next) { view.two = 'zzz'; next(); }); - app.page('abc', {content: '...'}) - .use(function (view) { - app.handleView('foo', view); - app.handleView('bar', view); - }); + app.pages('a.txt', {content: 'aaa'}); + app.pages('z.txt', {content: 'zzz'}); + + app.pages.getView('a.txt').should.have.property('one'); + app.pages.getView('a.txt').should.not.have.property('two'); - app.views.pages.abc.should.have.property('one', 'aaa'); - app.views.pages.abc.should.have.property('two', 'zzz'); + app.pages.getView('z.txt').should.not.have.property('one'); + app.pages.getView('z.txt').should.have.property('two'); }); }); }); diff --git a/test/app.include.js b/test/app.include.js index 510ec857..75e78285 100644 --- a/test/app.include.js +++ b/test/app.include.js @@ -1,17 +1,22 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); var verb = require('..'); var app, len; -describe('app', function () { +describe('app', function() { beforeEach(function() { app = verb(); + if (typeof app.include === 'undefined') { + app.create('include'); + } len = Object.keys(app.views.includes).length; }); - describe('add include', function () { - it('should add includes to `app.views.includes`:', function () { + describe('add include', function() { + it('should add includes to `app.views.includes`:', function() { app.include('a.hbs', {path: 'a.hbs', content: 'a'}); app.include('b.hbs', {path: 'b.hbs', content: 'b'}); app.include('c.hbs', {path: 'c.hbs', content: 'c'}); diff --git a/test/app.includes.js b/test/app.includes.js index be651314..2eaff36a 100644 --- a/test/app.includes.js +++ b/test/app.includes.js @@ -1,17 +1,22 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); var verb = require('..'); var app, len; -describe('app', function () { +describe('app', function() { beforeEach(function() { app = verb(); + if (typeof app.include === 'undefined') { + app.create('include'); + } len = Object.keys(app.views.includes).length; }) - describe('add includes', function () { - it('should add includes to `app.views.includes`:', function () { + describe('add includes', function() { + it('should add includes to `app.views.includes`:', function() { app.includes({ 'a.hbs': {path: 'a.hbs', content: 'a'}, 'b.hbs': {path: 'b.hbs', content: 'b'}, diff --git a/test/app.invoke.js b/test/app.invoke.js index 500c1658..e9f94e3b 100644 --- a/test/app.invoke.js +++ b/test/app.invoke.js @@ -29,6 +29,7 @@ describe('.invoke', function() { beforeEach(function() { verb = new Verb({prefix: 'generate'}); + verb.prefix = 'generate'; }); describe('invoke generators', function(cb) { @@ -152,6 +153,8 @@ describe('.invoke', function() { it('should extend with a generator invoked from node_modules by alias', function(cb) { verb.prefix = 'generate'; verb.register('abc', function(app) { + app.prefix = 'generate'; + assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -184,6 +187,8 @@ describe('.invoke', function() { it('should extend with a generator invoked from global modules by alias', function(cb) { verb.register('zzz', function(app) { + app.prefix = 'generate'; + assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -274,6 +279,7 @@ describe('.invoke', function() { }); verb.register('xyz', function(app) { + app.prefix = 'generate'; app.invoke('foo'); }); diff --git a/test/app.js b/test/app.js index ef8e4638..f63b8a61 100644 --- a/test/app.js +++ b/test/app.js @@ -1,4 +1,5 @@ -/* deps: coveralls istanbul */ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -7,90 +8,80 @@ var App = support.resolve(); var Base = App.Base; var app; -describe('app', function () { - describe('constructor', function () { - it('should create an instance of App:', function () { +describe('app', function() { + describe('constructor', function() { + it('should create an instance of App:', function() { app = new App(); assert(app instanceof App); }); - it('should new up without new:', function () { + it('should new up without new:', function() { app = App(); assert(app instanceof App); }); }); - describe('static methods', function () { - it('should expose `extend`:', function () { - assert(typeof App.extend ==='function'); + describe('static methods', function() { + it('should expose `extend`:', function() { + assert(typeof App.extend === 'function'); }); }); - describe('prototype methods', function () { + describe('prototype methods', function() { beforeEach(function() { app = new App(); }); - it('should expose `set`', function () { - assert(typeof app.set ==='function'); + it('should expose `set`', function() { + assert(typeof app.set === 'function'); }); - it('should expose `get`', function () { - assert(typeof app.get ==='function'); + it('should expose `get`', function() { + assert(typeof app.get === 'function'); }); - it('should expose `visit`', function () { - assert(typeof app.visit ==='function'); + it('should expose `visit`', function() { + assert(typeof app.visit === 'function'); }); - it('should expose `define`', function () { - assert(typeof app.define ==='function'); + it('should expose `define`', function() { + assert(typeof app.define === 'function'); }); - it('should expose `views`', function () { + it('should expose `views`', function() { assert(typeof app.views === 'object'); }); }); - describe('instance', function () { + describe('instance', function() { beforeEach(function() { app = new App(); }); - it('should set a value on the instance:', function () { + it('should set a value on the instance:', function() { app.set('a', 'b'); - assert(app.a ==='b'); + assert(app.a === 'b'); }); - it('should get a value from the instance:', function () { + it('should get a value from the instance:', function() { app.set('a', 'b'); - assert(app.get('a') ==='b'); + assert(app.get('a') === 'b'); }); }); - describe('initialization', function () { - it('should listen for errors:', function (done) { + describe('initialization', function() { + it('should listen for errors:', function(cb) { app = new App(); - app.on('error', function (err) { + app.on('error', function(err) { assert(err.message === 'foo'); - done(); + cb(); }); app.emit('error', new Error('foo')); }); - it('should mixin methods after init:', function () { - app = new App(); - app.option({ - mixins: { - foo: function () {} - } - }); - assert(typeof app.foo ==='function'); - }); - - it('should expose constructors from `lib`:', function () { + it('should expose constructors from `lib`:', function() { app = new App(); app.expose('Collection'); - assert(typeof app.Collection ==='function'); + assert(typeof app.Collection === 'function'); }); - it('should update constructors after init:', function () { + it('should update constructors after init:', function() { var Group = App.Group; function MyGroup() { Base.call(this); @@ -105,26 +96,26 @@ describe('app', function () { assert.equal(app.get('Group'), MyGroup); }); - it('should mixin prototype methods defined on options:', function () { + it('should mixin prototype methods defined on options:', function() { app = new App({ mixins: { - foo: function () {} + foo: function() {} } }); - assert(typeof app.foo ==='function'); + assert(typeof app.foo === 'function'); delete App.prototype.foo; }); - it('should expose `_` on app:', function () { + it('should expose `_` on app:', function() { app = new App(); - assert(typeof app._ ==='object'); + assert(typeof app._ === 'object'); }); - it('should not re-add `_` in init:', function () { + it('should not re-add `_` in init:', function() { app = new App(); app._.foo = 'bar'; app.defaultConfig(); - assert(app._.foo ==='bar'); + assert(app._.foo === 'bar'); }); }); }); diff --git a/test/app.layout.js b/test/app.layout.js index 264d9a1f..384708e5 100644 --- a/test/app.layout.js +++ b/test/app.layout.js @@ -1,16 +1,21 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); var assemble = require('..'); var app; -describe('.layout()', function () { +describe('.layout()', function() { beforeEach(function() { app = assemble(); + if (!app.layout) { + app.create('layout', {viewType: 'layout'}); + } }); - describe('add layout', function () { - it('should add layouts to `app.views.layouts`:', function () { + describe('add layout', function() { + it('should add layouts to `app.views.layouts`:', function() { app.layout('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); app.layout('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); app.layout('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); diff --git a/test/app.layouts.js b/test/app.layouts.js index 12c99c55..ff6ad3d2 100644 --- a/test/app.layouts.js +++ b/test/app.layouts.js @@ -1,16 +1,21 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); var assemble = require('..'); var app; -describe('.layouts()', function () { +describe('.layouts()', function() { beforeEach(function() { app = assemble(); + if (!app.layout) { + app.create('layout', {viewType: 'layout'}); + } }); - describe('add layouts', function () { - it('should add layouts to `app.views.layouts`:', function () { + describe('add layouts', function() { + it('should add layouts to `app.views.layouts`:', function() { app.layouts({ 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, diff --git a/test/app.list.compile.js b/test/app.list.compile.js index a49f81ca..2efa2235 100644 --- a/test/app.list.compile.js +++ b/test/app.list.compile.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -6,13 +8,13 @@ var App = support.resolve(); var List = App.List; var list; -describe('app.list.compile', function () { - beforeEach(function () { +describe('app.list.compile', function() { + beforeEach(function() { list = new List(); list.engine('tmpl', require('engine-base')); }); - it('should compile an item:', function () { + it('should compile an item:', function() { var buffer = new Buffer('a b c'); var item = list.addItem('a.tmpl', {contents: buffer}) .compile(); @@ -20,7 +22,7 @@ describe('app.list.compile', function () { assert(typeof item.fn === 'function'); }); - it('should use the compiled function to render:', function () { + it('should use the compiled function to render:', function() { var buffer = new Buffer('a <%= title %> c'); var item = list.addItem('a.tmpl', {contents: buffer}) .compile(); @@ -30,7 +32,7 @@ describe('app.list.compile', function () { assert(item.fn({title: 'z'}) === 'a z c'); }); - it('should compile a view by name:', function () { + it('should compile a view by name:', function() { var buffer = new Buffer('a <%= title %> c'); list.addItem('a.tmpl', {contents: buffer}); diff --git a/test/app.list.js b/test/app.list.js index 217d5ddd..b1b0a4be 100644 --- a/test/app.list.js +++ b/test/app.list.js @@ -9,44 +9,42 @@ var App = support.resolve(); var List = App.List; var app; -describe('list', function () { - describe('method', function () { - beforeEach(function () { +describe('app.list', function() { + describe('method', function() { + beforeEach(function() { app = new App(); }); - it('should expose the list method', function () { + it('should expose the list method', function() { assert(typeof app.list === 'function'); }); - it('should return a new list', function () { + it('should return a new list', function() { var list = app.list(); assert(typeof list === 'object'); }); - it('should have isList property', function () { + it('should have isList property', function() { var list = app.list(); assert(list.isList === true); }); }); - describe('adding items', function () { - beforeEach(function () { + describe('adding items', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); app.create('pages'); }); - it('should add an item to a list:', function () { + it('should add an item to a list:', function() { app.pages('test/fixtures/pages/a.hbs'); var list = app.list(); list.addItem(app.pages.getView('test/fixtures/pages/a.hbs')); assert(list.hasItem('test/fixtures/pages/a.hbs')); }); - it('should expose the `option` method from a list:', function () { + it('should expose the `option` method from a list:', function() { var list = app.list(); list.option('a', 'b'); assert(list.options); @@ -54,12 +52,12 @@ describe('list', function () { }); }); - describe('addItem', function () { - beforeEach(function () { + describe('addItem', function() { + beforeEach(function() { app = new App(); }); - it('should add items to a list', function () { + it('should add items to a list', function() { var pages = app.list({List: List}); pages.addItem('foo'); pages.addItem('bar'); @@ -70,7 +68,7 @@ describe('list', function () { pages.items.hasOwnProperty('baz'); }); - it('should create a list from an existing list:', function () { + it('should create a list from an existing list:', function() { var pages = app.list({List: List}); pages.addItem('foo'); pages.addItem('bar'); @@ -83,27 +81,25 @@ describe('list', function () { }); }); - describe('rendering items', function () { - beforeEach(function () { + describe('rendering items', function() { + beforeEach(function() { app = new App(); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); app.create('pages'); }); - it('should render a item with inherited app.render', function (done) { + it('should render a item with inherited app.render', function(cb) { app.page('test/fixtures/templates/a.tmpl') - .use(function (item) { - if (!item.contents.toString()) { + .use(function(item) { + if (!item.content) { item.contents = fs.readFileSync(item.path); } }) .set('data.name', 'Brian') - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'Brian'); - done(); + cb(); }); }); }); diff --git a/test/app.list.render.js b/test/app.list.render.js new file mode 100644 index 00000000..a0f19207 --- /dev/null +++ b/test/app.list.render.js @@ -0,0 +1,157 @@ +'use strict'; + +require('mocha'); +require('should'); +var async = require('async'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var pages, app; + +describe('render', function() { + describe('rendering', function() { + beforeEach(function() { + app = App(); + pages = app.create('pages'); + app.engine('tmpl', require('engine-base')); + pages.engine('tmpl', require('engine-base')); + }); + + it('should throw an error when no callback is given:', function() { + (function() { + app.pages.render({}); + }).should.throw('Views#render is async and expects a callback function'); + }); + + it('should throw an error when an engine is not defined:', function(cb) { + pages.addView('foo.bar', {content: '<%= name %>'}); + var page = pages.getView('foo.bar'); + + app.pages.render(page, function(err) { + assert(err.message === 'Views#render cannot find an engine for: .bar'); + cb(); + }); + }); + + it('should use helpers defined on app to render a view:', function(cb) { + var locals = {name: 'Halle'}; + app.helper('upper', function(str) { + return str.toUpperCase(str) + 'app'; + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + app.render(page, function(err, res) { + if (err) return cb(err); + + assert(res.content === 'a HALLEapp b'); + cb(); + }); + }); + + it('should use helpers defined on app to render a view with collection.render:', function(cb) { + var locals = {name: 'Halle'}; + app.helper('upper', function(str) { + return str.toUpperCase(str) + 'app'; + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + pages.helper('upper', app._.helpers.sync.upper); + var page = pages.getView('a.tmpl'); + + pages.render(page, function(err, res) { + if (err) return cb(err); + + assert(res.content === 'a HALLEapp b'); + cb(); + }); + }); + + it('should use helpers when rendering a view:', function(cb) { + var locals = {name: 'Halle'}; + pages.helper('upper', function(str) { + return str.toUpperCase(str); + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = pages.getView('a.tmpl'); + + pages.render(page, function(err, res) { + if (err) return cb(err); + assert(res.content === 'a HALLE b'); + cb(); + }); + }); + + it('should render a template when contents is a buffer:', function(cb) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function(err, view) { + if (err) return cb(err); + assert(view.contents.toString() === 'b'); + cb(); + }); + }); + + it('should render a template when content is a string:', function(cb) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = pages.getView('a.tmpl'); + + pages.render(view, function(err, view) { + if (err) return cb(err); + assert(view.contents.toString() === 'b'); + cb(); + }); + }); + + it('should render a view from its path:', function(cb) { + pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + + pages.render('a.tmpl', function(err, view) { + if (err) return cb(err); + assert(view.content === 'b'); + cb(); + }); + }); + + it('should use a plugin for rendering:', function(cb) { + pages.engine('tmpl', require('engine-base')); + pages.option('engine', 'tmpl'); + + pages.addViews({ + 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, + 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, + 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, + 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, + 'e': {content: '<%= title %>', locals: {title: 'eee'}}, + 'f': {content: '<%= title %>', locals: {title: 'fff'}}, + 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, + 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, + 'i': {content: '<%= title %>', locals: {title: 'iii'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + }); + + pages.use(function(collection) { + collection.option('pager', false); + + collection.renderEach = function(cb) { + var list = new List(collection); + async.map(list.items, function(item, next) { + collection.render(item, next); + }, cb); + }; + }); + + pages.renderEach(function(err, items) { + if (err) return cb(err); + assert(items[0].content === 'aaa'); + assert(items[9].content === 'jjj'); + assert(items.length === 10); + cb(); + }); + }); + }); +}); diff --git a/test/app.lookups.js b/test/app.lookups.js index 27ecface..1a7dfd55 100644 --- a/test/app.lookups.js +++ b/test/app.lookups.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); @@ -8,23 +10,23 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('lookups', function () { - beforeEach(function () { +describe('app.lookups', function() { + beforeEach(function() { app = new App(); - app.option('renameKey', function (key) { + app.option('renameKey', function(key) { return path.basename(key); }); app.create('pages') - .use(function (pages) { - pages.on('addViews', function (glob) { + .use(function(pages) { + pages.on('addViews', function(glob) { var files = resolve.sync(glob); - files.forEach(function (fp) { + files.forEach(function(fp) { pages.addView(fp, {path: fp}); }); pages.loaded = true; }); - return function (view) { - view.read = function () { + return function(view) { + view.read = function() { this.contents = fs.readFileSync(this.path); }; return view; @@ -34,68 +36,68 @@ describe('lookups', function () { app.pages('test/fixtures/templates/*.tmpl'); }); - describe('getView', function () { - it('should find a view', function () { + describe('getView', function() { + it('should find a view', function() { var view = app.getView('pages', 'a.tmpl'); assert(typeof view.path === 'string'); }); - it('should find a view using the renameKey function', function () { + it('should find a view using the renameKey function', function() { var view = app.getView('pages', 'test/fixtures/templates/a.tmpl'); assert(typeof view.path === 'string'); }); - it('should return null when nothing is found', function () { + it('should return undefined when nothing is found', function() { var view = app.getView('pages', 'test/fixtures/templates/foo.tmpl'); - assert(view === null); + assert(typeof view === 'undefined'); }); - it('should find a view using a glob pattern', function () { - var view = app.getView('pages', 'a', function (key) { + it('should find a view using a glob pattern', function() { + var view = app.getView('pages', 'a', function(key) { return key + '.tmpl'; }); assert(typeof view.path === 'string'); }); }); - describe('getViews', function () { - it('should return the collection object if passed:', function () { + describe('getViews', function() { + it('should return the collection object if passed:', function() { var views = app.getViews(app.views.pages); assert(Object.keys(views).length > 1); }); - it('should return the specified collection with the plural name:', function () { + it('should return the specified collection with the plural name:', function() { var views = app.getViews('pages'); assert(Object.keys(views).length > 1); }); - it('should return the specified collection with the singular name:', function () { + it('should return the specified collection with the singular name:', function() { var views = app.getViews('page'); assert(Object.keys(views).length > 1); }); - it('should return null when the collection is not found:', function () { - (function () { + it('should return null when the collection is not found:', function() { + (function() { app.getViews('nada'); }).should.throw('getViews cannot find collection: nada'); }); }); - describe('find', function () { - it('should return null when a view is not found:', function () { - (function () { + describe('find', function() { + it('should return null when a view is not found:', function() { + (function() { app.find({}); }).should.throw('expected name to be a string.'); }); - it('should find a view by collection name:', function () { + it('should find a view by collection name:', function() { var view = app.find('a.tmpl', 'pages'); assert(typeof view.path === 'string'); }); - it('should find a view by collection name:', function () { + it('should find a view by collection name:', function() { app = new App(); - app.option('renameKey', function (key) { + app.option('renameKey', function(key) { return path.basename(key); }); app.create('pages'); @@ -104,7 +106,7 @@ describe('lookups', function () { assert(typeof view.path === 'string'); }); - it('should find a view without a collection name:', function () { + it('should find a view without a collection name:', function() { var view = app.find('a.tmpl'); assert(typeof view.path === 'string'); }); diff --git a/test/app.mergePartials.js b/test/app.mergePartials.js new file mode 100644 index 00000000..edd65c44 --- /dev/null +++ b/test/app.mergePartials.js @@ -0,0 +1,106 @@ +'use strict'; + +require('should'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.mergePartials', function() { + beforeEach(function() { + app = new App(); + }); + + it('should merge multiple partials collections onto one collection:', function() { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials(); + actual.should.have.property('partials'); + actual.partials.should.have.properties(['a', 'b', 'c']); + }); + + it('should keep partials collections on separaet collections:', function() { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({mergePartials: false}); + actual.should.not.have.property('partials'); + actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); + }); + + it('should emit `mergePartials`:', function() { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + var arr = []; + + app.on('onMerge', function(view) { + arr.push(view.content); + }); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({mergePartials: false}); + actual.should.not.have.property('partials'); + actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); + arr.should.eql(['aaa', 'bbb', 'ccc']); + }); + + it('should handle `onMerge` middleware:', function() { + var opts = { viewType: 'partial' }; + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.onMerge(/./, function(view, next) { + view.content += ' onMerge'; + next(); + }); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({mergePartials: false}); + actual.should.eql({ + foos: {a: 'aaa onMerge'}, + bars: {b: 'bbb onMerge'}, + bazs: {c: 'ccc onMerge'} + }); + }); + + it('should skip views with `nomerge=true`:', function() { + var opts = { viewType: 'partial' }; + + app.create('foo', opts); + app.create('bar', opts); + app.create('baz', opts); + + app.onMerge(/[ab]/, function(view, next) { + view.options.nomerge = true; + next(); + }); + + app.foo('a', {path: 'a', content: 'aaa'}); + app.bar('b', {path: 'b', content: 'bbb'}); + app.baz('c', {path: 'c', content: 'ccc'}); + + var actual = app.mergePartials({mergePartials: false}); + actual.should.eql({ bazs: { c: 'ccc' } }); + }); +}); diff --git a/test/app.middleware.js b/test/app.middleware.js index 25843ed2..b5c07aa0 100644 --- a/test/app.middleware.js +++ b/test/app.middleware.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,16 +7,16 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('middleware', function () { - beforeEach(function () { +describe('app.middleware', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('pages'); }); - it('should call the all method for every middleware method:', function () { + it('should call the all method for every middleware method:', function() { var i = 0; - app.all(/./, function (view, next) { + app.all(/./, function(view, next) { assert(typeof view.path === 'string'); i++; next(); @@ -25,9 +27,9 @@ describe('middleware', function () { assert(i === 1); }); - it('should call the onLoad method when a view is loaded:', function () { + it('should call the onLoad method when a view is loaded:', function() { var i = 0; - app.onLoad(/./, function (view, next) { + app.onLoad(/./, function(view, next) { assert(typeof view.path === 'string'); i++; next(); @@ -38,23 +40,23 @@ describe('middleware', function () { assert(i === 1); }); - it('should emit an event when a handler is called:', function (done) { + it('should emit an event when a handler is called:', function(cb) { var i = 0; - app.on('onLoad', function () { + app.on('onLoad', function() { i++; }); - app.on('preRender', function () { + app.on('preRender', function() { i++; }); - app.on('preCompile', function () { + app.on('preCompile', function() { i++; }); app.page('foo.tmpl', {content: 'foo'}) - .render(function (err) { - if (err) return done(err); + .render(function(err) { + if (err) return cb(err); assert(i === 3); - done(); + cb(); }); }); }); diff --git a/test/app.onLoad.js b/test/app.onLoad.js new file mode 100644 index 00000000..dac994ed --- /dev/null +++ b/test/app.onLoad.js @@ -0,0 +1,50 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.onLoad', function() { + beforeEach(function() { + app = new App(); + }); + + describe('app.collection', function() { + it('should emit an onLoad when view is created', function(cb) { + var collection = app.collection(); + + app.on('onLoad', function(view) { + assert(view.path === 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + }); + + describe('view collections', function() { + it('should emit an onLoad when view is created', function(cb) { + app.create('posts'); + + app.on('onLoad', function(view) { + assert(view.path === 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert(view.path === 'blog/foo.js'); + next(); + }); + + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + }); + }); +}); diff --git a/test/app.option.js b/test/app.option.js index 12bdd6f9..ec417827 100644 --- a/test/app.option.js +++ b/test/app.option.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,17 +7,17 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('app.option', function () { - beforeEach(function () { +describe('app.option', function() { + beforeEach(function() { app = new App(); }); - it('should set a key-value pair on options:', function () { + it('should set a key-value pair on options:', function() { app.option('a', 'b'); assert(app.options.a === 'b'); }); - it('should set an object on options:', function () { + it('should set an object on options:', function() { app.option({c: 'd'}); assert(app.options.c === 'd'); }); diff --git a/test/app.page.js b/test/app.page.js new file mode 100644 index 00000000..a73b277e --- /dev/null +++ b/test/app.page.js @@ -0,0 +1,47 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.page()', function() { + beforeEach(function() { + app = assemble(); + if (!app.pages) { + app.create('pages'); + } + }); + + describe('add page', function() { + it('should add pages to `app.views.pages`:', function() { + app.page('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); + app.page('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); + app.page('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); + assert(Object.keys(app.views.pages).length === 3); + }); + }); + + describe('load', function() { + it('should throw an error if a glob is passed', function(cb) { + try { + app.page('test/fixtures/pages/*.hbs'); + cb(new Error('expected an error')); + } catch (err) { + assert(err); + assert(err.message); + assert(err.message === 'loadView does not support globs, only filepaths.'); + cb(); + } + }); + }); + + describe('load', function() { + it('should load a page from a non-glob filepath', function() { + app.page('test/fixtures/pages/a.hbs'); + console.log(app.views.pages); + assert(Object.keys(app.views.pages).length === 1); + }); + }); +}); diff --git a/test/app.pages.js b/test/app.pages.js new file mode 100644 index 00000000..b8800e7f --- /dev/null +++ b/test/app.pages.js @@ -0,0 +1,34 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.pages()', function() { + beforeEach(function() { + app = assemble(); + if (!app.pages) { + app.create('pages'); + } + }); + + describe('add pages', function() { + it('should add pages to `app.views.pages`:', function() { + app.pages({ + 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, + 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + }); + assert(Object.keys(app.views.pages).length === 3); + }); + }); + + describe('load pages', function() { + it('should load pages onto `app.views.pages`:', function() { + app.pages('test/fixtures/pages/*.hbs'); + assert(Object.keys(app.views.pages).length === 3); + }); + }); +}); diff --git a/test/app.partial.js b/test/app.partial.js new file mode 100644 index 00000000..d9889e84 --- /dev/null +++ b/test/app.partial.js @@ -0,0 +1,25 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.partial()', function() { + beforeEach(function() { + app = assemble(); + if (!app.partials) { + app.create('partials', {viewType: 'partial'}); + } + }); + + describe('add partial', function() { + it('should add partials to `app.views.partials`:', function() { + app.partial('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); + app.partial('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); + app.partial('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); + assert(Object.keys(app.views.partials).length === 3); + }); + }); +}); diff --git a/test/app.partials.js b/test/app.partials.js new file mode 100644 index 00000000..15909369 --- /dev/null +++ b/test/app.partials.js @@ -0,0 +1,28 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var assemble = require('..'); +var app; + +describe('.partials()', function() { + beforeEach(function() { + app = assemble(); + if (!app.partials) { + app.create('partials', {viewType: 'partial'}); + } + }); + + describe('add partials', function() { + it('should add partials to `app.views.partials`:', function() { + app.partials({ + 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, + 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + }); + + assert(Object.keys(app.views.partials).length === 3); + }); + }); +}); diff --git a/test/app.render.js b/test/app.render.js index e796d953..6f6b119b 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,84 +7,82 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('render', function () { - describe('rendering', function () { - beforeEach(function () { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); +describe('app.render', function() { + beforeEach(function() { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('page'); + }); - it('should throw an error when no callback is given:', function () { - (function() { - app.render({}); - }).should.throw('Templates#render is async and expects a callback function'); - }); + it('should throw an error when no callback is given:', function() { + (function() { + app.render({}); + }).should.throw('Templates#render is async and expects a callback function'); + }); - it('should throw an error when an engine is not defined:', function (done) { - app.page('foo.bar', {content: '<%= name %>'}); - var page = app.pages.getView('foo.bar'); + it('should throw an error when an engine is not defined:', function(cb) { + app.page('foo.bar', {content: '<%= name %>'}); + var page = app.pages.getView('foo.bar'); - app.render(page, function(err) { - assert(err.message === 'Templates#render cannot find an engine for: .bar'); - done(); - }); + app.render(page, function(err) { + assert(err.message === 'Templates#render cannot find an engine for: .bar'); + cb(); }); + }); - it('should use helpers to render a view:', function (done) { - var locals = {name: 'Halle'}; + it('should use helpers to render a view:', function(cb) { + var locals = {name: 'Halle'}; - app.helper('upper', function (str) { - return str.toUpperCase(str); - }); + app.helper('upper', function(str) { + return str.toUpperCase(str); + }); - app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = app.pages.getView('a.tmpl'); + app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, res) { - if (err) return done(err); + app.render(page, function(err, res) { + if (err) return cb(err); - assert(res.contents.toString() === 'a HALLE b'); - done(); - }); + assert(res.contents.toString() === 'a HALLE b'); + cb(); }); + }); - it('should use helpers when rendering a view:', function (done) { - var locals = {name: 'Halle'}; - app.helper('upper', function (str) { - return str.toUpperCase(str); - }); + it('should use helpers when rendering a view:', function(cb) { + var locals = {name: 'Halle'}; + app.helper('upper', function(str) { + return str.toUpperCase(str); + }); - app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = app.pages.getView('a.tmpl'); + app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, res) { - if (err) return done(err); - assert(res.contents.toString() === 'a HALLE b'); - done(); - }); + app.render(page, function(err, res) { + if (err) return cb(err); + assert(res.contents.toString() === 'a HALLE b'); + cb(); }); + }); - it('should render a template when contents is a buffer:', function (done) { - app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = app.pages.getView('a.tmpl'); + it('should render a template when contents is a buffer:', function(cb) { + app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = app.pages.getView('a.tmpl'); - app.render(view, function (err, view) { - if (err) return done(err); - assert(view.contents.toString() === 'b'); - done(); - }); + app.render(view, function(err, view) { + if (err) return cb(err); + assert(view.contents.toString() === 'b'); + cb(); }); + }); - it('should render a template when content is a string:', function (done) { - app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = app.pages.getView('a.tmpl'); + it('should render a template when content is a string:', function(cb) { + app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var view = app.pages.getView('a.tmpl'); - app.render(view, function (err, view) { - if (err) return done(err); - assert(view.contents.toString() === 'b'); - done(); - }); + app.render(view, function(err, view) { + if (err) return cb(err); + assert(view.contents.toString() === 'b'); + cb(); }); }); -}); +}); \ No newline at end of file diff --git a/test/app.renderFile.js b/test/app.renderFile.js index 1b3b478d..fff740d8 100644 --- a/test/app.renderFile.js +++ b/test/app.renderFile.js @@ -7,7 +7,7 @@ var path = require('path'); var app; describe('app.renderFile()', function() { - beforeEach(function () { + beforeEach(function() { app = assemble(); app.engine('hbs', require('engine-handlebars')); app.engine('*', require('engine-base')); @@ -17,11 +17,11 @@ describe('app.renderFile()', function() { app.file('b', {content: 'this is <%= title() %>'}); app.file('c', {content: 'this is <%= title() %>'}); - app.option('renameKey', function (key) { + app.option('renameKey', function(key) { return path.basename(key, path.extname(key)); }); - app.helper('title', function () { + app.helper('title', function() { var view = this.context.view; var key = view.key; var ctx = this.context[key]; @@ -30,105 +30,105 @@ describe('app.renderFile()', function() { }); }); - it('should render views from src', function (done) { + it('should render views from src', function(cb) { var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); var files = []; stream.pipe(app.renderFile()) - .on('error', done) - .on('data', function (file) { + .on('error', cb) + .on('data', function(file) { files.push(file); }) - .on('end', function () { + .on('end', function() { assert.equal(files[0].basename, 'a.hbs'); assert.equal(files[1].basename, 'b.hbs'); assert.equal(files[2].basename, 'c.hbs'); - done(); + cb(); }); }); - it('should render views with the engine that matches the file extension', function (done) { + it('should render views with the engine that matches the file extension', function(cb) { var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); var files = []; stream.pipe(app.renderFile()) - .on('error', done) - .on('data', function (file) { + .on('error', cb) + .on('data', function(file) { files.push(file); }) - .on('end', function () { - assert(/

a<\/h1>/.test(files[0].content)); - assert(/

b<\/h1>/.test(files[1].content)); - assert(/

c<\/h1>/.test(files[2].content)); - done(); + .on('end', function() { + assert(/

a<\/h1>/.test(files[0].contents.toString())); + assert(/

b<\/h1>/.test(files[1].contents.toString())); + assert(/

c<\/h1>/.test(files[2].contents.toString())); + cb(); }); }); - it('should render views from src with the engine passed on the opts', function (done) { + it('should render views from src with the engine passed on the opts', function(cb) { var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); var files = []; stream.pipe(app.renderFile({engine: '*'})) - .on('error', done) - .on('data', function (file) { + .on('error', cb) + .on('data', function(file) { files.push(file); }) - .on('end', function () { - assert(/

a<\/h2>/.test(files[0].content)); - assert(/

b<\/h2>/.test(files[1].content)); - assert(/

c<\/h2>/.test(files[2].content)); - done(); + .on('end', function() { + assert(/

a<\/h2>/.test(files[0].contents.toString())); + assert(/

b<\/h2>/.test(files[1].contents.toString())); + assert(/

c<\/h2>/.test(files[2].contents.toString())); + cb(); }); }); - it('should use the context passed on the opts', function (done) { + it('should use the context passed on the opts', function(cb) { var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); var files = []; stream.pipe(app.renderFile({a: {title: 'foo'}})) - .on('error', done) - .on('data', function (file) { + .on('error', cb) + .on('data', function(file) { files.push(file); }) - .on('end', function () { - assert(/

foo<\/h1>/.test(files[0].content)); - assert(/

b<\/h1>/.test(files[1].content)); - assert(/

c<\/h1>/.test(files[2].content)); - done(); + .on('end', function() { + assert(/

foo<\/h1>/.test(files[0].contents.toString())); + assert(/

b<\/h1>/.test(files[1].contents.toString())); + assert(/

c<\/h1>/.test(files[2].contents.toString())); + cb(); }); }); - it('should render the files in a collection', function (cb) { + it('should render the files in a collection', function(cb) { var files = []; app.toStream('files') .pipe(app.renderFile()) .on('error', cb) - .on('data', function (file) { + .on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); files.push(file); }) - .on('end', function () { - assert(/this is a/.test(files[0].content)); - assert(/this is b/.test(files[1].content)); - assert(/this is c/.test(files[2].content)); + .on('end', function() { + assert(/this is a/.test(files[0].contents.toString())); + assert(/this is b/.test(files[1].contents.toString())); + assert(/this is c/.test(files[2].contents.toString())); assert.equal(files.length, 3); cb(); }); }); - it('should handle engine errors', function (cb) { + it('should handle engine errors', function(cb) { app.create('notdefined', {engine: '*'}); app.notdefined('foo', {content: '<%= bar %>'}); app.toStream('notdefined') .pipe(app.renderFile()) - .on('error', function (err) { + .on('error', function(err) { assert.equal(typeof err, 'object'); assert.equal(err.message, 'bar is not defined'); cb(); }) - .on('end', function () { + .on('end', function() { cb(new Error('expected renderFile to handle the error.')); }); }); diff --git a/test/app.route.js b/test/app.route.js index c7ab2621..9b7689f2 100644 --- a/test/app.route.js +++ b/test/app.route.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,45 +7,45 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('routes', function () { +describe('app.route', function() { beforeEach(function() { app = new App(); }); describe('routes', function() { - it('should create a route for the given path:', function (done) { + it('should create a route for the given path:', function(cb) { app = new App(); app.create('posts'); app.on('all', function(msg) { - assert(msg === 'done'); - done(); + assert(msg === 'cb'); + cb(); }); app.route('blog/:title') .all(function(view, next) { - app.emit('all', 'done'); + app.emit('all', 'cb'); next(); }); app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); - it('should emit events when a route method is called:', function (done) { + it('should emit events when a route method is called:', function(cb) { app = new App(); app.create('posts'); app.on('onLoad', function(view) { assert(view.path === 'blog/foo.js'); - done(); + cb(); }); - app.param('title', function (view, next, title) { + app.param('title', function(view, next, title) { assert(title === 'foo.js'); next(); }); - app.onLoad('blog/:title', function (view, next) { + app.onLoad('blog/:title', function(view, next) { assert(view.path === 'blog/foo.js'); next(); }); @@ -51,22 +53,22 @@ describe('routes', function () { app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); - it('should emit errors', function (done) { + it('should emit errors', function(cb) { app = new App(); app.create('posts'); app.on('error', function(err) { assert(err.message === 'false == true'); - done(); + cb(); }); // wrong... - app.param('title', function (view, next, title) { + app.param('title', function(view, next, title) { assert(title === 'fo.js'); next(); }); - app.onLoad('/blog/:title', function (view, next) { + app.onLoad('/blog/:title', function(view, next) { assert(view.path === '/blog/foo.js'); next(); }); @@ -74,16 +76,16 @@ describe('routes', function () { app.post('whatever', {path: '/blog/foo.js', content: 'bar baz'}); }); - it('should have path property', function () { + it('should have path property', function() { var route = new app.Route('/blog/:year/:month/:day/:slug').all([ - function () {} + function() {} ]); route.path.should.equal('/blog/:year/:month/:day/:slug'); }); - it('should have stack property', function () { + it('should have stack property', function() { var route = new app.Route('/blog/:year/:month/:day/:slug').all([ - function () {} + function() {} ]); route.stack.should.be.instanceof(Array); diff --git a/test/app.src.js b/test/app.src.js index bd2b0891..969a41f0 100644 --- a/test/app.src.js +++ b/test/app.src.js @@ -7,34 +7,34 @@ var join = require('path').join; var app; describe('src()', function() { - beforeEach(function () { + beforeEach(function() { app = new App(); }); - it('should return a stream', function (done) { + it('should return a stream', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee')); assert(stream); assert.equal(typeof stream.on, 'function'); assert.equal(typeof stream.pipe, 'function'); - done(); + cb(); }); - it('should return an input stream from a flat glob', function (done) { + it('should return an input stream from a flat glob', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee')); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); - stream.on('end', function () { - done(); + stream.on('end', function() { + cb(); }); }); - it('should return an input stream for multiple globs', function (done) { + it('should return an input stream for multiple globs', function(cb) { var globArray = [ join(__dirname, './fixtures/generic/run.dmc'), join(__dirname, './fixtures/generic/test.dmc') @@ -42,21 +42,21 @@ describe('src()', function() { var stream = app.src(globArray); var files = []; - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); files.push(file); }); - stream.on('end', function () { + stream.on('end', function() { files.length.should.equal(2); files[0].path.should.equal(globArray[0]); files[1].path.should.equal(globArray[1]); - done(); + cb(); }); }); - it('should return an input stream for multiple globs with negation', function (done) { + it('should return an input stream for multiple globs with negation', function(cb) { var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); var globArray = [ join(__dirname, './fixtures/generic/*.dmc'), @@ -65,85 +65,85 @@ describe('src()', function() { var stream = app.src(globArray); var files = []; - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); files.push(file); }); - stream.on('end', function () { + stream.on('end', function() { files.length.should.equal(1); files[0].path.should.equal(expectedPath); - done(); + cb(); }); }); - it('should return an input stream with no contents when read is false', function (done) { + it('should return an input stream with no contents when read is false', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.not.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); }); - stream.on('end', function () { - done(); + stream.on('end', function() { + cb(); }); }); - it('should return an input stream with contents as stream when buffer is false', function (done) { + it('should return an input stream with contents as stream when buffer is false', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); var buf = ''; - file.contents.on('data', function (d) { + file.contents.on('data', function(d) { buf += d; }); - file.contents.on('end', function () { + file.contents.on('end', function() { buf.should.equal('Hello world!'); - done(); + cb(); }); join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); }); }); - it('should return an input stream from a deep glob', function (done) { + it('should return an input stream from a deep glob', function(cb) { var stream = app.src(join(__dirname, './fixtures/**/*.jade')); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); String(file.contents).should.equal('test template'); }); - stream.on('end', function () { - done(); + stream.on('end', function() { + cb(); }); }); - it('should return an input stream from a deeper glob', function (done) { + it('should return an input stream from a deeper glob', function(cb) { var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); var a = 0; - stream.on('error', done); - stream.on('data', function () { + stream.on('error', cb); + stream.on('data', function() { ++a; }); - stream.on('end', function () { + stream.on('end', function() { a.should.equal(2); - done(); + cb(); }); }); - it('should return a file stream from a flat path', function (done) { + it('should return a file stream from a flat path', function(cb) { var a = 0; var stream = app.src(join(__dirname, './fixtures/test.coffee')); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { ++a; should.exist(file); should.exist(file.path); @@ -151,35 +151,35 @@ describe('src()', function() { join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); - stream.on('end', function () { + stream.on('end', function() { a.should.equal(1); - done(); + cb(); }); }); - it('should return a stream', function (done) { + it('should return a stream', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee')); should.exist(stream); should.exist(stream.on); - done(); + cb(); }); - it('should return an input stream from a flat glob', function (done) { + it('should return an input stream from a flat glob', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee')); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); - stream.on('end', function () { - done(); + stream.on('end', function() { + cb(); }); }); - it('should return an input stream for multiple globs', function (done) { + it('should return an input stream for multiple globs', function(cb) { var globArray = [ join(__dirname, './fixtures/generic/run.dmc'), join(__dirname, './fixtures/generic/test.dmc') @@ -187,21 +187,21 @@ describe('src()', function() { var stream = app.src(globArray); var files = []; - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); files.push(file); }); - stream.on('end', function () { + stream.on('end', function() { files.length.should.equal(2); files[0].path.should.equal(globArray[0]); files[1].path.should.equal(globArray[1]); - done(); + cb(); }); }); - it('should return an input stream for multiple globs, with negation', function (done) { + it('should return an input stream for multiple globs, with negation', function(cb) { var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); var globArray = [ join(__dirname, './fixtures/generic/*.dmc'), @@ -210,76 +210,66 @@ describe('src()', function() { var stream = app.src(globArray); var files = []; - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); files.push(file); }); - stream.on('end', function () { + stream.on('end', function() { files.length.should.equal(1); files[0].path.should.equal(expectedPath); - done(); + cb(); }); }); - it('should return an input stream with no contents when read is false', function (done) { + it('should return an input stream with no contents when read is false', function(cb) { var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.not.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); }); - stream.on('end', function () { - done(); + stream.on('end', function() { + cb(); }); }); - it.skip('should throw an error when buffer is false', function (done) { - app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}) - .on('error', function () { - done(); - }) - .on('data', function () { - done(new Error('should have thrown an error')); - }); - }); - - it('should return an input stream from a deep glob', function (done) { + it('should return an input stream from a deep glob', function(cb) { app.src(join(__dirname, './fixtures/**/*.jade')) - .on('error', done) - .on('data', function (file) { + .on('error', cb) + .on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); String(file.contents).should.equal('test template'); }) - .on('end', function () { - done(); + .on('end', function() { + cb(); }); }); - it('should return an input stream from a deeper glob', function (done) { + it('should return an input stream from a deeper glob', function(cb) { var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); var a = 0; - stream.on('error', done); - stream.on('data', function () { + stream.on('error', cb); + stream.on('data', function() { ++a; }); - stream.on('end', function () { + stream.on('end', function() { a.should.equal(2); - done(); + cb(); }); }); - it('should return a file stream from a flat path', function (done) { + it('should return a file stream from a flat path', function(cb) { var a = 0; var stream = app.src(join(__dirname, './fixtures/test.coffee')); - stream.on('error', done); - stream.on('data', function (file) { + stream.on('error', cb); + stream.on('data', function(file) { ++a; should.exist(file); should.exist(file.path); @@ -287,9 +277,9 @@ describe('src()', function() { join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); - stream.on('end', function () { + stream.on('end', function() { a.should.equal(1); - done(); + cb(); }); }); }); diff --git a/test/app.store.js b/test/app.store.js new file mode 100644 index 00000000..76f0de78 --- /dev/null +++ b/test/app.store.js @@ -0,0 +1,333 @@ +'use strict'; + +require('mocha'); +require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var store = require('base-store'); +var support = require('./support'); +var verb = support.resolve(); +var app; + +describe('store', function() { + describe('methods', function() { + beforeEach(function() { + app = verb({cli: true}); + app.use(store()); + app.store.create('app-data-tests'); + }); + + afterEach(function() { + app.store.data = {}; + app.store.del({force: true}); + app.options.cli = false; + }); + + it('should `.set()` a value on the store', function() { + app.store.set('one', 'two'); + app.store.data.one.should.equal('two'); + }); + + it('should `.set()` an object', function() { + app.store.set({four: 'five', six: 'seven'}); + app.store.data.four.should.equal('five'); + app.store.data.six.should.equal('seven'); + }); + + it('should `.set()` a nested value', function() { + app.store.set('a.b.c.d', {e: 'f'}); + app.store.data.a.b.c.d.e.should.equal('f'); + }); + + it('should `.union()` a value on the store', function() { + app.store.union('one', 'two'); + app.store.data.one.should.eql(['two']); + }); + + it('should not union duplicate values', function() { + app.store.union('one', 'two'); + app.store.data.one.should.eql(['two']); + + app.store.union('one', ['two']); + app.store.data.one.should.eql(['two']); + }); + + it('should concat an existing array:', function() { + app.store.union('one', 'a'); + app.store.data.one.should.eql(['a']); + + app.store.union('one', ['b']); + app.store.data.one.should.eql(['a', 'b']); + + app.store.union('one', ['c', 'd']); + app.store.data.one.should.eql(['a', 'b', 'c', 'd']); + }); + + it('should return true if a key `.has()` on the store', function() { + app.store.set('foo', 'bar'); + app.store.set('baz', null); + app.store.set('qux', undefined); + + assert(app.store.has('foo')); + assert(app.store.has('baz')); + assert(!app.store.has('bar')); + assert(!app.store.has('qux')); + }); + + it('should return true if a nested key `.has()` on the store', function() { + app.store.set('a.b.c.d', {x: 'zzz'}); + app.store.set('a.b.c.e', {f: null}); + app.store.set('a.b.g.j', {k: undefined}); + + assert(!app.store.has('a.b.bar')); + assert(app.store.has('a.b.c.d')); + assert(app.store.has('a.b.c.d.x')); + assert(!app.store.has('a.b.c.d.z')); + assert(app.store.has('a.b.c.e')); + assert(app.store.has('a.b.c.e.f')); + assert(!app.store.has('a.b.c.e.z')); + assert(app.store.has('a.b.g.j')); + assert(!app.store.has('a.b.g.j.k')); + assert(!app.store.has('a.b.g.j.z')); + }); + + it('should return true if a key exists `.hasOwn()` on the store', function() { + app.store.set('foo', 'bar'); + app.store.set('baz', null); + app.store.set('qux', undefined); + + assert(app.store.hasOwn('foo')); + assert(!app.store.hasOwn('bar')); + assert(app.store.hasOwn('baz')); + assert(app.store.hasOwn('qux')); + }); + + it('should return true if a nested key exists `.hasOwn()` on the store', function() { + app.store.set('a.b.c.d', {x: 'zzz'}); + app.store.set('a.b.c.e', {f: null}); + app.store.set('a.b.g.j', {k: undefined}); + + assert(!app.store.hasOwn('a.b.bar')); + assert(app.store.hasOwn('a.b.c.d')); + assert(app.store.hasOwn('a.b.c.d.x')); + assert(!app.store.hasOwn('a.b.c.d.z')); + assert(app.store.has('a.b.c.e.f')); + assert(app.store.hasOwn('a.b.c.e.f')); + assert(!app.store.hasOwn('a.b.c.e.bar')); + assert(!app.store.has('a.b.g.j.k')); + assert(app.store.hasOwn('a.b.g.j.k')); + assert(!app.store.hasOwn('a.b.g.j.foo')); + }); + + it('should `.get()` a stored value', function() { + app.store.set('three', 'four'); + app.store.get('three').should.equal('four'); + }); + + it('should `.get()` a nested value', function() { + app.store.set({a: {b: {c: 'd'}}}); + app.store.get('a.b.c').should.equal('d'); + }); + + it('should `.del()` a stored value', function() { + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.data.should.have.property('a'); + app.store.data.should.have.property('c'); + + app.store.del('a'); + app.store.del('c'); + app.store.data.should.not.have.property('a'); + app.store.data.should.not.have.property('c'); + }); + + it('should `.del()` multiple stored values', function() { + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.set('e', 'f'); + app.store.del(['a', 'c', 'e']); + app.store.data.should.eql({}); + }); + }); +}); + +describe('create', function() { + beforeEach(function() { + app = verb({cli: true}); + app.use(store()); + app.store.create('abc'); + + // init the actual store json file + app.store.set('a', 'b'); + }); + + afterEach(function() { + app.store.data = {}; + app.store.del({force: true}); + app.options.cli = false; + }); + + it('should expose a `create` method', function() { + assert.equal(typeof app.store.create, 'function'); + }); + + it('should create a "sub-store" with the given name', function() { + var store = app.store.create('created'); + assert.equal(store.name, 'created'); + }); + + it('should create a "sub-store" with the project name when no name is passed', function() { + var store = app.store.create('app-store'); + assert.equal(store.name, 'app-store'); + }); + + it('should throw an error when a conflicting store name is used', function(cb) { + try { + app.store.create('create'); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'Cannot create store: "create", since "create" is a reserved property key. Please choose a different store name.'); + cb(); + } + }); + + it('should add a store object to store[name]', function() { + app.store.create('foo'); + assert.equal(typeof app.store.foo, 'object'); + assert.equal(typeof app.store.foo.set, 'function'); + app.store.foo.del({force: true}); + }); + + it('should save the store in a namespaced directory under the parent', function() { + app.store.create('foo'); + var dir = path.dirname(app.store.path); + + assert.equal(app.store.foo.path, path.join(dir, 'verb/foo.json')); + app.store.foo.set('a', 'b'); + app.store.foo.del({force: true}); + }); + + it('should set values on the custom store', function() { + app.store.create('foo'); + app.store.foo.set('a', 'b'); + assert.equal(app.store.foo.data.a, 'b'); + app.store.foo.del({force: true}); + }); + + it('should get values from the custom store', function() { + app.store.create('foo'); + app.store.foo.set('a', 'b'); + assert.equal(app.store.foo.get('a'), 'b'); + app.store.foo.del({force: true}); + }); +}); + +describe('events', function() { + beforeEach(function() { + app = verb({cli: true}); + app.use(store()); + app.store.create('abc'); + }); + + afterEach(function() { + app.store.data = {}; + app.store.del({force: true}); + app.options.cli = false; + }); + + it('should emit `set` when an object is set:', function() { + var keys = []; + app.store.on('set', function(key) { + keys.push(key); + }); + + app.store.set({a: {b: {c: 'd'}}}); + keys.should.eql(['a']); + }); + + it('should emit `set` when a key/value pair is set:', function() { + var keys = []; + + app.store.on('set', function(key) { + keys.push(key); + }); + + app.store.set('a', 'b'); + keys.should.eql(['a']); + }); + + it('should emit `set` when an object value is set:', function() { + var keys = []; + + app.store.on('set', function(key) { + keys.push(key); + }); + + app.store.set('a', {b: 'c'}); + keys.should.eql(['a']); + }); + + it('should emit `set` when an array of objects is passed:', function(cb) { + var keys = []; + + app.store.on('set', function(key) { + keys.push(key); + }); + + app.store.set([{a: 'b'}, {c: 'd'}]); + keys.should.eql(['a', 'c']); + cb(); + }); + + it('should emit `has`:', function(cb) { + var keys = []; + + app.store.on('has', function(val) { + assert(val); + cb(); + }); + + app.store.set('a', 'b'); + app.store.has('a'); + }); + + it('should emit `del` when a value is delted:', function(cb) { + app.store.on('del', function(keys) { + keys.should.eql('a'); + assert.equal(typeof app.store.get('a'), 'undefined'); + cb(); + }); + + app.store.set('a', {b: 'c'}); + app.store.get('a').should.eql({b: 'c'}); + app.store.del('a'); + }); + + it('should emit deleted keys on `del`:', function(cb) { + var arr = []; + + app.store.on('del', function(key) { + arr.push(key); + assert.equal(Object.keys(app.store.data).length, 0); + }); + + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.set('e', 'f'); + + app.store.del({force: true}); + arr.should.eql(['a', 'c', 'e']); + cb(); + }); + + it('should throw an error if force is not passed', function() { + app.store.set('a', 'b'); + app.store.set('c', 'd'); + app.store.set('e', 'f'); + + (function() { + app.store.del(); + }).should.throw('options.force is required to delete the entire cache.'); + }); +}); diff --git a/test/app.symlink.js b/test/app.symlink.js index 69e8d459..4bc3ddd3 100644 --- a/test/app.symlink.js +++ b/test/app.symlink.js @@ -35,7 +35,7 @@ describe('symlink stream', function() { beforeEach(wipeOut); afterEach(wipeOut); - it('should pass through writes with cwd', function(done) { + it('should pass through writes with cwd', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var expectedFile = new File({ @@ -48,7 +48,7 @@ describe('symlink stream', function() { var onEnd = function(){ buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); - done(); + cb(); }; var stream = app.symlink('./actual/', {cwd: __dirname}); @@ -60,7 +60,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should pass through writes with default cwd', function(done) { + it('should pass through writes with default cwd', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var expectedFile = new File({ @@ -73,7 +73,7 @@ describe('symlink stream', function() { var onEnd = function(){ buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); - done(); + cb(); }; var stream = app.symlink(path.join(__dirname, './actual/')); @@ -85,7 +85,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should make link to the right folder with relative cwd', function(done) { + it('should make link to the right folder with relative cwd', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); var expectedPath = path.join(__dirname, './actual/test.coffee'); @@ -108,7 +108,7 @@ describe('symlink stream', function() { fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); fs.readlinkSync(expectedPath).should.equal(inputPath); - done(); + cb(); }; var stream = app.symlink('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); @@ -120,7 +120,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should write buffer files to the right folder with function and relative cwd', function(done) { + it('should write buffer files to the right folder with function and relative cwd', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); var expectedPath = path.join(__dirname, './actual/test.coffee'); @@ -143,7 +143,7 @@ describe('symlink stream', function() { fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); fs.readlinkSync(expectedPath).should.equal(inputPath); - done(); + cb(); }; var stream = app.symlink(function(file){ @@ -159,7 +159,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should write buffer files to the right folder', function(done) { + it('should write buffer files to the right folder', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); var expectedPath = path.join(__dirname, './actual/test.coffee'); @@ -186,7 +186,7 @@ describe('symlink stream', function() { fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); fs.readlinkSync(expectedPath).should.equal(inputPath); - done(); + cb(); }; var stream = app.symlink('./actual/', {cwd: __dirname}); @@ -198,7 +198,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should write streaming files to the right folder', function(done) { + it('should write streaming files to the right folder', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); var expectedPath = path.join(__dirname, './actual/test.coffee'); @@ -226,7 +226,7 @@ describe('symlink stream', function() { fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); fs.readlinkSync(expectedPath).should.equal(inputPath); - done(); + cb(); }; var stream = app.symlink('./actual/', {cwd: __dirname}); @@ -242,7 +242,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should write directories to the right folder', function(done) { + it('should write directories to the right folder', function(cb) { var inputPath = path.join(__dirname, './fixtures/wow'); var inputBase = path.join(__dirname, './fixtures/'); var expectedPath = path.join(__dirname, './actual/wow'); @@ -271,7 +271,7 @@ describe('symlink stream', function() { fs.readlinkSync(expectedPath).should.equal(inputPath); fs.lstatSync(expectedPath).isDirectory().should.equal(false); fs.statSync(expectedPath).isDirectory().should.equal(true); - done(); + cb(); }; var stream = app.symlink('./actual/', {cwd: __dirname}); @@ -283,7 +283,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should use different modes for files and directories', function(done) { + it('should use different modes for files and directories', function(cb) { var inputBase = path.join(__dirname, './fixtures'); var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); var expectedBase = path.join(__dirname, './actual/wow'); @@ -300,7 +300,7 @@ describe('symlink stream', function() { var onEnd = function(){ realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); realMode(buffered[0].stat.mode).should.equal(expectedFileMode); - done(); + cb(); }; var stream = app.symlink('./actual/', { @@ -317,7 +317,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should change to the specified base', function(done) { + it('should change to the specified base', function(cb) { var inputBase = path.join(__dirname, './fixtures'); var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); @@ -330,7 +330,7 @@ describe('symlink stream', function() { var onEnd = function(){ buffered[0].base.should.equal(inputBase); - done(); + cb(); }; var stream = app.symlink('./actual/', { @@ -346,7 +346,7 @@ describe('symlink stream', function() { stream.end(); }); - it('should report IO errors', function(done) { + it('should report IO errors', function(cb) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); var expectedContents = fs.readFileSync(inputPath); @@ -369,18 +369,18 @@ describe('symlink stream', function() { var stream = app.symlink('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.code.should.equal('EACCES'); - done(); + cb(); }); stream.write(expectedFile); }); ['end', 'finish'].forEach(function(eventName) { - it('should emit ' + eventName + ' event', function(done) { + it('should emit ' + eventName + ' event', function(cb) { var srcPath = path.join(__dirname, './fixtures/test.coffee'); var stream = app.symlink('./actual/', {cwd: __dirname}); stream.on(eventName, function() { - done(); + cb(); }); var file = new File({ diff --git a/test/app.task.js b/test/app.task.js index a34956b0..9ccfa4d6 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -1,48 +1,47 @@ 'use strict'; -require('mocha'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var App = require('..'); +var app; -describe('.generate', function() { +describe('task()', function() { beforeEach(function() { - generate = new Generate(); + app = new App(); }); it('should register a task', function() { var fn = function(cb) { cb(); }; - generate.task('default', fn); - assert.equal(typeof generate.tasks.default, 'object'); - assert.equal(generate.tasks.default.fn, fn); + app.task('default', fn); + assert.equal(typeof app.tasks.default, 'object'); + assert.equal(app.tasks.default.fn, fn); }); it('should register a task with an array of dependencies', function() { - generate.task('default', ['foo', 'bar'], function(cb) { + app.task('default', ['foo', 'bar'], function(cb) { cb(); }); - assert.equal(typeof generate.tasks.default, 'object'); - assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof app.tasks.default, 'object'); + assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); }); it('should register a task with a list of strings as dependencies', function() { - generate.task('default', 'foo', 'bar', function(cb) { + app.task('default', 'foo', 'bar', function(cb) { cb(); }); - assert.equal(typeof generate.tasks.default, 'object'); - assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof app.tasks.default, 'object'); + assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); }); it('should run a task', function(cb) { var count = 0; - generate.task('default', function(cb) { + app.task('default', function(cb) { count++; cb(); }); - generate.build('default', function(err) { + app.build('default', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -50,16 +49,22 @@ describe('.generate', function() { }); it('should throw an error when a task with unregistered dependencies is run', function(cb) { - generate.task('default', ['foo', 'bar']); - generate.build('default', function(err) { - assert(err); + var count = 0; + app.task('default', ['foo', 'bar'], function(cb) { + count++; + cb(); + }); + + app.build('default', function(err) { + if (!err) return cb(new Error('Expected an error to be thrown.')); + assert.equal(count, 0); cb(); }); }); it('should throw an error when `.build` is called without a callback function.', function() { try { - generate.build('default'); + app.build('default'); throw new Error('Expected an error to be thrown.'); } catch (err) { } @@ -67,23 +72,24 @@ describe('.generate', function() { it('should emit task events', function(cb) { var events = []; - generate.on('task:starting', function(task) { + app.on('task:starting', function(task) { events.push('starting.' + task.name); }); - generate.on('task:finished', function(task) { + app.on('task:finished', function(task) { events.push('finished.' + task.name); }); - generate.on('task:error', function(e, task) { + app.on('task:error', function(err, task) { events.push('error.' + task.name); }); - generate.task('foo', function(cb) { + + app.task('foo', function(cb) { cb(); }); - generate.task('bar', ['foo'], function(cb) { + app.task('bar', ['foo'], function(cb) { cb(); }); - generate.task('default', ['bar']); - generate.build('default', function(err) { + app.task('default', ['bar']); + app.build('default', function(err) { if (err) return cb(err); assert.deepEqual(events, [ 'starting.default', @@ -98,49 +104,52 @@ describe('.generate', function() { }); it('should emit an error event when an error is passed back in a task', function(cb) { - generate.on('error', function(err) { + app.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - generate.task('default', function(cb) { + app.task('default', function(cb) { return cb(new Error('This is an error')); }); - generate.build('default', function(err) { + app.build('default', function(err) { if (err) return cb(); cb(new Error('Expected an error')); }); }); it('should emit an error event when an error is thrown in a task', function(cb) { - generate.on('error', function(err) { + var errors = 0; + app.on('error', function(err) { + errors++; assert(err); assert.equal(err.message, 'This is an error'); }); - generate.task('default', function(cb) { + app.task('default', function(cb) { cb(new Error('This is an error')); }); - generate.build('default', function(err) { - assert(err); - cb(); + app.build('default', function(err) { + assert.equal(errors, 1); + if (err) return cb(); + cb(new Error('Expected an error')); }); }); it('should run dependencies before running the dependent task.', function(cb) { var seq = []; - generate.task('foo', function(cb) { + app.task('foo', function(cb) { seq.push('foo'); cb(); }); - generate.task('bar', function(cb) { + app.task('bar', function(cb) { seq.push('bar'); cb(); }); - generate.task('default', ['foo', 'bar'], function(cb) { + app.task('default', ['foo', 'bar'], function(cb) { seq.push('default'); cb(); }); - generate.build('default', function(err) { + app.build('default', function(err) { if (err) return cb(err); assert.deepEqual(seq, ['foo', 'bar', 'default']); cb(); diff --git a/test/app.toStream.js b/test/app.toStream.js index 97bd30ec..fddfb93c 100644 --- a/test/app.toStream.js +++ b/test/app.toStream.js @@ -6,7 +6,7 @@ var should = require('should'); var app; describe('toStream()', function() { - beforeEach(function () { + beforeEach(function() { app = assemble(); app.create('pages'); app.page('a', {content: 'this is A'}); @@ -19,44 +19,44 @@ describe('toStream()', function() { app.post('z', {content: 'this is Z'}); }); - it('should return a stream', function (cb) { + it('should return a stream', function(cb) { var stream = app.toStream(); should.exist(stream); should.exist(stream.on); cb(); }); - it('should return a stream for a collection', function (cb) { + it('should return a stream for a collection', function(cb) { var stream = app.toStream('pages'); should.exist(stream); should.exist(stream.on); cb(); }); - it('should stack handle multiple collections', function (cb) { + it('should stack handle multiple collections', function(cb) { var files = []; app.toStream('pages') .pipe(app.toStream('posts')) .on('data', function(file) { files.push(file); }) - .on('end', function () { + .on('end', function() { assert.equal(files.length, 6); cb(); }); }); - it('should push each item in the collection into the stream', function (cb) { + it('should push each item in the collection into the stream', function(cb) { var files = []; app.toStream('pages') .on('error', cb) - .on('data', function (file) { + .on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); files.push(file.path); }) - .on('end', function () { + .on('end', function() { assert.equal(files.length, 3); cb(); }); diff --git a/test/app.use.js b/test/app.use.js index 875e8587..2fc683bd 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -7,35 +9,35 @@ var Views = App.Views; var View = App.View; var app; -describe('app.use', function () { - beforeEach(function () { +describe('app.use', function() { + beforeEach(function() { app = new App(); }); - it('should expose the instance to `use`:', function (done) { - app.use(function (inst) { + it('should expose the instance to `use`:', function(cb) { + app.use(function(inst) { assert(inst instanceof App); - done(); + cb(); }); }); - it('should be chainable:', function (done) { - app.use(function (inst) { - assert(inst instanceof App); - }) - .use(function (inst) { + it('should be chainable:', function(cb) { + app.use(function(inst) { + assert(inst instanceof App); + }) + .use(function(inst) { assert(inst instanceof App); }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - done(); + cb(); }); }); - it('should pass to collection `use` if a function is returned:', function () { - app.use(function (inst) { + it('should pass to collection `use` if a function is returned:', function() { + app.use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { collection.foo = collection.addView; assert(collection instanceof Views); return collection; @@ -52,27 +54,27 @@ describe('app.use', function () { assert(app.views.pages.hasOwnProperty('c.md')); }); - it('should be chainable when a collection function is returned:', function () { + it('should be chainable when a collection function is returned:', function() { app - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { collection.foo = collection.addView; assert(collection instanceof Views); return collection; }; }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { collection.bar = collection.addView; assert(collection instanceof Views); return collection; }; }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { collection.baz = collection.addView; assert(collection instanceof Views); return collection; @@ -90,15 +92,15 @@ describe('app.use', function () { assert(app.views.pages.hasOwnProperty('c.md')); }); - it('should pass to view `use` if collection.use returns a function:', function () { - app.use(function (inst) { + it('should pass to view `use` if collection.use returns a function:', function() { + app.use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { assert(collection instanceof Views); collection.foo = collection.addView; - return function (view) { + return function(view) { assert(view instanceof View); view.foo = collection.addView.bind(collection); return view; @@ -116,44 +118,44 @@ describe('app.use', function () { assert(app.views.pages.hasOwnProperty('c.md')); }); - it('should be chainable when a view function is returned:', function () { + it('should be chainable when a view function is returned:', function() { app - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { assert(collection instanceof Views); collection.foo = collection.addView; - return function (view) { + return function(view) { assert(view instanceof View); view.a = collection.addView.bind(collection); return view; }; }; }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { assert(collection instanceof Views); collection.bar = collection.addView; - return function (view) { + return function(view) { assert(view instanceof View); view.b = collection.addView.bind(collection); return view; }; }; }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { assert(collection instanceof Views); collection.baz = collection.addView; - return function (view) { + return function(view) { assert(view instanceof View); view.c = collection.addView.bind(collection); return view; @@ -179,46 +181,46 @@ describe('app.use', function () { assert(app.views.pages.hasOwnProperty('z.md')); }); - it('should work with multiple collections:', function () { + it('should work with multiple collections:', function() { app - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { assert(collection instanceof Views); collection.foo = collection.addView; - return function (view) { + return function(view) { assert(view instanceof View); view.a = collection.addView.bind(collection); return view; }; }; }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); - return function (collection) { + return function(collection) { assert(collection instanceof Views); collection.bar = collection.addView; - return function (view) { + return function(view) { assert(view instanceof View); view.b = collection.addView.bind(collection); return view; }; }; }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof App); assert(this instanceof App); - return function (collection) { + return function(collection) { collection.baz = collection.addView; assert(collection instanceof Views); assert(this instanceof Views); - return function (view) { + return function(view) { assert(this instanceof View); assert(view instanceof View); view.c = collection.addView.bind(collection); diff --git a/test/app.view.compile.js b/test/app.view.compile.js index e325c925..7616ce25 100644 --- a/test/app.view.compile.js +++ b/test/app.view.compile.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,29 +7,29 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('app.view.compile', function () { - describe('compile method', function () { - beforeEach(function () { +describe('app.view.compile', function() { + describe('compile method', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('page'); }); - it('should compile a view:', function () { + it('should compile a view:', function() { var buffer = new Buffer('a b c'); var view = app.page('a.tmpl', {contents: buffer}) .compile(); assert(typeof view.fn === 'function'); }); - it('should compile a view with settings:', function () { + it('should compile a view with settings:', function() { var buffer = new Buffer('a b c'); var view = app.page('a.tmpl', {contents: buffer}) .compile({foo: 'bar'}); assert(typeof view.fn === 'function'); }); - it('should compile a view with isAsync flag:', function () { + it('should compile a view with isAsync flag:', function() { var buffer = new Buffer('a b c'); var view = app.page('a.tmpl', {contents: buffer}) .compile(true); diff --git a/test/app.view.render.js b/test/app.view.render.js index fcf30877..064c4c3e 100644 --- a/test/app.view.render.js +++ b/test/app.view.render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,37 +7,37 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('helpers', function () { - describe('rendering', function () { - beforeEach(function () { +describe('app.view.render', function() { + describe('rendering', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('page'); }); - it('should use helpers to render a view:', function (done) { + it('should use helpers to render a view:', function(cb) { var locals = {name: 'Halle'}; - app.helper('upper', function (str) { + app.helper('upper', function(str) { return str.toUpperCase(str); }); var buffer = new Buffer('a <%= upper(name) %> b'); app.page('a.tmpl', {contents: buffer, locals: locals}) - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a HALLE b'); - done(); + cb(); }); }); - it('should support helpers as an array:', function (done) { + it('should support helpers as an array:', function(cb) { var locals = {name: 'Halle'}; app.helpers([ { - lower: function (str) { + lower: function(str) { return str.toLowerCase(str); } } @@ -43,48 +45,48 @@ describe('helpers', function () { var buffer = new Buffer('a <%= lower(name) %> b'); app.page('a.tmpl', {contents: buffer, locals: locals}) - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a halle b'); - done(); + cb(); }); }); - it('should support helpers as an object:', function (done) { + it('should support helpers as an object:', function(cb) { var locals = {name: 'Halle'}; app.helpers({ - prepend: function (prefix, str) { + prepend: function(prefix, str) { return prefix + str; } }); var buffer = new Buffer('a <%= prepend("foo ", name) %> b'); app.page('a.tmpl', {contents: buffer, locals: locals}) - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a foo Halle b'); - done(); + cb(); }); }); - it('should use the engine defined on view options:', function (done) { + it('should use the engine defined on view options:', function(cb) { app.engine('hbs', require('engine-handlebars')); var locals = {name: 'Halle'}; app.helpers({ - prepend: function (prefix, str) { + prepend: function(prefix, str) { return prefix + str; } }); var buffer = new Buffer('a {{prepend "foo " name}} b'); app.page('a.tmpl', {contents: buffer, locals: locals, options: {engine: 'hbs'}}) - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a foo Halle b'); - done(); + cb(); }); }); }); diff --git a/test/app.viewTypes.js b/test/app.viewTypes.js new file mode 100644 index 00000000..982538bf --- /dev/null +++ b/test/app.viewTypes.js @@ -0,0 +1,54 @@ +'use strict'; + +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.viewTypes', function() { + describe('view types', function() { + beforeEach(function() { + app = new App(); + app.engine('tmpl', require('engine-base')); + }); + + it('should add collection (plural) to the `viewTypes` object', function() { + app.viewTypes = []; // reset + app.create('foo', {viewType: 'layout'}); + app.create('bar', {viewType: 'layout'}); + assert.deepEqual(app.viewTypes.layout, [ 'foos', 'bars' ]); + + app.create('baz', {viewType: 'renderable'}); + assert.deepEqual(app.viewTypes.renderable, [ 'bazs' ]); + }); + + it('should add collection to the given viewType', function() { + app.create('layout', {viewType: 'layout'}); + assert(app.layouts.options.viewType[0] === 'layout'); + }); + + it('should return true if a collection has the given viewType', function() { + app.create('layout', {viewType: 'layout'}); + assert(app.layouts.isType('layout')); + assert(!app.layouts.isType('partial')); + }); + + it('should add types to the collection', function() { + app.create('layout', {viewType: 'layout'}); + app.layouts.viewType('partial'); + assert(app.layouts.options.viewType[0] === 'layout'); + assert(app.layouts.options.viewType[1] === 'partial'); + }); + + it('should add a collection to multiple viewTypes', function() { + app.create('foo', {viewType: ['layout', 'renderable']}); + assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); + }); + + it('should expose viewType on `view`', function() { + app.create('foo', {viewType: ['layout', 'renderable']}); + var a = app.foo('a', {content: ''}); + assert.deepEqual(app.foos.options.viewType, a.options.viewType); + }); + }); +}); diff --git a/test/app.views.js b/test/app.views.js new file mode 100644 index 00000000..aaa0f264 --- /dev/null +++ b/test/app.views.js @@ -0,0 +1,29 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.views', function() { + beforeEach(function() { + app = new App(); + app.engine('tmpl', require('engine-base')); + app.create('partials', {viewType: 'partial'}); + app.create('pages'); + }); + + it('should add a view to a collection', function() { + app.partial('foo', {content: 'bar'}); + assert(app.views.partials.foo); + }); + + it('should create a helper for a view collection', function() { + app.page('abc', {content: 'xyz'}); + assert(app.views.pages.abc); + assert(app._.helpers.async.page); + }); +}); + diff --git a/test/app.watch.js b/test/app.watch.js deleted file mode 100644 index 698b768f..00000000 --- a/test/app.watch.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var fs = require('fs'); -var App = require('..'); -var app; - -describe.skip('watch()', function () { - beforeEach(function () { - app = new App({runtimes: false}); - }); - - it('should watch files and run a task when files change', function (done) { - this.timeout(750); - - var count = 0, watch; - app.task('default', function (cb) { - count++; - cb(); - }); - - app.task('close', function (cb) { - watch.close(); - app.emit('close'); - cb(); - }); - - app.task('watch', function (cb) { - watch = app.watch('test/fixtures/watch/*.txt', ['default', 'close']); - fs.writeFile('test/fixtures/watch/test.txt', 'test', function (err) { - if (err) return cb(err); - app.on('close', cb); - }); - }); - - app.build(['watch'], function (err) { - if (err) return done(err); - assert.equal(count, 1); - done(); - }); - }); -}); diff --git a/test/collection.engines.js b/test/collection.engines.js index edb94f1e..71b2a815 100644 --- a/test/collection.engines.js +++ b/test/collection.engines.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -11,18 +13,18 @@ describe('collection engines', function() { pages = new Views(); }); - it('should throw an error when engine name is invalid:', function () { - (function () { + it('should throw an error when engine name is invalid:', function() { + (function() { pages.engine(null, {}); }).should.throw('expected engine ext to be a string or array.'); }); - it('should register an engine to the given extension', function () { - pages.engine('hbs', function () {}); + it('should register an engine to the given extension', function() { + pages.engine('hbs', function() {}); assert(typeof pages.engines['.hbs'] === 'object'); }); - it('should set an engine with the given extension', function () { + it('should set an engine with the given extension', function() { var hbs = function() {}; hbs.render = function() {}; hbs.renderFile = function() {}; @@ -32,145 +34,145 @@ describe('collection engines', function() { assert(pages.engines['.hbs'].render); }); - it('should get an engine:', function () { - pages.engine('hbs', function () {}); + it('should get an engine:', function() { + pages.engine('hbs', function() {}); var hbs = pages.engine('hbs'); assert(typeof hbs === 'object'); assert(hbs.hasOwnProperty('render')); assert(hbs.hasOwnProperty('compile')); }); - it('should register multiple engines to the given extension', function () { - pages.engine(['hbs', 'md'], function () {}); + it('should register multiple engines to the given extension', function() { + pages.engine(['hbs', 'md'], function() {}); assert(typeof pages.engines['.hbs'] === 'object'); assert(typeof pages.engines['.md'] === 'object'); }); }); -describe('engines', function () { - beforeEach(function () { +describe('engines', function() { + beforeEach(function() { pages = new Views(); pages.addView('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); pages.addView('bar.tmpl', {content: 'A <%= letter %> {{ letter }} C'}); }); - it('should register an engine:', function () { - pages.engine('a', {render: function () {}}); + it('should register an engine:', function() { + pages.engine('a', {render: function() {}}); pages.engines.should.have.property('.a'); }); - it('should use custom delimiters:', function (done) { + it('should use custom delimiters:', function(cb) { pages.engine('tmpl', require('engine-base'), { delims: ['{{', '}}'] }); - pages.render('foo.tmpl', {letter: 'B'}, function (err, res) { - if (err) return done(err); + pages.render('foo.tmpl', {letter: 'B'}, function(err, res) { + if (err) return cb(err); res.content.should.equal('A <%= letter %> B C'); - done(); + cb(); }); }); - it('should override individual delims values:', function (done) { + it('should override individual delims values:', function(cb) { pages.engine('tmpl', require('engine-base'), { interpolate: /\{{([^}]+)}}/g, evaluate: /\{{([^}]+)}}/g, escape: /\{{-([^}]+)}}/g }); - pages.render('bar.tmpl', {letter: 'B'}, function (err, res) { - if (err) return done(err); + pages.render('bar.tmpl', {letter: 'B'}, function(err, res) { + if (err) return cb(err); res.content.should.equal('A <%= letter %> B C'); - done(); + cb(); }); }); - it('should get an engine:', function () { + it('should get an engine:', function() { pages.engine('a', { - render: function () {} + render: function() {} }); var a = pages.engine('a'); a.should.have.property('render'); }); }); -describe('engine selection:', function () { - beforeEach(function (done) { +describe('engine selection:', function() { + beforeEach(function(cb) { collection = new Views(); collection.engine('tmpl', require('engine-base')); collection.engine('hbs', require('engine-handlebars')); - done(); + cb(); }); - it('should get the engine from file extension:', function (done) { + it('should get the engine from file extension:', function(cb) { var pages = new Views(); pages.engine('tmpl', require('engine-base')); pages.engine('hbs', require('engine-handlebars')); pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on the collection:', function (done) { + it('should use the engine defined on the collection:', function(cb) { var posts = new Views({engine: 'hbs'}); posts.engine('tmpl', require('engine-base')); posts.engine('hbs', require('engine-handlebars')); posts.addView('a', {content: '{{a}}', locals: {a: 'b'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on the view:', function (done) { + it('should use the engine defined on the view:', function(cb) { var posts = new Views(); posts.engine('tmpl', require('engine-base')); posts.engine('hbs', require('engine-handlebars')); posts.addView('a', {content: '{{a}}', engine: 'hbs', locals: {a: 'b'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on view.options:', function (done) { + it('should use the engine defined on view.options:', function(cb) { var posts = new Views(); posts.engine('tmpl', require('engine-base')); posts.engine('hbs', require('engine-handlebars')); posts.addView('a', {content: '{{a}}', data: {a: 'b'}, options: {engine: 'hbs'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on view.data:', function (done) { + it('should use the engine defined on view.data:', function(cb) { var posts = new Views(); posts.engine('tmpl', require('engine-base')); posts.engine('hbs', require('engine-handlebars')); posts.addView('a', {content: '{{a}}', locals: {a: 'b'}, data: {engine: 'hbs'}}) - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use the engine defined on render locals:', function (done) { + it('should use the engine defined on render locals:', function(cb) { var posts = new Views(); posts.engine('tmpl', require('engine-base')); posts.engine('hbs', require('engine-handlebars')); posts.addView('a', {content: '{{a}}', locals: {a: 'b'}}) - .render({engine: 'hbs'}, function (err, view) { - if (err) return done(err); + .render({engine: 'hbs'}, function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); }); diff --git a/test/collection.events.js b/test/collection.events.js index e9198359..5abab928 100644 --- a/test/collection.events.js +++ b/test/collection.events.js @@ -1,19 +1,21 @@ +'use strict'; + require('should'); var support = require('./support'); var App = support.resolve(); var app; -describe('collection events', function () { - beforeEach(function () { +describe('collection events', function() { + beforeEach(function() { app = new App(); app.create('page'); }); - it('should emit events:', function () { + it('should emit events:', function() { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); var events = []; - app.pages.on('option', function (key) { + app.pages.on('option', function(key) { events.push(key); }); diff --git a/test/collection.getView.js b/test/collection.getView.js new file mode 100644 index 00000000..fb587230 --- /dev/null +++ b/test/collection.getView.js @@ -0,0 +1,34 @@ +'use strict'; + +var path = require('path'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('collection.getView', function() { + beforeEach(function() { + app = new App(); + app.create('page'); + + app.page('foo', {content: 'this is foo'}); + app.page('bar.md', {content: 'this is bar'}); + app.page('a/b/c/baz.md', {content: 'this is baz'}); + app.page('test/fixtures/templates/a.tmpl'); + }); + + it('should get a view by name', function() { + assert(app.pages.getView('foo')); + }); + + it('should get a view with the key modified by the given function', function() { + var view = app.pages.getView('foo.md', function(key) { + return path.basename(key, path.extname(key)); + }); + assert(view); + }); + + it('should get a view by full path', function() { + assert(app.pages.getView('a/b/c/baz.md')); + }); +}); diff --git a/test/collection.js b/test/collection.js index 62484080..8665e857 100644 --- a/test/collection.js +++ b/test/collection.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var path = require('path'); @@ -12,28 +14,28 @@ var Item = App.Item; var Collection = App.Collection; var collection; -describe('collection', function () { - describe('constructor', function () { - it('should create an instance of Collection', function () { +describe('collection', function() { + describe('constructor', function() { + it('should create an instance of Collection', function() { var collection = new Collection(); assert(collection instanceof Collection); assert(typeof collection === 'object'); }); - it('should instantiate without new', function () { + it('should instantiate without new', function() { var collection = Collection(); assert(collection instanceof Collection); assert(typeof collection === 'object'); }); }); - describe('static methods', function () { - it('should expose `extend`', function () { - assert(typeof Collection.extend ==='function'); + describe('static methods', function() { + it('should expose `extend`', function() { + assert(typeof Collection.extend === 'function'); }); }); - describe('prototype methods', function () { + describe('prototype methods', function() { beforeEach(function() { collection = new Collection(); }); @@ -59,37 +61,37 @@ describe('collection', function () { 'hasListeners' ]; - methods.forEach(function (method) { - it('should expose ' + method + ' method', function () { + methods.forEach(function(method) { + it('should expose ' + method + ' method', function() { assert(typeof collection[method] === 'function'); }); }); - it('should expose isCollection property', function () { + it('should expose isCollection property', function() { assert(typeof collection.isCollection === 'boolean'); }); - it('should expose queue property', function () { + it('should expose queue property', function() { assert(Array.isArray(collection.queue)); }); - it('should expose items property', function () { + it('should expose items property', function() { assert(typeOf(collection.items) === 'object'); }); - it('should expose options property', function () { + it('should expose options property', function() { assert(typeOf(collection.options) === 'object'); }); }); }); -describe('methods', function () { +describe('methods', function() { beforeEach(function() { collection = new Collection(); }); - describe('chaining', function () { - it('should allow collection methods to be chained', function () { + describe('chaining', function() { + it('should allow collection methods to be chained', function() { collection .addItems({'a.hbs': {path: 'a.hbs'}}) .addItems({'b.hbs': {path: 'b.hbs'}}) @@ -103,20 +105,20 @@ describe('methods', function () { }); }); - describe('use', function () { - it('should expose the instance to plugins', function () { + describe('use', function() { + it('should expose the instance to plugins', function() { collection - .use(function (inst) { + .use(function(inst) { inst.foo = 'bar'; }); assert(collection.foo === 'bar'); }); - it('should expose `item` when the plugin returns a function', function () { + it('should expose `item` when the plugin returns a function', function() { collection - .use(function () { - return function (item) { + .use(function() { + return function(item) { item.foo = 'bar'; }; }); @@ -131,24 +133,24 @@ describe('methods', function () { }); }); - describe('get / set', function () { - it('should set a value on the instance', function () { + describe('get / set', function() { + it('should set a value on the instance', function() { collection.set('a', 'b'); - assert(collection.a ==='b'); + assert(collection.a === 'b'); }); - it('should get a value from the instance', function () { + it('should get a value from the instance', function() { collection.set('a', 'b'); - assert(collection.get('a') ==='b'); + assert(collection.get('a') === 'b'); }); }); - describe('adding items', function () { - beforeEach(function () { + describe('adding items', function() { + beforeEach(function() { collection = new Collection(); }); - it('should load a item onto the respective collection', function () { + it('should load a item onto the respective collection', function() { collection.addItem('a.hbs'); collection.items.should.have.property('a.hbs'); }); @@ -159,7 +161,7 @@ describe('methods', function () { collection = new Collection(); }); - it('should return a single collection item from a key-value pair', function () { + it('should return a single collection item from a key-value pair', function() { var one = collection.item('one', {content: 'foo'}); var two = collection.item('two', {content: 'bar'}); @@ -171,7 +173,7 @@ describe('methods', function () { assert(two.path === 'two'); }); - it('should return a single collection item from an object', function () { + it('should return a single collection item from an object', function() { var one = collection.item({path: 'one', content: 'foo'}); var two = collection.item({path: 'two', content: 'bar'}); @@ -187,13 +189,13 @@ describe('methods', function () { collection = new Collection(); }); - it('should throw an error when args are invalid', function () { - (function () { + it('should throw an error when args are invalid', function() { + (function() { collection.addItem(function() {}); }).should.throw('expected value to be an object.'); }); - it('should add a item to `items`', function () { + it('should add a item to `items`', function() { collection.addItem('foo'); collection.items.should.have.property('foo'); @@ -202,12 +204,12 @@ describe('methods', function () { assert(isBuffer(collection.items.one.contents)); }); - it('should create an instance of `Item`', function () { + it('should create an instance of `Item`', function() { collection.addItem('one', {content: '...'}); assert(collection.items.one instanceof collection.Item); }); - it('should allow an `Item` constructor to be passed', function () { + it('should allow an `Item` constructor to be passed', function() { Item.prototype.foo = function(key, value) { this[key] = value; }; @@ -217,7 +219,7 @@ describe('methods', function () { assert(collection.items.one.bar === 'baz'); }); - it('should allow an instance of `Item` to be passed', function () { + it('should allow an instance of `Item` to be passed', function() { var collection = new Collection({Item: Item}); var item = new Item({content: '...'}); collection.addItem('one', item); @@ -228,12 +230,8 @@ describe('methods', function () { }); }); - describe('addItems', function () { - beforeEach(function() { - collection = new Collection(); - }); - - it('should add multiple items', function () { + describe('addItems', function() { + it('should add multiple items', function() { collection.addItems({ one: {content: 'foo'}, two: {content: 'bar'} @@ -242,7 +240,7 @@ describe('methods', function () { assert(isBuffer(collection.items.two.contents)); }); - it('should create items from an instance of Collection', function () { + it('should create items from an instance of Collection', function() { collection.addItems({ one: {content: 'foo'}, two: {content: 'bar'} @@ -252,7 +250,7 @@ describe('methods', function () { assert(isBuffer(pages.items.two.contents)); }); - it('should add an array of plain-objects', function () { + it('should add an array of plain-objects', function() { collection.addItems([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -261,7 +259,7 @@ describe('methods', function () { assert(isBuffer(collection.items.two.contents)); }); - it('should add an array of items', function () { + it('should add an array of items', function() { var list = new List([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -278,7 +276,7 @@ describe('methods', function () { collection = new Collection(); }); - it('should add a list of items', function () { + it('should add a list of items', function() { collection.addList([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -287,7 +285,7 @@ describe('methods', function () { assert(isBuffer(collection.items.two.contents)); }); - it('should add a list of items from the constructor', function () { + it('should add a list of items from the constructor', function() { var list = new List([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -298,25 +296,25 @@ describe('methods', function () { assert(isBuffer(collection.items.two.contents)); }); - it('should throw an error when list is not an array', function () { + it('should throw an error when list is not an array', function() { var items = new Collection(); - (function () { + (function() { items.addList(); }).should.throw('expected list to be an array.'); - (function () { + (function() { items.addList({}); }).should.throw('expected list to be an array.'); - (function () { + (function() { items.addList('foo'); }).should.throw('expected list to be an array.'); }); - it('should load an array of items from an event', function () { + it('should load an array of items from an event', function() { var collection = new Collection(); - collection.on('addList', function (list) { + collection.on('addList', function(list) { while (list.length) { collection.addItem({path: list.pop()}); } @@ -327,20 +325,20 @@ describe('methods', function () { assert(collection.items['a.txt'].path === 'a.txt'); }); - it('should load an array of items from the addList callback:', function () { + it('should load an array of items from the addList callback:', function() { var collection = new Collection(); - collection.addList(['a.txt', 'b.txt', 'c.txt'], function (fp) { + collection.addList(['a.txt', 'b.txt', 'c.txt'], function(fp) { return {path: fp}; }); assert(collection.items.hasOwnProperty('a.txt')); assert(collection.items['a.txt'].path === 'a.txt'); }); - it('should load an object of items from an event', function () { + it('should load an object of items from an event', function() { var collection = new Collection(); - collection.on('addItems', function (items) { + collection.on('addItems', function(items) { for (var key in items) { collection.addItem('foo/' + key, items[key]); delete items[key]; @@ -357,10 +355,10 @@ describe('methods', function () { assert(collection.items['foo/a'].path === 'a.txt'); }); - it('should signal `loaded` when finished (addItems)', function () { + it('should signal `loaded` when finished (addItems)', function() { var collection = new Collection(); - collection.on('addItems', function (items) { + collection.on('addItems', function(items) { for (var key in items) { if (key === 'c') { collection.loaded = true; @@ -381,10 +379,10 @@ describe('methods', function () { assert(collection.items['foo/a'].path === 'a.txt'); }); - it('should signal `loaded` when finished (addList)', function () { + it('should signal `loaded` when finished (addList)', function() { var collection = new Collection(); - collection.on('addList', function (items) { + collection.on('addList', function(items) { for (var i = 0; i < items.length; i++) { var item = items[i]; if (item.key === 'c') { @@ -412,7 +410,7 @@ describe('methods', function () { beforeEach(function() { collection = new Collection(); }); - it('should get a item from `items`', function () { + it('should get a item from `items`', function() { collection.addItem('one', {content: 'aaa'}); collection.addItem('two', {content: 'zzz'}); assert(isBuffer(collection.items.one.contents)); @@ -423,25 +421,25 @@ describe('methods', function () { }); }); -describe('queue', function () { - beforeEach(function () { +describe('queue', function() { + beforeEach(function() { collection = new Collection(); }); - it('should emit arguments on addItem', function (done) { - collection.on('addItem', function (args) { + it('should emit arguments on addItem', function(cb) { + collection.on('addItem', function(args) { assert(args[0] === 'a'); assert(args[1] === 'b'); assert(args[2] === 'c'); assert(args[3] === 'd'); assert(args[4] === 'e'); - done(); + cb(); }); collection.addItem('a', 'b', 'c', 'd', 'e'); }); - it('should expose the `queue` property for loading items', function () { + it('should expose the `queue` property for loading items', function() { collection.queue.push(collection.item('b', {path: 'b'})); collection.addItem('a', {path: 'a'}); @@ -449,8 +447,8 @@ describe('queue', function () { assert(collection.items.hasOwnProperty('b')); }); - it('should load all items on the queue when addItem is called', function () { - collection.on('addItem', function (args) { + it('should load all items on the queue when addItem is called', function() { + collection.on('addItem', function(args) { var len = args.length; var last = args[len - 1]; if (typeof last === 'string') { @@ -477,12 +475,12 @@ describe('options', function() { collection = new Collection(); }); - it('should expose the `option` method', function () { + it('should expose the `option` method', function() { collection.option('foo', 'bar'); collection.options.should.have.property('foo', 'bar'); }); - it('should be chainable', function () { + it('should be chainable', function() { collection.option('foo', 'bar') .addItems('a.hbs') .addItems('b.hbs') @@ -496,17 +494,17 @@ describe('options', function() { ]); }); - it('should set a key/value pair on options', function () { + it('should set a key/value pair on options', function() { collection.option('a', 'b'); assert(collection.options.a === 'b'); }); - it('should set an object on options', function () { + it('should set an object on options', function() { collection.option({c: 'd'}); assert(collection.options.c === 'd'); }); - it('should get an option', function () { + it('should get an option', function() { collection.option({c: 'd'}); var c = collection.option('c'); assert(c === 'd'); @@ -516,7 +514,7 @@ describe('options', function() { describe('options.renameKey', function() { beforeEach(function() { collection = new Collection({ - renameKey: function (key) { + renameKey: function(key) { return path.basename(key); } }); @@ -527,12 +525,12 @@ describe('options', function() { assert(collection.items['d.hbs'].contents.toString() === 'foo bar baz'); }); - it('should get a item with the renamed key', function () { + it('should get a item with the renamed key', function() { collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); assert(collection.getItem('d.hbs').contents.toString() === 'foo bar baz'); }); - it('should get a item with the original key', function () { + it('should get a item with the original key', function() { collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); assert(collection.getItem('a/b/c/d.hbs').contents.toString() === 'foo bar baz'); }); diff --git a/test/collection.options.js b/test/collection.options.js index 4994fd41..17f6b515 100644 --- a/test/collection.options.js +++ b/test/collection.options.js @@ -1,21 +1,23 @@ +'use strict'; + require('should'); var support = require('./support'); var App = support.resolve(); var app; -describe('collection.option()', function () { - beforeEach(function () { +describe('collection.option()', function() { + beforeEach(function() { app = new App(); app.create('page'); }); - it('should set an option:', function () { + it('should set an option:', function() { app.pages.options.should.not.have.property('foo'); app.pages.option('foo', 'bar'); app.pages.options.should.have.property('foo'); }); - it('should extend options:', function () { + it('should extend options:', function() { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); app.pages.option('a', 'b'); app.pages.option('c', 'd'); diff --git a/test/collection.render.js b/test/collection.render.js index d7c1579d..810326ad 100644 --- a/test/collection.render.js +++ b/test/collection.render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var async = require('async'); @@ -8,96 +10,96 @@ var List = App.List; var Views = App.Views; var pages; -describe('render', function () { - describe('rendering', function () { - beforeEach(function () { +describe('render', function() { + describe('rendering', function() { + beforeEach(function() { pages = new Views(); pages.engine('tmpl', require('engine-base')); }); - it('should throw an error when no callback is given:', function () { + it('should throw an error when no callback is given:', function() { (function() { pages.render({}); }).should.throw('Views#render is async and expects a callback function'); }); - it('should throw an error when an engine is not defined:', function (done) { + it('should throw an error when an engine is not defined:', function(cb) { pages.addView('foo.bar', {content: '<%= name %>'}); var page = pages.getView('foo.bar'); pages.render(page, function(err) { assert(err.message === 'Views#render cannot find an engine for: .bar'); - done(); + cb(); }); }); - it('should use helpers to render a view:', function (done) { + it('should use helpers to render a view:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function (str) { + pages.helper('upper', function(str) { return str.toUpperCase(str); }); pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getView('a.tmpl'); - pages.render(page, function (err, res) { - if (err) return done(err); + pages.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); - it('should use helpers when rendering a view:', function (done) { + it('should use helpers when rendering a view:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function (str) { + pages.helper('upper', function(str) { return str.toUpperCase(str); }); pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getView('a.tmpl'); - pages.render(page, function (err, res) { - if (err) return done(err); + pages.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); - it('should render a template when contents is a buffer:', function (done) { + it('should render a template when contents is a buffer:', function(cb) { pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); var view = pages.getView('a.tmpl'); - pages.render(view, function (err, view) { - if (err) return done(err); + pages.render(view, function(err, view) { + if (err) return cb(err); assert(view.contents.toString() === 'b'); - done(); + cb(); }); }); - it('should render a template when content is a string:', function (done) { + it('should render a template when content is a string:', function(cb) { pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); var view = pages.getView('a.tmpl'); - pages.render(view, function (err, view) { - if (err) return done(err); + pages.render(view, function(err, view) { + if (err) return cb(err); assert(view.contents.toString() === 'b'); - done(); + cb(); }); }); - it('should render a view from its path:', function (done) { + it('should render a view from its path:', function(cb) { pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - pages.render('a.tmpl', function (err, view) { - if (err) return done(err); + pages.render('a.tmpl', function(err, view) { + if (err) return cb(err); assert(view.content === 'b'); - done(); + cb(); }); }); - it('should use a plugin for rendering:', function (done) { + it('should use a plugin for rendering:', function(cb) { pages.engine('tmpl', require('engine-base')); pages.option('engine', 'tmpl'); @@ -114,24 +116,24 @@ describe('render', function () { 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, }); - pages.use(function (collection) { + pages.use(function(collection) { collection.option('pager', false); - collection.renderEach = function (cb) { + collection.renderEach = function(cb) { var list = new List(collection); - async.map(list.items, function (item, next) { + async.map(list.items, function(item, next) { collection.render(item, next); }, cb); }; }); - pages.renderEach(function (err, items) { - if (err) return done(err); + pages.renderEach(function(err, items) { + if (err) return cb(err); assert(items[0].content === 'aaa'); assert(items[9].content === 'jjj'); assert(items.length === 10); - done(); + cb(); }); }); }); diff --git a/test/collection.use.js b/test/collection.use.js index bc1c045f..a8eb32ac 100644 --- a/test/collection.use.js +++ b/test/collection.use.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -7,33 +9,33 @@ var Collection = App.Collection; var Item = App.Item; var collection; -describe('collection.use', function () { - beforeEach(function () { +describe('collection.use', function() { + beforeEach(function() { collection = new Collection(); }); - it('should expose the instance to `use`:', function (done) { - collection.use(function (inst) { + it('should expose the instance to `use`:', function(cb) { + collection.use(function(inst) { assert(inst instanceof Collection); - done(); + cb(); }); }); - it('should be chainable:', function (done) { - collection.use(function (inst) { - assert(inst instanceof Collection); - }) - .use(function (inst) { + it('should be chainable:', function(cb) { + collection.use(function(inst) { + assert(inst instanceof Collection); + }) + .use(function(inst) { assert(inst instanceof Collection); }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof Collection); - done(); + cb(); }); }); - it('should expose the collection to a plugin:', function () { - collection.use(function (items) { + it('should expose the collection to a plugin:', function() { + collection.use(function(items) { assert(items instanceof Collection); items.foo = items.addItem.bind(items); }); @@ -42,17 +44,17 @@ describe('collection.use', function () { assert(collection.items.hasOwnProperty('a')); }); - it('should expose collection when chained:', function () { + it('should expose collection when chained:', function() { collection - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); items.foo = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); items.bar = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); items.baz = items.addItem.bind(items); }); @@ -68,18 +70,18 @@ describe('collection.use', function () { assert(collection.items.hasOwnProperty('c')); }); - it('should work when a custom `Item` constructor is passed:', function () { + it('should work when a custom `Item` constructor is passed:', function() { collection = new Collection({Item: require('vinyl')}); collection - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); items.foo = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); items.bar = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); items.baz = items.addItem.bind(items); }); @@ -95,11 +97,11 @@ describe('collection.use', function () { assert(collection.items.hasOwnProperty('c')); }); - it('should pass to item `use` if a function is returned:', function () { - collection.use(function (items) { + it('should pass to item `use` if a function is returned:', function() { + collection.use(function(items) { assert(items instanceof Collection); - return function (item) { + return function(item) { item.foo = items.addItem.bind(items); assert(item instanceof Item); }; @@ -116,28 +118,28 @@ describe('collection.use', function () { assert(collection.items.hasOwnProperty('d')); }); - it('should be chainable when a item function is returned:', function () { + it('should be chainable when a item function is returned:', function() { collection - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); - return function (item) { + return function(item) { item.foo = items.addItem.bind(items); assert(item instanceof Item); }; }) - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); - return function (item) { + return function(item) { item.bar = items.addItem.bind(items); assert(item instanceof Item); }; }) - .use(function (items) { + .use(function(items) { assert(items instanceof Collection); - return function (item) { + return function(item) { item.baz = items.addItem.bind(items); assert(item instanceof Item); }; diff --git a/test/fixtures/copy/a.txt b/test/fixtures/copy/a.txt new file mode 100644 index 00000000..7c4a013e --- /dev/null +++ b/test/fixtures/copy/a.txt @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/test/fixtures/copy/b.txt b/test/fixtures/copy/b.txt new file mode 100644 index 00000000..01f02e32 --- /dev/null +++ b/test/fixtures/copy/b.txt @@ -0,0 +1 @@ +bbb \ No newline at end of file diff --git a/test/fixtures/copy/c.txt b/test/fixtures/copy/c.txt new file mode 100644 index 00000000..2383bd58 --- /dev/null +++ b/test/fixtures/copy/c.txt @@ -0,0 +1 @@ +ccc \ No newline at end of file diff --git a/test/fixtures/helpers/a.js b/test/fixtures/helpers/a.js index 44919f59..4e4b7183 100644 --- a/test/fixtures/helpers/a.js +++ b/test/fixtures/helpers/a.js @@ -1,3 +1,3 @@ module.exports = function aaa() { -}; \ No newline at end of file +}; diff --git a/test/fixtures/helpers/b.js b/test/fixtures/helpers/b.js index 73142e0c..b02f87bc 100644 --- a/test/fixtures/helpers/b.js +++ b/test/fixtures/helpers/b.js @@ -1,3 +1,3 @@ module.exports = function bbb() { -}; \ No newline at end of file +}; diff --git a/test/fixtures/helpers/c.js b/test/fixtures/helpers/c.js index ae7c3579..f5735ca7 100644 --- a/test/fixtures/helpers/c.js +++ b/test/fixtures/helpers/c.js @@ -1,3 +1,3 @@ module.exports = function ccc() { -}; \ No newline at end of file +}; diff --git a/test/fixtures/helpers/obj.js b/test/fixtures/helpers/obj.js index a139721f..07a122a4 100644 --- a/test/fixtures/helpers/obj.js +++ b/test/fixtures/helpers/obj.js @@ -6,4 +6,4 @@ exports.two = function two() { }; exports.three = function three() { -}; \ No newline at end of file +}; diff --git a/test/fixtures/posts/a.hbs b/test/fixtures/posts/a.hbs new file mode 100644 index 00000000..556558e4 --- /dev/null +++ b/test/fixtures/posts/a.hbs @@ -0,0 +1,4 @@ +--- +title: AAA +--- +This is {{title}} \ No newline at end of file diff --git a/test/fixtures/posts/b.hbs b/test/fixtures/posts/b.hbs new file mode 100644 index 00000000..8b19d24d --- /dev/null +++ b/test/fixtures/posts/b.hbs @@ -0,0 +1,4 @@ +--- +title: BBB +--- +This is {{title}} \ No newline at end of file diff --git a/test/fixtures/posts/c.hbs b/test/fixtures/posts/c.hbs new file mode 100644 index 00000000..d5a7421e --- /dev/null +++ b/test/fixtures/posts/c.hbs @@ -0,0 +1,4 @@ +--- +title: CCC +--- +This is {{title}} \ No newline at end of file diff --git a/test/fixtures/templates/a.tmpl b/test/fixtures/templates/a.tmpl index 4994bb36..36f1f1b5 100644 --- a/test/fixtures/templates/a.tmpl +++ b/test/fixtures/templates/a.tmpl @@ -1 +1 @@ -{%= name %} \ No newline at end of file +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/b.tmpl b/test/fixtures/templates/b.tmpl index 4994bb36..36f1f1b5 100644 --- a/test/fixtures/templates/b.tmpl +++ b/test/fixtures/templates/b.tmpl @@ -1 +1 @@ -{%= name %} \ No newline at end of file +<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/c.tmpl b/test/fixtures/templates/c.tmpl index 4994bb36..36f1f1b5 100644 --- a/test/fixtures/templates/c.tmpl +++ b/test/fixtures/templates/c.tmpl @@ -1 +1 @@ -{%= name %} \ No newline at end of file +<%= name %> \ No newline at end of file diff --git a/test/group.js b/test/group.js index 601e91ee..38cdeced 100644 --- a/test/group.js +++ b/test/group.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); @@ -11,102 +13,129 @@ var List = App.List; var Group = App.Group; var group; -describe('group', function () { - describe('constructor', function () { - it('should create an instance of Group:', function () { +describe('group', function() { + describe('constructor', function() { + it('should create an instance of Group:', function() { var group = new Group(); assert(group instanceof Group); }); - it('should create an instance of Group with default List:', function () { + it('should create an instance of Group with default List:', function() { var group = new Group(); assert.deepEqual(group.List, List); }); - it('should create an instance of Group with custom List:', function () { - function CustomList () { + it('should create an instance of Group with custom List:', function() { + function CustomList() { List.apply(this, arguments); } List.extend(CustomList); - var group = new Group({List: CustomList}); + var group = new Group({ + List: CustomList + }); assert.deepEqual(group.List, CustomList); }); }); - describe('static methods', function () { - it('should expose `extend`:', function () { - assert(typeof Group.extend ==='function'); + describe('static methods', function() { + it('should expose `extend`:', function() { + assert(typeof Group.extend === 'function'); }); }); - describe('prototype methods', function () { + describe('prototype methods', function() { beforeEach(function() { group = new Group(); }); - it('should expose `use`', function () { - assert(typeof group.use ==='function'); + it('should expose `use`', function() { + assert(typeof group.use === 'function'); }); - it('should expose `set`', function () { - assert(typeof group.set ==='function'); + it('should expose `set`', function() { + assert(typeof group.set === 'function'); }); - it('should expose `get`', function () { - assert(typeof group.get ==='function'); + it('should expose `get`', function() { + assert(typeof group.get === 'function'); }); - it('should expose `visit`', function () { - assert(typeof group.visit ==='function'); + it('should expose `visit`', function() { + assert(typeof group.visit === 'function'); }); - it('should expose `define`', function () { - assert(typeof group.define ==='function'); + it('should expose `define`', function() { + assert(typeof group.define === 'function'); }); }); - describe('instance', function () { + describe('instance', function() { beforeEach(function() { group = new Group(); }); - it('should expose options:', function () { + it('should expose options:', function() { assert(typeof group.options === 'object'); }); - it('should set a value on the instance:', function () { + it('should set a value on the instance:', function() { group.set('a', 'b'); - assert(group.a ==='b'); + assert(group.a === 'b'); }); - it('should get a value from the instance:', function () { + it('should get a value from the instance:', function() { group.set('a', 'b'); - assert(group.get('a') ==='b'); + assert(group.get('a') === 'b'); }); }); describe('get', function() { - it('should get a normal value when not an array', function () { - var group = new Group({'foo': {items: [1,2,3]}}); - assert.deepEqual(group.get('foo'), {items: [1,2,3]}); - }); - - it('should get an instance of List when value is an array', function () { - var group = new Group({'foo': {items: [{path: 'one.hbs'},{path: 'two.hbs'}, {path: 'three.hbs'}]}}); + it('should get a normal value when not an array', function() { + var group = new Group({ + 'foo': { + items: [1, 2, 3] + } + }); + assert.deepEqual(group.get('foo'), { + items: [1, 2, 3] + }); + }); + + it('should get an instance of List when value is an array', function() { + var group = new Group({ + 'foo': { + items: [{ + path: 'one.hbs' + }, { + path: 'two.hbs' + }, { + path: 'three.hbs' + }] + } + }); var list = group.get('foo.items'); assert(list instanceof List); assert.deepEqual(list.items.length, 3); }); - it('should throw an error when trying to use a List method on a non List value', function () { - (function () { - var group = new Group({'foo': {items: [1,2,3]}}); + it('should throw an error when trying to use a List method on a non List value', function() { + (function() { + var group = new Group({ + 'foo': { + items: [1, 2, 3] + } + }); var foo = group.get('foo'); foo.paginate(); }).should.throw('paginate can only be used with an array of `List` items.'); }); - it('should not override properties already existing on non List values', function (done) { - var group = new Group({'foo': {items: [1,2,3], paginate: function () { - assert(true); - done(); - }}}); + it('should not override properties already existing on non List values', function(cb) { + var group = new Group({ + 'foo': { + items: [1, 2, 3], + paginate: function() { + assert(true); + cb(); + } + } + }); var foo = group.get('foo'); foo.paginate(); }); @@ -117,18 +146,22 @@ describe('group', function () { group = new Group(); }); - it('should use middleware on a group:', function () { - group.set('one', {contents: new Buffer('aaa')}); - group.set('two', {contents: new Buffer('zzz')}); + it('should use plugins on a group:', function() { + group.set('one', { + contents: new Buffer('aaa') + }); + group.set('two', { + contents: new Buffer('zzz') + }); group - .use(function (group) { + .use(function(group) { group.options = {}; }) - .use(function (group) { + .use(function(group) { group.options.foo = 'bar'; }) - .use(function () { + .use(function() { this.set('one', 'two'); }); @@ -136,5 +169,4 @@ describe('group', function () { assert(group.options.foo === 'bar'); }); }); -}); - +}); \ No newline at end of file diff --git a/test/handlers.js b/test/handlers.js index 4c07276a..96c5c532 100644 --- a/test/handlers.js +++ b/test/handlers.js @@ -5,30 +5,30 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('handlers', function () { - describe('custom handlers', function () { - beforeEach(function () { +describe('handlers', function() { + describe('custom handlers', function() { + beforeEach(function() { app = new App(); app.create('page'); }); - it('should add custom middleware handlers:', function () { + it('should add custom middleware handlers:', function() { app.handler('foo'); app.handler('bar'); - app.pages.use(function () { - return function (view) { + app.pages.use(function() { + return function(view) { app.handle('foo', view); app.handle('bar', view); }; }); - app.foo(/a/, function (view, next) { + app.foo(/a/, function(view, next) { view.one = 'aaa'; next(); }); - app.bar(/z/, function (view, next) { + app.bar(/z/, function(view, next) { view.two = 'zzz'; next(); }); diff --git a/test/helpers.js b/test/helpers.js index 33398d54..4db1f27f 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,7 +1,8 @@ +'use strict'; + require('mocha'); require('should'); var path = require('path'); -var Base = require('base-methods'); var assert = require('assert'); var consolidate = require('consolidate'); var handlebars = require('engine-handlebars'); @@ -11,40 +12,33 @@ require('swig'); var support = require('./support'); var App = support.resolve(); -var helpers = App._.proto.helpers; -var init = App._.proto.init; +var helpers = App._.plugin.helpers; +var init = App._.plugin.init; var app; -describe('helpers', function () { - describe('constructor', function () { - it('should create an instance of Helpers:', function () { - app = new App(); - assert(app instanceof App); - }); - }); - - describe('prototype methods', function () { +describe('helpers', function() { + describe('prototype methods', function() { beforeEach(function() { app = new App(); }); - it('should expose `helper`', function () { - assert(typeof app.helper ==='function'); + it('should expose `helper`', function() { + assert.equal(typeof app.helper, 'function'); }); - it('should expose `asyncHelper`', function () { - assert(typeof app.asyncHelper ==='function'); + it('should expose `asyncHelper`', function() { + assert.equal(typeof app.asyncHelper, 'function'); }); }); - describe('instance', function () { - it('should prime _', function () { + describe('instance', function() { + it('should prime _', function() { function Foo() { - Base.call(this); + App.call(this); init(this); } - Base.extend(Foo); + App.extend(Foo); var foo = new Foo(); helpers(foo); - assert(typeof foo._ ==='object'); + assert.equal(typeof foo._, 'object'); }); }); @@ -53,67 +47,67 @@ describe('helpers', function () { app = new App(); }); - it('should add a sync helper to the `sync` object:', function () { - app.helper('one', function () {}); - assert(typeof app._.helpers.sync.one === 'function'); + it('should add a sync helper to the `sync` object:', function() { + app.helper('one', function() {}); + assert.equal(typeof app._.helpers.sync.one, 'function'); }); - it('should load a glob of sync helper functions:', function () { + it('should load a glob of sync helper functions:', function() { app.helpers('test/fixtures/helpers/[a-c].js'); - assert(typeof app._.helpers.sync.c === 'function'); - assert(typeof app._.helpers.sync.b === 'function'); - assert(typeof app._.helpers.sync.a === 'function'); + assert.equal(typeof app._.helpers.sync.c, 'function'); + assert.equal(typeof app._.helpers.sync.b, 'function'); + assert.equal(typeof app._.helpers.sync.a, 'function'); }); - it('should fail gracefully on bad globs:', function (cb) { + it('should fail gracefully on bad globs:', function(cb) { try { app.helpers('test/fixtures/helpers/*.foo'); cb(); - } catch(err) { + } catch (err) { cb(new Error('should not throw an error.')); } }); - it('should add a glob of sync helper objects:', function () { + it('should add a glob of sync helper objects:', function() { app.helpers('test/fixtures/helpers/!([a-c]).js'); - assert(typeof app._.helpers.sync.one === 'function'); - assert(typeof app._.helpers.sync.two === 'function'); - assert(typeof app._.helpers.sync.three === 'function'); + assert.equal(typeof app._.helpers.sync.one, 'function'); + assert.equal(typeof app._.helpers.sync.two, 'function'); + assert.equal(typeof app._.helpers.sync.three, 'function'); }); - it('should add a glob with mixed helper objects and functions:', function () { + it('should add a glob with mixed helper objects and functions:', function() { app.helpers('test/fixtures/helpers/*.js'); - assert(typeof app._.helpers.sync.a === 'function'); - assert(typeof app._.helpers.sync.b === 'function'); - assert(typeof app._.helpers.sync.c === 'function'); - assert(typeof app._.helpers.sync.one === 'function'); - assert(typeof app._.helpers.sync.two === 'function'); - assert(typeof app._.helpers.sync.three === 'function'); + assert.equal(typeof app._.helpers.sync.a, 'function'); + assert.equal(typeof app._.helpers.sync.b, 'function'); + assert.equal(typeof app._.helpers.sync.c, 'function'); + assert.equal(typeof app._.helpers.sync.one, 'function'); + assert.equal(typeof app._.helpers.sync.two, 'function'); + assert.equal(typeof app._.helpers.sync.three, 'function'); }); - it('should add an object of sync helpers to the `sync` object:', function () { + it('should add an object of sync helpers to the `sync` object:', function() { app.helpers({ - x: function () {}, - y: function () {}, - z: function () {} + x: function() {}, + y: function() {}, + z: function() {} }); - assert(typeof app._.helpers.sync.x === 'function'); - assert(typeof app._.helpers.sync.y === 'function'); - assert(typeof app._.helpers.sync.z === 'function'); + assert.equal(typeof app._.helpers.sync.x, 'function'); + assert.equal(typeof app._.helpers.sync.y, 'function'); + assert.equal(typeof app._.helpers.sync.z, 'function'); }); - it('should add a helper "group":', function () { + it('should add a helper "group":', function() { app.helperGroup('foo', { - x: function () {}, - y: function () {}, - z: function () {} + x: function() {}, + y: function() {}, + z: function() {} }); - assert(typeof app._.helpers.sync.foo.x === 'function'); - assert(typeof app._.helpers.sync.foo.y === 'function'); - assert(typeof app._.helpers.sync.foo.z === 'function'); + assert.equal(typeof app._.helpers.sync.foo.x, 'function'); + assert.equal(typeof app._.helpers.sync.foo.y, 'function'); + assert.equal(typeof app._.helpers.sync.foo.z, 'function'); }); }); @@ -122,93 +116,93 @@ describe('helpers', function () { app = new App(); }); - it('should add an async helper to the `async` object:', function () { - app.asyncHelper('two', function () {}); - assert(typeof app._.helpers.async.two === 'function'); + it('should add an async helper to the `async` object:', function() { + app.asyncHelper('two', function() {}); + assert.equal(typeof app._.helpers.async.two, 'function'); }); - it('should load a glob of async helper functions:', function () { + it('should load a glob of async helper functions:', function() { app.asyncHelpers('test/fixtures/helpers/[a-c].js'); - assert(typeof app._.helpers.async.a === 'function'); - assert(typeof app._.helpers.async.b === 'function'); - assert(typeof app._.helpers.async.c === 'function'); + assert.equal(typeof app._.helpers.async.a, 'function'); + assert.equal(typeof app._.helpers.async.b, 'function'); + assert.equal(typeof app._.helpers.async.c, 'function'); }); - it('should add a glob of async helper objects:', function () { + it('should add a glob of async helper objects:', function() { app.asyncHelpers('test/fixtures/helpers/!([a-c]).js'); - assert(typeof app._.helpers.async.one === 'function'); - assert(typeof app._.helpers.async.two === 'function'); - assert(typeof app._.helpers.async.three === 'function'); + assert.equal(typeof app._.helpers.async.one, 'function'); + assert.equal(typeof app._.helpers.async.two, 'function'); + assert.equal(typeof app._.helpers.async.three, 'function'); }); - it('should fail gracefully on bad globs:', function (cb) { + it('should fail gracefully on bad globs:', function(cb) { try { app.asyncHelpers('test/fixtures/helpers/*.foo'); cb(); - } catch(err) { + } catch (err) { cb(new Error('should not throw an error.')); } }); - it('should add a glob with mixed helper objects and functions:', function () { + it('should add a glob with mixed helper objects and functions:', function() { app.asyncHelpers('test/fixtures/helpers/*.js'); - assert(typeof app._.helpers.async.a === 'function'); - assert(typeof app._.helpers.async.b === 'function'); - assert(typeof app._.helpers.async.c === 'function'); - assert(typeof app._.helpers.async.one === 'function'); - assert(typeof app._.helpers.async.two === 'function'); - assert(typeof app._.helpers.async.three === 'function'); + assert.equal(typeof app._.helpers.async.a, 'function'); + assert.equal(typeof app._.helpers.async.b, 'function'); + assert.equal(typeof app._.helpers.async.c, 'function'); + assert.equal(typeof app._.helpers.async.one, 'function'); + assert.equal(typeof app._.helpers.async.two, 'function'); + assert.equal(typeof app._.helpers.async.three, 'function'); }); - it('should add an object of async helpers to the `async` object:', function () { + it('should add an object of async helpers to the `async` object:', function() { app.asyncHelpers({ - x: function () {}, - y: function () {}, - z: function () {} + x: function() {}, + y: function() {}, + z: function() {} }); - assert(typeof app._.helpers.async.x === 'function'); - assert(typeof app._.helpers.async.y === 'function'); - assert(typeof app._.helpers.async.z === 'function'); + assert.equal(typeof app._.helpers.async.x, 'function'); + assert.equal(typeof app._.helpers.async.y, 'function'); + assert.equal(typeof app._.helpers.async.z, 'function'); }); - it('should add an async helper "group":', function () { + it('should add an async helper "group":', function() { app.helperGroup('foo', { - x: function () {}, - y: function () {}, - z: function () {} + x: function() {}, + y: function() {}, + z: function() {} }, true); - assert(typeof app._.helpers.async.foo.x === 'function'); - assert(typeof app._.helpers.async.foo.y === 'function'); - assert(typeof app._.helpers.async.foo.z === 'function'); + assert.equal(typeof app._.helpers.async.foo.x, 'function'); + assert.equal(typeof app._.helpers.async.foo.y, 'function'); + assert.equal(typeof app._.helpers.async.foo.z, 'function'); }); }); }); -describe('sync helpers', function () { - beforeEach(function () { +describe('sync helpers', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('page'); }); - it('should register a helper:', function () { - app.helper('a', function () {}); - app.helper('b', function () {}); + it('should register a helper:', function() { + app.helper('a', function() {}); + app.helper('b', function() {}); assert(app._.helpers.sync.hasOwnProperty('a')); assert(app._.helpers.sync.hasOwnProperty('b')); }); - it('should use a helper:', function (cb) { + it('should use a helper:', function(cb) { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= upper(a) %>', locals: {a: 'bbb'}}); - app.helper('upper', function (str) { + app.helper('upper', function(str) { return str.toUpperCase(); }); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { + app.render(page, function(err, view) { if (err) return cb(err); assert.equal(typeof view.contents.toString(), 'string'); @@ -217,19 +211,23 @@ describe('sync helpers', function () { }); }); - it('should use a namespaced helper:', function (cb) { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= foo.upper(a) %>', locals: {a: 'bbb'}}); + it('should use a namespaced helper:', function(cb) { + app.pages('a.tmpl', { + path: 'a.tmpl', + content: '<%= foo.upper(a) %>', + locals: { + a: 'bbb' + } + }); app.helperGroup('foo', { - upper: function (str) { + upper: function(str) { return str.toUpperCase(); } }); - // console.log(app._.helpers) - var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { + app.render(page, function(err, view) { if (err) return cb(err); assert.equal(typeof view.contents.toString(), 'string'); @@ -239,40 +237,57 @@ describe('sync helpers', function () { }); }); -describe('async helpers', function () { - beforeEach(function () { +describe('async helpers', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); + app.create('partials', {viewType: 'partial'}); app.create('page'); }); - it('should register an async helper:', function () { - app.asyncHelper('a', function () {}); - app.asyncHelper('b', function () {}); + it('should register an async helper:', function() { + app.asyncHelper('a', function() {}); + app.asyncHelper('b', function() {}); app._.helpers.async.should.have.property('a'); app._.helpers.async.should.have.property('b'); }); - it('should use an async helper:', function (cb) { + it('should use an async helper:', function(cb) { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= lower(a) %>', locals: {a: 'BBB'}}); - app.asyncHelper('lower', function (str, next) { + app.asyncHelper('lower', function(str, next) { if (typeof next !== 'function') return str; next(null, str.toLowerCase()); }); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { + app.render(page, function(err, view) { if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'bbb'); cb(); }); }); + + it('should use load a file from the file system in a helper', function(cb) { + app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= getPartial("test/fixtures/posts/a.txt") %>', locals: {a: 'BBB'}}); + app.asyncHelper('getPartial', function(name, next) { + var view = this.app.partials.getView(name); + next(null, view.content); + }); + + var page = app.pages.getView('a.tmpl'); + app.render(page, function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert(/AAA/.test(view.content)); + cb(); + }); + }); }); -describe('built-in helpers:', function () { - describe('automatically created helpers for default view types:', function () { - beforeEach(function () { +describe('built-in helpers:', function() { + describe('automatically generated helpers for default view types:', function() { + beforeEach(function() { app = new App({rethrow: false}); app.engine('md', require('engine-base')); app.engine('tmpl', require('engine-base')); @@ -280,60 +295,85 @@ describe('built-in helpers:', function () { app.create('pages'); // parse front matter - app.onLoad(/./, function (view, next) { + app.onLoad(/./, function(view, next) { matter.parse(view, next); }); }); - it('should expose front matter to the `partial` helper.', function (cb) { + it('should expose front matter to the `partial` helper.', function(cb) { app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); - app.render('b.md', function (err, res) { + app.render('b.md', function(err, res) { if (err) return cb(err); res.content.should.equal('foo AAA bar'); cb(); }); }); - it('should use helper locals.', function (cb) { - app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); + it('should emit `helper` when a built-in helper is called', function(cb) { + app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); + app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); + + app.once('helper', function(msg) { + assert(msg); + assert.equal(msg, 'partial helper > rendering "a.md"'); + cb(); + }); + + app.render('b.md', function(err, res) { + }); + }); + + it('should use helper locals.', function(cb) { + app.partial('abc.md', {content: '<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); - app.render('xyz.md', {name: 'DDD'}, function (err, res) { + app.render('xyz.md', {name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo CCC bar'); cb(); }); }); - it('should use front matter data.', function (cb) { + it('should use front matter data.', function(cb) { app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - app.render('xyz.md', {name: 'DDD'}, function (err, res) { + app.render('xyz.md', {name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo AAA bar'); cb(); }); }); - it('should use partial locals:', function (cb) { + it('should prefer helper locals over front-matter', function(cb) { + app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function(err, res) { + if (err) return cb(err); + res.content.should.equal('foo CCC bar'); + cb(); + }); + }); + + it('should use partial locals:', function(cb) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) - .render({name: 'DDD'}, function (err, res) { + .render({name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo EEE bar'); cb(); }); }); - it('should use locals from the `view.render` method:', function (cb) { + it('should use locals from the `view.render` method:', function(cb) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) - .render({name: 'DDD'}, function (err, res) { + .render({name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo EEE bar'); @@ -341,21 +381,37 @@ describe('built-in helpers:', function () { }); }); - it('should use locals from the `app.render` method:', function (cb) { + it('should use locals from the `app.render` method:', function(cb) { app.partial('abc.md', {content: '<%= name %>'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - app.render('xyz.md', {name: 'DDD'}, function (err, res) { + app.render('xyz.md', {name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo DDD bar'); cb(); }); }); - it('should return throw an error when a partial is missing.', function (cb) { + it('should use a `helperContext` function from app.options', function(cb) { + app.option('helperContext', function(view, locals) { + return { name: 'blah' }; + }); + + app.partial('abc.md', {content: '<%= name %>'}); + app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); + + app.render('xyz.md', {name: 'DDD'}, function(err, res) { + if (err) return cb(err); + res.content.should.equal('foo blah bar'); + cb(); + }); + }); + + it('should return an empty string when the partial is missing.', function(cb) { app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("def.md", { name: "CCC" }) %> bar'}); - app.render('xyz.md', {name: 'DDD'}, function (err, res) { + + app.render('xyz.md', {name: 'DDD'}, function(err, res) { assert(err); assert.equal(err.message, 'helper "partial" cannot find "def.md"'); cb(); @@ -363,48 +419,48 @@ describe('built-in helpers:', function () { }); }); - describe('helper context:', function () { - beforeEach(function () { + describe('helper context:', function() { + beforeEach(function() { app = new App({rethrow: false}); app.engine(['tmpl', 'md'], require('engine-base')); app.create('partial', { viewType: 'partial' }); app.create('page'); // parse front matter - app.onLoad(/./, function (view, next) { + app.onLoad(/./, function(view, next) { matter.parse(view, next); }); }); - it('should prefer helper locals over view locals.', function (cb) { + it('should prefer helper locals over view locals.', function(cb) { app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); - app.render('xyz.md', {name: 'DDD'}, function (err, res) { + app.render('xyz.md', {name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo CCC bar'); cb(); }); }); - it('should give preference to view locals over render locals.', function (cb) { + it('should give preference to view locals over render locals.', function(cb) { app.partial('abc.md', {content: '<%= name %>', locals: {name: 'BBB'}}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); var page = app.pages.getView('xyz.md'); - app.render(page, {name: 'DDD'}, function (err, res) { + app.render(page, {name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo BBB bar'); cb(); }); }); - it('should use render locals when other locals are not defined.', function (cb) { + it('should use render locals when other locals are not defined.', function(cb) { app.partial('abc.md', {content: '<%= name %>'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - app.render('xyz.md', {name: 'DDD'}, function (err, res) { + app.render('xyz.md', {name: 'DDD'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo DDD bar'); cb(); @@ -412,62 +468,126 @@ describe('built-in helpers:', function () { }); }); - describe('user-defined engines:', function () { - beforeEach(function () { + describe('user-defined engines:', function() { + beforeEach(function() { app = new App({rethrow: false}); app.create('partial', { viewType: 'partial' }); app.create('page'); // parse front matter - app.onLoad(/./, function (view, next) { + app.onLoad(/./, function(view, next) { matter.parse(view, next); }); }); - it('should use the `partial` helper with handlebars.', function (cb) { + it('should use the `partial` helper with handlebars.', function(cb) { app.engine(['tmpl', 'md'], require('engine-base')); app.engine('hbs', handlebars); app.partial('title.hbs', {content: '{{name}}', locals: {name: 'BBB'}}); app.page('a.hbs', {path: 'a.hbs', content: 'foo {{{partial "title.hbs" this}}} bar'}); - app.render('a.hbs', {name: 'Halle Nicole'}, function (err, res) { + app.render('a.hbs', {name: 'Halle Nicole'}, function(err, res) { if (err) return cb(err); res.content.should.equal('foo Halle Nicole bar'); cb(); }); }); - it('should use the `partial` helper with any engine.', function (cb) { + it('should use the `partial` helper with any engine.', function(cb) { app.engine('hbs', handlebars); app.engine('md', handlebars); app.engine('swig', swig); app.engine('tmpl', require('engine-base')); - app.partial('a.hbs', {content: '---\nname: "AAA"\n---\n{{name}}', locals: {name: 'BBB'}}); - app.page('a.hbs', {path: 'a.hbs', content: '{{author}}', locals: {author: 'Halle Nicole'}}); - app.page('b.tmpl', {path: 'b.tmpl', content: '<%= author %>', locals: {author: 'Halle Nicole'}}); - app.page('d.swig', {path: 'd.swig', content: '{{author}}', locals: {author: 'Halle Nicole'}}); - app.page('e.swig', {path: 'e.swig', content: '{{author}}', locals: {author: 'Halle Nicole'}}); - app.page('f.hbs', {content: '{{author}}', locals: {author: 'Halle Nicole'}}); - app.page('g.md', {content: '---\nauthor: Brian Woodward\n---\n{{author}}', locals: {author: 'Halle Nicole'}}); - app.page('with-partial.hbs', {path: 'with-partial.hbs', content: '{{{partial "a.hbs" custom.locals}}}'}); + /** + * Partial + */ + + app.partial('a.hbs', { + content: '---\nname: "AAA"\n---\n{{name}}', + locals: { + name: 'BBB' + } + }); + + /** + * Pages + */ + + app.page('a.hbs', { + path: 'a.hbs', + content: '{{author}}', + locals: { + author: 'Halle Nicole' + } + }); + app.page('b.tmpl', { + path: 'b.tmpl', + content: '<%= author %>', + locals: { + author: 'Halle Nicole' + } + }); + app.page('d.swig', { + path: 'd.swig', + content: '{{author}}', + locals: { + author: 'Halle Nicole' + } + }); + app.page('e.swig', { + path: 'e.swig', + content: '{{author}}', + locals: { + author: 'Halle Nicole' + } + }); + app.page('f.hbs', { + content: '{{author}}', + locals: { + author: 'Halle Nicole' + } + }); + app.page('g.md', { + content: '---\nauthor: Brian Woodward\n---\n{{author}}', + locals: { + author: 'Halle Nicole' + } + }); + app.page('with-partial.hbs', { + path: 'with-partial.hbs', + content: '{{{partial "a.hbs" custom.locals}}}' + }); + + app.on('error', function(err) { + cb(err); + }); var locals = {custom: {locals: {name: 'Halle Nicole' }}}; - app.render('a.hbs', locals, function (err, res) { - if (err) return console.log(err); + app.render('a.hbs', locals, function(err, res) { + if (err) { + app.emit('error', err); + return; + } res.content.should.equal('Halle Nicole'); }); - app.render('with-partial.hbs', locals, function (err, res) { - if (err) return console.log(err); + app.render('with-partial.hbs', locals, function(err, res) { + if (err) { + app.emit('error', err); + return; + } res.content.should.equal('Halle Nicole'); }); var page = app.pages.getView('g.md'); locals.author = page.data.author || locals.author; - page.render(locals, function (err, res) { - if (err) return cb(err); + page.render(locals, function(err, res) { + if (err) { + app.emit('error', err); + return; + } res.content.should.equal('Brian Woodward'); cb(null, res.content); }); @@ -475,48 +595,48 @@ describe('built-in helpers:', function () { }); }); -describe('helpers integration', function () { - beforeEach(function () { +describe('helpers integration', function() { + beforeEach(function() { app = new App(); app.create('pages'); app.engine('md', require('engine-base')); }); - describe('.helpers()', function () { - it('should add helpers and use them in templates.', function (cb) { + describe('.helpers()', function() { + it('should add helpers and use them in templates.', function(cb) { app.helpers({ - upper: function (str) { + upper: function(str) { return str.toUpperCase(); } }); app.page('doc.md', {content: 'a <%= upper(name) %> b'}) - .render({name: 'Halle'}, function (err, res) { + .render({name: 'Halle'}, function(err, res) { if (err) return cb(err); - assert(res.content === 'a HALLE b'); + assert.equal(res.content, 'a HALLE b'); cb(); }); }); }); - describe('helper options:', function () { - it('should expose `this.options` to helpers:', function (cb) { - app.helper('cwd', function (fp) { + describe('helper options:', function() { + it('should expose `this.options` to helpers:', function(cb) { + app.helper('cwd', function(fp) { return path.join(this.options.cwd, fp); }); app.option('one', 'two'); app.option('cwd', 'foo/bar'); app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) - .render(function (err, res) { + .render(function(err, res) { if (err) return cb(err); - assert(res.content === 'a foo/bar/baz b'); + assert.equal(res.content, 'a foo/bar/baz b'); cb(); }); }); - it('should pass helper options to helpers:', function (cb) { - app.helper('cwd', function (fp) { + it('should pass helper options to helpers:', function(cb) { + app.helper('cwd', function(fp) { return path.join(this.options.cwd, fp); }); @@ -524,72 +644,72 @@ describe('helpers integration', function () { app.option('helper.whatever', '...'); app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) - .render(function (err, res) { + .render(function(err, res) { if (err) return cb(err); - assert(res.content === 'a foo/bar/baz b'); + assert.equal(res.content, 'a foo/bar/baz b'); cb(); }); }); }); - describe('options.helpers', function () { - it('should register helpers passed on the options:', function (cb) { + describe('options.helpers', function() { + it('should register helpers passed on the options:', function(cb) { app.option({ helpers: { - upper: function (str) { + upper: function(str) { return str.toUpperCase(); }, - foo: function (str) { + foo: function(str) { return 'foo' + str; } } }); app.page('doc.md', {content: 'a <%= upper(name) %> <%= foo("bar") %> b'}) - .render({name: 'Halle'}, function (err, res) { + .render({name: 'Halle'}, function(err, res) { if (err) return cb(err); - assert(res.content === 'a HALLE foobar b'); + assert.equal(res.content, 'a HALLE foobar b'); cb(); }); }); }); - describe('options.helpers', function () { - it('should add helpers and use them in templates.', function (cb) { + describe('options.helpers', function() { + it('should add helpers and use them in templates.', function(cb) { app.options.helpers = { - upper: function (str) { + upper: function(str) { return str.toUpperCase(); }, - foo: function (str) { + foo: function(str) { return 'foo' + str; } }; app.page('doc.md', {content: 'a <%= upper(name) %> b'}) - .render({name: 'Halle'}, function (err, res) { + .render({name: 'Halle'}, function(err, res) { if (err) return cb(err); - assert(res.content === 'a HALLE b'); + assert.equal(res.content, 'a HALLE b'); cb(); }); }); }); }); -describe('collection helpers', function () { - beforeEach(function () { +describe('collection helpers', function() { + beforeEach(function() { app = new App(); app.create('posts'); app.create('pages', {engine: 'hbs'}); app.create('partials', {viewType: 'partial', engine: 'hbs'}); app.create('snippet', {viewType: 'partial'}); app.engine('hbs', require('engine-handlebars')); - app.helper('log', function (ctx) { + app.helper('log', function(ctx) { console.log(ctx); }); }); - describe('plural', function () { - it('should get the given collection', function (cb) { + describe('plural', function() { + it('should get the given collection as a block helper', function(cb) { app.post('a.hbs', {content: 'foo'}); app.post('b.hbs', {content: 'bar'}); app.post('c.hbs', {content: 'baz'}); @@ -601,53 +721,78 @@ describe('collection helpers', function () { app.page('index.hbs', { content: '{{> list.hbs }}' }) - .render(function (err, res) { + .render(function(err, res) { if (err) return cb(err); - assert(res.content === 'foobarbaz'); + assert.equal(res.content, 'foobarbaz'); + cb(); + }); + }); + + it('should get the given collection as a helper expression', function(cb) { + app.post('a.hbs', {content: 'foo'}); + app.post('b.hbs', {content: 'bar'}); + app.post('c.hbs', {content: 'baz'}); + + app.helper('list', function(list) { + return list.items.map(function(item) { + return '- ' + item.content; + }).join('\n'); + }); + + app.partial('list.hbs', { + content: '{{list (posts)}}' + }); + + app.page('index.hbs', { + content: '{{> list.hbs }}' + }) + .render(function(err, res) { + if (err) return cb(err); + try { + assert.equal(res.content, '- foo\n- bar\n- baz'); + } catch (err) { + return cb(err); + } cb(); }); }); }); - describe('single', function () { - it('should get a view from an unspecified collection', function (cb) { + describe('single', function() { + it('should get a view from an unspecified collection', function(cb) { app.post('a.hbs', {content: 'post-a'}); app.post('b.hbs', {content: 'post-b'}); - var one = app.page('one', {content: '{{view "a.hbs"}}'}) - .compile() - .fn(); - - var two = app.page('two', {content: '{{view "b.hbs"}}'}) - .compile() - .fn(); - - assert(one === 'post-a'); - assert(two === 'post-b'); - cb(); + app.page('one', {content: '{{view "a.hbs"}}'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(res.content, 'post-a'); + cb(); + }); }); - it('should return an empty string if not found', function (cb) { - var one = app.page('one', {content: '{{view "foo.hbs"}}'}) - .compile() - .fn(); - assert(one === ''); - cb(); + it('should return an empty string if not found', function(cb) { + app.page('one', {content: '{{view "foo.hbs"}}'}) + .render(function(err, view) { + if (err) return cb(err); + assert.equal(view.content, ''); + cb(); + }); }); it('should handle engine errors', function(cb) { - app.post('foo.hbs', {content: '{{one "two"}}'}) + app.post('foo.hbs', {content: '{{one "two"}}'}); app.page('one', {content: '{{posts "foo.hbs"}}'}) - .render(function (err) { + .render(function(err) { assert(err); - assert(typeof err === 'object'); - assert(typeof err.message === 'string'); + assert.equal(typeof err, 'object'); + assert.equal(typeof err.message, 'string'); assert(/Missing helper: "one"/.test(err.message)); cb(); }); }); - it('should handle engine errors2', function (cb) { + it('should handle engine errors2', function(cb) { app.engine('tmpl', require('engine-base')); app.create('foo', {engine: 'tmpl'}); app.create('bar', {engine: 'tmpl'}); @@ -655,36 +800,38 @@ describe('collection helpers', function () { app.create('foo', {viewType: 'partial'}); app.foo('foo.tmpl', {path: 'foo.tmpl', content: '<%= blah.bar %>'}); app.bar('one.tmpl', {content: '<%= foo("foo.tmpl") %>'}) - .render(function (err) { + .render(function(err) { assert(err); - assert(typeof err === 'object'); + assert.equal(typeof err, 'object'); assert(/blah is not defined/.test(err.message)); cb(); }); }); - it('should work with non-handlebars engine', function (cb) { + it('should work with non-handlebars engine', function(cb) { app.engine('tmpl', require('engine-base')); app.create('foo', {engine: 'tmpl'}); app.create('bar', {engine: 'tmpl'}); app.foo('a.tmpl', {content: 'foo-a'}); - app.foo('b.tmpl', {content: 'foo-b'}); + app.bar('b.tmpl', {content: 'bar-b'}); - var one = app.bar('one', {content: '<%= view("a.tmpl") %>'}) - .compile() - .fn(); + app.bar('one', {content: '<%= view("a.tmpl", "foos", { render: true }) %>'}) + .render(function(err, foo) { + if (err) return cb(err); - var two = app.bar('two', {content: '<%= view("b.tmpl") %>'}) - .compile() - .fn(); + assert.equal(foo.content, 'foo-a'); - assert(one === 'foo-a'); - assert(two === 'foo-b'); - cb(); + app.bar('two', {content: '<%= view("b.tmpl", "bars", { render: true }) %>'}) + .render(function(err, bar) { + if (err) return cb(err); + assert.equal(bar.content, 'bar-b'); + cb(); + }); + }); }); - it('should get a specific view from the given collection', function (cb) { + it('should render a specific view from the given collection', function(cb) { app.post('a.hbs', {content: 'post-a'}); app.post('b.hbs', {content: 'post-b'}); app.post('c.hbs', {content: 'post-c'}); @@ -692,17 +839,20 @@ describe('collection helpers', function () { app.page('b.hbs', {content: 'page-b'}); app.page('c.hbs', {content: 'page-c'}); - var one = app.page('one', {content: '{{view "a.hbs" "posts"}}'}) - .compile() - .fn(); + app.page('one', {content: '{{view "a.hbs" "posts" render=true}}'}) + .render(function(err, post) { + if (err) return cb(err); + + assert.equal(post.content, 'post-a'); - var two = app.page('two', {content: '{{view "b.hbs" "pages"}}'}) - .compile() - .fn(); + app.page('two', {content: '{{view "b.hbs" "pages" render=true}}'}) + .render(function(err, page) { + if (err) return cb(err); + assert.equal(page.content, 'page-b') + cb(); + }); + }); - assert(one === 'post-a'); - assert(two === 'page-b'); - cb(); }); }); }); diff --git a/test/is.js b/test/is.js new file mode 100644 index 00000000..bfac324f --- /dev/null +++ b/test/is.js @@ -0,0 +1,69 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); + +describe('is', function() { + describe('isApp', function() { + it('should return true if value is an instance of App', function() { + var app = new App(); + assert(App.isApp(app)); + }); + it('should return false if value is not an instance of App', function() { + assert(!App.isApp('foo')); + }); + }); + + describe('isCollection', function() { + it('should return true if value is an instance of Collection', function() { + var collection = new App.Collection(); + assert(App.isCollection(collection)); + }); + it('should return false if value is not an instance of Collection', function() { + assert(!App.isCollection('foo')); + }); + }); + + describe('isViews', function() { + it('should return true if value is an instance of Views', function() { + var collection = new App.Views(); + assert(App.isViews(collection)); + }); + it('should return false if value is not an instance of Views', function() { + assert(!App.isViews('foo')); + }); + }); + + describe('isList', function() { + it('should return true if value is an instance of List', function() { + var collection = new App.List(); + assert(App.isList(collection)); + }); + it('should return false if value is not an instance of List', function() { + assert(!App.isList('foo')); + }); + }); + + describe('isView', function() { + it('should return true if value is an instance of View', function() { + var collection = new App.View(); + assert(App.isView(collection)); + }); + it('should return false if value is not an instance of View', function() { + assert(!App.isView('foo')); + }); + }); + + describe('isItem', function() { + it('should return true if value is an instance of Item', function() { + var collection = new App.Item(); + assert(App.isItem(collection)); + }); + it('should return false if value is not an instance of Item', function() { + assert(!App.isItem('foo')); + }); + }); +}); + diff --git a/test/item.js b/test/item.js index 7550a076..7c2990a1 100644 --- a/test/item.js +++ b/test/item.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); var should = require('should'); var fs = require('fs'); @@ -11,107 +13,107 @@ var App = support.resolve(); var Item = App.Item; var item; -describe('Item', function () { - describe('instance', function () { - it('should create an instance of Item:', function () { +describe('Item', function() { + describe('instance', function() { + it('should create an instance of Item:', function() { item = new Item(); assert(item instanceof Item); }); - it('should instantiate without new:', function () { + it('should instantiate without new:', function() { item = Item(); assert(item instanceof Item); }); - it('inspect should not double name `Stream` when ctor is `Stream`', function(done) { + it('inspect should not double name `Stream` when ctor is `Stream`', function(cb) { var val = new Stream(); var item = new Item({contents: val}); - done(); + cb(); }); }); - describe('static methods', function () { - it('should expose `extend`:', function () { + describe('static methods', function() { + it('should expose `extend`:', function() { assert(typeof Item.extend === 'function'); }); }); - describe('prototype methods', function () { - beforeEach(function () { + describe('prototype methods', function() { + beforeEach(function() { item = new Item(); }); - it('should expose `set`:', function () { + it('should expose `set`:', function() { assert(typeof item.set === 'function'); }); - it('should expose `get`:', function () { + it('should expose `get`:', function() { assert(typeof item.get === 'function'); }); - it('should expose `del`:', function () { + it('should expose `del`:', function() { assert(typeof item.del === 'function'); }); - it('should expose `define`:', function () { + it('should expose `define`:', function() { assert(typeof item.define === 'function'); }); - it('should expose `visit`:', function () { + it('should expose `visit`:', function() { assert(typeof item.visit === 'function'); }); }); - describe('properties', function () { - it('should expose an `options` property', function () { + describe('properties', function() { + it('should expose an `options` property', function() { item = new Item({}); assert.deepEqual(item.options, {}); assert(item.hasOwnProperty('options')); }); - it('should add `options` when passed on the constructor', function () { + it('should add `options` when passed on the constructor', function() { item = new Item({options: {foo: 'bar'}}); assert(item.options.foo === 'bar'); }); - it('should expose a `data` property', function () { + it('should expose a `data` property', function() { item = new Item({app: {}}); assert.deepEqual(item.data, {}); assert(item.hasOwnProperty('data')); }); - it('should add `data` when passed on the constructor', function () { + it('should add `data` when passed on the constructor', function() { item = new Item({data: {foo: 'bar'}}); assert(item.data.foo === 'bar'); }); - it('should add `locals` when passed on the constructor', function () { + it('should add `locals` when passed on the constructor', function() { item = new Item({locals: {foo: 'bar'}}); assert(item.locals.foo === 'bar'); }); }); - describe('set', function () { - it('should set properties on the object', function () { + describe('set', function() { + it('should set properties on the object', function() { item = new Item(); item.set('foo', 'bar'); assert.equal(item.foo, 'bar'); }); }); - describe('get', function () { - it('should get properties from the object', function () { + describe('get', function() { + it('should get properties from the object', function() { item = new Item(); item.set('foo', 'bar'); assert.equal(item.get('foo'), 'bar'); }); }); - describe('cwd', function () { - it('should get properties from the object', function () { + describe('cwd', function() { + it('should get properties from the object', function() { item = new Item({cwd: 'test/fixtures'}); assert(item.cwd === 'test/fixtures'); }); }); - describe('clone', function () { - it('should clone the item:', function () { + describe('clone', function() { + it('should clone the item:', function() { item = new Item({content: 'foo'}); item.set({path: 'foo/bar'}); item.set('options.one', 'two'); @@ -127,7 +129,7 @@ describe('Item', function () { assert(item.get('options.three') === 'four'); }); - it('should deep clone the entire object', function () { + it('should deep clone the entire object', function() { item = new Item({content: 'foo'}); item.set({path: 'foo/bar'}); item.set('options.one', 'two'); @@ -140,8 +142,8 @@ describe('Item', function () { }); }); - describe('visit', function () { - it('should visit all properties on an object and call the specified method', function () { + describe('visit', function() { + it('should visit all properties on an object and call the specified method', function() { item = new Item(); var obj = { foo: 'bar', @@ -154,7 +156,7 @@ describe('Item', function () { assert.equal(item.get('baz'), 'bang'); }); - it('should visit all properties on all objects in an array and call the specified method', function () { + it('should visit all properties on all objects in an array and call the specified method', function() { item = new Item(); var arr = [{foo: 'bar', bar: 'baz', baz: 'bang'}]; item.visit('set', arr); @@ -173,173 +175,173 @@ describe('Item', function () { describe('Item', function() { describe('isVinyl()', function() { - it('should return true on a vinyl object', function(done) { + it('should return true on a vinyl object', function(cb) { var item = new Item(); assert(Item.isVinyl(item) === true); - done(); + cb(); }); - it('should return false on a normal object', function(done) { + it('should return false on a normal object', function(cb) { assert(Item.isVinyl({}) === false); - done(); + cb(); }); - it('should return false on a null object', function(done) { + it('should return false on a null object', function(cb) { assert(Item.isVinyl({}) === false); - done(); + cb(); }); }); describe('constructor()', function() { - it('should default cwd to process.cwd', function(done) { + it('should default cwd to process.cwd', function(cb) { var item = new Item(); item.cwd.should.equal(process.cwd()); - done(); + cb(); }); - it('should default base to cwd', function(done) { + it('should default base to cwd', function(cb) { var cwd = '/'; var item = new Item({cwd: cwd}); item.base.should.equal(cwd); - done(); + cb(); }); - it('should default base to cwd even when none is given', function(done) { + it('should default base to cwd even when none is given', function(cb) { var item = new Item(); item.base.should.equal(process.cwd()); - done(); + cb(); }); - it('should default path to null', function(done) { + it('should default path to null', function(cb) { var item = new Item(); should.not.exist(item.path); - done(); + cb(); }); - it('should default history to []', function(done) { + it('should default history to []', function(cb) { var item = new Item(); item.history.should.eql([]); - done(); + cb(); }); - it('should default stat to null', function(done) { + it('should default stat to null', function(cb) { var item = new Item(); should.not.exist(item.stat); - done(); + cb(); }); - it('should default contents to null', function(done) { + it('should default contents to null', function(cb) { var item = new Item(); should.not.exist(item.contents); - done(); + cb(); }); - it('should set base to given value', function(done) { + it('should set base to given value', function(cb) { var val = '/'; var item = new Item({base: val}); item.base.should.equal(val); - done(); + cb(); }); - it('should set cwd to given value', function(done) { + it('should set cwd to given value', function(cb) { var val = '/'; var item = new Item({cwd: val}); item.cwd.should.equal(val); - done(); + cb(); }); - it('should set path to given value', function(done) { + it('should set path to given value', function(cb) { var val = '/test.coffee'; var item = new Item({path: val}); item.path.should.equal(val); item.history.should.eql([val]); - done(); + cb(); }); - it('should set history to given value', function(done) { + it('should set history to given value', function(cb) { var val = '/test.coffee'; var item = new Item({history: [val]}); item.path.should.equal(val); item.history.should.eql([val]); - done(); + cb(); }); - it('should set stat to given value', function(done) { + it('should set stat to given value', function(cb) { var val = {}; var item = new Item({stat: val}); item.stat.should.equal(val); - done(); + cb(); }); - it('should set contents to given value', function(done) { + it('should set contents to given value', function(cb) { var val = new Buffer('test'); var item = new Item({contents: val}); item.contents.should.equal(val); - done(); + cb(); }); }); describe('isBuffer()', function() { - it('should return true when the contents are a Buffer', function(done) { + it('should return true when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var item = new Item({contents: val}); item.isBuffer().should.equal(true); - done(); + cb(); }); - it('should return false when the contents are a Stream', function(done) { + it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var item = new Item({contents: val}); item.isBuffer().should.equal(false); - done(); + cb(); }); - it('should return false when the contents are a null', function(done) { + it('should return false when the contents are a null', function(cb) { var item = new Item({contents: null}); item.isBuffer().should.equal(false); - done(); + cb(); }); }); describe('isStream()', function() { - it('should return false when the contents are a Buffer', function(done) { + it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var item = new Item({contents: val}); item.isStream().should.equal(false); - done(); + cb(); }); - it('should return true when the contents are a Stream', function(done) { + it('should return true when the contents are a Stream', function(cb) { var val = new Stream(); var item = new Item({contents: val}); item.isStream().should.equal(true); - done(); + cb(); }); - it('should return false when the contents are a null', function(done) { + it('should return false when the contents are a null', function(cb) { var item = new Item({contents: null}); item.isStream().should.equal(false); - done(); + cb(); }); }); describe('isNull()', function() { - it('should return false when the contents are a Buffer', function(done) { + it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var item = new Item({contents: val}); item.isNull().should.equal(false); - done(); + cb(); }); - it('should return false when the contents are a Stream', function(done) { + it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var item = new Item({contents: val}); item.isNull().should.equal(false); - done(); + cb(); }); - it('should return true when the contents are a null', function(done) { + it('should return true when the contents are a null', function(cb) { var item = new Item({contents: null}); item.isNull().should.equal(true); - done(); + cb(); }); }); @@ -350,29 +352,29 @@ describe('Item', function() { } }; - it('should return false when the contents are a Buffer', function(done) { + it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var item = new Item({contents: val, stat: fakeStat}); item.isDirectory().should.equal(false); - done(); + cb(); }); - it('should return false when the contents are a Stream', function(done) { + it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var item = new Item({contents: val, stat: fakeStat}); item.isDirectory().should.equal(false); - done(); + cb(); }); - it('should return true when the contents are a null', function(done) { + it('should return true when the contents are a null', function(cb) { var item = new Item({contents: null, stat: fakeStat}); item.isDirectory().should.equal(true); - done(); + cb(); }); }); describe('clone()', function() { - it('should copy all attributes over with Buffer', function(done) { + it('should copy all attributes over with Buffer', function(cb) { var options = { cwd: '/', base: '/test/', @@ -388,10 +390,10 @@ describe('Item', function() { item2.path.should.equal(item.path); item2.contents.should.not.equal(item.contents, 'buffer ref should be different'); item2.contents.toString('utf8').should.equal(item.contents.toString('utf8')); - done(); + cb(); }); - it('should copy buffer\'s reference with option contents: false', function(done) { + it('should copy buffer\'s reference with option contents: false', function(cb) { var options = { cwd: '/', base: '/test/', @@ -410,10 +412,10 @@ describe('Item', function() { var copy3 = item.clone({ contents: 'any string' }); copy3.contents.should.not.equal(item.contents); - done(); + cb(); }); - it('should copy all attributes over with Stream', function(done) { + it('should copy all attributes over with Stream', function(cb) { var contents = new Stream.PassThrough(); var options = { cwd: '/', @@ -442,10 +444,10 @@ describe('Item', function() { data2.should.eql(data, 'stream contents should be the same'); })); })); - done(); + cb(); }); - it('should copy all attributes over with null', function(done) { + it('should copy all attributes over with null', function(cb) { var options = { cwd: '/', base: '/test/', @@ -460,10 +462,10 @@ describe('Item', function() { item2.base.should.equal(item.base); item2.path.should.equal(item.path); should.not.exist(item2.contents); - done(); + cb(); }); - it('should properly clone the `stat` property', function(done) { + it('should properly clone the `stat` property', function(cb) { var options = { cwd: '/', base: '/test/', @@ -477,10 +479,14 @@ describe('Item', function() { assert(copy.stat.isFile()); assert(!copy.stat.isDirectory()); - done(); + + assert(item.stat.hasOwnProperty('birthtime')); + assert(copy.stat.hasOwnProperty('birthtime')); + assert.deepEqual(item.stat, copy.stat); + cb(); }); - it('should properly clone the `history` property', function(done) { + it('should properly clone the `history` property', function(cb) { var options = { cwd: '/', base: '/test/', @@ -495,10 +501,10 @@ describe('Item', function() { copy.history[0].should.equal(options.path); copy.path = 'lol'; item.path.should.not.equal(copy.path); - done(); + cb(); }); - it('should copy custom properties', function(done) { + it('should copy custom properties', function(cb) { var options = { cwd: '/', base: '/test/', @@ -517,10 +523,10 @@ describe('Item', function() { item2.custom.should.equal(item.custom); item2.custom.a.should.equal(item.custom.a); - done(); + cb(); }); - it('should copy history', function(done) { + it('should copy history', function(cb) { var options = { cwd: '/', base: '/test/', @@ -545,10 +551,10 @@ describe('Item', function() { ]); item2.path.should.eql('/test/test-938di2s.js'); - done(); + cb(); }); - it('should copy all attributes deeply', function(done) { + it('should copy all attributes deeply', function(cb) { var options = { cwd: '/', base: '/test/', @@ -579,12 +585,12 @@ describe('Item', function() { item5.custom.should.equal(item.custom); item5.custom.a.should.equal(item.custom.a); - done(); + cb(); }); }); describe('pipe()', function() { - it('should write to stream with Buffer', function(done) { + it('should write to stream with Buffer', function(cb) { var options = { cwd: '/', base: '/test/', @@ -599,13 +605,13 @@ describe('Item', function() { chunk.toString('utf8').should.equal(options.contents.toString('utf8')); }); stream.on('end', function() { - done(); + cb(); }); var ret = item.pipe(stream); ret.should.equal(stream, 'should return the stream'); }); - it('should pipe to stream with Stream', function(done) { + it('should pipe to stream with Stream', function(cb) { var testChunk = new Buffer('test'); var options = { cwd: '/', @@ -619,7 +625,7 @@ describe('Item', function() { should.exist(chunk); (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - done(); + cb(); }); var ret = item.pipe(stream); ret.should.equal(stream, 'should return the stream'); @@ -627,7 +633,7 @@ describe('Item', function() { item.contents.write(testChunk); }); - it('should do nothing with null', function(done) { + it('should do nothing with null', function(cb) { var options = { cwd: '/', base: '/test/', @@ -640,13 +646,13 @@ describe('Item', function() { throw new Error('should not write'); }); stream.on('end', function() { - done(); + cb(); }); var ret = item.pipe(stream); ret.should.equal(stream, 'should return the stream'); }); - it('should write to stream with Buffer', function(done) { + it('should write to stream with Buffer', function(cb) { var options = { cwd: '/', base: '/test/', @@ -659,7 +665,7 @@ describe('Item', function() { should.exist(chunk); (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); chunk.toString('utf8').should.equal(options.contents.toString('utf8')); - done(); + cb(); }); stream.on('end', function() { throw new Error('should not end'); @@ -668,7 +674,7 @@ describe('Item', function() { ret.should.equal(stream, 'should return the stream'); }); - it('should pipe to stream with Stream', function(done) { + it('should pipe to stream with Stream', function(cb) { var testChunk = new Buffer('test'); var options = { cwd: '/', @@ -682,7 +688,7 @@ describe('Item', function() { should.exist(chunk); (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - done(); + cb(); }); stream.on('end', function() { throw new Error('should not end'); @@ -693,7 +699,7 @@ describe('Item', function() { item.contents.write(testChunk); }); - it('should do nothing with null', function(done) { + it('should do nothing with null', function(cb) { var options = { cwd: '/', base: '/test/', @@ -710,27 +716,27 @@ describe('Item', function() { }); var ret = item.pipe(stream, {end: false}); ret.should.equal(stream, 'should return the stream'); - process.nextTick(done); + process.nextTick(cb); }); }); describe('inspect()', function() { - it('should return correct format when no contents and no path', function(done) { + it('should return correct format when no contents and no path', function(cb) { var item = new Item(); item.inspect().should.equal(''); - done(); + cb(); }); - it('should return correct format when Buffer and no path', function(done) { + it('should return correct format when Buffer and no path', function(cb) { var val = new Buffer('test'); var item = new Item({ contents: val }); item.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when Buffer and relative path', function(done) { + it('should return correct format when Buffer and relative path', function(cb) { var val = new Buffer('test'); var item = new Item({ cwd: '/', @@ -739,10 +745,10 @@ describe('Item', function() { contents: val }); item.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when Buffer and only path and no base', function(done) { + it('should return correct format when Buffer and only path and no base', function(cb) { var val = new Buffer('test'); var item = new Item({ cwd: '/', @@ -751,10 +757,10 @@ describe('Item', function() { }); delete item.base; item.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when Stream and relative path', function(done) { + it('should return correct format when Stream and relative path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', @@ -762,10 +768,10 @@ describe('Item', function() { contents: new Stream.PassThrough() }); item.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when null and relative path', function(done) { + it('should return correct format when null and relative path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', @@ -773,56 +779,56 @@ describe('Item', function() { contents: null }); item.inspect().should.equal(''); - done(); + cb(); }); }); describe('contents get/set', function() { - it('should work with Buffer', function(done) { + it('should work with Buffer', function(cb) { var val = new Buffer('test'); var item = new Item(); item.contents = val; item.contents.should.equal(val); - done(); + cb(); }); - it('should work with Stream', function(done) { + it('should work with Stream', function(cb) { var val = new Stream.PassThrough(); var item = new Item(); item.contents = val; item.contents.should.equal(val); - done(); + cb(); }); - it('should work with null', function(done) { + it('should work with null', function(cb) { var val = null; var item = new Item(); item.contents = val; (item.contents === null).should.equal(true); - done(); + cb(); }); - it('should work with string', function(done) { + it('should work with string', function(cb) { var val = 'test'; var item = new Item(); item.contents = val; item.contents.should.deepEqual(new Buffer(val)); - done(); + cb(); }); }); describe('relative get/set', function() { - it('should error on set', function(done) { + it('should error on set', function(cb) { var item = new Item(); try { item.relative = 'test'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should error on get when no base', function(done) { + it('should error on get when no base', function(cb) { var a; var item = new Item(); delete item.base; @@ -830,74 +836,74 @@ describe('Item', function() { a = item.relative; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var item = new Item(); try { a = item.relative; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return a relative path from base', function(done) { + it('should return a relative path from base', function(cb) { var item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); item.relative.should.equal('test.coffee'); - done(); + cb(); }); - it('should return a relative path from cwd', function(done) { + it('should return a relative path from cwd', function(cb) { var item = new Item({ cwd: '/', path: '/test/test.coffee' }); - item.relative.should.equal(path.join('test','test.coffee')); - done(); + item.relative.should.equal(path.join('test', 'test.coffee')); + cb(); }); }); describe('dirname get/set', function() { - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var item = new Item(); try { a = item.dirname; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return the dirname of the path', function(done) { + it('should return the dirname of the path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); item.dirname.should.equal('/test'); - done(); + cb(); }); - it('should error on set when no path', function(done) { + it('should error on set when no path', function(cb) { var item = new Item(); try { item.dirname = '/test'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should set the dirname of the path', function(done) { + it('should set the dirname of the path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', @@ -905,43 +911,43 @@ describe('Item', function() { }); item.dirname = '/test/foo'; item.path.should.equal('/test/foo/test.coffee'); - done(); + cb(); }); }); describe('basename get/set', function() { - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var item = new Item(); try { a = item.basename; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return the basename of the path', function(done) { + it('should return the basename of the path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); item.basename.should.equal('test.coffee'); - done(); + cb(); }); - it('should error on set when no path', function(done) { + it('should error on set when no path', function(cb) { var item = new Item(); try { item.basename = 'test.coffee'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should set the basename of the path', function(done) { + it('should set the basename of the path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', @@ -949,43 +955,43 @@ describe('Item', function() { }); item.basename = 'foo.png'; item.path.should.equal('/test/foo.png'); - done(); + cb(); }); }); describe('extname get/set', function() { - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var item = new Item(); try { a = item.extname; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return the extname of the path', function(done) { + it('should return the extname of the path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); item.extname.should.equal('.coffee'); - done(); + cb(); }); - it('should error on set when no path', function(done) { + it('should error on set when no path', function(cb) { var item = new Item(); try { item.extname = '.coffee'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should set the extname of the path', function(done) { + it('should set the extname of the path', function(cb) { var item = new Item({ cwd: '/', base: '/test/', @@ -993,7 +999,7 @@ describe('Item', function() { }); item.extname = '.png'; item.path.should.equal('/test/test.png'); - done(); + cb(); }); }); diff --git a/test/layouts.js b/test/layouts.js index f079d32f..f563cc5a 100644 --- a/test/layouts.js +++ b/test/layouts.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,55 +7,55 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('layouts', function () { - beforeEach(function () { +describe('layouts', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('layout', { viewType: 'layout' }); app.create('page'); }); - it('should apply a layout to a view:', function (done) { + it('should apply a layout to a view:', function(cb) { app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a b c'); - done(); + cb(); }); }); - it('should not apply a layout when `layoutApplied` is set:', function (done) { + it('should not apply a layout when `layoutApplied` is set:', function(cb) { app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); var page = app.pages.getView('a.tmpl'); page.option('layoutApplied', true); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'b'); - done(); + cb(); }); }); - it('should not apply a layout to itself:', function (done) { + it('should not apply a layout to itself:', function(cb) { app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c', layout: 'base'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a b c'); - done(); + cb(); }); }); - it('should apply nested layouts to a view:', function (done) { + it('should apply nested layouts to a view:', function(cb) { app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); @@ -62,15 +64,15 @@ describe('layouts', function () { app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); var page = app.pages.getView('z.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'outter c b a inner a b c outter'); - done(); + cb(); }); }); - it('should track layout stack history on `layoutStack`:', function (done) { + it('should track layout stack history on `layoutStack`:', function(cb) { app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); @@ -79,16 +81,16 @@ describe('layouts', function () { app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); var page = app.pages.getView('z.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert(view.layoutStack.length === 4); assert(typeof view.layoutStack[0] === 'object'); assert(typeof view.layoutStack[0].depth === 'number'); - done(); + cb(); }); }); - it('should track layout stack history on `layoutStack`:', function (done) { + it('should track layout stack history on `layoutStack`:', function(cb) { app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); @@ -97,15 +99,15 @@ describe('layouts', function () { app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); var page = app.pages.getView('z.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'outter c b a inner a b c outter'); - done(); + cb(); }); }); - it('should get layouts from `layout` viewTypes:', function (done) { + it('should get layouts from `layout` viewTypes:', function(cb) { app.create('section', { viewType: 'layout' }); app.create('block', { viewType: 'layout' }); @@ -117,11 +119,11 @@ describe('layouts', function () { app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); var page = app.pages.getView('z.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'outter c b a inner a b c outter'); - done(); + cb(); }); }); }); diff --git a/test/list.deleteItem.js b/test/list.deleteItem.js new file mode 100644 index 00000000..7ec1e012 --- /dev/null +++ b/test/list.deleteItem.js @@ -0,0 +1,74 @@ +'use strict'; + +require('mocha'); +require('should'); +var path = require('path'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var list; + +describe('list.deleteItem', function() { + beforeEach(function() { + list = new List(); + }); + + it('should delete an item from `items` when the key is passed', function() { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + + var a = list.getItem('a'); + list.deleteItem(a); + assert(list.items.length === 2); + + var c = list.getItem('c'); + list.deleteItem(c); + assert(list.items.length === 1); + }); + + it('should delete an item when the item instance is passed', function() { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + + var a = list.getItem('a'); + list.deleteItem(a); + assert(list.keys.length === 2); + }); + + it('should delete the key from `keys` when an item is deleted', function() { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + + var a = list.getItem('a'); + list.deleteItem(a); + assert(list.keys.length === 2); + + list.deleteItem('c'); + assert(list.keys.length === 1); + + assert(list.items[0].key === 'b'); + }); + + it('should remove an item from `items` by key', function() { + list.addItem('a', {content: '...'}); + list.addItem('b', {content: '...'}); + list.addItem('c', {content: '...'}); + assert(list.items.length === 3); + list.deleteItem('c'); + assert(list.items.length === 2); + list.deleteItem('b'); + assert(list.items[0].key === 'a'); + }); + + it('should do nothing when the item does not exist', function() { + assert(list.deleteItem('slfjslslks')); + }); +}); + diff --git a/test/list.getIndex.js b/test/list.getIndex.js new file mode 100644 index 00000000..362b4177 --- /dev/null +++ b/test/list.getIndex.js @@ -0,0 +1,105 @@ +'use strict'; + +require('mocha'); +require('should'); +var path = require('path'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var List = App.List; +var list; + +describe('list.getIndex', function() { + beforeEach(function() { + list = new List(); + list.items = []; + }); + + it('should get the index of a key when key is not renamed', function() { + list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); + list.addItem('a/b/c/eee.hbs', {content: 'eee'}); + assert.equal(list.getIndex('a/b/c/ddd.hbs'), 0); + assert.equal(list.getIndex('a/b/c/eee.hbs'), 1); + }); + + it('should get the index by path', function() { + list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd'}); + assert.equal(list.getIndex('a/b/c/d.md'), 0); + }); + + it('should get the index by relative path', function() { + list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); + assert.equal(list.getIndex('c/d.md'), 0); + }); + + it('should get the index by stem', function() { + list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); + assert.equal(list.getIndex('d'), 0); + }); + + it('should get the index by basename', function() { + list.addItem('a/b/c/d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); + assert.equal(list.getIndex('d.md'), 0); + }); + + it('should get the index by key', function() { + list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); + list.getItem('d.md').key = 'foo'; + assert.equal(list.getIndex('foo'), 0); + }); + + it('should get the index of a key for dotfiles', function() { + list.addItem('.gitignore', {content: 'ddd'}); + assert.equal(list.getIndex('.gitignore'), 0); + }); + + it('should throw an error when argument is undefined', function(cb) { + try { + list.getIndex(); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'expected a string or instance of Item'); + cb(); + } + }); + + it('should return `-1` when the item is not found', function() { + assert.equal(list.getIndex('flflflfl'), -1); + }); + + it('should get the correct index for dotfiles', function() { + list.addItem('.DS_Store', {content: '...'}); + list.addItem('.gitignore', {content: 'ddd'}); + list.addItem('.zzz', {content: '...'}); + assert.equal(list.getIndex('.gitignore'), 1); + }); + + it('should get the correct index for dotfiles by their extensions', function() { + list.addItem('a/b/c/.DS_Store', {content: '...'}); + list.addItem('a/b/c/.gitignore', {content: 'ddd'}); + list.addItem('a/b/c/.zzz', {content: '...'}); + assert.equal(list.getIndex('.gitignore'), 1); + }); + + it('should get the correct index for dotfiles by their paths', function() { + list.addItem('a/b/c/.DS_Store', {content: '...'}); + list.addItem('a/b/c/.gitignore', {content: 'ddd'}); + list.addItem('a/b/c/.zzz', {content: '...'}); + assert.equal(list.getIndex('a/b/c/.gitignore'), 1); + }); + + it('should get the index of a key when key is renamed', function() { + list = new List({ + renameKey: function(key) { + return path.basename(key); + } + }); + list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); + list.addItem('a/b/c/eee.hbs', {content: 'eee'}); + assert.equal(list.getIndex('a/b/c/ddd.hbs'), 0); + assert.equal(list.getIndex('ddd.hbs'), 0); + assert.equal(list.getIndex('a/b/c/eee.hbs'), 1); + assert.equal(list.getIndex('eee.hbs'), 1); + }); +}); + diff --git a/test/list.js b/test/list.js index 45e30412..e601ade3 100644 --- a/test/list.js +++ b/test/list.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var path = require('path'); @@ -14,26 +16,26 @@ var List = App.List; var Views = App.Views; var list, views; -describe('list', function () { - describe('constructor', function () { - it('should create an instance of List', function () { +describe('list', function() { + describe('constructor', function() { + it('should create an instance of List', function() { var list = new List(); assert(list instanceof List); }); - it('should instaniate without `new`', function () { + it('should instaniate without `new`', function() { var list = List(); assert(list instanceof List); }); }); - describe('static methods', function () { - it('should expose `extend`', function () { - assert(typeof List.extend ==='function'); + describe('static methods', function() { + it('should expose `extend`', function() { + assert(typeof List.extend === 'function'); }); }); - describe('prototype methods', function () { + describe('prototype methods', function() { beforeEach(function() { list = new List(); }); @@ -59,67 +61,67 @@ describe('list', function () { 'hasListeners' ]; - methods.forEach(function (method) { - it('should expose the ' + method + ' method', function () { + methods.forEach(function(method) { + it('should expose the ' + method + ' method', function() { assert(typeof list[method] === 'function'); }); }); - it('should expose the isList property', function () { + it('should expose the isList property', function() { assert(typeof list.isList === 'boolean'); }); - it('should expose the keys property', function () { + it('should expose the keys property', function() { assert(Array.isArray(list.keys)); }); - it('should expose the queue property', function () { + it('should expose the queue property', function() { assert(Array.isArray(list.queue)); }); - it('should expose the items property', function () { + it('should expose the items property', function() { assert(Array.isArray(list.items)); }); - it('should expose the options property', function () { + it('should expose the options property', function() { assert(typeOf(list.options) === 'object'); }); }); - describe('instance', function () { + describe('instance', function() { beforeEach(function() { list = new List(); }); - it('should set a value on the instance', function () { + it('should set a value on the instance', function() { list.set('a', 'b'); - assert(list.a ==='b'); + assert(list.a === 'b'); }); - it('should get a value from the instance', function () { + it('should get a value from the instance', function() { list.set('a', 'b'); - assert(list.get('a') ==='b'); + assert(list.get('a') === 'b'); }); }); - describe('use', function () { + describe('use', function() { beforeEach(function() { list = new List(); }); - it('should expose the instance to plugins', function () { + it('should expose the instance to plugins', function() { list - .use(function (inst) { + .use(function(inst) { inst.foo = 'bar'; }); assert(list.foo === 'bar'); }); - it('should expose `item` when the plugin returns a function', function () { + it('should expose `item` when the plugin returns a function', function() { list - .use(function () { - return function (item) { + .use(function() { + return function(item) { item.foo = 'bar'; }; }); @@ -138,8 +140,8 @@ describe('list', function () { beforeEach(function() { list = new List(); }); - - it('should add items to a list', function () { + + it('should add items to a list', function() { list.addItem('a', {content: '...'}); list.addItem('b', {content: '...'}); list.addItem('c', {content: '...'}); @@ -152,7 +154,7 @@ describe('list', function () { list = new List(); }); - it('should remove an item from `items`', function () { + it('should remove an item from `items`', function() { list.addItem('a', {content: '...'}); list.addItem('b', {content: '...'}); list.addItem('c', {content: '...'}); @@ -165,7 +167,7 @@ describe('list', function () { assert(list.items[0].key === 'b'); }); - it('should remove an item from `items` by key', function () { + it('should remove an item from `items` by key', function() { list.addItem('a', {content: '...'}); list.addItem('b', {content: '...'}); list.addItem('c', {content: '...'}); @@ -182,7 +184,7 @@ describe('list', function () { list = new List(); }); - it('should add an object with multiple items', function () { + it('should add an object with multiple items', function() { list.addItems({ one: {content: 'foo'}, two: {content: 'bar'} @@ -191,8 +193,8 @@ describe('list', function () { assert(isBuffer(list.items[1].contents)); }); - it('should signal `loaded` when finished (addItems)', function () { - list.on('addItems', function (items) { + it('should signal `loaded` when finished (addItems)', function() { + list.on('addItems', function(items) { for (var key in items) { if (key === 'c') { list.loaded = true; @@ -214,12 +216,12 @@ describe('list', function () { }); }); - describe('addList', function () { + describe('addList', function() { beforeEach(function() { list = new List(); }); - it('should add an array with multiple items', function () { + it('should add an array with multiple items', function() { list.addList([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -228,7 +230,7 @@ describe('list', function () { assert(isBuffer(list.items[1].contents)); }); - it('should take a callback on `addList`', function () { + it('should take a callback on `addList`', function() { function addContents(item) { item.contents = new Buffer(item.path.charAt(0)); } @@ -244,12 +246,12 @@ describe('list', function () { assert(isBuffer(list.items[2].contents)); }); - it('should throw an error when the list is not an array', function () { + it('should throw an error when the list is not an array', function() { function addContents(item) { item.contents = new Buffer(item.path.charAt(0)); } - (function () { + (function() { list.addList({ 'a.md': {locals: { date: '2014-01-01', foo: 'zzz', bar: 1 }}, 'f.md': {locals: { date: '2014-01-01', foo: 'mmm', bar: 2 }}, @@ -262,8 +264,8 @@ describe('list', function () { }).should.throw('expected list to be an array.'); }); - it('should signal `loaded` when finished (addList)', function () { - list.on('addList', function (items) { + it('should signal `loaded` when finished (addList)', function() { + list.on('addList', function(items) { var len = items.length, i = -1; while (++i < len) { if (items[i].path === 'd.md') { @@ -285,25 +287,25 @@ describe('list', function () { }); }); - describe('queue', function () { - beforeEach(function () { + describe('queue', function() { + beforeEach(function() { list = new List(); }); - it('should emit arguments on addItem', function (done) { - list.on('addItem', function (args) { + it('should emit arguments on addItem', function(cb) { + list.on('addItem', function(args) { assert(args[0] === 'a'); assert(args[1] === 'b'); assert(args[2] === 'c'); assert(args[3] === 'd'); assert(args[4] === 'e'); - done(); + cb(); }); list.addItem('a', 'b', 'c', 'd', 'e'); }); - it('should expose the `queue` property for loading items', function () { + it('should expose the `queue` property for loading items', function() { list.queue.push(list.item('b', {path: 'b'})); list.addItem('a', {path: 'a'}); @@ -311,8 +313,8 @@ describe('list', function () { assert(list.items[1].key === 'b'); }); - it('should load all items on the queue when addItem is called', function () { - list.on('addItem', function (args) { + it('should load all items on the queue when addItem is called', function() { + list.on('addItem', function(args) { var len = args.length; var last = args[len - 1]; if (typeof last === 'string') { @@ -350,12 +352,12 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should sort a list', function () { + it('should sort a list', function() { list = new List(); list.addList(items); var compare = function(prop) { - return function (a, b, fn) { + return function(a, b, fn) { var valA = get(a, prop); var valB = get(b, prop); return fn(valA, valB); @@ -384,12 +386,12 @@ describe('list', function () { ]); }); - it('should not sort the (original) instance list `items`', function () { + it('should not sort the (original) instance list `items`', function() { list = new List(); list.addList(items); var compare = function(prop) { - return function (a, b, fn) { + return function(a, b, fn) { var valA = get(a, prop); var valB = get(b, prop); return fn(valA, valB); @@ -422,12 +424,12 @@ describe('list', function () { ]); }); - it('should pass options to array-sort from the constructor', function () { + it('should pass options to array-sort from the constructor', function() { list = new List({sort: {reverse: true}}); list.addList(items); var compare = function(prop) { - return function (a, b, fn) { + return function(a, b, fn) { var valA = get(a, prop); var valB = get(b, prop); return fn(valA, valB); @@ -456,12 +458,12 @@ describe('list', function () { ]); }); - it('should pass options to array-sort from the sortBy method', function () { + it('should pass options to array-sort from the sortBy method', function() { list = new List(); list.addList(items); var compare = function(prop) { - return function (a, b, fn) { + return function(a, b, fn) { var valA = get(a, prop); var valB = get(b, prop); return fn(valA, valB); @@ -508,7 +510,7 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should group a list by a property', function () { + it('should group a list by a property', function() { list = new List(); list.addList(items); @@ -539,12 +541,12 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should group a list by a property', function () { + it('should group a list by a property', function() { list = new List(items); var context = list .sortBy('locals.date') - .groupBy(function (view) { + .groupBy(function(view) { var date = view.locals.date; view.locals.year = date.slice(0, 4); view.locals.month = date.slice(5, 7); @@ -577,7 +579,7 @@ describe('list', function () { { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, ]; - it('should paginate a list', function () { + it('should paginate a list', function() { list = new List(items); var res = list.paginate(); @@ -586,15 +588,15 @@ describe('list', function () { assert.containEql(res[1].items, items.slice(10)); }); - it('should add pager properties', function () { + it('should add pager properties', function() { list = new List({pager: true}); list.addList(items); - list.items.forEach(function (item, i) { + list.items.forEach(function(item, i) { assert.equal(item.data.pager.index, i); }); }); - it('should paginate a list with given options', function () { + it('should paginate a list with given options', function() { list = new List(items); var res = list.paginate({limit: 5}); @@ -610,7 +612,7 @@ describe('list', function () { views = new Views(); }); - it('should add views from an instance of Views', function () { + it('should add views from an instance of Views', function() { views.addViews({ one: {content: 'foo'}, two: {content: 'bar'} @@ -626,16 +628,16 @@ describe('list', function () { beforeEach(function() { list = new List(); }); - it('should get the index of a key when key is not renamed', function () { + it('should get the index of a key when key is not renamed', function() { list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); list.addItem('a/b/c/eee.hbs', {content: 'eee'}); assert(list.getIndex('a/b/c/ddd.hbs') === 0); assert(list.getIndex('a/b/c/eee.hbs') === 1); }); - it('should get the index of a key when key is renamed', function () { + it('should get the index of a key when key is renamed', function() { list = new List({ - renameKey: function (key) { + renameKey: function(key) { return path.basename(key); } }); @@ -653,7 +655,7 @@ describe('list', function () { list = new List(); }); - it('should get an view from `views`', function () { + it('should get an view from `views`', function() { list.addItem('one', {content: 'aaa'}); list.addItem('two', {content: 'zzz'}); assert(list.items.length === 2); @@ -669,15 +671,15 @@ describe('list', function () { list = new List(); }); - it('should use middleware on a list', function () { + it('should use middleware on a list', function() { list.addItem('one', {content: 'aaa'}); list.addItem('two', {content: 'zzz'}); list - .use(function () { + .use(function() { this.set('foo', 'bar'); }) - .use(function () { + .use(function() { this.set('one', 'two'); }); diff --git a/test/list.render.js b/test/list.render.js index b660836c..f29b848a 100644 --- a/test/list.render.js +++ b/test/list.render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var async = require('async'); @@ -7,96 +9,96 @@ var App = support.resolve(); var List = App.List; var pages; -describe('render', function () { - describe('rendering', function () { - beforeEach(function () { +describe('render', function() { + describe('rendering', function() { + beforeEach(function() { pages = new List(); pages.engine('tmpl', require('engine-base')); }); - it('should throw an error when no callback is given:', function () { + it('should throw an error when no callback is given:', function() { (function() { pages.render({}); }).should.throw('List#render is async and expects a callback function'); }); - it('should throw an error when an engine is not defined:', function (done) { + it('should throw an error when an engine is not defined:', function(cb) { pages.addItem('foo.bar', {content: '<%= name %>'}); var page = pages.getItem('foo.bar'); pages.render(page, function(err) { assert(err.message === 'List#render cannot find an engine for: .bar'); - done(); + cb(); }); }); - it('should use helpers to render a item:', function (done) { + it('should use helpers to render a item:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function (str) { + pages.helper('upper', function(str) { return str.toUpperCase(str); }); pages.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getItem('a.tmpl'); - pages.render(page, function (err, res) { - if (err) return done(err); + pages.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); - it('should use helpers when rendering a item:', function (done) { + it('should use helpers when rendering a item:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function (str) { + pages.helper('upper', function(str) { return str.toUpperCase(str); }); pages.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getItem('a.tmpl'); - pages.render(page, function (err, res) { - if (err) return done(err); + pages.render(page, function(err, res) { + if (err) return cb(err); assert(res.content === 'a HALLE b'); - done(); + cb(); }); }); - it('should render a template when contents is a buffer:', function (done) { + it('should render a template when contents is a buffer:', function(cb) { pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); var item = pages.getItem('a.tmpl'); - pages.render(item, function (err, item) { - if (err) return done(err); + pages.render(item, function(err, item) { + if (err) return cb(err); assert(item.contents.toString() === 'b'); - done(); + cb(); }); }); - it('should render a template when content is a string:', function (done) { + it('should render a template when content is a string:', function(cb) { pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); var item = pages.getItem('a.tmpl'); - pages.render(item, function (err, item) { - if (err) return done(err); + pages.render(item, function(err, item) { + if (err) return cb(err); assert(item.contents.toString() === 'b'); - done(); + cb(); }); }); - it('should render a item from its path:', function (done) { + it('should render a item from its path:', function(cb) { pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - pages.render('a.tmpl', function (err, item) { - if (err) return done(err); + pages.render('a.tmpl', function(err, item) { + if (err) return cb(err); assert(item.content === 'b'); - done(); + cb(); }); }); - it('should use a plugin for rendering:', function (done) { + it('should use a plugin for rendering:', function(cb) { pages.engine('tmpl', require('engine-base')); pages.option('engine', 'tmpl'); @@ -113,24 +115,24 @@ describe('render', function () { 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, }); - pages.use(function (collection) { + pages.use(function(collection) { collection.option('pager', false); - collection.renderEach = function (cb) { + collection.renderEach = function(cb) { var list = new List(collection); - async.map(list.items, function (item, next) { + async.map(list.items, function(item, next) { collection.render(item, next); }, cb); }; }); - pages.renderEach(function (err, items) { - if (err) return done(err); + pages.renderEach(function(err, items) { + if (err) return cb(err); assert(items[0].content === 'aaa'); assert(items[9].content === 'jjj'); assert(items.length === 10); - done(); + cb(); }); }); }); diff --git a/test/list.use.js b/test/list.use.js index c192a009..e3257b84 100644 --- a/test/list.use.js +++ b/test/list.use.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -7,33 +9,33 @@ var List = App.List; var Item = App.Item; var list; -describe('list.use', function () { - beforeEach(function () { +describe('list.use', function() { + beforeEach(function() { list = new List(); }); - it('should expose the instance to `use`:', function (done) { - list.use(function (inst) { + it('should expose the instance to `use`:', function(cb) { + list.use(function(inst) { assert(inst instanceof List); - done(); + cb(); }); }); - it('should be chainable:', function (done) { - list.use(function (inst) { - assert(inst instanceof List); - }) - .use(function (inst) { + it('should be chainable:', function(cb) { + list.use(function(inst) { + assert(inst instanceof List); + }) + .use(function(inst) { assert(inst instanceof List); }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof List); - done(); + cb(); }); }); - it('should expose the list to a plugin:', function () { - list.use(function (items) { + it('should expose the list to a plugin:', function() { + list.use(function(items) { assert(items instanceof List); items.foo = items.addItem.bind(items); }); @@ -42,17 +44,17 @@ describe('list.use', function () { assert(list.hasItem('a')); }); - it('should expose list when chained:', function () { + it('should expose list when chained:', function() { list - .use(function (items) { + .use(function(items) { assert(items instanceof List); items.foo = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof List); items.bar = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof List); items.baz = items.addItem.bind(items); }); @@ -68,18 +70,18 @@ describe('list.use', function () { assert(list.hasItem('c')); }); - it('should work when a custom `Item` constructor is passed:', function () { + it('should work when a custom `Item` constructor is passed:', function() { list = new List({Item: require('vinyl')}); list - .use(function (items) { + .use(function(items) { assert(items instanceof List); items.foo = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof List); items.bar = items.addItem.bind(items); }) - .use(function (items) { + .use(function(items) { assert(items instanceof List); items.baz = items.addItem.bind(items); }); @@ -95,11 +97,11 @@ describe('list.use', function () { assert(list.hasItem('c')); }); - it('should pass to item `use` if a function is returned:', function () { - list.use(function (items) { + it('should pass to item `use` if a function is returned:', function() { + list.use(function(items) { assert(items instanceof List); - return function (item) { + return function(item) { item.foo = items.addItem.bind(items); assert(item.isItem || item.isView); }; @@ -116,28 +118,28 @@ describe('list.use', function () { assert(list.hasItem('d')); }); - it('should be chainable when a item function is returned:', function () { + it('should be chainable when a item function is returned:', function() { list - .use(function (items) { + .use(function(items) { assert(items instanceof List); - return function (item) { + return function(item) { item.foo = items.addItem.bind(items); assert(item instanceof Item); }; }) - .use(function (items) { + .use(function(items) { assert(items instanceof List); - return function (item) { + return function(item) { item.bar = items.addItem.bind(items); assert(item instanceof Item); }; }) - .use(function (items) { + .use(function(items) { assert(items instanceof List); - return function (item) { + return function(item) { item.baz = items.addItem.bind(items); assert(item instanceof Item); }; diff --git a/test/mergePartials.js b/test/mergePartials.js index 0145e071..2874e2f4 100644 --- a/test/mergePartials.js +++ b/test/mergePartials.js @@ -1,14 +1,18 @@ +'use strict'; + require('should'); var support = require('./support'); var App = support.resolve(); var app; -describe('mergePartials', function () { - beforeEach(function () { +describe('mergePartials', function() { + beforeEach(function() { app = new App(); + // reset views + app.views = {}; }); - it('should merge multiple partials collections onto one collection:', function () { + it('should merge multiple partials collections onto one collection:', function() { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); @@ -23,7 +27,7 @@ describe('mergePartials', function () { actual.partials.should.have.properties(['a', 'b', 'c']); }); - it('should keep partials collections on separate collections:', function () { + it('should keep partials collections on separaet collections:', function() { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); @@ -38,14 +42,14 @@ describe('mergePartials', function () { actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); }); - it('should emit `mergePartials`:', function () { + it('should emit `mergePartials`:', function() { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); app.create('baz', opts); var arr = []; - app.on('onMerge', function (view) { + app.on('onMerge', function(view) { arr.push(view.content); }); @@ -59,13 +63,13 @@ describe('mergePartials', function () { arr.should.eql(['aaa', 'bbb', 'ccc']); }); - it('should handle `onMerge` middleware:', function () { + it('should handle `onMerge` middleware:', function() { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); app.create('baz', opts); - app.onMerge(/./, function (view, next) { + app.onMerge(/./, function(view, next) { view.content += ' onMerge'; next(); }); @@ -82,14 +86,14 @@ describe('mergePartials', function () { }); }); - it('should skip views with `nomerge=true`:', function () { + it('should skip views with `nomerge=true`:', function() { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); app.create('baz', opts); - app.onMerge(/[ab]/, function (view, next) { + app.onMerge(/[ab]/, function(view, next) { view.options.nomerge = true; next(); }); diff --git a/test/partials.js b/test/partials.js index 575e7b54..72b719d8 100644 --- a/test/partials.js +++ b/test/partials.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,8 +7,8 @@ var support = require('./support'); var App = support.resolve(); var app, pages; -describe('partials', function () { - beforeEach(function () { +describe('partials', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.engine('hbs', require('engine-handlebars')); @@ -17,20 +19,20 @@ describe('partials', function () { pages = app.create('page'); }); - it('should inject a partial with a helper:', function (done) { + it('should inject a partial with a helper:', function(cb) { app.include('base', {path: 'base.tmpl', content: 'xyz'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'a <%= include("base") %> c'}); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a xyz c'); - done(); + cb(); }); }); - it('should inject a partial with a helper on a collection:', function (done) { + it('should inject a partial with a helper on a collection:', function(cb) { app.include('base', {path: 'base.tmpl', content: 'xyz'}); pages.engine('.tmpl', require('engine-handlebars')); pages.helpers(app._.helpers.sync); @@ -38,15 +40,15 @@ describe('partials', function () { pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{include "base" }} c'}); var page = pages.getView('a.tmpl'); - pages.render(page, function (err, view) { - if (err) return done(err); + pages.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a xyz c'); - done(); + cb(); }); }); - it('should use handlebars partial with a helper on a collection:', function (done) { + it('should use handlebars partial with a helper on a collection:', function(cb) { app.include('base', {path: 'base.tmpl', content: 'xyz'}); pages.engine('.tmpl', require('engine-handlebars')); pages.helpers(app._.helpers.sync); @@ -56,45 +58,45 @@ describe('partials', function () { var page = pages.getView('a.tmpl'); var locals = app.mergePartials(this.options); - pages.render(page, locals, function (err, view) { - if (err) return done(err); + pages.render(page, locals, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a xyz c'); - done(); + cb(); }); }); - it('should use layouts with partials:', function (done) { + it('should use layouts with partials:', function(cb) { app.layout('default', {path: 'a.tmpl', content: 'a {% body %} c'}); app.include('b', {path: 'b.tmpl', content: 'b', layout: 'default'}); app.pages('a.tmpl', {path: 'c.tmpl', content: '<%= include("b") %>'}); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a b c'); - done(); + cb(); }); }); - it('should add `layoutApplied` after layout is applied:', function (done) { + it('should add `layoutApplied` after layout is applied:', function(cb) { app.layout('default', {path: 'a.tmpl', content: 'a {% body %} c'}); app.include('b', {path: 'b.tmpl', content: 'b', layout: 'default'}); app.pages('a.tmpl', {path: 'c.tmpl', content: '<%= include("b") %>'}); var page = app.pages.getView('a.tmpl'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(app.layouts.getView('default').options.layoutApplied); assert.equal(view.content, 'a b c'); - done(); + cb(); }); }); - it('should pass partials to handlebars:', function (done) { - app.onMerge(/\.hbs$/, function (view, next) { + it('should pass partials to handlebars:', function(cb) { + app.onMerge(/\.hbs$/, function(view, next) { app.applyLayout(view); next(); }); @@ -104,16 +106,16 @@ describe('partials', function () { app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); var page = app.pages.getView('a.hbs'); - app.render(page, function (err, view) { - if (err) return done(err); + app.render(page, function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a foo c'); - done(); + cb(); }); }); - it('should only merge in the specified viewTypes:', function (done) { - app.onMerge(/\.hbs$/, function (view, next) { + it('should only merge in the specified viewTypes:', function(cb) { + app.onMerge(/\.hbs$/, function(view, next) { app.applyLayout(view); next(); }); @@ -126,17 +128,17 @@ describe('partials', function () { app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); app.pages.getView('a.hbs') - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a foo c'); - done(); + cb(); }); }); - it('should merge the specified viewTypes in the order defined:', function (done) { - app.onMerge(/\.hbs$/, function (view, next) { + it('should merge the specified viewTypes in the order defined:', function(cb) { + app.onMerge(/\.hbs$/, function(view, next) { app.applyLayout(view); next(); }); @@ -149,16 +151,16 @@ describe('partials', function () { app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); app.pages.getView('a.hbs') - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a bar c'); - done(); + cb(); }); }); - it('should not merge in partials with `options.nomerge` defined:', function (done) { - app.onMerge(/\.hbs$/, function (view, next) { + it('should not merge in partials with `options.nomerge` defined:', function(cb) { + app.onMerge(/\.hbs$/, function(view, next) { app.applyLayout(view); next(); }); @@ -171,16 +173,16 @@ describe('partials', function () { app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); app.pages.getView('a.hbs') - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a foo c'); - done(); + cb(); }); }); - it('should emit an `onMerge` event:', function (done) { - app.on('onMerge', function (view) { + it('should emit an `onMerge` event:', function(cb) { + app.on('onMerge', function(view) { app.applyLayout(view); }); @@ -192,11 +194,11 @@ describe('partials', function () { app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); app.pages.getView('a.hbs') - .render(function (err, view) { - if (err) return done(err); + .render(function(err, view) { + if (err) return cb(err); assert.equal(typeof view.content, 'string'); assert.equal(view.content, 'a bar c'); - done(); + cb(); }); }); }); diff --git a/test/questions.js b/test/questions.js index cd336493..ac9272a8 100644 --- a/test/questions.js +++ b/test/questions.js @@ -63,7 +63,9 @@ describe('content', function() { it('should ask a question and use config value to answer:', function(cb) { app.question('a', 'b'); - app.config.process({data: {a: 'zzz'}}); + app.config.process({data: {a: 'zzz'}}, function(err) { + if (err) cb(err); + }); app.ask('a', function(err, answer) { assert(!err); @@ -75,7 +77,9 @@ describe('content', function() { it('should prefer data from config over store.data', function(cb) { app.question('a', 'b'); - app.config.process({data: {a: 'zzz'}}); + app.config.process({data: {a: 'zzz'}}, function(err) { + if (err) cb(err); + }); app.store.set('a', 'c'); app.ask('a', function(err, answer) { diff --git a/test/renameKey.js b/test/renameKey.js index 9662f039..801c1c29 100644 --- a/test/renameKey.js +++ b/test/renameKey.js @@ -1,3 +1,5 @@ +'use strict'; + var path = require('path'); var support = require('./support'); var App = support.resolve(); @@ -7,16 +9,16 @@ function renameKey(key) { return path.basename(key, path.extname(key)); } -describe('renameKey', function () { - beforeEach(function () { +describe('renameKey', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('pages'); app.create('posts'); }); - describe('global options:', function () { - it('should use `renameKey` function defined on global opts:', function () { + describe('global options:', function() { + it('should use `renameKey` function defined on global opts:', function() { app.option('renameKey', renameKey); app.posts('a/b/c/a.txt', {content: '...'}); @@ -32,7 +34,7 @@ describe('renameKey', function () { app.views.posts.should.have.property('e'); }); - it('should not have conflicts when view name is the collection name:', function () { + it('should not have conflicts when view name is the collection name:', function() { app.option('renameKey', renameKey); app.post('a/b/c/post.txt', {content: 'this is contents'}); @@ -43,10 +45,10 @@ describe('renameKey', function () { }); }); - describe('create method:', function () { - it('should use `renameKey` option chained from the `create` method:', function () { + describe('create method:', function() { + it('should use `renameKey` option chained from the `create` method:', function() { app.create('post') - .option('renameKey', function (key) { + .option('renameKey', function(key) { return 'posts/' + path.basename(key); }); @@ -64,10 +66,10 @@ describe('renameKey', function () { }); }); - describe('create method:', function () { - it('should use `renameKey` defined on the `create` method:', function () { + describe('create method:', function() { + it('should use `renameKey` defined on the `create` method:', function() { app.create('post', { - renameKey: function (key) { + renameKey: function(key) { return 'posts/' + path.basename(key); } }); @@ -86,10 +88,10 @@ describe('renameKey', function () { }); }); - describe('collections:', function () { - describe('setting:', function () { - it('should get a view with the `renameKey` defined on app.options:', function () { - app.option('renameKey', function (key) { + describe('collections:', function() { + describe('setting:', function() { + it('should get a view with the `renameKey` defined on app.options:', function() { + app.option('renameKey', function(key) { return 'foo/' + path.basename(key); }); @@ -102,12 +104,12 @@ describe('renameKey', function () { app.views.posts.should.have.property('foo/c.txt'); }); - it('should use `renameKey` defined on collection.options:', function () { - app.pages.option('renameKey', function (key) { + it('should use `renameKey` defined on collection.options:', function() { + app.pages.option('renameKey', function(key) { return 'page/' + path.basename(key); }); - app.posts.option('renameKey', function (key) { + app.posts.option('renameKey', function(key) { return 'post/' + path.basename(key); }); @@ -136,8 +138,8 @@ describe('renameKey', function () { app.views.posts.should.have.property('post/e.txt'); }); - it('should use the `collection.renameKey()` method:', function () { - app.pages.renameKey(function (key) { + it('should use the `collection.renameKey()` method:', function() { + app.pages.renameKey(function(key) { return 'baz/' + path.basename(key); }); @@ -154,8 +156,8 @@ describe('renameKey', function () { app.views.pages.should.have.property('baz/e.txt'); }); - it('should use the `app.renameKey()` method:', function () { - app.renameKey(function (key) { + it('should use the `app.renameKey()` method:', function() { + app.renameKey(function(key) { return 'app/' + path.basename(key); }); @@ -172,7 +174,7 @@ describe('renameKey', function () { app.views.pages.should.have.property('app/e.txt'); }); - it('should prefer collection method over app.options:', function () { + it('should prefer collection method over app.options:', function() { // this works when you switch the order around... app.pages.renameKey(function pagesRenameKey(key) { return 'aaa/' + path.basename(key); @@ -194,11 +196,11 @@ describe('renameKey', function () { app.views.pages.should.have.property('aaa/e.txt'); }); - it('should prefer collection method over app method:', function () { - app.pages.renameKey(function (key) { + it('should prefer collection method over app method:', function() { + app.pages.renameKey(function(key) { return 'aaa/' + path.basename(key); }); - app.renameKey(function (key) { + app.renameKey(function(key) { return 'zzz/' + path.basename(key); }); @@ -215,11 +217,11 @@ describe('renameKey', function () { app.views.pages.should.have.property('aaa/e.txt'); }); - it('should prefer collection options over app.options:', function () { - app.pages.option('renameKey', function (key) { + it('should prefer collection options over app.options:', function() { + app.pages.option('renameKey', function(key) { return 'collection/' + path.basename(key); }); - app.option('renameKey', function (key) { + app.option('renameKey', function(key) { return 'app/' + path.basename(key); }); @@ -236,11 +238,11 @@ describe('renameKey', function () { app.views.pages.should.have.property('collection/e.txt'); }); - it('should prefer collection options over app method:', function () { - app.pages.option('renameKey', function (key) { + it('should prefer collection options over app method:', function() { + app.pages.option('renameKey', function(key) { return 'collection/' + path.basename(key); }); - app.renameKey(function (key) { + app.renameKey(function(key) { return 'app/' + path.basename(key); }); @@ -257,7 +259,7 @@ describe('renameKey', function () { app.views.pages.should.have.property('collection/e.txt'); }); - it('should use renameKey on chained methods:', function () { + it('should use renameKey on chained methods:', function() { app.page('test/fixtures/pages/a.txt', { options: { renameKey: function foo(key) { @@ -281,15 +283,15 @@ describe('renameKey', function () { }); }); - describe('getting', function () { - beforeEach(function () { + describe('getting', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('post'); app.create('page'); }); - it('should get a view with the `renameKey` defined on the `create` method:', function () { + it('should get a view with the `renameKey` defined on the `create` method:', function() { app.create('post', { renameKey: function createRenameKey(key) { return 'posts/' + path.basename(key); @@ -304,8 +306,8 @@ describe('renameKey', function () { app.posts.getView('posts/a.txt').should.have.property('path', 'a/b/c/a.txt'); }); - it('should get a view with `renameKey` on collection.options:', function () { - app.pages.option('renameKey', function (key) { + it('should get a view with `renameKey` on collection.options:', function() { + app.pages.option('renameKey', function(key) { return 'bar/' + path.basename(key); }); @@ -318,8 +320,8 @@ describe('renameKey', function () { app.views.pages.should.have.property('bar/c.txt'); }); - it('should get a view with the the `app.renameKey()` method:', function () { - app.renameKey(function (key) { + it('should get a view with the the `app.renameKey()` method:', function() { + app.renameKey(function(key) { return 'baz/' + path.basename(key); }); @@ -332,8 +334,8 @@ describe('renameKey', function () { app.views.pages.should.have.property('baz/c.txt'); }); - it('should get a view with the the `collection.renameKey()` method:', function () { - app.pages.renameKey(function (key) { + it('should get a view with the the `collection.renameKey()` method:', function() { + app.pages.renameKey(function(key) { return 'baz/' + path.basename(key); }); diff --git a/test/render.js b/test/render.js index d03eedc3..a4dd2da4 100644 --- a/test/render.js +++ b/test/render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -5,70 +7,65 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('render', function () { - describe('engine', function () { +describe('render', function() { + describe('engine', function() { var view; - beforeEach(function () { + beforeEach(function() { app = new App({silent: true}); app.engine('tmpl', require('engine-base')); app.create('page'); view = {contents: new Buffer('a <%= name %> b'), locals: {name: 'Halle'}}; }); - it('should render a view from an object:', function (done) { + it('should render a view from an object:', function(cb) { app.page('a.tmpl', view) - .render(function (err, res) { - if (err) return done(err); + .render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a Halle b'); - done(); + cb(); }); }); - it('should throw an error when a variable is undefined:', function (done) { - delete app.cache.data.name; - delete view.locals.name; + it('should throw an error when a variable is undefined:', function(cb) { + view = {contents: new Buffer('a <%= foo %> b')}; app.page('a.tmpl', view) - .render(function (err) { - assert(err.message === 'name is not defined'); - done(); + .render(function(err) { + assert(err.message === 'foo is not defined'); + cb(); }); }); - it('should re-throw an error when rethrow is true:', function (done) { - app = new App({rethrow: true, silent: true}); - - delete app.cache.data.name; - delete view.locals.name; + it('should re-throw an error when rethrow is true:', function(cb) { + view = {contents: new Buffer('a <%= foo %> b')}; + app = new App({rethrow: true, silent: true}); app.engine('tmpl', require('engine-base')); app.create('page'); app.page('a.tmpl', view) - .render(function (err) { - assert(err.message === 'name is not defined'); - done(); + .render(function(err) { + assert(err.message === 'foo is not defined'); + cb(); }); }); - it('should emit a re-thrown error when rethrow is true:', function (done) { - app = new App({rethrow: true, silent: false}); - - delete app.cache.data.name; - delete view.locals.name; + it('should emit a re-thrown error when rethrow is true:', function(cb) { + view = {contents: new Buffer('a <%= foo %> b')}; + app = new App({rethrow: true, silent: false}); app.engine('tmpl', require('engine-base')); app.create('page'); app.on('error', function(err) { - assert(err.message === 'name is not defined'); - done(); + assert(err.message === 'foo is not defined'); + cb(); }); app.page('a.tmpl', view) - .render(function (err) { - assert(err.message === 'name is not defined'); + .render(function(err) { + assert(err.message === 'foo is not defined'); }); }); }); diff --git a/test/routes.js b/test/routes.js index fed137f5..dc45fa7d 100644 --- a/test/routes.js +++ b/test/routes.js @@ -1,3 +1,5 @@ +'use strict'; + require('should'); var assert = require('assert'); var support = require('./support'); @@ -19,15 +21,15 @@ function prepend(str) { }; } -describe('routes', function () { - beforeEach(function () { +describe('routes', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('page'); }); - describe('params', function () { - it('should call param function when routing', function(done) { + describe('params', function() { + it('should call param function when routing', function(cb) { app.param('id', function(view, next, id) { assert.equal(id, '123'); next(); @@ -38,12 +40,12 @@ describe('routes', function () { next(); }); - app.router.handle({ path: '/foo/123/bar' }, done); + app.router.handle({ path: '/foo/123/bar' }, cb); }); }); - describe('onLoad middleware', function () { - it('should run when templates are loaded:', function () { + describe('onLoad middleware', function() { + it('should run when templates are loaded:', function() { app.onLoad(/\.tmpl/, prepend('onLoad')); app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>'}); @@ -52,46 +54,46 @@ describe('routes', function () { }); }); - describe('preCompile middleware', function () { - it('should run before templates are compiled:', function () { + describe('preCompile middleware', function() { + it('should run before templates are compiled:', function() { }); }); - describe('postCompile middleware', function () { - it('should run after templates are compiled:', function () { + describe('postCompile middleware', function() { + it('should run after templates are compiled:', function() { }); }); - describe('preRender middleware', function () { - it('should run before templates are rendered:', function (done) { + describe('preRender middleware', function() { + it('should run before templates are rendered:', function(cb) { app.preRender(/\.tmpl/, prepend('preRender')); app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); var page = app.pages.getView('a.tmpl'); page.contents.toString().should.equal('<%= name %>'); - page.render({}, function (err, res) { - if (err) return done(err); + page.render({}, function(err, res) { + if (err) return cb(err); res.contents.toString().should.equal('preRender aaa'); - done(); + cb(); }); }); }); - describe('postRender middleware', function () { - it('should run after templates are rendered:', function (done) { + describe('postRender middleware', function() { + it('should run after templates are rendered:', function(cb) { app.postRender(/\.tmpl/, append('postRender')); app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); var page = app.pages.getView('a.tmpl'); page.contents.toString().should.equal('<%= name %>'); - page.render({}, function (err, res) { - if (err) return done(err); + page.render({}, function(err, res) { + if (err) return cb(err); res.contents.toString().should.equal('aaa postRender'); - done(); + cb(); }); }); }); diff --git a/test/store.js b/test/store.js index c54b0fc5..260f2bff 100644 --- a/test/store.js +++ b/test/store.js @@ -9,17 +9,17 @@ var assert = require('assert'); var App = require('../'); var app; -describe('store', function () { - beforeEach(function () { +describe('store', function() { + beforeEach(function() { app = new App(); }); - afterEach(function (cb) { + afterEach(function(cb) { app.store.del({force: true}); cb(); }); - it('should create a store at the given `cwd`', function () { + it('should create a store at the given `cwd`', function() { app = new App({store: {cwd: __dirname + '/actual'}}); app.store.set('foo', 'bar'); assert(path.basename(app.store.path) === 'verb.json'); @@ -28,35 +28,35 @@ describe('store', function () { assert(fs.existsSync(path.join(__dirname, 'actual', 'verb.json'))); }); - it('should create a store using the given `indent` value', function () { + it('should create a store using the given `indent` value', function() { app = new App({store: {cwd: __dirname + '/actual', indent: 0}}); app.store.set('foo', 'bar'); var contents = fs.readFileSync(path.join(__dirname, 'actual', 'verb.json'), 'utf8'); assert(contents === '{"foo":"bar"}'); }); - it('should set a value on the store', function () { + it('should set a value on the store', function() { app.store.set('one', 'two'); app.store.data.one.should.equal('two'); }); - it('should set an object', function () { + it('should set an object', function() { app.store.set({four: 'five', six: 'seven'}); app.store.data.four.should.equal('five'); app.store.data.six.should.equal('seven'); }); - it('should set a nested value', function () { + it('should set a nested value', function() { app.store.set('a.b.c.d', {e: 'f'}); app.store.data.a.b.c.d.e.should.equal('f'); }); - it('should union a value onto an array on the store', function () { + it('should union a value onto an array on the store', function() { app.store.union('one', 'two'); app.store.data.one.should.eql(['two']); }); - it('should not union duplicate values', function () { + it('should not union duplicate values', function() { app.store.union('one', 'two'); app.store.data.one.should.eql(['two']); @@ -64,7 +64,7 @@ describe('store', function () { app.store.data.one.should.eql(['two']); }); - it('should concat an existing array:', function () { + it('should concat an existing array:', function() { app.store.union('one', 'a'); app.store.data.one.should.eql(['a']); @@ -75,80 +75,80 @@ describe('store', function () { app.store.data.one.should.eql(['a', 'b', 'c', 'd']); }); - it('should return true if a key `.has()` on the store', function () { + it('should return true if a key `.has()` on the store', function() { app.store.set('foo', 'bar'); app.store.set('baz', null); app.store.set('qux', undefined); - app.store.has('foo').should.eql(true); - app.store.has('bar').should.eql(false); - app.store.has('baz').should.eql(false); - app.store.has('qux').should.eql(false); + assert(app.store.has('foo')); + assert(app.store.has('baz')); + assert(!app.store.has('bar')); + assert(!app.store.has('qux')); }); - it('should return true if a nested key `.has()` on the store', function () { + it('should return true if a nested key `.has()` on the store', function() { app.store.set('a.b.c.d', {x: 'zzz'}); app.store.set('a.b.c.e', {f: null}); app.store.set('a.b.g.j', {k: undefined}); - app.store.has('a.b.bar').should.eql(false); - app.store.has('a.b.c.d').should.eql(true); - app.store.has('a.b.c.d.x').should.eql(true); - app.store.has('a.b.c.d.z').should.eql(false); - app.store.has('a.b.c.e').should.eql(true); - app.store.has('a.b.c.e.f').should.eql(false); - app.store.has('a.b.c.e.z').should.eql(false); - app.store.has('a.b.g.j').should.eql(true); - app.store.has('a.b.g.j.k').should.eql(false); - app.store.has('a.b.g.j.z').should.eql(false); + assert(app.store.has('a.b.c.d')); + assert(app.store.has('a.b.c.d.x')); + assert(app.store.has('a.b.c.e')); + assert(app.store.has('a.b.c.e.f')); + assert(app.store.has('a.b.g.j')); + assert(!app.store.has('a.b.bar')); + assert(!app.store.has('a.b.c.d.z')); + assert(!app.store.has('a.b.c.e.z')); + assert(!app.store.has('a.b.g.j.k')); + assert(!app.store.has('a.b.g.j.z')); }); - it('should return true if a key exists `.hasOwn()` on the store', function () { + it('should return true if a key exists `.hasOwn()` on the store', function() { app.store.set('foo', 'bar'); app.store.set('baz', null); app.store.set('qux', undefined); - app.store.hasOwn('foo').should.eql(true); - app.store.hasOwn('bar').should.eql(false); - app.store.hasOwn('baz').should.eql(true); - app.store.hasOwn('qux').should.eql(true); + assert(app.store.hasOwn('foo')); + assert(!app.store.hasOwn('bar')); + assert(app.store.hasOwn('baz')); + assert(app.store.hasOwn('qux')); }); - it('should return true if a nested key exists `.hasOwn()` on the store', function () { + it('should return true if a nested key exists `.hasOwn()` on the store', function() { app.store.set('a.b.c.d', {x: 'zzz'}); app.store.set('a.b.c.e', {f: null}); app.store.set('a.b.g.j', {k: undefined}); - app.store.hasOwn('a.b.bar').should.eql(false); - app.store.hasOwn('a.b.c.d').should.eql(true); - app.store.hasOwn('a.b.c.d.x').should.eql(true); - app.store.hasOwn('a.b.c.d.z').should.eql(false); - app.store.has('a.b.c.e.f').should.eql(false); - app.store.hasOwn('a.b.c.e.f').should.eql(true); - app.store.hasOwn('a.b.c.e.bar').should.eql(false); - app.store.has('a.b.g.j.k').should.eql(false); - app.store.hasOwn('a.b.g.j.k').should.eql(true); - app.store.hasOwn('a.b.g.j.foo').should.eql(false); + assert(app.store.hasOwn('a.b.c.d')); + assert(app.store.hasOwn('a.b.c.d.x')); + assert(app.store.has('a.b.c.e.f')); + assert(app.store.hasOwn('a.b.c.e.f')); + assert(app.store.hasOwn('a.b.g.j.k')); + assert(!app.store.hasOwn('a.b.bar')); + assert(!app.store.hasOwn('a.b.c.d.z')); + assert(!app.store.hasOwn('a.b.c.e.bar')); + assert(!app.store.has('a.b.g.j.k')); + assert(!app.store.hasOwn('a.b.g.j.foo')); }); - it('should `.get()` a stored value', function () { + it('should `.get()` a stored value', function() { app.store.set('three', 'four'); app.store.get('three').should.equal('four'); }); - it('should `.get()` a nested value', function () { + it('should `.get()` a nested value', function() { app.store.set({a: {b: {c: 'd'}}}); app.store.get('a.b.c').should.equal('d'); }); - it('should `.del()` a stored value', function () { + it('should `.del()` a stored value', function() { app.store.set('a', 'b'); app.store.set('c', 'd'); app.store.del('a'); app.store.should.not.have.property('a'); }); - it('should `.del()` multiple stored values', function () { + it('should `.del()` multiple stored values', function() { app.store.set('a', 'b'); app.store.set('c', 'd'); app.store.set('e', 'f'); @@ -157,20 +157,20 @@ describe('store', function () { }); }); -describe('events', function () { - beforeEach(function () { +describe('events', function() { + beforeEach(function() { app = new App(); app.store = new Store('verb-tests'); }); - afterEach(function (cb) { + afterEach(function(cb) { app.store.del({force: true}); cb(); }); - it('should emit `set` when an object is set:', function () { + it('should emit `set` when an object is set:', function() { var keys = []; - app.store.on('set', function (key) { + app.store.on('set', function(key) { keys.push(key); }); @@ -178,9 +178,9 @@ describe('events', function () { keys.should.eql(['a']); }); - it('should emit `set` when a key/value pair is set:', function () { + it('should emit `set` when a key/value pair is set:', function() { var keys = []; - app.store.on('set', function (key) { + app.store.on('set', function(key) { keys.push(key); }); @@ -188,9 +188,9 @@ describe('events', function () { keys.should.eql(['a']); }); - it('should emit `set` when an object value is set:', function () { + it('should emit `set` when an object value is set:', function() { var keys = []; - app.store.on('set', function (key) { + app.store.on('set', function(key) { keys.push(key); }); @@ -198,9 +198,9 @@ describe('events', function () { keys.should.eql(['a']); }); - it('should emit `set` when an array of objects is passed:', function () { + it('should emit `set` when an array of objects is passed:', function() { var keys = []; - app.store.on('set', function (key) { + app.store.on('set', function(key) { keys.push(key); }); @@ -208,8 +208,8 @@ describe('events', function () { keys.should.eql(['a', 'c']); }); - it('should emit `del` when a value is deleted:', function (cb) { - app.store.on('del', function (keys) { + it('should emit `del` when a value is deleted:', function(cb) { + app.store.on('del', function(keys) { keys.should.equal('a'); assert(typeof app.store.get('a') === 'undefined'); cb(); diff --git a/test/support/ignore.js b/test/support/ignore.js index ef599038..7dcbb755 100644 --- a/test/support/ignore.js +++ b/test/support/ignore.js @@ -3,4 +3,4 @@ module.exports = [ 'removeEventListener', 'removeAllListeners', 'removeListener' -]; \ No newline at end of file +]; diff --git a/test/support/index.js b/test/support/index.js index fc91929e..e2194a5c 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -1,7 +1,7 @@ 'use strict'; var path = require('path'); -var loadPkg = require('load-pkg'); +var loadpkg = require('load-pkg'); var assert = require('assert'); var ignore = require('./ignore'); var cache = {}; @@ -36,7 +36,7 @@ exports.resolve = function(filepath) { return cache[key]; } - var pkg = loadPkg.sync(process.cwd()); + var pkg = loadpkg.sync(process.cwd()); var prefix = pkg.name !== 'templates' ? 'templates' : ''; @@ -56,20 +56,9 @@ exports.resolve = function(filepath) { function tryResolve(name) { try { return require.resolve(name); - } catch(err) {} + } catch (err) {} try { return require.resolve(path.resolve(name)); - } catch(err) {} + } catch (err) {} } - -function tryRequire(name) { - try { - return require(name); - } catch(err) {} - - try { - return require(path.resolve(name)); - } catch(err) {} -} - diff --git a/test/view.content.js b/test/view.content.js index ccd21f75..bf356778 100644 --- a/test/view.content.js +++ b/test/view.content.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); @@ -6,16 +8,16 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('content', function () { - beforeEach(function () { +describe('content', function() { + beforeEach(function() { app = new App(); app.create('page'); app.engine('tmpl', require('engine-base')); }); - it('should normalize the `content` property on a view to a string:', function (done) { + it('should normalize the `content` property on a view to a string:', function(cb) { app.page('abc', {path: 'test/fixtures/templates/a.tmpl'}) - .set('read', function () { + .set('read', function() { this.contents = fs.readFileSync(this.path); return this; }); @@ -24,6 +26,6 @@ describe('content', function () { assert('content' in app.views.pages.abc); assert(typeof app.views.pages.abc.content === 'string'); - done(); + cb(); }); }); diff --git a/test/view.events.js b/test/view.events.js index eef1dcdc..837b9553 100644 --- a/test/view.events.js +++ b/test/view.events.js @@ -1,20 +1,22 @@ +'use strict'; + require('should'); var support = require('./support'); var App = support.resolve(); var app; -describe('view.option()', function () { - beforeEach(function () { +describe('view.option()', function() { + beforeEach(function() { app = new App(); app.create('page'); }); - it('should emit events:', function () { + it('should emit events:', function() { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); var page = app.pages.getView('a.tmpl'); var events = []; - page.on('option', function (key) { + page.on('option', function(key) { events.push(key); }); diff --git a/test/view.isType.js b/test/view.isType.js new file mode 100644 index 00000000..f56e903e --- /dev/null +++ b/test/view.isType.js @@ -0,0 +1,26 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var View = App.View; +var view; + +describe('view.isType', function() { + beforeEach(function() { + view = new View(); + }); + + it('should expose thie "isType" method', function() { + assert.equal(typeof view.isType, 'function'); + }); + + it('should return true if a view is the given "type"', function() { + assert(view.isType('renderable')); + }); + + it('should return false if a view is not the given "type"', function() { + assert(!view.isType('partial')); + }); +}); diff --git a/test/view.js b/test/view.js index 3890bb81..1052d9b9 100644 --- a/test/view.js +++ b/test/view.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); var should = require('should'); var fs = require('fs'); @@ -10,102 +12,102 @@ var App = support.resolve(); var View = App.View; var view; -describe('View', function () { - describe('instance', function () { - it('should create an instance of View:', function () { +describe('View', function() { + describe('instance', function() { + it('should create an instance of View:', function() { view = new View(); assert(view instanceof View); }); }); - describe('static methods', function () { - it('should expose `extend`:', function () { + describe('static methods', function() { + it('should expose `extend`:', function() { assert(typeof View.extend === 'function'); }); }); - describe('prototype methods', function () { - beforeEach(function () { + describe('prototype methods', function() { + beforeEach(function() { view = new View(); }); - it('should expose `set`:', function () { + it('should expose `set`:', function() { assert(typeof view.set === 'function'); }); - it('should expose `get`:', function () { + it('should expose `get`:', function() { assert(typeof view.get === 'function'); }); - it('should expose `del`:', function () { + it('should expose `del`:', function() { assert(typeof view.del === 'function'); }); - it('should expose `define`:', function () { + it('should expose `define`:', function() { assert(typeof view.define === 'function'); }); - it('should expose `visit`:', function () { + it('should expose `visit`:', function() { assert(typeof view.visit === 'function'); }); - it('should expose `compile`:', function () { + it('should expose `compile`:', function() { assert(typeof view.compile === 'function'); }); - it('should expose `render`:', function () { + it('should expose `render`:', function() { assert(typeof view.render === 'function'); }); }); - describe('properties', function () { - it('should expose an `options` property', function () { + describe('properties', function() { + it('should expose an `options` property', function() { view = new View({}); assert.deepEqual(view.options, {}); assert(view.hasOwnProperty('options')); }); - it('should add `options` when passed on the constructor', function () { + it('should add `options` when passed on the constructor', function() { view = new View({options: {foo: 'bar'}}); assert(view.options.foo === 'bar'); }); - it('should expose a `data` property', function () { + it('should expose a `data` property', function() { view = new View({app: {}}); assert.deepEqual(view.data, {}); assert(view.hasOwnProperty('data')); }); - it('should add `data` when passed on the constructor', function () { + it('should add `data` when passed on the constructor', function() { view = new View({data: {foo: 'bar'}}); assert(view.data.foo === 'bar'); }); - it('should add `locals` when passed on the constructor', function () { + it('should add `locals` when passed on the constructor', function() { view = new View({locals: {foo: 'bar'}}); assert(view.locals.foo === 'bar'); }); }); - describe('set', function () { - it('should set properties on the object', function () { + describe('set', function() { + it('should set properties on the object', function() { view = new View(); view.set('foo', 'bar'); assert.equal(view.foo, 'bar'); }); }); - describe('get', function () { - it('should get properties from the object', function () { + describe('get', function() { + it('should get properties from the object', function() { view = new View(); view.set('foo', 'bar'); assert.equal(view.get('foo'), 'bar'); }); }); - describe('cwd', function () { - it('should get properties from the object', function () { + describe('cwd', function() { + it('should get properties from the object', function() { view = new View({cwd: 'test/fixtures'}); assert(view.cwd === 'test/fixtures'); }); }); - describe('clone', function () { - it('should clone the view:', function () { + describe('clone', function() { + it('should clone the view:', function() { view = new View({content: 'foo'}); view.set({path: 'foo/bar'}); view.set('options.one', 'two'); @@ -121,7 +123,7 @@ describe('View', function () { assert(view.get('options.three') === 'four'); }); - it('should deep clone the entire object', function () { + it('should deep clone the entire object', function() { view = new View({content: 'foo'}); view.set({path: 'foo/bar'}); view.set('options.one', 'two'); @@ -134,8 +136,8 @@ describe('View', function () { }); }); - describe('visit', function () { - it('should visit all properties on an object and call the specified method', function () { + describe('visit', function() { + it('should visit all properties on an object and call the specified method', function() { view = new View(); var obj = { foo: 'bar', @@ -148,7 +150,7 @@ describe('View', function () { assert.equal(view.get('baz'), 'bang'); }); - it('should visit all properties on all objects in an array and call the specified method', function () { + it('should visit all properties on all objects in an array and call the specified method', function() { view = new View(); var arr = [{foo: 'bar', bar: 'baz', baz: 'bang'}]; view.visit('set', arr); @@ -158,41 +160,41 @@ describe('View', function () { }); }); - describe('compile', function () { - it('should get view.layout from view.data.layout', function () { + describe('compile', function() { + it('should get view.layout from view.data.layout', function() { view = new View({path: 'foo', contents: 'a b c', data: {layout: 'default'}}); assert(view.layout === 'default'); }); - it('should get view.layout from view.options.layout', function () { + it('should get view.layout from view.options.layout', function() { view = new View({path: 'foo', contents: 'a b c', options: {layout: 'default'}}); assert(view.layout === 'default'); }); - it('should get view.layout from view.locals.layout', function () { + it('should get view.layout from view.locals.layout', function() { view = new View({path: 'foo', contents: 'a b c', locals: {layout: 'default'}}); assert(view.layout === 'default'); }); - it('should get view.layout from the view', function () { + it('should get view.layout from the view', function() { view = new View({path: 'foo', contents: 'a b c', layout: 'default'}); assert(view.layout === 'default'); }); - it('should add a compiled function to `view.fn`', function () { + it('should add a compiled function to `view.fn`', function() { view = new View({path: 'foo', contents: 'a <%= name %> z'}); view.compile(); assert(typeof view.fn === 'function'); }); - it('should render a compiled template', function (done) { + it('should render a compiled template', function(cb) { view = new View({path: 'foo', contents: 'a <%= name %> z'}); view.compile(); - view.render({name: 'Halle'}, function (err, res) { - if (err) return done(err); + view.render({name: 'Halle'}, function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a Halle z'); - done(); + cb(); }); }); - it('should render `fn` using data passed on the constructor', function (done) { + it('should render `fn` using data passed on the constructor', function(cb) { view = new View({ path: 'foo', contents: 'a <%= name %> z', @@ -202,25 +204,25 @@ describe('View', function () { }); view.compile(); - view.render(function (err, res) { - if (err) return done(err); + view.render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a Brooke z'); - done(); + cb(); }); }); }); - describe('render', function () { - it('should render a template', function (done) { + describe('render', function() { + it('should render a template', function(cb) { view = new View({path: 'foo', contents: 'a <%= name %> z'}); - view.render({name: 'Halle'}, function (err, res) { - if (err) return done(err); + view.render({name: 'Halle'}, function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a Halle z'); - done(); + cb(); }); }); - it('should render fn using data passed on the constructor', function (done) { + it('should render fn using data passed on the constructor', function(cb) { view = new View({ path: 'foo', contents: 'a <%= name %> z', @@ -229,22 +231,22 @@ describe('View', function () { } }); - view.render(function (err, res) { - if (err) return done(err); + view.render(function(err, res) { + if (err) return cb(err); assert(res.contents.toString() === 'a Brooke z'); - done(); + cb(); }); }); - it('should pass errors in the callback.', function (done) { + it('should pass errors in the callback.', function(cb) { view = new View({ path: 'foo', contents: 'a <%= name %> z' }); - view.render(function (err) { + view.render(function(err) { assert(err.message === 'name is not defined'); - done(); + cb(); }); }); }); @@ -258,173 +260,173 @@ describe('View', function () { describe('View', function() { describe('isVinyl()', function() { - it('should return true on a vinyl object', function(done) { + it('should return true on a vinyl object', function(cb) { var view = new View(); assert(View.isVinyl(view) === true); - done(); + cb(); }); - it('should return false on a normal object', function(done) { + it('should return false on a normal object', function(cb) { assert(View.isVinyl({}) === false); - done(); + cb(); }); - it('should return false on a null object', function(done) { + it('should return false on a null object', function(cb) { assert(View.isVinyl({}) === false); - done(); + cb(); }); }); describe('constructor()', function() { - it('should default cwd to process.cwd', function(done) { + it('should default cwd to process.cwd', function(cb) { var view = new View(); view.cwd.should.equal(process.cwd()); - done(); + cb(); }); - it('should default base to cwd', function(done) { + it('should default base to cwd', function(cb) { var cwd = '/'; var view = new View({cwd: cwd}); view.base.should.equal(cwd); - done(); + cb(); }); - it('should default base to cwd even when none is given', function(done) { + it('should default base to cwd even when none is given', function(cb) { var view = new View(); view.base.should.equal(process.cwd()); - done(); + cb(); }); - it('should default path to null', function(done) { + it('should default path to null', function(cb) { var view = new View(); should.not.exist(view.path); - done(); + cb(); }); - it('should default history to []', function(done) { + it('should default history to []', function(cb) { var view = new View(); view.history.should.eql([]); - done(); + cb(); }); - it('should default stat to null', function(done) { + it('should default stat to null', function(cb) { var view = new View(); should.not.exist(view.stat); - done(); + cb(); }); - it('should default contents to null', function(done) { + it('should default contents to null', function(cb) { var view = new View(); should.not.exist(view.contents); - done(); + cb(); }); - it('should set base to given value', function(done) { + it('should set base to given value', function(cb) { var val = '/'; var view = new View({base: val}); view.base.should.equal(val); - done(); + cb(); }); - it('should set cwd to given value', function(done) { + it('should set cwd to given value', function(cb) { var val = '/'; var view = new View({cwd: val}); view.cwd.should.equal(val); - done(); + cb(); }); - it('should set path to given value', function(done) { + it('should set path to given value', function(cb) { var val = '/test.coffee'; var view = new View({path: val}); view.path.should.equal(val); view.history.should.eql([val]); - done(); + cb(); }); - it('should set history to given value', function(done) { + it('should set history to given value', function(cb) { var val = '/test.coffee'; var view = new View({history: [val]}); view.path.should.equal(val); view.history.should.eql([val]); - done(); + cb(); }); - it('should set stat to given value', function(done) { + it('should set stat to given value', function(cb) { var val = {}; var view = new View({stat: val}); view.stat.should.equal(val); - done(); + cb(); }); - it('should set contents to given value', function(done) { + it('should set contents to given value', function(cb) { var val = new Buffer('test'); var view = new View({contents: val}); view.contents.should.equal(val); - done(); + cb(); }); }); describe('isBuffer()', function() { - it('should return true when the contents are a Buffer', function(done) { + it('should return true when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val}); view.isBuffer().should.equal(true); - done(); + cb(); }); - it('should return false when the contents are a Stream', function(done) { + it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val}); view.isBuffer().should.equal(false); - done(); + cb(); }); - it('should return false when the contents are a null', function(done) { + it('should return false when the contents are a null', function(cb) { var view = new View({contents: null}); view.isBuffer().should.equal(false); - done(); + cb(); }); }); describe('isStream()', function() { - it('should return false when the contents are a Buffer', function(done) { + it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val}); view.isStream().should.equal(false); - done(); + cb(); }); - it('should return true when the contents are a Stream', function(done) { + it('should return true when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val}); view.isStream().should.equal(true); - done(); + cb(); }); - it('should return false when the contents are a null', function(done) { + it('should return false when the contents are a null', function(cb) { var view = new View({contents: null}); view.isStream().should.equal(false); - done(); + cb(); }); }); describe('isNull()', function() { - it('should return false when the contents are a Buffer', function(done) { + it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val}); view.isNull().should.equal(false); - done(); + cb(); }); - it('should return false when the contents are a Stream', function(done) { + it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val}); view.isNull().should.equal(false); - done(); + cb(); }); - it('should return true when the contents are a null', function(done) { + it('should return true when the contents are a null', function(cb) { var view = new View({contents: null}); view.isNull().should.equal(true); - done(); + cb(); }); }); @@ -435,29 +437,29 @@ describe('View', function() { } }; - it('should return false when the contents are a Buffer', function(done) { + it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val, stat: fakeStat}); view.isDirectory().should.equal(false); - done(); + cb(); }); - it('should return false when the contents are a Stream', function(done) { + it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val, stat: fakeStat}); view.isDirectory().should.equal(false); - done(); + cb(); }); - it('should return true when the contents are a null', function(done) { + it('should return true when the contents are a null', function(cb) { var view = new View({contents: null, stat: fakeStat}); view.isDirectory().should.equal(true); - done(); + cb(); }); }); describe('clone()', function() { - it('should copy all attributes over with Buffer', function(done) { + it('should copy all attributes over with Buffer', function(cb) { var options = { cwd: '/', base: '/test/', @@ -473,10 +475,10 @@ describe('View', function() { view2.path.should.equal(view.path); view2.contents.should.not.equal(view.contents, 'buffer ref should be different'); view2.contents.toString('utf8').should.equal(view.contents.toString('utf8')); - done(); + cb(); }); - it('should copy buffer\'s reference with option contents: false', function(done) { + it('should copy buffer\'s reference with option contents: false', function(cb) { var options = { cwd: '/', base: '/test/', @@ -495,10 +497,10 @@ describe('View', function() { var copy3 = view.clone({ contents: 'any string' }); copy3.contents.should.not.equal(view.contents); - done(); + cb(); }); - it('should copy all attributes over with Stream', function(done) { + it('should copy all attributes over with Stream', function(cb) { var contents = new Stream.PassThrough(); var options = { cwd: '/', @@ -527,10 +529,10 @@ describe('View', function() { data2.should.eql(data, 'stream contents should be the same'); })); })); - done(); + cb(); }); - it('should copy all attributes over with null', function(done) { + it('should copy all attributes over with null', function(cb) { var options = { cwd: '/', base: '/test/', @@ -545,10 +547,10 @@ describe('View', function() { view2.base.should.equal(view.base); view2.path.should.equal(view.path); should.not.exist(view2.contents); - done(); + cb(); }); - it('should properly clone the `stat` property', function(done) { + it('should properly clone the `stat` property', function(cb) { var options = { cwd: '/', base: '/test/', @@ -562,10 +564,14 @@ describe('View', function() { assert(copy.stat.isFile()); assert(!copy.stat.isDirectory()); - done(); + + assert(view.stat.hasOwnProperty('birthtime')); + assert(copy.stat.hasOwnProperty('birthtime')); + assert.deepEqual(view.stat, copy.stat); + cb(); }); - it('should properly clone the `history` property', function(done) { + it('should properly clone the `history` property', function(cb) { var options = { cwd: '/', base: '/test/', @@ -580,10 +586,10 @@ describe('View', function() { copy.history[0].should.equal(options.path); copy.path = 'lol'; view.path.should.not.equal(copy.path); - done(); + cb(); }); - it('should copy custom properties', function(done) { + it('should copy custom properties', function(cb) { var options = { cwd: '/', base: '/test/', @@ -602,10 +608,10 @@ describe('View', function() { view2.custom.should.equal(view.custom); view2.custom.a.should.equal(view.custom.a); - done(); + cb(); }); - it('should copy history', function(done) { + it('should copy history', function(cb) { var options = { cwd: '/', base: '/test/', @@ -630,10 +636,10 @@ describe('View', function() { ]); view2.path.should.eql('/test/test-938di2s.js'); - done(); + cb(); }); - it('should copy all attributes deeply', function(done) { + it('should copy all attributes deeply', function(cb) { var options = { cwd: '/', base: '/test/', @@ -664,12 +670,12 @@ describe('View', function() { view5.custom.should.equal(view.custom); view5.custom.a.should.equal(view.custom.a); - done(); + cb(); }); }); describe('pipe()', function() { - it('should write to stream with Buffer', function(done) { + it('should write to stream with Buffer', function(cb) { var options = { cwd: '/', base: '/test/', @@ -684,13 +690,13 @@ describe('View', function() { chunk.toString('utf8').should.equal(options.contents.toString('utf8')); }); stream.on('end', function() { - done(); + cb(); }); var ret = view.pipe(stream); ret.should.equal(stream, 'should return the stream'); }); - it('should pipe to stream with Stream', function(done) { + it('should pipe to stream with Stream', function(cb) { var testChunk = new Buffer('test'); var options = { cwd: '/', @@ -704,7 +710,7 @@ describe('View', function() { should.exist(chunk); (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - done(); + cb(); }); var ret = view.pipe(stream); ret.should.equal(stream, 'should return the stream'); @@ -712,7 +718,7 @@ describe('View', function() { view.contents.write(testChunk); }); - it('should do nothing with null', function(done) { + it('should do nothing with null', function(cb) { var options = { cwd: '/', base: '/test/', @@ -725,13 +731,13 @@ describe('View', function() { throw new Error('should not write'); }); stream.on('end', function() { - done(); + cb(); }); var ret = view.pipe(stream); ret.should.equal(stream, 'should return the stream'); }); - it('should write to stream with Buffer', function(done) { + it('should write to stream with Buffer', function(cb) { var options = { cwd: '/', base: '/test/', @@ -744,7 +750,7 @@ describe('View', function() { should.exist(chunk); (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); chunk.toString('utf8').should.equal(options.contents.toString('utf8')); - done(); + cb(); }); stream.on('end', function() { throw new Error('should not end'); @@ -753,7 +759,7 @@ describe('View', function() { ret.should.equal(stream, 'should return the stream'); }); - it('should pipe to stream with Stream', function(done) { + it('should pipe to stream with Stream', function(cb) { var testChunk = new Buffer('test'); var options = { cwd: '/', @@ -767,7 +773,7 @@ describe('View', function() { should.exist(chunk); (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - done(); + cb(); }); stream.on('end', function() { throw new Error('should not end'); @@ -778,7 +784,7 @@ describe('View', function() { view.contents.write(testChunk); }); - it('should do nothing with null', function(done) { + it('should do nothing with null', function(cb) { var options = { cwd: '/', base: '/test/', @@ -795,27 +801,27 @@ describe('View', function() { }); var ret = view.pipe(stream, {end: false}); ret.should.equal(stream, 'should return the stream'); - process.nextTick(done); + process.nextTick(cb); }); }); describe('inspect()', function() { - it('should return correct format when no contents and no path', function(done) { + it('should return correct format when no contents and no path', function(cb) { var view = new View(); view.inspect().should.equal(''); - done(); + cb(); }); - it('should return correct format when Buffer and no path', function(done) { + it('should return correct format when Buffer and no path', function(cb) { var val = new Buffer('test'); var view = new View({ contents: val }); view.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when Buffer and relative path', function(done) { + it('should return correct format when Buffer and relative path', function(cb) { var val = new Buffer('test'); var view = new View({ cwd: '/', @@ -824,10 +830,10 @@ describe('View', function() { contents: val }); view.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when Buffer and only path and no base', function(done) { + it('should return correct format when Buffer and only path and no base', function(cb) { var val = new Buffer('test'); var view = new View({ cwd: '/', @@ -836,10 +842,10 @@ describe('View', function() { }); delete view.base; view.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when Stream and relative path', function(done) { + it('should return correct format when Stream and relative path', function(cb) { var view = new View({ cwd: '/', base: '/test/', @@ -847,10 +853,10 @@ describe('View', function() { contents: new Stream.PassThrough() }); view.inspect().should.equal('>'); - done(); + cb(); }); - it('should return correct format when null and relative path', function(done) { + it('should return correct format when null and relative path', function(cb) { var view = new View({ cwd: '/', base: '/test/', @@ -858,56 +864,56 @@ describe('View', function() { contents: null }); view.inspect().should.equal(''); - done(); + cb(); }); }); describe('contents get/set', function() { - it('should work with Buffer', function(done) { + it('should work with Buffer', function(cb) { var val = new Buffer('test'); var view = new View(); view.contents = val; view.contents.should.equal(val); - done(); + cb(); }); - it('should work with Stream', function(done) { + it('should work with Stream', function(cb) { var val = new Stream.PassThrough(); var view = new View(); view.contents = val; view.contents.should.equal(val); - done(); + cb(); }); - it('should work with null', function(done) { + it('should work with null', function(cb) { var val = null; var view = new View(); view.contents = val; (view.contents === null).should.equal(true); - done(); + cb(); }); - it('should work with string', function(done) { + it('should work with string', function(cb) { var val = 'test'; var view = new View(); view.contents = val; view.contents.should.deepEqual(new Buffer(val)); - done(); + cb(); }); }); describe('relative get/set', function() { - it('should error on set', function(done) { + it('should error on set', function(cb) { var view = new View(); try { view.relative = 'test'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should error on get when no base', function(done) { + it('should error on get when no base', function(cb) { var a; var view = new View(); delete view.base; @@ -915,74 +921,74 @@ describe('View', function() { a = view.relative; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var view = new View(); try { a = view.relative; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return a relative path from base', function(done) { + it('should return a relative path from base', function(cb) { var view = new View({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); view.relative.should.equal('test.coffee'); - done(); + cb(); }); - it('should return a relative path from cwd', function(done) { + it('should return a relative path from cwd', function(cb) { var view = new View({ cwd: '/', path: '/test/test.coffee' }); - view.relative.should.equal(path.join('test','test.coffee')); - done(); + view.relative.should.equal(path.join('test', 'test.coffee')); + cb(); }); }); describe('dirname get/set', function() { - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var view = new View(); try { a = view.dirname; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return the dirname of the path', function(done) { + it('should return the dirname of the path', function(cb) { var view = new View({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); view.dirname.should.equal('/test'); - done(); + cb(); }); - it('should error on set when no path', function(done) { + it('should error on set when no path', function(cb) { var view = new View(); try { view.dirname = '/test'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should set the dirname of the path', function(done) { + it('should set the dirname of the path', function(cb) { var view = new View({ cwd: '/', base: '/test/', @@ -990,43 +996,43 @@ describe('View', function() { }); view.dirname = '/test/foo'; view.path.should.equal('/test/foo/test.coffee'); - done(); + cb(); }); }); describe('basename get/set', function() { - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var view = new View(); try { a = view.basename; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return the basename of the path', function(done) { + it('should return the basename of the path', function(cb) { var view = new View({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); view.basename.should.equal('test.coffee'); - done(); + cb(); }); - it('should error on set when no path', function(done) { + it('should error on set when no path', function(cb) { var view = new View(); try { view.basename = 'test.coffee'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should set the basename of the path', function(done) { + it('should set the basename of the path', function(cb) { var view = new View({ cwd: '/', base: '/test/', @@ -1034,43 +1040,43 @@ describe('View', function() { }); view.basename = 'foo.png'; view.path.should.equal('/test/foo.png'); - done(); + cb(); }); }); describe('extname get/set', function() { - it('should error on get when no path', function(done) { + it('should error on get when no path', function(cb) { var a; var view = new View(); try { a = view.extname; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should return the extname of the path', function(done) { + it('should return the extname of the path', function(cb) { var view = new View({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); view.extname.should.equal('.coffee'); - done(); + cb(); }); - it('should error on set when no path', function(done) { + it('should error on set when no path', function(cb) { var view = new View(); try { view.extname = '.coffee'; } catch (err) { should.exist(err); - done(); + cb(); } }); - it('should set the extname of the path', function(done) { + it('should set the extname of the path', function(cb) { var view = new View({ cwd: '/', base: '/test/', @@ -1078,7 +1084,7 @@ describe('View', function() { }); view.extname = '.png'; view.path.should.equal('/test/test.png'); - done(); + cb(); }); }); diff --git a/test/view.option.js b/test/view.option.js index 4190be74..ebde730d 100644 --- a/test/view.option.js +++ b/test/view.option.js @@ -1,15 +1,17 @@ +'use strict'; + require('should'); var support = require('./support'); var App = support.resolve(); var app; -describe('view.option()', function () { - beforeEach(function () { +describe('view.option()', function() { + beforeEach(function() { app = new App(); app.create('page'); }); - it('should set an option:', function () { + it('should set an option:', function() { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); var page = app.pages.getView('a.tmpl'); @@ -18,7 +20,7 @@ describe('view.option()', function () { page.options.should.have.property('foo'); }); - it('should extend options:', function () { + it('should extend options:', function() { app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); var page = app.pages.getView('a.tmpl'); page.option('a', 'b'); diff --git a/test/view.render.js b/test/view.render.js index 5fe5c7dd..da0d8df0 100644 --- a/test/view.render.js +++ b/test/view.render.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var support = require('./support'); @@ -5,9 +7,9 @@ var App = support.resolve(); var View = App.View; var view, app; -describe('helpers', function () { - describe('rendering', function () { - beforeEach(function () { +describe('helpers', function() { + describe('rendering', function() { + beforeEach(function() { app = new App(); view = new View(); app.engine('tmpl', require('engine-base')); @@ -15,38 +17,38 @@ describe('helpers', function () { app.create('pages'); }); - it('should expose `.render` for rendering a view:', function (done) { + it('should expose `.render` for rendering a view:', function(cb) { app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) - .render({a: 'bbb'}, function (err, res) { - if (err) return done(err); + .render({a: 'bbb'}, function(err, res) { + if (err) return cb(err); res.content.should.equal('bbb'); - done(); + cb(); }); }); - it('should render a view with a layout', function (done) { + it('should render a view with a layout', function(cb) { app.layout('default.tmpl', {content: 'a {% body %} b'}); app.page('a.tmpl', {content: '<%= title %>', layout: 'default.tmpl'}) - .render({title: 'zzz'}, function (err, res) { - if (err) return done(err); + .render({title: 'zzz'}, function(err, res) { + if (err) return cb(err); res.content.should.equal('a zzz b'); - done(); + cb(); }); }); - it('should render a view with a layout', function (done) { + it('should render a view with a layout', function(cb) { app.layout('foo.tmpl', {content: 'a {% body %} a'}); app.layout('bar.tmpl', {content: 'b {% body %} b'}); app.pages('a.tmpl', {content: '<%= title %>'}); app.pages.getView('a.tmpl') - .option('resolveLayout', function () { + .option('resolveLayout', function() { return 'bar.tmpl'; }) - .render({title: 'zzz'}, function (err, res) { - if (err) return done(err); + .render({title: 'zzz'}, function(err, res) { + if (err) return cb(err); res.content.should.equal('b zzz b'); - done(); + cb(); }); }); }); diff --git a/test/view.set.js b/test/view.set.js index fd594692..321219bc 100644 --- a/test/view.set.js +++ b/test/view.set.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var fs = require('fs'); @@ -6,18 +8,18 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('set', function () { - beforeEach(function () { +describe('set', function() { + beforeEach(function() { app = new App(); app.create('page'); - app.engine('tmpl', require('engine-base'), { - delims: ['{%', '%}'] - }); + app.engine('tmpl', require('engine-base')); + + app.cache.data = {}; }); - it('should set a property on a view:', function (done) { + it('should set a property on a view:', function(cb) { app.page('abc', {path: 'test/fixtures/templates/a.tmpl'}) - .set('read', function () { + .set('read', function() { this.contents = fs.readFileSync(this.path); return this; }); @@ -26,11 +28,10 @@ describe('set', function () { app.views.pages.abc .read() .set('data.name', 'Brooke') - .render(function (err, res) { - if (err) return done(err); - + .render(function(err, res) { + if (err) return cb(err); assert(res.content === 'Brooke'); - done(); + cb(); }); }); }); diff --git a/test/view.use.js b/test/view.use.js index 3706c0b0..d7e714ad 100644 --- a/test/view.use.js +++ b/test/view.use.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -6,50 +8,50 @@ var App = support.resolve(); var View = App.View; var view; -describe('view.use', function () { - beforeEach(function () { +describe('view.use', function() { + beforeEach(function() { view = new View(); }); - it('should expose the instance to `use`:', function (done) { - view.use(function (inst) { + it('should expose the instance to `use`:', function(cb) { + view.use(function(inst) { assert(inst instanceof View); - done(); + cb(); }); }); - it('should be chainable:', function (done) { - view.use(function (inst) { - assert(inst instanceof View); - }) - .use(function (inst) { + it('should be chainable:', function(cb) { + view.use(function(inst) { + assert(inst instanceof View); + }) + .use(function(inst) { assert(inst instanceof View); }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof View); - done(); + cb(); }); }); - it('should expose the view to a plugin:', function () { - view.use(function (view) { + it('should expose the view to a plugin:', function() { + view.use(function(view) { assert(view instanceof View); - view.foo = function (str) { + view.foo = function(str) { return str + ' ' + 'bar'; }; }); assert(view.foo('foo') === 'foo bar'); }); - it('should be chainable:', function () { + it('should be chainable:', function() { view - .use(function (view) { + .use(function(view) { view.a = 'aaa'; }) - .use(function (view) { + .use(function(view) { view.b = 'bbb'; }) - .use(function (view) { + .use(function(view) { view.c = 'ccc'; }); diff --git a/test/viewTypes.js b/test/viewTypes.js index 47da0a9f..22bf9fe8 100644 --- a/test/viewTypes.js +++ b/test/viewTypes.js @@ -1,16 +1,18 @@ +'use strict'; + var assert = require('assert'); var support = require('./support'); var App = support.resolve(); var app; -describe('viewType', function () { - describe('view types', function () { - beforeEach(function () { +describe('viewType', function() { + describe('view types', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); }); - it('should add collection (plural) to the `viewTypes` object', function () { + it('should add collection (plural) to the `viewTypes` object', function() { app.viewTypes = []; // reset app.create('foo', {viewType: 'layout'}); app.create('bar', {viewType: 'layout'}); @@ -20,12 +22,12 @@ describe('viewType', function () { assert.deepEqual(app.viewTypes.renderable, [ 'bazs' ]); }); - it('should add collection to the given viewType', function () { + it('should add collection to the given viewType', function() { app.create('layout', {viewType: 'layout'}); assert(app.layouts.options.viewType[0] === 'layout'); }); - it('should add a collection to multiple viewTypes', function () { + it('should add a collection to multiple viewTypes', function() { app.create('foo', {viewType: ['layout', 'renderable']}); assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); }); diff --git a/test/views.getView.js b/test/views.getView.js new file mode 100644 index 00000000..0b742140 --- /dev/null +++ b/test/views.getView.js @@ -0,0 +1,62 @@ +'use strict'; + +require('mocha'); +require('should'); +var path = require('path'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var Views = App.Views; +var views; + +describe('views.getView', function() { + beforeEach(function() { + views = new Views(); + views.option('renameKey', function(key, view) { + return '123/' + (view ? view.basename : path.basename(key)); + }); + + views.addView('one', {content: 'this is one'}); + views.addView('a/b/c/one.txt', {content: 'this is a/b/c/one.txt'}); + views.addView('a/b/c/two.txt', {content: '...'}); + views.addView('a/b/c/three.txt', {content: 'this is three', base: 'a/b/c'}); + }); + + it('should throw an error when a string is not passed', function(cb) { + try { + views.getView(null); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'expected a string'); + cb(); + } + }); + + it('should get a view by `key`', function() { + assert(views.getView('a/b/c/one.txt')); + assert.equal(views.getView('123/a/b/c/one.txt').content, 'this is a/b/c/one.txt'); + }); + + it('should get a view by `path', function() { + assert(views.getView('a/b/c/one.txt')); + assert.equal(views.getView('a/b/c/one.txt').content, 'this is a/b/c/one.txt'); + }); + + it('should get a view by `relative', function() { + assert(views.getView('123/one.txt')); + assert.equal(views.getView('123/one.txt').content, 'this is a/b/c/one.txt'); + }); + + it('should get a view by `basename`', function() { + assert(views.getView('one.txt')); + assert.equal(views.getView('one.txt').content, 'this is a/b/c/one.txt'); + }); + + it('should get a view by `filename`', function() { + assert(views.getView('one')); + assert.equal(views.getView('one').content, 'this is one'); + + assert(views.getView('three')); + assert.equal(views.getView('three').content, 'this is three'); + }); +}); diff --git a/test/views.js b/test/views.js index 8f99b7d4..16a50b68 100644 --- a/test/views.js +++ b/test/views.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var path = require('path'); @@ -11,26 +13,26 @@ var View = App.View; var Views = App.Views; var collection; -describe('views', function () { - describe('constructor', function () { - it('should create an instance of Views:', function () { +describe('views', function() { + describe('constructor', function() { + it('should create an instance of Views:', function() { var collection = new Views(); assert(collection instanceof Views); }); - it('should instantiate without `new`:', function () { + it('should instantiate without `new`:', function() { var collection = Views(); assert(collection instanceof Views); }); }); - describe('static methods', function () { - it('should expose `extend`:', function () { - assert(typeof Views.extend ==='function'); + describe('static methods', function() { + it('should expose `extend`:', function() { + assert(typeof Views.extend === 'function'); }); }); - describe('prototype methods', function () { + describe('prototype methods', function() { beforeEach(function() { collection = new Views(); }); @@ -56,42 +58,42 @@ describe('views', function () { 'hasListeners' ]; - methods.forEach(function (method) { - it('should expose ' + method + ' method', function () { + methods.forEach(function(method) { + it('should expose ' + method + ' method', function() { assert(typeof collection[method] === 'function'); }); }); - it('should expose isCollection property', function () { + it('should expose isCollection property', function() { assert(typeof collection.isCollection === 'boolean'); }); - it('should expose queue property', function () { + it('should expose queue property', function() { assert(Array.isArray(collection.queue)); }); - it('should expose views property', function () { + it('should expose views property', function() { assert(typeOf(collection.views) === 'object'); }); - it('should expose options property', function () { + it('should expose options property', function() { assert(typeOf(collection.options) === 'object'); }); }); - describe('instance', function () { + describe('instance', function() { beforeEach(function() { collection = new Views(); }); - it('should set a value on the instance:', function () { + it('should set a value on the instance:', function() { collection.set('a', 'b'); - assert(collection.a ==='b'); + assert(collection.a === 'b'); }); - it('should get a value from the instance:', function () { + it('should get a value from the instance:', function() { collection.set('a', 'b'); - assert(collection.get('a') ==='b'); + assert(collection.get('a') === 'b'); }); }); @@ -100,17 +102,17 @@ describe('views', function () { collection = new Views(); }); - it('should set a key/value pair on options:', function () { + it('should set a key/value pair on options:', function() { collection.option('a', 'b'); assert(collection.options.a === 'b'); }); - it('should set an object on options:', function () { + it('should set an object on options:', function() { collection.option({c: 'd'}); assert(collection.options.c === 'd'); }); - it('should get an option:', function () { + it('should get an option:', function() { collection.option({c: 'd'}); var c = collection.option('c'); assert(c === 'd'); @@ -122,13 +124,13 @@ describe('views', function () { collection = new Views(); }); - it('should throw an error when args are invalid:', function () { - (function () { + it('should throw an error when args are invalid:', function() { + (function() { collection.addView(function() {}); }).should.throw('expected value to be an object.'); }); - it('should add a view to `views`:', function () { + it('should add a view to `views`:', function() { collection.addView('foo'); collection.views.should.have.property('foo'); @@ -137,12 +139,12 @@ describe('views', function () { assert(isBuffer(collection.views.one.contents)); }); - it('should create an instance of `View`:', function () { + it('should create an instance of `View`:', function() { collection.addView('one', {content: '...'}); assert(collection.views.one instanceof collection.View); }); - it('should allow an `View` constructor to be passed:', function () { + it('should allow an `View` constructor to be passed:', function() { View.prototype.foo = function(key, value) { this[key] = value; }; @@ -152,7 +154,7 @@ describe('views', function () { assert(collection.views.one.bar === 'baz'); }); - it('should allow an instance of `View` to be passed:', function () { + it('should allow an instance of `View` to be passed:', function() { var collection = new Views({View: View}); var view = new View({content: '...'}); collection.addView('one', view); @@ -168,31 +170,31 @@ describe('views', function () { collection = new Views(); }); - it('should emit an error if a string glob pattern is passed', function (done) { + it('should emit an error if a string glob pattern is passed', function(cb) { try { collection.addViews('*.js'); - done(new Error('expected an error')); - } catch(err) { + cb(new Error('expected an error')); + } catch (err) { assert(err); assert(err.message); assert(/glob/.test(err.message)); - done(); + cb(); } }); - it('should emit an error if an array glob pattern is passed', function (done) { + it('should emit an error if an array glob pattern is passed', function(cb) { try { collection.addViews(['*.js']); - done(new Error('expected an error')); - } catch(err) { + cb(new Error('expected an error')); + } catch (err) { assert(err); assert(err.message); assert(/glob/.test(err.message)); - done(); + cb(); } }); - it('should add multiple views:', function () { + it('should add multiple views:', function() { collection.addViews({ one: {content: 'foo'}, two: {content: 'bar'} @@ -201,7 +203,7 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); - it('should return the collection instance for chaining:', function () { + it('should return the collection instance for chaining:', function() { var views = collection.addViews({ one: {content: 'foo'}, two: {content: 'bar'} @@ -213,7 +215,7 @@ describe('views', function () { assert(view.content === 'foo'); }); - it('should create views from an instance of Views', function () { + it('should create views from an instance of Views', function() { collection.addViews({ one: {content: 'foo'}, two: {content: 'bar'} @@ -223,7 +225,7 @@ describe('views', function () { assert(isBuffer(pages.views.two.contents)); }); - it('should add an array of views:', function () { + it('should add an array of views:', function() { collection.addViews([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -238,7 +240,7 @@ describe('views', function () { collection = new Views(); }); - it('should return a single collection view from a key-value pair', function () { + it('should return a single collection view from a key-value pair', function() { var one = collection.view('one', {content: 'foo'}); var two = collection.view('two', {content: 'bar'}); @@ -248,7 +250,7 @@ describe('views', function () { assert(two.path === 'two'); }); - it('should return a single collection view from an object', function () { + it('should return a single collection view from an object', function() { var one = collection.view({path: 'one', content: 'foo'}); var two = collection.view({path: 'two', content: 'bar'}); @@ -264,31 +266,31 @@ describe('views', function () { collection = new Views(); }); - it('should emit an error if a string glob pattern is passed', function (done) { + it('should emit an error if a string glob pattern is passed', function(cb) { try { collection.addList('*.js'); - done(new Error('expected an error')); - } catch(err) { + cb(new Error('expected an error')); + } catch (err) { assert(err); assert(err.message); assert(/glob/.test(err.message)); - done(); + cb(); } }); - it('should emit an error if an array glob pattern is passed', function (done) { + it('should emit an error if an array glob pattern is passed', function(cb) { try { collection.addList(['*.js']); - done(new Error('expected an error')); - } catch(err) { + cb(new Error('expected an error')); + } catch (err) { assert(err); assert(err.message); assert(/glob/.test(err.message)); - done(); + cb(); } }); - it('should add a list of views:', function () { + it('should add a list of views:', function() { collection.addList([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -297,7 +299,7 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); - it('should add a list from the constructor:', function () { + it('should add a list from the constructor:', function() { var list = new List([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -308,7 +310,7 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); - it('should add list items from the constructor:', function () { + it('should add list items from the constructor:', function() { var list = new List([ {path: 'one', content: 'foo'}, {path: 'two', content: 'bar'} @@ -319,50 +321,50 @@ describe('views', function () { assert(isBuffer(collection.views.two.contents)); }); - it('should throw an error when list is not an array:', function () { + it('should throw an error when list is not an array:', function() { var views = new Views(); - (function () { + (function() { views.addList(); }).should.throw('expected list to be an array.'); - (function () { + (function() { views.addList({}); }).should.throw('expected list to be an array.'); - (function () { + (function() { views.addList('foo'); }).should.throw('expected list to be an array.'); }); - it('should load an array of items from an event:', function () { - var pages = new Views(); + it('should load an array of items from an event:', function() { + var collection = new Views(); - pages.on('addList', function (list) { + collection.on('addList', function(list) { while (list.length) { - pages.addView({path: list.pop()}); + collection.addView({path: list.pop()}); } this.loaded = true; }); - pages.addList(['a.txt', 'b.txt', 'c.txt']); - assert(pages.views.hasOwnProperty('a.txt')); - assert(pages.views['a.txt'].path === 'a.txt'); + collection.addList(['a.txt', 'b.txt', 'c.txt']); + assert(collection.views.hasOwnProperty('a.txt')); + assert(collection.views['a.txt'].path === 'a.txt'); }); - it('should load an array of items from the addList callback:', function () { + it('should load an array of items from the addList callback:', function() { var collection = new Views(); - collection.addList(['a.txt', 'b.txt', 'c.txt'], function (fp) { + collection.addList(['a.txt', 'b.txt', 'c.txt'], function(fp) { return {path: fp}; }); assert(collection.views.hasOwnProperty('a.txt')); assert(collection.views['a.txt'].path === 'a.txt'); }); - it('should load an object of views from an event:', function () { + it('should load an object of views from an event:', function() { var collection = new Views(); - collection.on('addViews', function (views) { + collection.on('addViews', function(views) { for (var key in views) { collection.addView('foo/' + key, views[key]); delete views[key]; @@ -379,10 +381,10 @@ describe('views', function () { assert(collection.views['foo/a'].path === 'a.txt'); }); - it('should signal `loaded` when finished:', function () { + it('should signal `loaded` when finished:', function() { var collection = new Views(); - collection.on('addViews', function (views) { + collection.on('addViews', function(views) { for (var key in views) { if (key === 'c') break; collection.addView('foo/' + key, views[key]); @@ -405,7 +407,7 @@ describe('views', function () { beforeEach(function() { collection = new Views(); }); - it('should get a view from `views`:', function () { + it('should get a view from `views`:', function() { collection.addView('one', {content: 'aaa'}); collection.addView('two', {content: 'zzz'}); assert(isBuffer(collection.views.one.contents)); @@ -420,7 +422,7 @@ describe('views', function () { collection = new Views(); }); - it('should get the number of views:', function () { + it('should get the number of views:', function() { collection.addView('one', {content: 'aaa'}); collection.addView('two', {content: 'zzz'}); assert(Object.keys(collection.views).length === 2); @@ -432,7 +434,7 @@ describe('options', function() { describe('options.renameKey', function() { beforeEach(function() { collection = new Views({ - renameKey: function (key) { + renameKey: function(key) { return path.basename(key); } }); @@ -443,12 +445,12 @@ describe('options', function() { assert(collection.views['d.hbs'].contents.toString() === 'foo bar baz'); }); - it('should get a view with the renamed key:', function () { + it('should get a view with the renamed key:', function() { collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); assert(collection.getView('d.hbs').contents.toString() === 'foo bar baz'); }); - it('should get a view with the original key:', function () { + it('should get a view with the original key:', function() { collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); assert(collection.getView('a/b/c/d.hbs').contents.toString() === 'foo bar baz'); }); @@ -456,25 +458,25 @@ describe('options', function() { }); -describe('queue', function () { - beforeEach(function () { +describe('queue', function() { + beforeEach(function() { collection = new Views(); }); - it('should emit arguments on addView', function (done) { - collection.on('addView', function (args) { + it('should emit arguments on addView', function(cb) { + collection.on('addView', function(args) { assert(args[0] === 'a'); assert(args[1] === 'b'); assert(args[2] === 'c'); assert(args[3] === 'd'); assert(args[4] === 'e'); - done(); + cb(); }); collection.addView('a', 'b', 'c', 'd', 'e'); }); - it('should expose the `queue` property for loading views', function () { + it('should expose the `queue` property for loading views', function() { collection.queue.push(collection.view('b', {path: 'b'})); collection.addView('a', {path: 'a'}); @@ -482,8 +484,8 @@ describe('queue', function () { assert(collection.views.hasOwnProperty('b')); }); - it('should load all views on the queue when addView is called', function () { - collection.on('addView', function (args) { + it('should load all views on the queue when addView is called', function() { + collection.on('addView', function(args) { var len = args.length; var last = args[len - 1]; if (typeof last === 'string') { @@ -502,4 +504,4 @@ describe('queue', function () { assert(collection.views.hasOwnProperty('c.html')); assert(collection.getView('c.html').content === 'ccc'); }); -}); \ No newline at end of file +}); diff --git a/test/views.use.js b/test/views.use.js index 09d47dfb..838e7f3c 100644 --- a/test/views.use.js +++ b/test/views.use.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); require('should'); var assert = require('assert'); @@ -7,33 +9,33 @@ var Views = App.Views; var View = App.View; var collection; -describe('views.use', function () { - beforeEach(function () { +describe('views.use', function() { + beforeEach(function() { collection = new Views(); }); - it('should expose the instance to `use`:', function (done) { - collection.use(function (inst) { + it('should expose the instance to `use`:', function(cb) { + collection.use(function(inst) { assert(inst instanceof Views); - done(); + cb(); }); }); - it('should be chainable:', function (done) { - collection.use(function (inst) { - assert(inst instanceof Views); - }) - .use(function (inst) { + it('should be chainable:', function(cb) { + collection.use(function(inst) { + assert(inst instanceof Views); + }) + .use(function(inst) { assert(inst instanceof Views); }) - .use(function (inst) { + .use(function(inst) { assert(inst instanceof Views); - done(); + cb(); }); }); - it('should expose the collection to a plugin:', function () { - collection.use(function (views) { + it('should expose the collection to a plugin:', function() { + collection.use(function(views) { assert(views instanceof Views); views.foo = views.addView.bind(views); }); @@ -42,17 +44,17 @@ describe('views.use', function () { assert(collection.views.hasOwnProperty('a')); }); - it('should expose collection when chained:', function () { + it('should expose collection when chained:', function() { collection - .use(function (views) { + .use(function(views) { assert(views instanceof Views); views.foo = views.addView.bind(views); }) - .use(function (views) { + .use(function(views) { assert(views instanceof Views); views.bar = views.addView.bind(views); }) - .use(function (views) { + .use(function(views) { assert(views instanceof Views); views.baz = views.addView.bind(views); }); @@ -68,18 +70,18 @@ describe('views.use', function () { assert(collection.views.hasOwnProperty('c')); }); - it('should work when a custom `View` constructor is passed:', function () { + it('should work when a custom `View` constructor is passed:', function() { collection = new Views({View: require('vinyl')}); collection - .use(function (views) { + .use(function(views) { assert(views instanceof Views); views.foo = views.addView.bind(views); }) - .use(function (views) { + .use(function(views) { assert(views instanceof Views); views.bar = views.addView.bind(views); }) - .use(function (views) { + .use(function(views) { assert(views instanceof Views); views.baz = views.addView.bind(views); }); @@ -95,11 +97,11 @@ describe('views.use', function () { assert(collection.views.hasOwnProperty('c')); }); - it('should pass to view `use` if a function is returned:', function () { - collection.use(function (views) { + it('should pass to view `use` if a function is returned:', function() { + collection.use(function(views) { assert(views instanceof Views); - return function (view) { + return function(view) { view.foo = views.addView.bind(views); assert(view instanceof View); }; @@ -116,28 +118,28 @@ describe('views.use', function () { assert(collection.views.hasOwnProperty('d')); }); - it('should be chainable when a view function is returned:', function () { + it('should be chainable when a view function is returned:', function() { collection - .use(function (views) { + .use(function(views) { assert(views instanceof Views); - return function (view) { + return function(view) { view.foo = views.addView.bind(views); assert(view instanceof View); }; }) - .use(function (views) { + .use(function(views) { assert(views instanceof Views); - return function (view) { + return function(view) { view.bar = views.addView.bind(views); assert(view instanceof View); }; }) - .use(function (views) { + .use(function(views) { assert(views instanceof Views); - return function (view) { + return function(view) { view.baz = views.addView.bind(views); assert(view instanceof View); }; From d2c2e2d564c1c92e31ff12ec84453e7ca3411245 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 6 Feb 2016 05:24:37 -0500 Subject: [PATCH 146/282] just silly. this needs to be refactored --- lib/schema.js | 71 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/lib/schema.js b/lib/schema.js index 8725d600..6b95ad95 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -6,17 +6,40 @@ var Schema = require('map-schema'); var merge = require('mixin-deep'); var utils = require('generator-util'); var inflect = require('inflection'); +var normalized = {}; module.exports = function(config) { return function(app) { + if (this.isRegistered('base-schema')) return; + this.define('schema', function(options) { var opts = merge({app: this}, config, this.options, options); + opts.sortArrays = false; var schema = new Schema(opts) .field('options', 'object') .field('data', 'object') .field('plugins', ['array', 'object', 'string'], { - normalize: normalize + normalize: function(val, key, options, schema) { + if (normalized[key]) return normalized[key]; + var pipeline = []; + var res = {}; + + if (Array.isArray(val)) { + app.option('pipeline', val); + var len = val.length; + var idx = -1; + + while (++idx < len) { + var name = val[idx]; + if (!app.plugins.hasOwnProperty(name)) { + res[name] = utils.tryRequire(name); + } + } + } + normalized[key] = res; + return res; + } }) .field('helpers', ['array', 'object', 'string'], { normalize: normalize @@ -63,13 +86,16 @@ module.exports = function(config) { } } }); + return schema; }); }; }; function normalize(val, key, options, schema) { + if (normalized[key]) return normalized[key]; var res = {}; + switch(typeOf(val)) { case 'array': res = configArray(val, key, options, schema); @@ -81,6 +107,8 @@ function normalize(val, key, options, schema) { res = configString(val, key, options, schema); break; } + + normalized[key] = res; return res; } @@ -93,7 +121,7 @@ function configString(val, key, options, schema) { } res[val] = obj; - return configObject(res, key, options, schema); + return normalize(res, key, options, schema); } function configObject(obj, key, options, schema) { @@ -103,8 +131,15 @@ function configObject(obj, key, options, schema) { for (var prop in obj) { if (obj.hasOwnProperty(prop)) { var val = obj[prop]; + if (val.isNormalized) { + res[prop] = val; + continue; + } + + if (app[key] && typeof app[key][prop] === 'function') { + val.fn = app[key][prop]; - if (key === prop && typeof val === 'string') { + } else if (typeof val === 'string' && key === prop) { var o = tryRequire(path.resolve(val)); if (typeof o === 'function') { val.fn = o; @@ -127,28 +162,30 @@ function configObject(obj, key, options, schema) { var alias = toAlias(key, prop); delete val.name; - var opts = { options: val, name: name }; + var opts = val.options || { options: val, name: name }; if (val.hasOwnProperty('path')) { opts.path = path.resolve(val.path); delete val.path; } var modulename = opts.path || opts.name; - var fn = tryRequire(modulename); - var fp; - - if (!fn) { - fp = path.resolve(app.cwd, 'node_modules', modulename); - fn = tryRequire(fp); - if (fn) opts.path = fp; - } + if (typeof val.fn !== 'function') { + var fn = tryRequire(modulename); + var fp; + + if (!fn) { + fp = path.resolve(app.cwd, 'node_modules', modulename); + fn = tryRequire(fp); + if (fn) opts.path = fp; + } - if (typeof fn === 'undefined') { - handleError(app, modulename, 'helpers'); - continue; + if (typeof fn === 'undefined') { + handleError(app, modulename, 'helpers'); + continue; + } + opts.fn = val.fn || fn; } - - opts.fn = val.fn || fn; + opts.isNormalized = true; res[alias] = opts; } } From 9c0cd066e05e31ec49081951fb970f6d688a24b5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 6 Feb 2016 05:51:30 -0500 Subject: [PATCH 147/282] setup verb defaults --- bin/verb.js | 15 +++++++------ index.js | 32 +++++++++++++++++++++++++-- lib/config/plugins.js | 3 +-- lib/generators/default.js | 1 + lib/generators/generator.js | 19 ++++++++++++++++ lib/generators/templates/generator.js | 10 +++++++++ lib/generators/templates/verbfile.js | 4 +++- lib/generators/verbfile.js | 1 - lib/utils.js | 3 ++- 9 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 lib/generators/generator.js create mode 100644 lib/generators/templates/generator.js diff --git a/bin/verb.js b/bin/verb.js index 171401b7..12a59806 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,29 +1,30 @@ #!/usr/bin/env node +process.env.VERB_CLI = true; var runtimes = require('base-runtimes'); var Verb = require('..'); var verb = new Verb(); // register default "fallback" generator verb.register('fallback', require('../lib/generators/default')); +verb.register('generator', require('../lib/generators/generator')); verb.register('verbfile', require('../lib/generators/verbfile')); verb.register('verbmd', require('../lib/generators/verbmd')); // run generator and/or tasks verb.runner('verbfile.js', function(err, argv, app) { if (err) { - console.log(err.message); + console.error(err.message); process.exit(1); } - this.use(runtimes(config)); - // var app = verb.getGenerator('default'); - var config = this.loadSettings(argv); - this.set('cache.config', config); + app.use(runtimes(config)); - this.config.process(config, function(err) { - if (err) throw err; + var config = app.loadSettings(argv); + app.set('cache.config', config); + app.config.process(config, function(err) { + if (err) throw err; app.cli.process(argv, function(err) { if (err) throw err; diff --git a/index.js b/index.js index 9bd8596c..bb2b28df 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ var fs = require('fs'); var async = require('async'); var debug = require('debug')('verb'); +var util = require('generator-util'); var Assemble = require('assemble-core'); var settings = require('./lib/settings'); var plugins = require('./lib/plugins'); @@ -52,7 +53,7 @@ Verb.prototype.initVerb = function(opts) { this.prefix = opts.prefix || 'verb-generate'; this.data({runner: require('./package')}); - this.initPlugins(); + this.initPlugins(this.options); this.create('files'); this.create('docs'); @@ -60,13 +61,25 @@ Verb.prototype.initVerb = function(opts) { this.define('lazyCreate', function(name, opts) { if (!this[name]) this.create(name, opts); }); + + var plugin = this.plugin; + this.define('plugin', function(name) { + var pipeline = this.options.pipeline; + if (arguments.length === 1 && pipeline) { + var idx = pipeline.indexOf(name); + if (idx !== -1) { + pipeline.splice(idx, 1); + } + } + return plugin.apply(this, arguments); + }); }; /** * Initialize verb plugins */ -Verb.prototype.initPlugins = function() { +Verb.prototype.initPlugins = function(opts) { this.use(plugins.generators({prefix: 'verb-generate'})); this.use(plugins.pipeline()); this.use(plugins.loader()); @@ -75,6 +88,15 @@ Verb.prototype.initPlugins = function() { this.use(plugins.ask()); this.use(settings()); this.use(config()); + + if (opts.cli === true || process.env.VERB_CLI) { + + // modify create, dest and src methods to automatically + // use cwd from generators unless overridden by the user + util.create(this); + util.dest(this); + util.src(this); + } }; Verb.prototype.conflicts = function(patterns, options, cb) { @@ -113,6 +135,12 @@ Verb.prototype.conflicts = function(patterns, options, cb) { }); }; +/** + * Expose static `is*` methods from Templates + */ + +Assemble._.plugin.is(Verb); + /** * Expose `Verb` */ diff --git a/lib/config/plugins.js b/lib/config/plugins.js index 8aa7185e..64d839bb 100644 --- a/lib/config/plugins.js +++ b/lib/config/plugins.js @@ -8,8 +8,7 @@ module.exports = function(app) { return function(plugins) { for (var key in plugins) { if (plugins.hasOwnProperty(key)) { - var val = plugins[key]; - this.plugin(key, val.options, val.fn); + this.plugin(key, plugins[key]); } } }; diff --git a/lib/generators/default.js b/lib/generators/default.js index 8c47dade..d4361b4c 100644 --- a/lib/generators/default.js +++ b/lib/generators/default.js @@ -7,6 +7,7 @@ module.exports = function(verb) { verb.register('verbmd', require('./verbmd')); verb.task('default', function(cb) { + console.log('verb > default task'); verb.generate('verbfiles', cb); }); }; diff --git a/lib/generators/generator.js b/lib/generators/generator.js new file mode 100644 index 00000000..0365d1e6 --- /dev/null +++ b/lib/generators/generator.js @@ -0,0 +1,19 @@ +'use strict'; + +var utils = require('../utils'); + +module.exports = function(verb) { + verb.task('generator', function(cb) { + verb.src('templates/generator.js', {cwd: __dirname}) + .pipe(verb.dest(function(file) { + file.basename = verb.options.f || file.basename; + return verb.cwd; + })) + .on('end', function() { + console.log('created file'); + cb(); + }); + }); + + verb.task('default', ['generator']); +}; diff --git a/lib/generators/templates/generator.js b/lib/generators/templates/generator.js new file mode 100644 index 00000000..5a1810bc --- /dev/null +++ b/lib/generators/templates/generator.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function(verb) { + verb.task('<%= alias %>', function(cb) { + console.log('verb generator > default task'); + cb(); + }); + + verb.task('default', ['<%= alias %>']); +}; diff --git a/lib/generators/templates/verbfile.js b/lib/generators/templates/verbfile.js index fff2aa10..0b230ad2 100644 --- a/lib/generators/templates/verbfile.js +++ b/lib/generators/templates/verbfile.js @@ -1,7 +1,9 @@ 'use strict'; +var fs = require('fs'); + module.exports = function(verb) { - verb.doc('.verb.md'); + verb.doc('readme.md', {content: fs.readFileSync('.verb.md')}); verb.task('default', function(cb) { console.log('verbfile > default task'); diff --git a/lib/generators/verbfile.js b/lib/generators/verbfile.js index 675498b9..ed6bcb3d 100644 --- a/lib/generators/verbfile.js +++ b/lib/generators/verbfile.js @@ -3,7 +3,6 @@ var utils = require('../utils'); module.exports = function(verb) { - verb.task('verbfile', function(cb) { var opts = { cwd: verb.cwd, filename: 'verbfile.js' }; utils.conflict(verb, opts, function(err, conflict) { diff --git a/lib/utils.js b/lib/utils.js index a57e04ed..df1a84de 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -13,6 +13,7 @@ require('extend-shallow', 'extend'); require('resolve-glob', 'glob'); require('is-affirmative'); require('try-open'); +require = fn; utils.exists = function(filepath) { if (typeof filepath === 'undefined') { @@ -70,7 +71,7 @@ utils.ask = function(app, filename, cb) { return; } - // if the answer is falsey, we're done + // if the answer is falsey, we're cb if (!utils.isAffirmative(answers.writefile)) { cb(); return; From 9eb65192b52b95fe93b1a017b01fea8648462b83 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 6 Feb 2016 05:51:55 -0500 Subject: [PATCH 148/282] lint and update deps --- .eslintrc.json | 227 ++++++++----------------------------------------- .travis.yml | 2 + package.json | 49 +++++++---- 3 files changed, 73 insertions(+), 205 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index cc6a8679..7b5d047f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,98 +3,38 @@ "modules": true, "experimentalObjectRestSpread": true }, + "env": { "browser": false, "es6": true, "node": true, "mocha": true }, + "globals": { "document": false, "navigator": false, "window": false }, + "rules": { "accessor-pairs": 2, - "arrow-spacing": [ - 2, - { - "before": true, - "after": true - } - ], - "block-spacing": [ - 2, - "always" - ], - "brace-style": [ - 2, - "1tbs", - { - "allowSingleLine": true - } - ], - "comma-dangle": [ - 2, - "never" - ], - "comma-spacing": [ - 2, - { - "before": false, - "after": true - } - ], - "comma-style": [ - 2, - "last" - ], + "arrow-spacing": [2, { "before": true, "after": true }], + "block-spacing": [2, "always"], + "brace-style": [2, "1tbs", { "allowSingleLine": true }], + "comma-dangle": [2, "never"], + "comma-spacing": [2, { "before": false, "after": true }], + "comma-style": [2, "last"], "constructor-super": 2, - "curly": [ - 2, - "multi-line" - ], - "dot-location": [ - 2, - "property" - ], + "curly": [2, "multi-line"], + "dot-location": [2, "property"], "eol-last": 2, - "eqeqeq": [ - 2, - "allow-null" - ], - "generator-star-spacing": [ - 2, - { - "before": true, - "after": true - } - ], - "handle-callback-err": [ - 2, - "^(err|error)$" - ], - "indent": [ - 2, - 2, - { - "SwitchCase": 1 - } - ], - "key-spacing": [ - 2, - { - "beforeColon": false, - "afterColon": true - } - ], - "new-cap": [ - 2, - { - "newIsCap": true, - "capIsNew": false - } - ], + "eqeqeq": [2, "allow-null"], + "generator-star-spacing": [2, { "before": true, "after": true }], + "handle-callback-err": [2, "^(err|error)$" ], + "indent": [2, 2, { "SwitchCase": 1 }], + "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "new-cap": [2, { "newIsCap": true, "capIsNew": false }], "new-parens": 2, "no-array-constructor": 2, "no-caller": 2, @@ -115,18 +55,12 @@ "no-extend-native": 2, "no-extra-bind": 2, "no-extra-boolean-cast": 2, - "no-extra-parens": [ - 2, - "functions" - ], + "no-extra-parens": [2, "functions"], "no-fallthrough": 2, "no-floating-decimal": 2, "no-func-assign": 2, "no-implied-eval": 2, - "no-inner-declarations": [ - 2, - "functions" - ], + "no-inner-declarations": [2, "functions"], "no-invalid-regexp": 2, "no-irregular-whitespace": 2, "no-iterator": 2, @@ -136,12 +70,7 @@ "no-mixed-spaces-and-tabs": 2, "no-multi-spaces": 2, "no-multi-str": 2, - "no-multiple-empty-lines": [ - 2, - { - "max": 1 - } - ], + "no-multiple-empty-lines": [2, { "max": 1 }], "no-native-reassign": 2, "no-negated-in-lhs": 2, "no-new": 2, @@ -167,112 +96,30 @@ "no-undef": 2, "no-undef-init": 2, "no-unexpected-multiline": 2, - "no-unneeded-ternary": [ - 2, - { - "defaultAssignment": false - } - ], + "no-unneeded-ternary": [2, { "defaultAssignment": false }], "no-unreachable": 2, - "no-unused-vars": [ - 2, - { - "vars": "all", - "args": "none" - } - ], + "no-unused-vars": [2, { "vars": "all", "args": "none" }], "no-useless-call": 0, "no-with": 2, - "one-var": [ - 0, - { - "initialized": "never" - } - ], - "operator-linebreak": [ - 0, - "after", - { - "overrides": { - "?": "before", - ":": "before" - } - } - ], - "padded-blocks": [ - 0, - "never" - ], - "quotes": [ - 2, - "single", - "avoid-escape" - ], + "one-var": [0, { "initialized": "never" }], + "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], + "padded-blocks": [0, "never"], + "quotes": [2, "single", "avoid-escape"], "radix": 2, - "semi": [ - 2, - "always" - ], - "semi-spacing": [ - 2, - { - "before": false, - "after": true - } - ], - "space-after-keywords": [ - 2, - "always" - ], - "space-before-blocks": [ - 2, - "always" - ], - "space-before-function-paren": [ - 2, - "never" - ], - "space-before-keywords": [ - 2, - "always" - ], - "space-in-parens": [ - 2, - "never" - ], + "semi": [2, "always"], + "semi-spacing": [2, { "before": false, "after": true }], + "space-after-keywords": [2, "always"], + "space-before-blocks": [2, "always"], + "space-before-function-paren": [2, "never"], + "space-before-keywords": [2, "always"], + "space-in-parens": [2, "never"], "space-infix-ops": 2, "space-return-throw-case": 2, - "space-unary-ops": [ - 2, - { - "words": true, - "nonwords": false - } - ], - "spaced-comment": [ - 0, - "always", - { - "markers": [ - "global", - "globals", - "eslint", - "eslint-disable", - "*package", - "!", - "," - ] - } - ], + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], "use-isnan": 2, "valid-typeof": 2, - "wrap-iife": [ - 2, - "any" - ], - "yoda": [ - 2, - "never" - ] + "wrap-iife": [2, "any"], + "yoda": [2, "never"] } } diff --git a/.travis.yml b/.travis.yml index d6e658ef..09768f0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ sudo: false language: node_js node_js: - "stable" + - "5" + - "4" - "0.12" - "0.10" matrix: diff --git a/package.json b/package.json index b0043a57..a824247e 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,16 @@ "index.js" ], "main": "index.js", - "bin": { - "verb": "bin/verb.js" - }, "engines": { "node": ">=0.10.0" }, "scripts": { "test": "mocha" }, + "preferGlobal": true, + "bin": { + "verb": "bin/verb.js" + }, "dependencies": { "assemble-core": "^0.11.2", "assemble-loader": "^0.3.0", @@ -29,7 +30,7 @@ "base-config": "^0.4.0", "base-fs-rename": "^0.1.0", "base-generators": "^0.1.4", - "base-pipeline": "^0.2.1", + "base-pipeline": "^0.2.3", "base-questions": "^0.3.0", "base-runner": "^0.5.6", "base-runtimes": "^0.1.2", @@ -47,29 +48,47 @@ "try-open": "^0.1.0" }, "devDependencies": { + "base-store": "^0.3.6", + "buffer-equal": "^1.0.0", + "consolidate": "^0.14.0", + "data-store": "^0.14.0", + "define-property": "^0.2.5", + "engine-base": "^0.1.2", + "engine-handlebars": "^0.8.0", + "event-stream": "^3.3.2", "generate-foo": "^0.1.5", - "generator-util": "^0.2.0", + "generator-util": "^0.2.4", + "get-value": "^2.0.3", "global-modules": "^0.2.0", + "graceful-fs": "^4.1.3", "gulp": "^3.9.0", "gulp-eslint": "^1.1.1", - "gulp-format-md": "*", + "gulp-format-md": "^0.1.5", "gulp-istanbul": "^0.10.3", "gulp-mocha": "^2.2.0", "inflection": "^1.8.0", + "is-buffer": "^1.1.2", + "load-pkg": "^3.0.1", "mocha": "*", - "spawn-commands": "^0.3.1" - }, - "keywords": [], - "lintDeps": { - "ignore": [] + "parser-front-matter": "^1.3.0", + "rimraf": "^2.5.1", + "should": "*", + "sinon": "^1.17.3", + "spawn-commands": "^0.3.1", + "swig": "^1.4.2", + "through2": "^2.0.0", + "vinyl": "^1.1.1" }, "verb": { + "related": { + "list": [] + }, "layout": "default", "plugins": [ "gulp-format-md" - ], - "related": { - "list": [] - } + ] + }, + "lintDeps": { + "ignore": [] } } From 37991848d886f4524b02fd2473644ffab9cb23e9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 01:09:49 -0500 Subject: [PATCH 149/282] clean up generators --- foo.js | 10 ------ lib/generators/compose-example.js | 29 ---------------- lib/generators/default.js | 11 ++---- lib/generators/init.js | 52 ++++++++++++++++++++++++++++ lib/generators/templates/verbfile.js | 11 +++--- lib/generators/verbfile2.js | 48 ------------------------- lib/generators/verbfiles2.js | 45 ------------------------ lib/generators/verbmd2.js | 32 ----------------- lib/generators/welcome.js | 40 --------------------- 9 files changed, 61 insertions(+), 217 deletions(-) delete mode 100644 foo.js delete mode 100644 lib/generators/compose-example.js create mode 100644 lib/generators/init.js delete mode 100644 lib/generators/verbfile2.js delete mode 100644 lib/generators/verbfiles2.js delete mode 100644 lib/generators/verbmd2.js delete mode 100644 lib/generators/welcome.js diff --git a/foo.js b/foo.js deleted file mode 100644 index fc23c4fa..00000000 --- a/foo.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -var Verb = require('./'); -var verb = new Verb(); - -var opts = { dot: true }; - -verb.conflicts('lib/generators/templates/*', opts, function(err, views) { - console.log(views) -}); diff --git a/lib/generators/compose-example.js b/lib/generators/compose-example.js deleted file mode 100644 index 50423182..00000000 --- a/lib/generators/compose-example.js +++ /dev/null @@ -1,29 +0,0 @@ -// 'use strict'; - -// var combined = require('stream-combiner'); -// var filter = require('filter-views'); - -// module.exports = function(verb, base) { -// var toStream = combine(verb, ['foo', 'bar', 'baz']); - -// verb.task('files', function() { -// return toStream('docs', filter('*.qux')) -// .pipe(verb.dest('.')) -// }); -// } - -// function combine(app, generators) { -// return function(names, filter) { -// app.compose(generators).views(names, filter); -// var streams = []; - -// if (typeof names === 'string') { -// names = [names]; -// } - -// names.forEach(function(name) { -// streams.push(app.toStream(name)); -// }); -// return combined(streams); -// }; -// } diff --git a/lib/generators/default.js b/lib/generators/default.js index d4361b4c..2cd9ed7a 100644 --- a/lib/generators/default.js +++ b/lib/generators/default.js @@ -1,13 +1,8 @@ 'use strict'; -module.exports = function(verb) { - verb.register('welcome', require('./welcome')); - verb.register('verbfiles', require('./verbfiles')); - verb.register('verbfile', require('./verbfile')); - verb.register('verbmd', require('./verbmd')); - +module.exports = function verbDefault(verb) { verb.task('default', function(cb) { - console.log('verb > default task'); - verb.generate('verbfiles', cb); + console.log('No tasks were defined, doing nothing.'); + cb(); }); }; diff --git a/lib/generators/init.js b/lib/generators/init.js new file mode 100644 index 00000000..b103366d --- /dev/null +++ b/lib/generators/init.js @@ -0,0 +1,52 @@ +'use strict'; + +var debug = require('debug')('verb:generator'); +var isYes = require('is-affirmative'); + +module.exports = function(app, base) { + var store = app.store; + + app.questions + .set('setTasks', { + message: 'Default tasks', + type: 'input', + force: true + }) + .set('run.before', { + message: 'Enter the names of the generators and tasks to run:', + type: 'input', + force: true + }) + .set('run.after', { + message: 'Enter the names of one or more tasks, generators, or generators and tasks to run after `end` is emitted:', + type: 'input', + force: true + }); + + app.task('default', function(cb) { + console.log('Welcome to verb! Since this is your first time running verb,'); + console.log('you can specify the names of tasks and/or generators to run'); + console.log('every time you run verb. Tips:'); + console.log('- to run the `readme` generator, just enter "readme"') + console.log('- to run the `default` generator, just enter "default"') + console.log('- search npm for verb generators, and enter the names of the generators to run'); + app.ask('setTasks', function(err, answers) { + if (err) return cb(err); + var tasks = answers.setTasks; + store.set('tasks', tasks); + console.log('Tasks "%s" will now run with the verb command', tasks); + cb(); + }); + }); + + app.task('run', function(cb) { + app.ask('run', function(err, answers) { + answers.run.before = answers.run.before.split(' '); + answers.run.after = answers.run.after.split(' '); + var res = answers.run; + console.log('implement me!'); + console.log(res); + cb(); + }); + }); +}; diff --git a/lib/generators/templates/verbfile.js b/lib/generators/templates/verbfile.js index 0b230ad2..5e8b9abf 100644 --- a/lib/generators/templates/verbfile.js +++ b/lib/generators/templates/verbfile.js @@ -1,12 +1,13 @@ 'use strict'; -var fs = require('fs'); +var extend = require('extend-shallow'); module.exports = function(verb) { - verb.doc('readme.md', {content: fs.readFileSync('.verb.md')}); + verb.extendWith('verb-generate-readme'); - verb.task('default', function(cb) { - console.log('verbfile > default task'); - cb(); + verb.helper('foo', function(name, locals) { + var ctx = extend({}, this.context, locals); }); + + verb.task('default', ['readme']); }; diff --git a/lib/generators/verbfile2.js b/lib/generators/verbfile2.js deleted file mode 100644 index 01983d4e..00000000 --- a/lib/generators/verbfile2.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var path = require('path'); -var isYes = require('is-affirmative'); -var utils = require('../utils'); - -module.exports = function(verb) { - verb.task('verbfile', function(cb) { - ask(verb, 'verbfile.js', cb); - }); - - verb.task('verbmd', function(cb) { - ask(verb, '.verb.md', cb); - }); - - verb.task('default', ['verbmd', 'verbfile']); -}; - -function ask(app, filename, cb) { - if (utils.exists(filename)) return cb(); - - app.questions - .set('verbfiles', 'Can\'t find a verbfile.js or .verb.md, want to add one?', {save: false}) - .set('choose', 'Which file(s) would you like to add?', {save: false}); - - app.ask('verbfiles', function(err, answers) { - if (err) return cb(err); - - if (isYes(answers.verbfiles)) { - app.choices('choose', ['verbfile.js', '.verb.md'], function(err, choices) { - if (err) return cb(err); - - app.src(choices.choose[0], {cwd: path.resolve(__dirname, 'templates')}) - .pipe(app.dest(app.cwd)) - .on('end', cb); - }); - - } else { - cb(); - } - }); -} - -function filter(name) { - return function(key, file) { - return file.basename === name; - }; -} diff --git a/lib/generators/verbfiles2.js b/lib/generators/verbfiles2.js deleted file mode 100644 index 2dc7f8f5..00000000 --- a/lib/generators/verbfiles2.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -var path = require('path'); -var isYes = require('is-affirmative'); -var utils = require('../utils'); - -module.exports = function(verb) { - verb.task('verbfile', function(cb) { - ask(verb, 'verbfile.js', cb); - }); - - verb.task('default', ['verbfile']); -}; - -function ask(app, filename, cb) { - console.log(filename) - if (utils.exists(filename)) return cb(); - - app.questions - .set('verbfiles', 'Can\'t find a verbfile.js or .verb.md, want to add one?', {save: false}) - .set('choose', 'Which file(s) would you like to add?', {save: false}); - - app.ask('verbfiles', function(err, answers) { - if (err) return cb(err); - - if (isYes(answers.verbfiles)) { - app.choices('choose', ['verbfile.js', '.verb.md'], function(err, choices) { - if (err) return cb(err); - - app.src(choices.choose[0], {cwd: path.resolve(__dirname, 'templates')}) - .pipe(app.dest(app.cwd)) - .on('end', cb); - }); - - } else { - cb(); - } - }); -} - -function filter(name) { - return function(key, file) { - return file.basename === name; - }; -} diff --git a/lib/generators/verbmd2.js b/lib/generators/verbmd2.js deleted file mode 100644 index e07ef4e3..00000000 --- a/lib/generators/verbmd2.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -var isYes = require('is-affirmative'); - -module.exports = function(verb) { - verb.task('default', function(cb) { - verb.questions - .set('verbmd', 'Can\'t find a .verb.md, want to add one?', { - save: false - }); - - verb.ask('verbmd', function(err, answers) { - if (err) { - cb(err); - return; - } - - if (!isYes(answers.verbmd)) { - cb(); - return; - } - - verb.src('templates/.verb.md', {cwd: __dirname}) - .pipe(verb.dest(verb.cwd)) - .on('end', function() { - console.log('created verbfile.js'); - cb(); - }); - - }); - }); -}; diff --git a/lib/generators/welcome.js b/lib/generators/welcome.js deleted file mode 100644 index 5d186807..00000000 --- a/lib/generators/welcome.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var debug = require('debug')('verb:generator'); -var isYes = require('is-affirmative'); - -module.exports = function(app) { - app.questions - .set('tasks', { - message: 'Would you like to choose generators or tasks to run automatically when you run verb?', - force: true - }) - .set('run.before', { - message: 'Enter the names of the generators and tasks to run:', - type: 'input', - force: true - }) - .set('run.after', { - message: 'Enter the names of the generators and tasks to run after `end` is emitted:', - type: 'input', - force: true - }); - - app.task('default', function(cb) { - app.ask('tasks', function(err, answers) { - if (isYes(answers.tasks)) { - app.ask('run', function(err, answers) { - answers.run.before = answers.run.before.split(' '); - answers.run.after = answers.run.after.split(' '); - var res = answers.run; - console.log('implement me!'); - console.log(res) - cb(); - }); - } else { - console.log('aborted'); - cb(); - } - }); - }); -}; From 2c0331910bc5ecb30c10e059e67ccf87a0edf2b4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 01:10:10 -0500 Subject: [PATCH 150/282] simplify cli --- bin/verb.js | 20 ++++++-------------- lib/config.js | 2 +- lib/config/helpers.js | 2 +- lib/config/update.js | 2 +- lib/plugins.js | 1 + lib/settings.js | 17 ++++++++--------- lib/settings/configfile.js | 12 +++++------- lib/settings/store.js | 5 +++++ 8 files changed, 28 insertions(+), 33 deletions(-) create mode 100644 lib/settings/store.js diff --git a/bin/verb.js b/bin/verb.js index 12a59806..3db8ea76 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,32 +1,24 @@ #!/usr/bin/env node process.env.VERB_CLI = true; -var runtimes = require('base-runtimes'); var Verb = require('..'); var verb = new Verb(); -// register default "fallback" generator -verb.register('fallback', require('../lib/generators/default')); -verb.register('generator', require('../lib/generators/generator')); -verb.register('verbfile', require('../lib/generators/verbfile')); -verb.register('verbmd', require('../lib/generators/verbmd')); - // run generator and/or tasks -verb.runner('verbfile.js', function(err, argv, app) { +verb.runner('verbfile.js', function(err, opts, app) { if (err) { console.error(err.message); process.exit(1); } - app.use(runtimes(config)); - - var config = app.loadSettings(argv); - app.set('cache.config', config); + if (!app.hasConfigfile) { + app.register('default', require('../lib/generators/default')); + } - app.config.process(config, function(err) { + app.config.process(opts, function(err) { if (err) throw err; - app.cli.process(argv, function(err) { + app.cli.process(opts, function(err) { if (err) throw err; verb.emit('done'); diff --git a/lib/config.js b/lib/config.js index c3d16b3b..be6212a5 100644 --- a/lib/config.js +++ b/lib/config.js @@ -8,7 +8,7 @@ var plugins = require('./plugins'); module.exports = function(options) { return function(app) { - this.use(plugins.config()); + this.use(plugins.config(this.options)); this.config .map('data') diff --git a/lib/config/helpers.js b/lib/config/helpers.js index 2f8de881..db4369a3 100644 --- a/lib/config/helpers.js +++ b/lib/config/helpers.js @@ -6,7 +6,7 @@ module.exports = function(app) { if (helpers.hasOwnProperty(key)) { var val = helpers[key]; - if (val.async = true) { + if (val.async === true) { this.asyncHelper(key, val.fn); } else { this.helper(key, val.fn); diff --git a/lib/config/update.js b/lib/config/update.js index 9477c7ec..51419f02 100644 --- a/lib/config/update.js +++ b/lib/config/update.js @@ -2,6 +2,6 @@ module.exports = function(app) { return function(val) { - console.log('implement me!'); + console.log('verb.update config property is not implemented yet'); }; }; diff --git a/lib/plugins.js b/lib/plugins.js index e018c9ac..6418bb09 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -13,6 +13,7 @@ require('base-config', 'config'); require('base-fs-rename', 'rename'); require('base-generators', 'generators'); require('base-runner', 'runner'); +require('base-runtimes', 'runtimes'); require('base-pipeline', 'pipeline'); require('base-questions', 'ask'); diff --git a/lib/settings.js b/lib/settings.js index ef1de611..6c96efeb 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -8,24 +8,23 @@ var schema = require('./schema'); * Create */ -module.exports = function(options) { +module.exports = function(config) { return function() { - this.use(schema(options)); + this.use(schema(config)); this.define('settings', function(options) { return new Settings(this.schema(options)) }); - this.define('loadSettings', function(argv) { - var config = this.settings() - .set('verb', settings.pkg(this, 'verb')) + this.define('loadSettings', function(argv, options) { + var config = this.settings(this.options) + .set('runner', options || {}) + .set('store', settings.store(this)) .set('file', settings.configfile(this, 'verbfile')) .set('opts', settings.opts(this)) + .set('foo', settings.pkg(this, 'verb')) .set('argv', argv); - - var opts = config.merge(); - this.option(opts); - return opts; + return config; }); }; }; diff --git a/lib/settings/configfile.js b/lib/settings/configfile.js index 059aa3c8..783483f6 100644 --- a/lib/settings/configfile.js +++ b/lib/settings/configfile.js @@ -1,24 +1,22 @@ 'use strict'; var path = require('path'); -var merge = require('mixin-deep'); -var util = require('generator-util'); -var glob = require('matched'); +var utils = require('../utils'); module.exports = function(app, configfile) { var configname = path.basename(configfile, path.extname(configfile)); - var configfiles = glob.sync(configname + '.*', {cwd: app.cwd}); - var len = configfiles.length; + var files = utils.glob.sync(configname + '.*', { cwd: app.cwd }); + var len = files.length; var idx = -1; var res = {}; while (++idx < len) { - var name = configfiles[idx]; + var name = files[idx]; var ext = path.extname(name); var configpath = path.resolve(app.cwd, name); if (ext === '.json') { - merge(res, require(configpath)); + utils.merge(res, require(configpath)); } } return res; diff --git a/lib/settings/store.js b/lib/settings/store.js new file mode 100644 index 00000000..c57b14bc --- /dev/null +++ b/lib/settings/store.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function(app) { + return app.store.data; +}; From fd67a59d833d8c716f9faa3ffb84ea437ff7d267 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 15:00:03 -0500 Subject: [PATCH 151/282] move cli/config logic into base-runner and base-cli --- bin/verb.js | 19 ++- index.js | 63 +++------- lib/config.js | 76 ------------ lib/config/engines.js | 12 -- lib/config/helpers.js | 17 --- lib/config/index.js | 1 - lib/config/options.js | 7 -- lib/config/plugins.js | 15 --- lib/config/update.js | 7 -- lib/generators/init.js | 5 + lib/plugins.js | 1 - lib/schema.js | 231 ------------------------------------- lib/settings.js | 30 ----- lib/settings/configfile.js | 23 ---- lib/settings/index.js | 1 - lib/settings/opts.js | 5 - lib/settings/pkg.js | 34 ------ lib/settings/store.js | 5 - lib/utils.js | 3 +- package.json | 42 +++---- 20 files changed, 58 insertions(+), 539 deletions(-) delete mode 100644 lib/config.js delete mode 100644 lib/config/engines.js delete mode 100644 lib/config/helpers.js delete mode 100644 lib/config/index.js delete mode 100644 lib/config/options.js delete mode 100644 lib/config/plugins.js delete mode 100644 lib/config/update.js delete mode 100644 lib/schema.js delete mode 100644 lib/settings.js delete mode 100644 lib/settings/configfile.js delete mode 100644 lib/settings/index.js delete mode 100644 lib/settings/opts.js delete mode 100644 lib/settings/pkg.js delete mode 100644 lib/settings/store.js diff --git a/bin/verb.js b/bin/verb.js index 3db8ea76..98745642 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,11 +1,12 @@ #!/usr/bin/env node process.env.VERB_CLI = true; + var Verb = require('..'); var verb = new Verb(); // run generator and/or tasks -verb.runner('verbfile.js', function(err, opts, app) { +verb.runner('verbfile.js', function(err, argv, app) { if (err) { console.error(err.message); process.exit(1); @@ -15,10 +16,22 @@ verb.runner('verbfile.js', function(err, opts, app) { app.register('default', require('../lib/generators/default')); } - app.config.process(opts, function(err) { + var config = app.get('cache.config'); + + app.on('error', function(err) { + console.log(app.env); + console.log(); + + if (err.reason) { + console.log(err.reason); + process.exit(1); + } + }); + + app.config.process(config, function(err) { if (err) throw err; - app.cli.process(opts, function(err) { + app.cli.process(argv, function(err) { if (err) throw err; verb.emit('done'); diff --git a/index.js b/index.js index bb2b28df..9bc437ed 100644 --- a/index.js +++ b/index.js @@ -9,12 +9,9 @@ var fs = require('fs'); var async = require('async'); -var debug = require('debug')('verb'); -var util = require('generator-util'); +var debug = require('debug')('base:verb'); var Assemble = require('assemble-core'); -var settings = require('./lib/settings'); var plugins = require('./lib/plugins'); -var config = require('./lib/config'); var utils = require('./lib/utils'); /** @@ -34,7 +31,6 @@ function Verb(options) { } this.options = utils.extend({}, this.options, options); Assemble.call(this, options); - this.is('Verb'); this.initVerb(this.options); } @@ -49,10 +45,13 @@ Assemble.extend(Verb); */ Verb.prototype.initVerb = function(opts) { + this.is('Verb'); + this.name = 'verb'; this.configfile = opts.configfile || 'verbfile.js'; this.prefix = opts.prefix || 'verb-generate'; this.data({runner: require('./package')}); + this.data({verb: {related: {}, reflinks: []}}); this.initPlugins(this.options); this.create('files'); @@ -64,14 +63,17 @@ Verb.prototype.initVerb = function(opts) { var plugin = this.plugin; this.define('plugin', function(name) { - var pipeline = this.options.pipeline; - if (arguments.length === 1 && pipeline) { + var pipeline = this.options.pipeline || []; + if (arguments.length === 1 && pipeline.length) { var idx = pipeline.indexOf(name); if (idx !== -1) { pipeline.splice(idx, 1); } + } else if (!~pipeline.indexOf(name)) { + pipeline.push(name); } - return plugin.apply(this, arguments); + plugin.apply(this, arguments); + return this; }); }; @@ -86,55 +88,18 @@ Verb.prototype.initPlugins = function(opts) { this.use(plugins.runner()); this.use(plugins.rename({replace: true})); this.use(plugins.ask()); - this.use(settings()); - this.use(config()); if (opts.cli === true || process.env.VERB_CLI) { + this.use(plugins.runtimes(opts)); // modify create, dest and src methods to automatically // use cwd from generators unless overridden by the user - util.create(this); - util.dest(this); - util.src(this); + this.use(utils.create()); + utils.dest(this); + utils.src(this); } }; -Verb.prototype.conflicts = function(patterns, options, cb) { - if (typeof options === 'function') { - cb = options; - options = {}; - } - - this.overwrite = false; - var app = this; - - // function overwrite(filename, cb) { - // var msg = filename + ' already exists. Do you want to overwrite it?'; - // app.questions.set('conflict', msg, { save: false }); - - // app.ask('conflict', function(err, answers) { - - // }); - // } - - var views = this.collection(); - - utils.glob(patterns, options, function(err, files) { - if (err) return cb(err); - - async.each(files, function(fp, next) { - views.addView(fp); - next(); - }, function(err) { - if (err) { - cb(err); - } else { - cb(null, views); - } - }); - }); -}; - /** * Expose static `is*` methods from Templates */ diff --git a/lib/config.js b/lib/config.js deleted file mode 100644 index be6212a5..00000000 --- a/lib/config.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -var path = require('path'); -var typeOf = require('kind-of'); -var merge = require('mixin-deep'); -var config = require('./config/'); -var plugins = require('./plugins'); - -module.exports = function(options) { - return function(app) { - this.use(plugins.config(this.options)); - - this.config - .map('data') - // .map('create') - // .map('templates') - .map('reflinks', function(val) { - val = Array.isArray(val) ? val : [val]; - // this.pkg.set('verb.reflinks', val); - }) - .map('options', config.options(this)) - .map('option', 'options') - .map('plugins', config.plugins(this)) - .map('helpers', config.helpers(this)) - .map('engines', config.engines(this)) - }; -}; - -function normalize(val, key, options, schema) { - var res = {}; - switch(typeOf(val)) { - case 'array': - res = configArray(val, key, options, schema); - break; - case 'object': - res = configObject(val, key, options, schema); - break; - case 'string': - res = configString(val, key, options, schema); - break; - } - return res; -} - -function configString(val, key, options, schema) { - var res = {}; - res[val] = { key: val }; - return configObject(res, key, options, schema); -} - -function configObject(obj, key, options, schema) { - var res = {}; - - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { - var val = obj[prop]; - - if (typeof val === 'string') { - val = { name: val }; - } - - - } - } - return res; -} - -function configArray(val, key, options, schema) { - var len = val.length; - var idx = -1; - var res = {}; - while (++idx < len) { - merge(res, normalize(val[idx], key, options, schema)); - } - return res; -} diff --git a/lib/config/engines.js b/lib/config/engines.js deleted file mode 100644 index ff930bd5..00000000 --- a/lib/config/engines.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -module.exports = function(app) { - return function(engines) { - for (var key in engines) { - if (engines.hasOwnProperty(key)) { - var val = engines[key]; - this.engine(key, val.fn, val.options); - } - } - }; -}; diff --git a/lib/config/helpers.js b/lib/config/helpers.js deleted file mode 100644 index db4369a3..00000000 --- a/lib/config/helpers.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -module.exports = function(app) { - return function(helpers) { - for (var key in helpers) { - if (helpers.hasOwnProperty(key)) { - var val = helpers[key]; - - if (val.async === true) { - this.asyncHelper(key, val.fn); - } else { - this.helper(key, val.fn); - } - } - } - }; -}; diff --git a/lib/config/index.js b/lib/config/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/config/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/lib/config/options.js b/lib/config/options.js deleted file mode 100644 index 3bfc8ee3..00000000 --- a/lib/config/options.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = function(app) { - return function(val) { - this.option(val); - }; -}; diff --git a/lib/config/plugins.js b/lib/config/plugins.js deleted file mode 100644 index 64d839bb..00000000 --- a/lib/config/plugins.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -/** - * Load pipeline plugins - */ - -module.exports = function(app) { - return function(plugins) { - for (var key in plugins) { - if (plugins.hasOwnProperty(key)) { - this.plugin(key, plugins[key]); - } - } - }; -}; diff --git a/lib/config/update.js b/lib/config/update.js deleted file mode 100644 index 51419f02..00000000 --- a/lib/config/update.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = function(app) { - return function(val) { - console.log('verb.update config property is not implemented yet'); - }; -}; diff --git a/lib/generators/init.js b/lib/generators/init.js index b103366d..0389fb1c 100644 --- a/lib/generators/init.js +++ b/lib/generators/init.js @@ -12,6 +12,11 @@ module.exports = function(app, base) { type: 'input', force: true }) + .set('run', { + message: 'Do you want to run tasks when arbitrary flags are passed?', + type: 'input', + force: true + }) .set('run.before', { message: 'Enter the names of the generators and tasks to run:', type: 'input', diff --git a/lib/plugins.js b/lib/plugins.js index 6418bb09..f4c90cca 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -9,7 +9,6 @@ require = plugins; */ require('assemble-loader', 'loader'); -require('base-config', 'config'); require('base-fs-rename', 'rename'); require('base-generators', 'generators'); require('base-runner', 'runner'); diff --git a/lib/schema.js b/lib/schema.js deleted file mode 100644 index 6b95ad95..00000000 --- a/lib/schema.js +++ /dev/null @@ -1,231 +0,0 @@ -'use strict'; - -var path = require('path'); -var typeOf = require('kind-of'); -var Schema = require('map-schema'); -var merge = require('mixin-deep'); -var utils = require('generator-util'); -var inflect = require('inflection'); -var normalized = {}; - -module.exports = function(config) { - return function(app) { - if (this.isRegistered('base-schema')) return; - - this.define('schema', function(options) { - var opts = merge({app: this}, config, this.options, options); - opts.sortArrays = false; - var schema = new Schema(opts) - .field('options', 'object') - .field('data', 'object') - - .field('plugins', ['array', 'object', 'string'], { - normalize: function(val, key, options, schema) { - if (normalized[key]) return normalized[key]; - var pipeline = []; - var res = {}; - - if (Array.isArray(val)) { - app.option('pipeline', val); - var len = val.length; - var idx = -1; - - while (++idx < len) { - var name = val[idx]; - if (!app.plugins.hasOwnProperty(name)) { - res[name] = utils.tryRequire(name); - } - } - } - normalized[key] = res; - return res; - } - }) - .field('helpers', ['array', 'object', 'string'], { - normalize: normalize - }) - .field('asyncHelpers', ['array', 'object', 'string'], { - normalize: normalize - }) - .field('engines', ['array', 'object', 'string'], { - normalize: normalize - }) - // .field('use', ['array', 'object', 'string'], { - // normalize: normalize - // }) - .field('create', 'object', { - normalize: function(obj, key, options, schema) { - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { - if (!app[prop]) app.create(prop, obj[prop]); - } - } - } - }) - .field('layout', ['object', 'string']) - .field('templates', ['array', 'object'], { - normalize: function(val, key, options, schema) { - if (typeof val === 'string') { - - } else if (Array.isArray(val)) { - - - } else if (typeof val === 'object') { - for (var prop in val) { - if (val.hasOwnProperty(prop)) { - var value = val[prop]; - - if (!app[prop]) app.create(prop); - - if (typeof value === 'string') { - var obj = tryRequire(path.resolve(value)); - if (obj) app[prop].addViews(obj); - } - } - } - } - } - }); - - return schema; - }); - }; -}; - -function normalize(val, key, options, schema) { - if (normalized[key]) return normalized[key]; - var res = {}; - - switch(typeOf(val)) { - case 'array': - res = configArray(val, key, options, schema); - break; - case 'object': - res = configObject(val, key, options, schema); - break; - case 'string': - res = configString(val, key, options, schema); - break; - } - - normalized[key] = res; - return res; -} - -function configString(val, key, options, schema) { - var res = {}; - var obj = { key: val }; - - if (val.indexOf('./') === 0) { - obj.path = path.resolve(val); - } - - res[val] = obj; - return normalize(res, key, options, schema); -} - -function configObject(obj, key, options, schema) { - var app = schema.options.app; - var res = {}; - - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { - var val = obj[prop]; - if (val.isNormalized) { - res[prop] = val; - continue; - } - - if (app[key] && typeof app[key][prop] === 'function') { - val.fn = app[key][prop]; - - } else if (typeof val === 'string' && key === prop) { - var o = tryRequire(path.resolve(val)); - if (typeof o === 'function') { - val.fn = o; - } - - if (o && typeof o === 'object') { - for (var k in o) { - var v = o[k]; - res[k] = { fn: v, name: k }; - } - continue; - } - } - - if (typeof val === 'string') { - val = { name: val }; - } - - var name = val.name || prop; - var alias = toAlias(key, prop); - delete val.name; - - var opts = val.options || { options: val, name: name }; - if (val.hasOwnProperty('path')) { - opts.path = path.resolve(val.path); - delete val.path; - } - - var modulename = opts.path || opts.name; - if (typeof val.fn !== 'function') { - var fn = tryRequire(modulename); - var fp; - - if (!fn) { - fp = path.resolve(app.cwd, 'node_modules', modulename); - fn = tryRequire(fp); - if (fn) opts.path = fp; - } - - if (typeof fn === 'undefined') { - handleError(app, modulename, 'helpers'); - continue; - } - opts.fn = val.fn || fn; - } - opts.isNormalized = true; - res[alias] = opts; - } - } - return res; -} - -function configArray(val, key, options, schema) { - var len = val.length; - var idx = -1; - var res = {}; - while (++idx < len) { - merge(res, normalize(val[idx], key, options, schema)); - } - return res; -} - -function toAlias(prefix, key) { - if (key.length === 1) return key; - var re = new RegExp('^(' + inflections(prefix) + ')(\\W+)?'); - return key.replace(re, ''); -} - -function inflections(key) { - var single = inflect.singularize(key); - var plural = inflect.pluralize(key); - return [single, plural].join('|'); -} - -function handleError(app, name, prop) { - var appname = app.constructor.name.toLowerCase() + '.' + prop; - console.error('cannot resolve: `%s`, in package.json `%s` config', name, appname); -} - - -function tryRequire(name) { - try { - return require(name); - } catch (err) {} - - try { - return require(path.resolve(name)); - } catch (err) {} -} diff --git a/lib/settings.js b/lib/settings.js deleted file mode 100644 index 6c96efeb..00000000 --- a/lib/settings.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var Settings = require('merge-settings'); -var settings = require('./settings/'); -var schema = require('./schema'); - -/** - * Create - */ - -module.exports = function(config) { - return function() { - this.use(schema(config)); - - this.define('settings', function(options) { - return new Settings(this.schema(options)) - }); - - this.define('loadSettings', function(argv, options) { - var config = this.settings(this.options) - .set('runner', options || {}) - .set('store', settings.store(this)) - .set('file', settings.configfile(this, 'verbfile')) - .set('opts', settings.opts(this)) - .set('foo', settings.pkg(this, 'verb')) - .set('argv', argv); - return config; - }); - }; -}; diff --git a/lib/settings/configfile.js b/lib/settings/configfile.js deleted file mode 100644 index 783483f6..00000000 --- a/lib/settings/configfile.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('../utils'); - -module.exports = function(app, configfile) { - var configname = path.basename(configfile, path.extname(configfile)); - var files = utils.glob.sync(configname + '.*', { cwd: app.cwd }); - var len = files.length; - var idx = -1; - var res = {}; - - while (++idx < len) { - var name = files[idx]; - var ext = path.extname(name); - - var configpath = path.resolve(app.cwd, name); - if (ext === '.json') { - utils.merge(res, require(configpath)); - } - } - return res; -}; diff --git a/lib/settings/index.js b/lib/settings/index.js deleted file mode 100644 index 23b2930d..00000000 --- a/lib/settings/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('export-files')(__dirname); diff --git a/lib/settings/opts.js b/lib/settings/opts.js deleted file mode 100644 index d03e4af4..00000000 --- a/lib/settings/opts.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = function(app) { - return app.options; -}; diff --git a/lib/settings/pkg.js b/lib/settings/pkg.js deleted file mode 100644 index 6de40771..00000000 --- a/lib/settings/pkg.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -module.exports = function(app, prop) { - return app.pkg.get(prop) || {}; - // var pkg = require('./package'); - // var verb = pkg.verb || {}; - - // var schema = new Schema(options) - // .field('layout', ['object', 'string', 'null']) - // .field('related', ['array', 'object']) - // .field('reflinks', ['array', 'object']) - // .field('plugins', ['array', 'object'], { - // normalize: function(val) { - // if (typeof val === 'string') { - // return [val]; - // } - // return val; - // } - // }); - - // return schema.normalize(verb); -}; - - // .field('verb', 'object', verbSchema({ - // keys: [ - // 'layout', - // 'options', - // 'data', - // 'plugins', - // 'helpers', - // 'related', - // 'reflinks' - // ] - // })) diff --git a/lib/settings/store.js b/lib/settings/store.js deleted file mode 100644 index c57b14bc..00000000 --- a/lib/settings/store.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = function(app) { - return app.store.data; -}; diff --git a/lib/utils.js b/lib/utils.js index df1a84de..935dbe84 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,7 +1,7 @@ 'use strict'; var path = require('path'); -var utils = require('lazy-cache')(require); +var utils = require('generator-util'); var fn = require; require = utils; @@ -10,7 +10,6 @@ require = utils; */ require('extend-shallow', 'extend'); -require('resolve-glob', 'glob'); require('is-affirmative'); require('try-open'); require = fn; diff --git a/package.json b/package.json index a824247e..d992323a 100644 --- a/package.json +++ b/package.json @@ -27,27 +27,21 @@ "assemble-core": "^0.11.2", "assemble-loader": "^0.3.0", "async": "^1.5.2", - "base-config": "^0.4.0", - "base-fs-rename": "^0.1.0", - "base-generators": "^0.1.4", + "base-fs-rename": "^0.1.1", + "base-generators": "^0.1.6", "base-pipeline": "^0.2.3", - "base-questions": "^0.3.0", - "base-runner": "^0.5.6", - "base-runtimes": "^0.1.2", + "base-questions": "^0.3.2", + "base-runner": "^0.5.8", + "base-runtimes": "^0.1.3", "debug": "^2.2.0", - "export-files": "^2.1.1", "extend-shallow": "^2.0.1", + "generator-util": "^0.2.5", "is-affirmative": "^0.1.0", - "kind-of": "^3.0.2", "lazy-cache": "^1.0.3", - "map-schema": "^0.1.1", - "matched": "^0.4.1", - "merge-settings": "^0.1.0", - "mixin-deep": "^1.1.3", - "resolve-glob": "^0.1.8", "try-open": "^0.1.0" }, "devDependencies": { + "base-config": "^0.4.0", "base-store": "^0.3.6", "buffer-equal": "^1.0.0", "consolidate": "^0.14.0", @@ -57,7 +51,6 @@ "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", "generate-foo": "^0.1.5", - "generator-util": "^0.2.4", "get-value": "^2.0.3", "global-modules": "^0.2.0", "graceful-fs": "^4.1.3", @@ -66,11 +59,13 @@ "gulp-format-md": "^0.1.5", "gulp-istanbul": "^0.10.3", "gulp-mocha": "^2.2.0", - "inflection": "^1.8.0", "is-buffer": "^1.1.2", + "kind-of": "^3.0.2", "load-pkg": "^3.0.1", + "matched": "^0.4.1", "mocha": "*", "parser-front-matter": "^1.3.0", + "resolve-glob": "^0.1.8", "rimraf": "^2.5.1", "should": "*", "sinon": "^1.17.3", @@ -80,15 +75,22 @@ "vinyl": "^1.1.1" }, "verb": { - "related": { - "list": [] - }, "layout": "default", + "tasks": [ + "readme" + ], "plugins": [ "gulp-format-md" - ] + ], + "related": { + "list": [ + "assemble", + "base", + "generate" + ] + } }, "lintDeps": { "ignore": [] } -} +} \ No newline at end of file From f0566b1febfa70ebf5bf83adf2b37be09ab263cb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 15:00:14 -0500 Subject: [PATCH 152/282] verbfile and readme --- readme.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ verbfile.js | 8 +++++ 2 files changed, 99 insertions(+) create mode 100644 readme.md create mode 100644 verbfile.js diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..7f5ddd17 --- /dev/null +++ b/readme.md @@ -0,0 +1,91 @@ +# verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg)](https://travis-ci.org/verbose/verb) + +> Documentation generator for GitHub projects. + +## Install + +Install with [npm](https://www.npmjs.com/): + +```sh +$ npm i verb --save +``` + +## Usage + +```js +var verb = require('verb'); +``` + +## verbfile + +Your project's `verbfile.js` must either export an instance of Verb, or a function. + +**Instance** + +```js +var verb = require('verb'); +var app = verb(); + +module.exports = app; +``` + +**Function** + +```js +module.exports = function(verb) { + // "private" verb instance is created for this verbfile +}; +``` + +Also, when a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). + +## API + +### [Verb](index.js#L28) + +Create an instance of `Verb` with the given `options` + +**Params** + +* `options` **{Object}**: Settings to initialize with. + +**Example** + +```js +var Verb = require('verb'); +var verb = new Verb(); +``` + +## Related projects + +* [assemble](https://www.npmjs.com/package/assemble): Assemble is a powerful, extendable and easy to use static site generator for node.js. Used… [more](https://www.npmjs.com/package/assemble) | [homepage](https://github.com/assemble/assemble) +* [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base) | [homepage](https://github.com/node-base/base) +* [generate](https://www.npmjs.com/package/generate): Fast, composable, highly extendable project generator with a user-friendly and expressive API. | [homepage](https://github.com/generate/generate) + +## Running tests + +Install dev dependencies: + +```sh +$ npm i -d && npm test +``` + +## Contributing + +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). + +## Author + +**Jon Schlinkert** + +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) + +## License + +Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert) +Released under the [MIT license](https://github.com/verbose/verb/blob/master/LICENSE). + +*** + +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 09, 2016._ \ No newline at end of file diff --git a/verbfile.js b/verbfile.js new file mode 100644 index 00000000..1e8d2281 --- /dev/null +++ b/verbfile.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function custom(app) { + app.task('foo', function(cb) { + console.log('verb > verbfile >', this.name, 'task'); + cb(); + }); +}; From 682ac844f852f92c7d69a6ec8f8a8f6a48254a9a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 16:49:57 -0500 Subject: [PATCH 153/282] update verbfile to use readme generator --- verbfile.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/verbfile.js b/verbfile.js index 1e8d2281..89fdfec0 100644 --- a/verbfile.js +++ b/verbfile.js @@ -1,8 +1,12 @@ 'use strict'; -module.exports = function custom(app) { - app.task('foo', function(cb) { +module.exports = function(verb) { + verb.extendWith('readme'); + + verb.task('foo', function(cb) { console.log('verb > verbfile >', this.name, 'task'); cb(); }); + + verb.task('default', ['readme']); }; From 7eb039cc73e4902d26d195c75612b9a28c28aa9a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 16:50:35 -0500 Subject: [PATCH 154/282] minor formatting --- bin/verb.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 98745642..f6b627a8 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -16,8 +16,6 @@ verb.runner('verbfile.js', function(err, argv, app) { app.register('default', require('../lib/generators/default')); } - var config = app.get('cache.config'); - app.on('error', function(err) { console.log(app.env); console.log(); @@ -28,6 +26,8 @@ verb.runner('verbfile.js', function(err, argv, app) { } }); + var config = app.get('cache.config'); + app.config.process(config, function(err) { if (err) throw err; From c6c015f95033822ee61b2bfc54b06155f563afb9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 9 Feb 2016 18:34:40 -0500 Subject: [PATCH 155/282] inherit generate --- bin/verb.js | 4 +-- gulpfile.js | 37 +++++++++++++++++++++++ index.js | 78 +++++++++++++----------------------------------- lib/plugins.js | 23 -------------- package.json | 19 ++++-------- test/app.doc.js | 1 + test/app.docs.js | 1 + 7 files changed, 67 insertions(+), 96 deletions(-) delete mode 100644 lib/plugins.js diff --git a/bin/verb.js b/bin/verb.js index f6b627a8..76103d8c 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -process.env.VERB_CLI = true; +process.env.GENERATE_CLI = true; var Verb = require('..'); var verb = new Verb(); @@ -12,7 +12,7 @@ verb.runner('verbfile.js', function(err, argv, app) { process.exit(1); } - if (!app.hasConfigfile) { + if (!app.hasConfigfile && app.isDefaultTask) { app.register('default', require('../lib/generators/default')); } diff --git a/gulpfile.js b/gulpfile.js index aba1612d..93b294f6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,6 +4,7 @@ var gulp = require('gulp'); var mocha = require('gulp-mocha'); var istanbul = require('gulp-istanbul'); var eslint = require('gulp-eslint'); +var through = require('through2'); gulp.task('coverage', function() { return gulp.src(['index.js', 'lib/*.js']) @@ -23,4 +24,40 @@ gulp.task('eslint', function() { .pipe(eslint.format()); }); +gulp.task('vars', function() { + var utils = require('./lib/utils'); + var keys = Object.keys(utils); + var report = {}; + var cache = {}; + + return gulp.src(['index.js', 'lib/**/*.js', 'bin/*.']) + .pipe(through.obj(function(file, enc, next) { + var str = file.contents.toString(); + keys.forEach(function(key) { + report[key] = report[key] || 0; + var re = cache[key] || (cache[key] = new RegExp('\\.' + key, 'g')); + var m = str.match(re); + if (!m) return; + report[key]++; + }); + + next(null, file); + }, function(next) { + var keys = Object.keys(report); + var res = {}; + + keys.sort(function(a, b) { + return report[a] > report[b] ? -1 : 1; + }); + + keys.forEach(function(key) { + res[key] = report[key]; + }); + + console.log(res); + console.log(keys.length + 1, 'modules'); + next(); + })) +}); + gulp.task('default', ['mocha', 'eslint']); diff --git a/index.js b/index.js index 9bc437ed..11b55185 100644 --- a/index.js +++ b/index.js @@ -7,12 +7,10 @@ 'use strict'; -var fs = require('fs'); -var async = require('async'); var debug = require('debug')('base:verb'); -var Assemble = require('assemble-core'); -var plugins = require('./lib/plugins'); +var Generate = require('generate'); var utils = require('./lib/utils'); +var pkg = require('./package'); /** * Create an instance of `Verb` with the given `options` @@ -29,82 +27,46 @@ function Verb(options) { if (!(this instanceof Verb)) { return new Verb(options); } - this.options = utils.extend({}, this.options, options); - Assemble.call(this, options); + this.options = this.verbDefaults(options); + Generate.call(this, this.options); this.initVerb(this.options); } /** - * Extend `Assemble` + * Extend `Generate` */ -Assemble.extend(Verb); +Generate.extend(Verb); /** * Initialize verb defaults */ -Verb.prototype.initVerb = function(opts) { - this.is('Verb'); - this.name = 'verb'; - this.configfile = opts.configfile || 'verbfile.js'; - this.prefix = opts.prefix || 'verb-generate'; - - this.data({runner: require('./package')}); - this.data({verb: {related: {}, reflinks: []}}); - this.initPlugins(this.options); - - this.create('files'); - this.create('docs'); - this.define('util', utils); - this.define('lazyCreate', function(name, opts) { - if (!this[name]) this.create(name, opts); - }); - - var plugin = this.plugin; - this.define('plugin', function(name) { - var pipeline = this.options.pipeline || []; - if (arguments.length === 1 && pipeline.length) { - var idx = pipeline.indexOf(name); - if (idx !== -1) { - pipeline.splice(idx, 1); - } - } else if (!~pipeline.indexOf(name)) { - pipeline.push(name); - } - plugin.apply(this, arguments); - return this; - }); +Verb.prototype.verbDefaults = function(options) { + debug('initializing verb defaults'); + var defaults = {prefix: 'verb-generate', configfile: 'verbfile.js'}; + return utils.extend({}, defaults, this.options, options); }; /** - * Initialize verb plugins + * Initialize verb data */ -Verb.prototype.initPlugins = function(opts) { - this.use(plugins.generators({prefix: 'verb-generate'})); - this.use(plugins.pipeline()); - this.use(plugins.loader()); - this.use(plugins.runner()); - this.use(plugins.rename({replace: true})); - this.use(plugins.ask()); - - if (opts.cli === true || process.env.VERB_CLI) { - this.use(plugins.runtimes(opts)); - - // modify create, dest and src methods to automatically - // use cwd from generators unless overridden by the user - this.use(utils.create()); - utils.dest(this); - utils.src(this); - } +Verb.prototype.initVerb = function(opts) { + debug('initializing verb data'); + this.is('verb'); + this.name = 'verb'; + this.data({runner: pkg}); + this.data({verb: {related: {}, reflinks: []}}); + // this.create('files'); + // this.create('docs'); }; /** * Expose static `is*` methods from Templates */ -Assemble._.plugin.is(Verb); +Generate._.plugin.is(Verb); /** * Expose `Verb` diff --git a/lib/plugins.js b/lib/plugins.js deleted file mode 100644 index f4c90cca..00000000 --- a/lib/plugins.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var plugins = require('lazy-cache')(require); -var fn = require; -require = plugins; - -/** - * Plugins - */ - -require('assemble-loader', 'loader'); -require('base-fs-rename', 'rename'); -require('base-generators', 'generators'); -require('base-runner', 'runner'); -require('base-runtimes', 'runtimes'); -require('base-pipeline', 'pipeline'); -require('base-questions', 'ask'); - -/** - * Expose plugins - */ - -module.exports = plugins; diff --git a/package.json b/package.json index d992323a..795798d4 100644 --- a/package.json +++ b/package.json @@ -24,23 +24,16 @@ "verb": "bin/verb.js" }, "dependencies": { - "assemble-core": "^0.11.2", - "assemble-loader": "^0.3.0", - "async": "^1.5.2", - "base-fs-rename": "^0.1.1", - "base-generators": "^0.1.6", - "base-pipeline": "^0.2.3", "base-questions": "^0.3.2", - "base-runner": "^0.5.8", - "base-runtimes": "^0.1.3", "debug": "^2.2.0", "extend-shallow": "^2.0.1", + "generate": "^0.3.6", "generator-util": "^0.2.5", "is-affirmative": "^0.1.0", - "lazy-cache": "^1.0.3", "try-open": "^0.1.0" }, "devDependencies": { + "async": "^1.5.2", "base-config": "^0.4.0", "base-store": "^0.3.6", "buffer-equal": "^1.0.0", @@ -54,7 +47,7 @@ "get-value": "^2.0.3", "global-modules": "^0.2.0", "graceful-fs": "^4.1.3", - "gulp": "^3.9.0", + "gulp": "^3.9.1", "gulp-eslint": "^1.1.1", "gulp-format-md": "^0.1.5", "gulp-istanbul": "^0.10.3", @@ -67,11 +60,11 @@ "parser-front-matter": "^1.3.0", "resolve-glob": "^0.1.8", "rimraf": "^2.5.1", - "should": "*", + "should": "^8.2.2", "sinon": "^1.17.3", "spawn-commands": "^0.3.1", "swig": "^1.4.2", - "through2": "^2.0.0", + "through2": "^2.0.1", "vinyl": "^1.1.1" }, "verb": { @@ -93,4 +86,4 @@ "lintDeps": { "ignore": [] } -} \ No newline at end of file +} diff --git a/test/app.doc.js b/test/app.doc.js index b42c8673..e89198d6 100644 --- a/test/app.doc.js +++ b/test/app.doc.js @@ -9,6 +9,7 @@ var app; describe('app', function() { beforeEach(function() { app = verb(); + app.create('docs'); }); describe('add doc', function() { diff --git a/test/app.docs.js b/test/app.docs.js index be4ae9be..d392f723 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -9,6 +9,7 @@ var app; describe('app', function() { beforeEach(function() { app = verb(); + app.create('docs'); }); describe('add docs', function() { From 560be3a95f5b6f6aadc539079b678140726a3392 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 10 Feb 2016 04:05:18 -0500 Subject: [PATCH 156/282] remove unused deps --- lib/utils.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 935dbe84..c338198b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -9,27 +9,9 @@ require = utils; * Utils */ -require('extend-shallow', 'extend'); require('is-affirmative'); -require('try-open'); require = fn; -utils.exists = function(filepath) { - if (typeof filepath === 'undefined') { - return false; - } - var val = utils.tryOpen(path.resolve(filepath), 'r'); - return typeof val === 'number'; -}; - -utils.arrayify = function(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -}; - -utils.renameKey = function(key, view) { - return view ? view.filename : path.basename(key, path.extname(key)); -}; - utils.conflict = function(app, options, cb) { if (typeof cb !== 'function') { throw new TypeError('expected a callback function'); From 65c9cc453a01c6d284625d9a71005b0f37ac0768 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 10 Feb 2016 04:06:43 -0500 Subject: [PATCH 157/282] update tests --- test/app.cli.js | 1 - test/app.generate.js | 461 +++++++++++++++++++++++++++++++++++++++++-- test/app.task.js | 1 + 3 files changed, 451 insertions(+), 12 deletions(-) diff --git a/test/app.cli.js b/test/app.cli.js index b7c1c6c4..0627517b 100644 --- a/test/app.cli.js +++ b/test/app.cli.js @@ -2,7 +2,6 @@ require('mocha'); require('should'); -var path = require('path'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); diff --git a/test/app.generate.js b/test/app.generate.js index fa3d1e1c..8f2a9c99 100644 --- a/test/app.generate.js +++ b/test/app.generate.js @@ -1,22 +1,461 @@ 'use strict'; require('mocha'); -require('should'); -var path = require('path'); var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; +var Generate = require('..'); +var generate; -describe('app.generate', function() { +describe('.generate', function() { beforeEach(function() { - app = new App(); + generate = new Generate(); }); - describe('app.generate', function() { - it('should expose a generate method on app', function() { - // assert.equal(typeof app.generate, 'function'); + describe('generators', function(cb) { + it('should throw an error when a generator is not found', function(cb) { + generate.generate('fdsslsllsfjssl', function(err) { + assert(err); + assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + // special case + it('should throw an error when a generator is not found in argv.cwd', function(cb) { + generate.option('cwd', 'foo/bar/baz'); + generate.generate('sflsjljskksl', function(err) { + assert(err); + assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/generator.js"', err.message); + cb(); + }); + }); + + it('should throw an error when a stringified task is not found', function(cb) { + generate.register('fdsslsllsfjssl', function() {}); + generate.generate('fdsslsllsfjssl:foo', function(err) { + assert(err); + assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + it('should throw an error when a task is not found', function(cb) { + generate.register('fdsslsllsfjssl', function() {}); + generate.generate('fdsslsllsfjssl', ['foo'], function(err) { + assert(err); + assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + cb(); + }); + }); + + it('should not reformat error messages that are not about invalid tasks', function(cb) { + generate.task('default', function(cb) { + cb(new Error('whatever')); + }); + + generate.generate('default', function(err) { + assert(err); + assert.equal(err.message, 'whatever'); + cb(); + }); + }); + + it('should run a task on the instance', function(cb) { + generate.task('foo', function(next) { + next(); + }); + + generate.generate('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run a task instead of a generator of the same name', function(cb) { + generate.register('foo', function(app) { + app.task('default', function() { + cb(new Error('expected the task to run first')); + }); + }); + + generate.task('foo', function() { + cb(); + }); + + generate.generate('foo', function(err) { + assert(!err); + }); + }); + + it('should run a task on a generator with the same name when specified', function(cb) { + generate.register('foo', function(app) { + app.task('default', function() { + cb(); + }); + }); + + generate.task('foo', function() { + cb(new Error('expected the generator to run')); + }); + + generate.generate('foo:default', function(err) { + assert(!err); + }); + }); + + it('should run the default task on a generator', function(cb) { + generate.register('foo', function(app) { + app.task('default', function(next) { + next(); + }); + }); + + generate.generate('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run an array of tasks on the instance', function(cb) { + var count = 0; + generate.task('a', function(next) { + count++; + next(); + }); + generate.task('b', function(next) { + count++; + next(); + }); + generate.task('c', function(next) { + count++; + next(); + }); + + generate.generate('a,b,c', function(err) { + assert.equal(count, 3); + assert(!err); + cb(); + }); + }); + + it('should run the default task on the default generator', function(cb) { + var count = 0; + generate.register('default', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generate(function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the default task on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.generate('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('abc', function(next) { + count++; + next(); + }); + }); + + generate.generate('foo', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('a', function(next) { + count++; + next(); + }); + + app.task('b', function(next) { + count++; + next(); + }); + + app.task('c', function(next) { + count++; + next(); + }); + }); + + generate.generate('foo', 'a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); }); }); -}); + describe('sub-generators', function(cb) { + it('should run the default task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.sub', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.bar', ['a', 'b', 'c'], function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + + it('should run an multiple tasks on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.bar', 'a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + }); + + describe('cross-generator', function(cb) { + it('should run a generator from another generator', function(cb) { + var res = ''; + + generate.register('foo', function(app, two) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + res += 'foo > sub > default '; + generate.generate('bar.sub', next); + }); + }); + }); + + generate.register('bar', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + res += 'bar > sub > default '; + next(); + }); + }); + }); + + generate.generate('foo.sub', function(err) { + if (err) return cb(err); + assert.equal(res, 'foo > sub > default bar > sub > default '); + cb(); + }); + }); + + it('should run the specified task on a registered sub-generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + }); + + describe('events', function(cb) { + it('should emit generate', function(cb) { + generate.on('generate', function() { + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + }); + }); + + it('should expose the generator alias as the first parameter', function(cb) { + generate.on('generate', function(name) { + assert.equal(name, 'sub'); + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + }); + }); + + it('should expose the tasks array as the second parameter', function(cb) { + generate.on('generate', function(name, tasks) { + assert.deepEqual(tasks, ['abc']); + cb(); + }); + + generate.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + next(); + }); + + sub.task('abc', function(next) { + next(); + }); + }); + }); + + generate.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + }); + }); + }); +}); diff --git a/test/app.task.js b/test/app.task.js index 9ccfa4d6..4ae0141e 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -1,5 +1,6 @@ 'use strict'; +require('mocha'); var assert = require('assert'); var App = require('..'); var app; From 884e8c6d26576ec5097d551da389519e0faf1265 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 11 Feb 2016 08:24:29 -0500 Subject: [PATCH 158/282] fix helper and generate tests --- test/app.generate.js | 116 +++++++++++++++++++++---------------------- test/helpers.js | 7 ++- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/test/app.generate.js b/test/app.generate.js index 8f2a9c99..cdeb5da7 100644 --- a/test/app.generate.js +++ b/test/app.generate.js @@ -2,17 +2,17 @@ require('mocha'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var Verb = require('..'); +var verb; describe('.generate', function() { beforeEach(function() { - generate = new Generate(); + verb = new Verb(); }); describe('generators', function(cb) { it('should throw an error when a generator is not found', function(cb) { - generate.generate('fdsslsllsfjssl', function(err) { + verb.generate('fdsslsllsfjssl', function(err) { assert(err); assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); cb(); @@ -21,17 +21,17 @@ describe('.generate', function() { // special case it('should throw an error when a generator is not found in argv.cwd', function(cb) { - generate.option('cwd', 'foo/bar/baz'); - generate.generate('sflsjljskksl', function(err) { + verb.option('cwd', 'foo/bar/baz'); + verb.generate('sflsjljskksl', function(err) { assert(err); - assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/generator.js"', err.message); + assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); cb(); }); }); it('should throw an error when a stringified task is not found', function(cb) { - generate.register('fdsslsllsfjssl', function() {}); - generate.generate('fdsslsllsfjssl:foo', function(err) { + verb.register('fdsslsllsfjssl', function() {}); + verb.generate('fdsslsllsfjssl:foo', function(err) { assert(err); assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); cb(); @@ -39,8 +39,8 @@ describe('.generate', function() { }); it('should throw an error when a task is not found', function(cb) { - generate.register('fdsslsllsfjssl', function() {}); - generate.generate('fdsslsllsfjssl', ['foo'], function(err) { + verb.register('fdsslsllsfjssl', function() {}); + verb.generate('fdsslsllsfjssl', ['foo'], function(err) { assert(err); assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); cb(); @@ -48,11 +48,11 @@ describe('.generate', function() { }); it('should not reformat error messages that are not about invalid tasks', function(cb) { - generate.task('default', function(cb) { + verb.task('default', function(cb) { cb(new Error('whatever')); }); - generate.generate('default', function(err) { + verb.generate('default', function(err) { assert(err); assert.equal(err.message, 'whatever'); cb(); @@ -60,56 +60,56 @@ describe('.generate', function() { }); it('should run a task on the instance', function(cb) { - generate.task('foo', function(next) { + verb.task('foo', function(next) { next(); }); - generate.generate('foo', function(err) { + verb.generate('foo', function(err) { assert(!err); cb(); }); }); it('should run a task instead of a generator of the same name', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function() { cb(new Error('expected the task to run first')); }); }); - generate.task('foo', function() { + verb.task('foo', function() { cb(); }); - generate.generate('foo', function(err) { + verb.generate('foo', function(err) { assert(!err); }); }); it('should run a task on a generator with the same name when specified', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function() { cb(); }); }); - generate.task('foo', function() { + verb.task('foo', function() { cb(new Error('expected the generator to run')); }); - generate.generate('foo:default', function(err) { + verb.generate('foo:default', function(err) { assert(!err); }); }); it('should run the default task on a generator', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function(next) { next(); }); }); - generate.generate('foo', function(err) { + verb.generate('foo', function(err) { assert(!err); cb(); }); @@ -117,20 +117,20 @@ describe('.generate', function() { it('should run an array of tasks on the instance', function(cb) { var count = 0; - generate.task('a', function(next) { + verb.task('a', function(next) { count++; next(); }); - generate.task('b', function(next) { + verb.task('b', function(next) { count++; next(); }); - generate.task('c', function(next) { + verb.task('c', function(next) { count++; next(); }); - generate.generate('a,b,c', function(err) { + verb.generate('a,b,c', function(err) { assert.equal(count, 3); assert(!err); cb(); @@ -139,14 +139,14 @@ describe('.generate', function() { it('should run the default task on the default generator', function(cb) { var count = 0; - generate.register('default', function(app) { + verb.register('default', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generate(function(err) { + verb.generate(function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -155,14 +155,14 @@ describe('.generate', function() { it('should run the default task on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generate('foo', function(err) { + verb.generate('foo', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -171,7 +171,7 @@ describe('.generate', function() { it('should run the specified task on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -183,7 +183,7 @@ describe('.generate', function() { }); }); - generate.generate('foo', ['abc'], function(err) { + verb.generate('foo', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -192,7 +192,7 @@ describe('.generate', function() { it('should run an array of tasks on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -214,7 +214,7 @@ describe('.generate', function() { }); }); - generate.generate('foo', 'a,b,c', function(err) { + verb.generate('foo', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -225,7 +225,7 @@ describe('.generate', function() { describe('sub-generators', function(cb) { it('should run the default task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -239,7 +239,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', function(err) { + verb.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -248,7 +248,7 @@ describe('.generate', function() { it('should run the specified task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -262,7 +262,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + verb.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -271,7 +271,7 @@ describe('.generate', function() { it('should run an array of tasks on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -295,7 +295,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.bar', ['a', 'b', 'c'], function(err) { + verb.generate('foo.bar', ['a', 'b', 'c'], function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -304,7 +304,7 @@ describe('.generate', function() { it('should run an multiple tasks on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -328,7 +328,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.bar', 'a,b,c', function(err) { + verb.generate('foo.bar', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -340,16 +340,16 @@ describe('.generate', function() { it('should run a generator from another generator', function(cb) { var res = ''; - generate.register('foo', function(app, two) { + verb.register('foo', function(app, two) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'foo > sub > default '; - generate.generate('bar.sub', next); + verb.generate('bar.sub', next); }); }); }); - generate.register('bar', function(app) { + verb.register('bar', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'bar > sub > default '; @@ -358,7 +358,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', function(err) { + verb.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(res, 'foo > sub > default bar > sub > default '); cb(); @@ -367,7 +367,7 @@ describe('.generate', function() { it('should run the specified task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -381,7 +381,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + verb.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -391,11 +391,11 @@ describe('.generate', function() { describe('events', function(cb) { it('should emit generate', function(cb) { - generate.on('generate', function() { + verb.on('generate', function() { cb(); }); - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -407,18 +407,18 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + verb.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); it('should expose the generator alias as the first parameter', function(cb) { - generate.on('generate', function(name) { + verb.on('generate', function(name) { assert.equal(name, 'sub'); cb(); }); - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -430,18 +430,18 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + verb.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); it('should expose the tasks array as the second parameter', function(cb) { - generate.on('generate', function(name, tasks) { + verb.on('generate', function(name, tasks) { assert.deepEqual(tasks, ['abc']); cb(); }); - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -453,7 +453,7 @@ describe('.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + verb.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); diff --git a/test/helpers.js b/test/helpers.js index 4db1f27f..6a383c2e 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -794,12 +794,11 @@ describe('collection helpers', function() { it('should handle engine errors2', function(cb) { app.engine('tmpl', require('engine-base')); - app.create('foo', {engine: 'tmpl'}); + app.create('foo', {viewType: 'partial', engine: 'tmpl'}); app.create('bar', {engine: 'tmpl'}); - app.create('foo', {viewType: 'partial'}); - app.foo('foo.tmpl', {path: 'foo.tmpl', content: '<%= blah.bar %>'}); - app.bar('one.tmpl', {content: '<%= foo("foo.tmpl") %>'}) + app.foo('a.tmpl', {path: 'a.tmpl', content: '<%= blah.bar %>'}); + app.bar('one.tmpl', {content: '<%= foo("a.tmpl") %>'}) .render(function(err) { assert(err); assert.equal(typeof err, 'object'); From 212b4d787b2a7b0dbc57f36b0c289483f89d17c7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 11 Feb 2016 08:25:04 -0500 Subject: [PATCH 159/282] comments and error handling in `bin/verb.js` --- bin/verb.js | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 76103d8c..0f7b277e 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,21 +1,29 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; - var Verb = require('..'); var verb = new Verb(); -// run generator and/or tasks +/** + * Run verb generators and tasks + */ + verb.runner('verbfile.js', function(err, argv, app) { - if (err) { - console.error(err.message); - process.exit(1); - } + if (err) handleError(err); + + /** + * If the user does not have a `verbfile.js`, and the `default` + * task is defined, register a default fallback. + */ if (!app.hasConfigfile && app.isDefaultTask) { app.register('default', require('../lib/generators/default')); } + /** + * Listen for errors + */ + app.on('error', function(err) { console.log(app.env); console.log(); @@ -26,16 +34,35 @@ verb.runner('verbfile.js', function(err, argv, app) { } }); + /** + * Get the `config` object created by base-runner. This object + * is created by merging any or all of the following objects after + * normalizing them against a a schema: + * + * - globally stored settings, defined via `app.store.set()` or `--save=foo:bar` + * - options defined on verb's API + * - the `verb` object in package.json + * - argv, command line arguments + */ + var config = app.get('cache.config'); + // Map over config values app.config.process(config, function(err) { - if (err) throw err; + if (err) handleError(err); + // Process command line arguments app.cli.process(argv, function(err) { - if (err) throw err; + if (err) handleError(err); verb.emit('done'); process.exit(0); }); }); }); + +// placeholder +function handleError(err) { + console.log(err); + process.exit(1); +} From 2bc6336027ac0c8c5a74c12cc7fa43ce6566f0ad Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 11 Feb 2016 08:25:27 -0500 Subject: [PATCH 160/282] only create default collections when CLI is used --- index.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 11b55185..0bc13d54 100644 --- a/index.js +++ b/index.js @@ -44,7 +44,7 @@ Generate.extend(Verb); Verb.prototype.verbDefaults = function(options) { debug('initializing verb defaults'); - var defaults = {prefix: 'verb-generate', configfile: 'verbfile.js'}; + var defaults = { prefix: 'verb-generate', configfile: 'verbfile.js' }; return utils.extend({}, defaults, this.options, options); }; @@ -54,12 +54,16 @@ Verb.prototype.verbDefaults = function(options) { Verb.prototype.initVerb = function(opts) { debug('initializing verb data'); + this.is('verb'); this.name = 'verb'; this.data({runner: pkg}); this.data({verb: {related: {}, reflinks: []}}); - // this.create('files'); - // this.create('docs'); + + if (process.env.GENERATE_CLI) { + this.create('files'); + this.create('docs'); + } }; /** From 80c31d43ed39cdbd1ff9369f66a95525800a5c8d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 11 Feb 2016 08:25:50 -0500 Subject: [PATCH 161/282] remove unused deps, enable TOC in readme --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 795798d4..86951f44 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,9 @@ "dependencies": { "base-questions": "^0.3.2", "debug": "^2.2.0", - "extend-shallow": "^2.0.1", - "generate": "^0.3.6", + "generate": "^0.4.0", "generator-util": "^0.2.5", - "is-affirmative": "^0.1.0", - "try-open": "^0.1.0" + "is-affirmative": "^0.1.0" }, "devDependencies": { "async": "^1.5.2", @@ -68,6 +66,7 @@ "vinyl": "^1.1.1" }, "verb": { + "toc": true, "layout": "default", "tasks": [ "readme" From c99192bd9938cba08b537fa4cd7ab1fc37b6592f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 11 Feb 2016 08:26:12 -0500 Subject: [PATCH 162/282] update docs --- .verb.md | 56 +++++++++++++++++++++++++++++++++++++++++- readme.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/.verb.md b/.verb.md index 76241280..0fbd36ad 100644 --- a/.verb.md +++ b/.verb.md @@ -4,9 +4,45 @@ var verb = require('{%= name %}'); ``` +## What is verb? + +Verb is a documentation system that is simple and fast enough to use for generating the readmes for GitHub projects, but powerful and smart enough to build the most complex documentation projects around. + +Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. + +## Quickstart + +**Install readme generator** + +First, install the `verb-readme-generator` with the following command: + +```sh +$ npm i -g verb-readme-generator +``` + +**verbfile.js** + +Next, create a `verbfile.js` with the following code: + +```js +module.exports = function(app) { + app.extendWith('verb-readme-generator'); + + app.task('default', ['readme']); +}; +``` + +**Run verb** + +In the command line, run: + +```sh +$ verb +``` + ## verbfile -Your project's `verbfile.js` must either export an instance of Verb, or a function. +Your project's `verbfile.js` should either export an instance of Verb or a function. **Instance** @@ -27,5 +63,23 @@ module.exports = function(verb) { Also, when a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). +## CLI + +### Commands + +#### config + +Persist configuration settings to the `verb` object in `package.json`. + +```sh +$ verb --config +``` + + + +```sh +$ verb --config=layout:default +``` + ## API {%= apidocs("index.js") %} diff --git a/readme.md b/readme.md index 7f5ddd17..11061290 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,21 @@ > Documentation generator for GitHub projects. +- [Install](#install) +- [Usage](#usage) +- [What is verb?](#what-is-verb-) +- [Quickstart](#quickstart) +- [verbfile](#verbfile) +- [CLI](#cli) + * [Commands](#commands) + + [config](#config) +- [API](#api) +- [Related projects](#related-projects) +- [Running tests](#running-tests) +- [Contributing](#contributing) +- [Author](#author) +- [License](#license) + ## Install Install with [npm](https://www.npmjs.com/): @@ -16,9 +31,45 @@ $ npm i verb --save var verb = require('verb'); ``` +## What is verb? + +Verb is a documentation system that is simple and fast enough to use for generating the readmes for GitHub projects, but powerful and smart enough to build the most complex documentation projects around. + +Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. + +## Quickstart + +**Install readme generator** + +First, install the `verb-readme-generator` with the following command: + +```sh +$ npm i -g verb-readme-generator +``` + +**verbfile.js** + +Next, create a `verbfile.js` with the following code: + +```js +module.exports = function(app) { + app.extendWith('verb-readme-generator'); + + app.task('default', ['readme']); +}; +``` + +**Run verb** + +In the command line, run: + +```sh +$ verb +``` + ## verbfile -Your project's `verbfile.js` must either export an instance of Verb, or a function. +Your project's `verbfile.js` should either export an instance of Verb or a function. **Instance** @@ -39,9 +90,25 @@ module.exports = function(verb) { Also, when a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). +## CLI + +### Commands + +#### config + +Persist configuration settings to the `verb` object in `package.json`. + +```sh +$ verb --config +``` + +```sh +$ verb --config=layout:default +``` + ## API -### [Verb](index.js#L28) +### [Verb](index.js#L26) Create an instance of `Verb` with the given `options` @@ -88,4 +155,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 09, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 11, 2016._ \ No newline at end of file From 04a1bae1552520ee5d8183da06db358b651702e9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 11 Feb 2016 08:30:46 -0500 Subject: [PATCH 163/282] use static `.runner` method from `generate` --- bin/verb.js | 70 ++++++++++------------------------------------------- 1 file changed, 13 insertions(+), 57 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 0f7b277e..1266d047 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,68 +1,24 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; -var Verb = require('..'); -var verb = new Verb(); +var generator = require('../lib/generators'); +var verb = require('..'); /** - * Run verb generators and tasks + * Create verb "runner" */ -verb.runner('verbfile.js', function(err, argv, app) { - if (err) handleError(err); +var run = verb.runner('verbfile.js', generator); +var app = verb(); - /** - * If the user does not have a `verbfile.js`, and the `default` - * task is defined, register a default fallback. - */ +/** + * Run generators and tasks + */ - if (!app.hasConfigfile && app.isDefaultTask) { - app.register('default', require('../lib/generators/default')); +run(app, function(err, argv, app) { + if (err) { + console.log(err); + process.exit(1); } - - /** - * Listen for errors - */ - - app.on('error', function(err) { - console.log(app.env); - console.log(); - - if (err.reason) { - console.log(err.reason); - process.exit(1); - } - }); - - /** - * Get the `config` object created by base-runner. This object - * is created by merging any or all of the following objects after - * normalizing them against a a schema: - * - * - globally stored settings, defined via `app.store.set()` or `--save=foo:bar` - * - options defined on verb's API - * - the `verb` object in package.json - * - argv, command line arguments - */ - - var config = app.get('cache.config'); - - // Map over config values - app.config.process(config, function(err) { - if (err) handleError(err); - - // Process command line arguments - app.cli.process(argv, function(err) { - if (err) handleError(err); - - verb.emit('done'); - process.exit(0); - }); - }); + app.emit('done'); }); - -// placeholder -function handleError(err) { - console.log(err); - process.exit(1); -} From 477d53569a9add9b465bf317f13f15d24c139364 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:01:17 -0500 Subject: [PATCH 164/282] start adding CLI flags --- docs/src/flags.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 docs/src/flags.md diff --git a/docs/src/flags.md b/docs/src/flags.md new file mode 100644 index 00000000..c818bfb9 --- /dev/null +++ b/docs/src/flags.md @@ -0,0 +1,30 @@ +# Command line flags + +- `--emit` +- `--layout` +- `--set` +- `--toc` +- `--options` +- `--data` +- `--create` +- `--plugins` +- `--helpers` +- `--tasks` +- `--related` +- `--reflinks` + +## File properties + +- `--base` +- `--basename` +- `--cwd` +- `--dir` +- `--dirname` +- `--ext` +- `--extname` +- `--f` +- `--file` +- `--filename` +- `--path` +- `--root` +- `--stem` From 1fdb4d2f28faec389f2400ddec0f87df969831ec Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:01:28 -0500 Subject: [PATCH 165/282] clean up --- lib/errors.js | 9 --------- lib/generators/index.js | 3 +++ 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 lib/errors.js create mode 100644 lib/generators/index.js diff --git a/lib/errors.js b/lib/errors.js deleted file mode 100644 index 10067e94..00000000 --- a/lib/errors.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -module.exports = function(options) { - return function(app) { - if (this.isRegistered('base-errors')) return; - // to do - this.errors = this.errrors || []; - }; -} diff --git a/lib/generators/index.js b/lib/generators/index.js new file mode 100644 index 00000000..6356a28c --- /dev/null +++ b/lib/generators/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./default'); From 3656f9cf1de1bdd0e7efc53bda6378306efc4c07 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:04:11 -0500 Subject: [PATCH 166/282] custom `toFullname` function --- index.js | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 0bc13d54..063ee518 100644 --- a/index.js +++ b/index.js @@ -13,11 +13,11 @@ var utils = require('./lib/utils'); var pkg = require('./package'); /** - * Create an instance of `Verb` with the given `options` + * Create a verb application with `options`. * * ```js - * var Verb = require('verb'); - * var verb = new Verb(); + * var verb = require('verb'); + * var app = verb(); * ``` * @param {Object} `options` Settings to initialize with. * @api public @@ -38,16 +38,6 @@ function Verb(options) { Generate.extend(Verb); -/** - * Initialize verb defaults - */ - -Verb.prototype.verbDefaults = function(options) { - debug('initializing verb defaults'); - var defaults = { prefix: 'verb-generate', configfile: 'verbfile.js' }; - return utils.extend({}, defaults, this.options, options); -}; - /** * Initialize verb data */ @@ -60,12 +50,29 @@ Verb.prototype.initVerb = function(opts) { this.data({runner: pkg}); this.data({verb: {related: {}, reflinks: []}}); + // temporary, there is an easier way to do this + this.toFullname = function(str) { + var re = /(^verb-?|-?generat(e|or)-?)/g; + var alias = str.replace(re, ''); + return 'verb-' + alias + '-generator'; + }; + if (process.env.GENERATE_CLI) { this.create('files'); this.create('docs'); } }; +/** + * Initialize verb defaults + */ + +Verb.prototype.verbDefaults = function(options) { + debug('initializing verb defaults'); + var defaults = { prefix: 'verb', configfile: 'verbfile.js' }; + return utils.extend({}, defaults, this.options, options); +}; + /** * Expose static `is*` methods from Templates */ From 4746715e8e6d5c09fe2cad49164d17fd0bff98cd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:04:21 -0500 Subject: [PATCH 167/282] process.exit on `done` --- bin/verb.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bin/verb.js b/bin/verb.js index 1266d047..0d1c1e28 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -11,6 +11,10 @@ var verb = require('..'); var run = verb.runner('verbfile.js', generator); var app = verb(); +app.on('done', function() { + process.exit(0); +}); + /** * Run generators and tasks */ From f12314fdd2310495b1bfe9ee85997c18c0c6c979 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:06:52 -0500 Subject: [PATCH 168/282] clean up verbfile --- verbfile.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/verbfile.js b/verbfile.js index 89fdfec0..a13d3518 100644 --- a/verbfile.js +++ b/verbfile.js @@ -1,12 +1,14 @@ 'use strict'; +/** + * Example verbfile.js, extends `verb-readme-generator`, which + * provides the `readme` task for building the readme. + */ + module.exports = function(verb) { - verb.extendWith('readme'); + verb.extendWith('verb-readme-generator'); - verb.task('foo', function(cb) { - console.log('verb > verbfile >', this.name, 'task'); - cb(); - }); + // do anything here! verb.task('default', ['readme']); }; From 7cdeef64c24706225a885720d13b0524a8d3be20 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:06:59 -0500 Subject: [PATCH 169/282] generate docs --- .verb.md | 1 + readme.md | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.verb.md b/.verb.md index 0fbd36ad..b2018b6a 100644 --- a/.verb.md +++ b/.verb.md @@ -65,6 +65,7 @@ Also, when a function is exported, we refer to these as [verb generators](#verb- ## CLI + ### Commands #### config diff --git a/readme.md b/readme.md index 11061290..6ec395c9 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,8 @@ > Documentation generator for GitHub projects. +## TOC + - [Install](#install) - [Usage](#usage) - [What is verb?](#what-is-verb-) @@ -12,6 +14,7 @@ + [config](#config) - [API](#api) - [Related projects](#related-projects) +- [Generate docs](#generate-docs) - [Running tests](#running-tests) - [Contributing](#contributing) - [Author](#author) @@ -110,7 +113,7 @@ $ verb --config=layout:default ### [Verb](index.js#L26) -Create an instance of `Verb` with the given `options` +Create a verb application with `options`. **Params** @@ -119,8 +122,8 @@ Create an instance of `Verb` with the given `options` **Example** ```js -var Verb = require('verb'); -var verb = new Verb(); +var verb = require('verb'); +var app = verb(); ``` ## Related projects @@ -129,6 +132,20 @@ var verb = new Verb(); * [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base) | [homepage](https://github.com/node-base/base) * [generate](https://www.npmjs.com/package/generate): Fast, composable, highly extendable project generator with a user-friendly and expressive API. | [homepage](https://github.com/generate/generate) +## Generate docs + +Generate readme and API documentation with [verb](https://github.com/verbose/verb): + +```sh +$ npm i -d && npm run docs +``` + +Or, if [verb](https://github.com/verbose/verb) is installed globally: + +```sh +$ verb +``` + ## Running tests Install dev dependencies: @@ -155,4 +172,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 11, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.1.0, on February 12, 2016._ \ No newline at end of file From b16d6eccbd7ead8a04208056c73eafe7e5c12b52 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 01:07:14 -0500 Subject: [PATCH 170/282] reflinks --- package.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 86951f44..ec665d4d 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,13 @@ "base", "generate" ] - } + }, + "reflinks": [ + "assemble", + "base", + "generate", + "verb" + ] }, "lintDeps": { "ignore": [] From c8b788e57de0941fb3d2dd7babaf231053a188f6 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 02:08:58 -0500 Subject: [PATCH 171/282] update docs --- docs/recipes/3-ways-to-disable-toc.md | 6 ++--- docs/recipes/extend-a-generator.md | 33 -------------------------- docs/recipes/generate.js | 9 ++++++- docs/recipes/inspecting-the-context.md | 2 +- docs/recipes/render-a-list.md | 4 ++-- 5 files changed, 14 insertions(+), 40 deletions(-) delete mode 100644 docs/recipes/extend-a-generator.md diff --git a/docs/recipes/3-ways-to-disable-toc.md b/docs/recipes/3-ways-to-disable-toc.md index c6310868..0c33beb0 100644 --- a/docs/recipes/3-ways-to-disable-toc.md +++ b/docs/recipes/3-ways-to-disable-toc.md @@ -21,7 +21,7 @@ Some really useful information, that you can use - because it's so useful. Verb uses [falsey][] to check values for just a few different properties, including `toc`. So any value that the falsey lib evaluates as `falsey` will disable the `toc`. -**2: {{name}} config** +**2: verb config** Disable the TOC by defining it on an `options` property in your [local verb config](./settings.md): @@ -37,12 +37,12 @@ Disable the TOC by defining it on an `options` property in your [local verb conf } ``` -**3: {{name}}.option()** +**3: verb.option()** Use the `option` API to disable the TOC: ```js -{{name}}.option('toc', false); +verb.option('toc', false); ``` [falsey]: https://github.com/jonschlinkert/falsey \ No newline at end of file diff --git a/docs/recipes/extend-a-generator.md b/docs/recipes/extend-a-generator.md deleted file mode 100644 index a2a53733..00000000 --- a/docs/recipes/extend-a-generator.md +++ /dev/null @@ -1,33 +0,0 @@ -# Extending verbApps - -It's easy to extend a verbApp with the functionality, tasks and sub-verbApps of another verbApp. - -Let's say you want to extend verbApp `xyz` with verbApp `abc`. - -```js -verb.register('abc', function(app, base, env) { -}); - -verb.register('xyz', function(app, base, env) { - base.on('preBuild', function() { - var abc = base.getVerbApp('abc'); - abc.extendVerbApp(app); - }); -}); -``` - -**Pro tip** - -Run _both_ verbApps in different contexts: - -```js -verb.build('templates', function(err) { - if (err) return console.error(err); - - verb.verbApp('one') - .build('templates', function(err) { - if (err) return console.error(err); - console.log(verb.views.templates); - }); -}); -``` \ No newline at end of file diff --git a/docs/recipes/generate.js b/docs/recipes/generate.js index c7f4c91b..185bc9f0 100644 --- a/docs/recipes/generate.js +++ b/docs/recipes/generate.js @@ -24,7 +24,10 @@ module.exports = function(verb, base, env) { }); verb.register('docs', function(app) { - app.task('x', function() {}); + app.task('x', function(cb) { + console.log('docs > x'); + cb(); + }); app.task('y', function() {}); app.task('z', function() {}); @@ -34,4 +37,8 @@ module.exports = function(verb, base, env) { app.task('z', function() {}); }); }); + + verb.task('default', function(cb) { + verb.generate('docs:x', cb); + }); }; diff --git a/docs/recipes/inspecting-the-context.md b/docs/recipes/inspecting-the-context.md index f2975c04..d0de15ea 100644 --- a/docs/recipes/inspecting-the-context.md +++ b/docs/recipes/inspecting-the-context.md @@ -8,7 +8,7 @@ The context object is created in-memory at render time, and to inspect it we nee **Create a helper** -A simple `log` helper can be used to show any object we pass to it in the console. Add the following to your `{{configfile}}.js`: +A simple `log` helper can be used to show any object we pass to it in the console. Add the following to your `verbfile.js`: ```js app.helper('log', function(context) { diff --git a/docs/recipes/render-a-list.md b/docs/recipes/render-a-list.md index 401f45e2..b2e9e0ad 100644 --- a/docs/recipes/render-a-list.md +++ b/docs/recipes/render-a-list.md @@ -4,7 +4,7 @@ ## Preparation -First, create an `assemblefile.js`. The rest of this recipe assumes that you have the following defined: +First, create an `verbfile.js`. The rest of this recipe assumes that you have the following defined: ```js var assemble = require('assemble'); @@ -108,7 +108,7 @@ Since views are vinyl files, you'll need to inspect them to see what's available **Example** -Create a helper, arbitrarily named `ctx` (for context) or whatever you want, and add it to your `assemblefile.js`: +Create a helper, arbitrarily named `ctx` (for context) or whatever you want, and add it to your `verbfile.js`: ```js app.helper('ctx', function(context) { From ddf25ce78001c21c14c79f31487a75eab01f7be8 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 12 Feb 2016 02:10:43 -0500 Subject: [PATCH 172/282] start adding cli docs --- .verb.md | 83 ++++++++++++++++++++++++++++++++++------------ readme.md | 98 ++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 135 insertions(+), 46 deletions(-) diff --git a/.verb.md b/.verb.md index b2018b6a..ad821b1c 100644 --- a/.verb.md +++ b/.verb.md @@ -1,8 +1,3 @@ -## Usage - -```js -var verb = require('{%= name %}'); -``` ## What is verb? @@ -14,12 +9,22 @@ Verb is also highly pluggable, with first-class support for instance plugins, pi **Install readme generator** -First, install the `verb-readme-generator` with the following command: +The `verb-readme-generator` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: ```sh $ npm i -g verb-readme-generator ``` +**.verb.md** + +You should now be able to generate your project's readme from a `.verb.md` template with the following command: + +```sh +$ verb readme +``` + +Or you can go a step further by adding a `verbfile.js` to your project, allowing you to customize verb with generators, plugins, middleware, helpers, templates and more! + **verbfile.js** Next, create a `verbfile.js` with the following code: @@ -28,18 +33,11 @@ Next, create a `verbfile.js` with the following code: module.exports = function(app) { app.extendWith('verb-readme-generator'); + // call the `readme` task from verb-readme-generator app.task('default', ['readme']); }; ``` -**Run verb** - -In the command line, run: - -```sh -$ verb -``` - ## verbfile Your project's `verbfile.js` should either export an instance of Verb or a function. @@ -55,31 +53,76 @@ module.exports = app; **Function** +When a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). + ```js module.exports = function(verb) { // "private" verb instance is created for this verbfile }; ``` -Also, when a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). +## CLI Commands + +### file + +Specify the file to use instead of `verbfile.js`. + +```sh +$ verb --file +``` + +**Example** + +```sh +$ verb --file foo.js +``` + +### cwd + +Specify the cwd to use + +```sh +$ verb --cwd= +``` + +**Example** -## CLI +```sh +$ verb --cwd="foo/bar" +``` +Display the currently defined cwd: -### Commands +```sh +$ verb --cwd +``` -#### config +### config Persist configuration settings to the `verb` object in `package.json`. ```sh -$ verb --config +$ verb --config=[options] ``` +Most of the above CLI commands can be prefixed with `config=` to persist the value to package.json. + +#### tasks +Set the default tasks to run for a project: ```sh -$ verb --config=layout:default +$ verb --config=tasks: +``` + +**Example** + +```sh +# single task +$ verb --config=tasks:foo + +# array of tasks +$ verb --config=tasks:foo,bar,baz ``` ## API diff --git a/readme.md b/readme.md index 6ec395c9..f095a0ee 100644 --- a/readme.md +++ b/readme.md @@ -5,13 +5,14 @@ ## TOC - [Install](#install) -- [Usage](#usage) - [What is verb?](#what-is-verb-) - [Quickstart](#quickstart) - [verbfile](#verbfile) -- [CLI](#cli) - * [Commands](#commands) - + [config](#config) +- [CLI Commands](#cli-commands) + * [file](#file) + * [cwd](#cwd) + * [config](#config) + + [tasks](#tasks) - [API](#api) - [Related projects](#related-projects) - [Generate docs](#generate-docs) @@ -28,12 +29,6 @@ Install with [npm](https://www.npmjs.com/): $ npm i verb --save ``` -## Usage - -```js -var verb = require('verb'); -``` - ## What is verb? Verb is a documentation system that is simple and fast enough to use for generating the readmes for GitHub projects, but powerful and smart enough to build the most complex documentation projects around. @@ -44,12 +39,22 @@ Verb is also highly pluggable, with first-class support for instance plugins, pi **Install readme generator** -First, install the `verb-readme-generator` with the following command: +The `verb-readme-generator` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: ```sh $ npm i -g verb-readme-generator ``` +**.verb.md** + +You should now be able to generate your project's readme from a `.verb.md` template with the following command: + +```sh +$ verb readme +``` + +Or you can go a step further by adding a `verbfile.js` to your project, allowing you to customize verb with generators, plugins, middleware, helpers, templates and more! + **verbfile.js** Next, create a `verbfile.js` with the following code: @@ -58,18 +63,11 @@ Next, create a `verbfile.js` with the following code: module.exports = function(app) { app.extendWith('verb-readme-generator'); + // call the `readme` task from verb-readme-generator app.task('default', ['readme']); }; ``` -**Run verb** - -In the command line, run: - -```sh -$ verb -``` - ## verbfile Your project's `verbfile.js` should either export an instance of Verb or a function. @@ -85,28 +83,76 @@ module.exports = app; **Function** +When a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). + ```js module.exports = function(verb) { // "private" verb instance is created for this verbfile }; ``` -Also, when a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). +## CLI Commands + +### file -## CLI +Specify the file to use instead of `verbfile.js`. -### Commands +```sh +$ verb --file +``` + +**Example** -#### config +```sh +$ verb --file foo.js +``` + +### cwd + +Specify the cwd to use + +```sh +$ verb --cwd= +``` + +**Example** + +```sh +$ verb --cwd="foo/bar" +``` + +Display the currently defined cwd: + +```sh +$ verb --cwd +``` + +### config Persist configuration settings to the `verb` object in `package.json`. ```sh -$ verb --config +$ verb --config=[options] +``` + +Most of the above CLI commands can be prefixed with `config=` to persist the value to package.json. + +#### tasks + +Set the default tasks to run for a project: + +```sh +$ verb --config=tasks: ``` +**Example** + ```sh -$ verb --config=layout:default +# single task +$ verb --config=tasks:foo + +# array of tasks +$ verb --config=tasks:foo,bar,baz ``` ## API @@ -172,4 +218,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.1.0, on February 12, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 12, 2016._ \ No newline at end of file From f6acbe848e637443128f8c3ad73cc7aff157d43b Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Fri, 12 Feb 2016 13:06:51 -0500 Subject: [PATCH 173/282] including bin and lib in files --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ec665d4d..759c9cfc 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ }, "license": "MIT", "files": [ - "index.js" + "index.js", + "bin/", + "lib/" ], "main": "index.js", "engines": { From da59edbabee3fa3a57382257a596c5aca6b49d58 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 16 Feb 2016 16:38:54 -0500 Subject: [PATCH 174/282] update tests --- test/app.generator.js | 8 +-- test/app.mergePartials.js | 12 ++--- test/mergePartials.js | 110 -------------------------------------- 3 files changed, 10 insertions(+), 120 deletions(-) delete mode 100644 test/mergePartials.js diff --git a/test/app.generator.js b/test/app.generator.js index 1e899a26..674ed635 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -21,14 +21,14 @@ describe('.generator', function() { }); it('should register a generator function by alias', function() { - verb.generator('verb-generate-abc', function() {}); + verb.generator('verb-abc-generator', function() {}); assert(verb.generators.hasOwnProperty('abc')); }); }); describe('get > alias', function() { it('should get a generator by alias', function() { - verb.generator('verb-generate-abc', function() {}); + verb.generator('verb-abc-generator', function() {}); var abc = verb.generator('abc'); assert(abc); assert.equal(typeof abc, 'object'); @@ -37,8 +37,8 @@ describe('.generator', function() { describe('get > name', function() { it('should get a generator by name', function() { - verb.generator('verb-generate-abc', function() {}); - var abc = verb.generator('verb-generate-abc'); + verb.generator('verb-abc-generator', function() {}); + var abc = verb.generator('verb-abc-generator'); assert(abc); assert.equal(typeof abc, 'object'); }); diff --git a/test/app.mergePartials.js b/test/app.mergePartials.js index edd65c44..7d4a1114 100644 --- a/test/app.mergePartials.js +++ b/test/app.mergePartials.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('app.mergePartials', function() { +describe('app.mergePartialsSync', function() { beforeEach(function() { app = new App(); }); @@ -20,7 +20,7 @@ describe('app.mergePartials', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials(); + var actual = app.mergePartialsSync(); actual.should.have.property('partials'); actual.partials.should.have.properties(['a', 'b', 'c']); }); @@ -35,7 +35,7 @@ describe('app.mergePartials', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({mergePartials: false}); + var actual = app.mergePartialsSync({mergePartials: false}); actual.should.not.have.property('partials'); actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); }); @@ -55,7 +55,7 @@ describe('app.mergePartials', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({mergePartials: false}); + var actual = app.mergePartialsSync({mergePartials: false}); actual.should.not.have.property('partials'); actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); arr.should.eql(['aaa', 'bbb', 'ccc']); @@ -76,7 +76,7 @@ describe('app.mergePartials', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({mergePartials: false}); + var actual = app.mergePartialsSync({mergePartials: false}); actual.should.eql({ foos: {a: 'aaa onMerge'}, bars: {b: 'bbb onMerge'}, @@ -100,7 +100,7 @@ describe('app.mergePartials', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartials({mergePartials: false}); + var actual = app.mergePartialsSync({mergePartials: false}); actual.should.eql({ bazs: { c: 'ccc' } }); }); }); diff --git a/test/mergePartials.js b/test/mergePartials.js deleted file mode 100644 index 2874e2f4..00000000 --- a/test/mergePartials.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('mergePartials', function() { - beforeEach(function() { - app = new App(); - // reset views - app.views = {}; - }); - - it('should merge multiple partials collections onto one collection:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials(); - actual.should.have.property('partials'); - actual.partials.should.have.properties(['a', 'b', 'c']); - }); - - it('should keep partials collections on separaet collections:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.not.have.property('partials'); - actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); - }); - - it('should emit `mergePartials`:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - var arr = []; - - app.on('onMerge', function(view) { - arr.push(view.content); - }); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.not.have.property('partials'); - actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); - arr.should.eql(['aaa', 'bbb', 'ccc']); - }); - - it('should handle `onMerge` middleware:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.onMerge(/./, function(view, next) { - view.content += ' onMerge'; - next(); - }); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.eql({ - foos: {a: 'aaa onMerge'}, - bars: {b: 'bbb onMerge'}, - bazs: {c: 'ccc onMerge'} - }); - }); - - it('should skip views with `nomerge=true`:', function() { - var opts = { viewType: 'partial' }; - - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.onMerge(/[ab]/, function(view, next) { - view.options.nomerge = true; - next(); - }); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.eql({ bazs: { c: 'ccc' } }); - }); -}); - - From b900f3b1c5ff648f803d3f0ea247b9c78294718c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 20 Feb 2016 03:38:08 -0500 Subject: [PATCH 175/282] remove variables in docs --- docs/src/content/collections.md | 12 ++++++------ docs/src/content/data.md | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/src/content/collections.md b/docs/src/content/collections.md index 8e89927b..fc90476b 100644 --- a/docs/src/content/collections.md +++ b/docs/src/content/collections.md @@ -10,7 +10,7 @@ **What is a "view collection"?** -A view collection has all of the features of a "generic" {{appname}} collection, along with special features and methods that are specific to managing views (templates). +A view collection has all of the features of a "generic" verb collection, along with special features and methods that are specific to managing views (templates). **Special features** @@ -25,11 +25,11 @@ View collections have methods getting, setting and finding views, as well as ass ## Create -The `create` method is used for adding custom "view collections" to {{appname}}. A few things happen when the method is used: +The `create` method is used for adding custom "view collections" to verb. A few things happen when the method is used: - -- This exposed `page` and `pages` methods on {{appname}} ({{appname}} automatically detects inflections - plural and singular forms) +- This exposed `page` and `pages` methods on verb (verb automatically detects inflections - plural and singular forms) - A `pages` @@ -56,7 +56,7 @@ View collections have methods for adding, finding and getting views, such as: Views (templates) are stored on the `views` object of a collection, allowing views to be looked up by key. -Keys can be customized and renamed using a `renameKey` function passed on the options of the collection, or to rename all keys in all collections, you may pass a `renameKey` function on the {{appname}} options. +Keys can be customized and renamed using a `renameKey` function passed on the options of the collection, or to rename all keys in all collections, you may pass a `renameKey` function on the verb options. Ultimately this gives you full control over how views are named and how lookups are done. @@ -74,9 +74,9 @@ Lists are similar to collections but instead of storing an object of views, `ite * [sorting](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L359), and * [grouping](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L333) -_(the above links go to [templates](https://github.com/jonschlinkert/templates), which is the underlying library that provides {{appname}} with methods for managing views, collections, rendering, engines, and so on. This lib could be used to create your own static site generator if you need something different)_ +_(the above links go to [templates](https://github.com/jonschlinkert/templates), which is the underlying library that provides verb with methods for managing views, collections, rendering, engines, and so on. This lib could be used to create your own static site generator if you need something different)_ -Last, keep in mind that {{appname}} is highly pluggable, so you can extend it to do whatever you need (plugins can be used on {{appname}} itself, a collection, or even a specific view). Let us know if you want to do something that you think ought to be in {{appname}} itself and we can discuss ways to implement it. +Last, keep in mind that verb is highly pluggable, so you can extend it to do whatever you need (plugins can be used on verb itself, a collection, or even a specific view). Let us know if you want to do something that you think ought to be in verb itself and we can discuss ways to implement it. [lists-from-collections]: https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L77-L83 [collections-from-lists]: https://github.com/jonschlinkert/templates/blob/master/lib/views.js#L75-L81 diff --git a/docs/src/content/data.md b/docs/src/content/data.md index 3a5f8405..2f720291 100644 --- a/docs/src/content/data.md +++ b/docs/src/content/data.md @@ -7,34 +7,34 @@ related: ['#store'] reflinks: ['base-store', 'base-data'] --- -{{upper name}} keeps "data" on two different objects, depending on your needs. +Verb keeps "data" on two different objects, depending on your needs. | **Storage object** | **Description** | **Methods** | | --- | --- | --- | -| `{{name}}.cache.data` | Kept in-memory | `{{name}}.data()` (function) | -| `{{name}}.store.data` | Persisted to disk | `{{name}}.store` (object with methods) | +| `verb.cache.data` | Kept in-memory | `verb.data()` (function) | +| `verb.store.data` | Persisted to disk | `verb.store` (object with methods) | -## {{name}}.data +## verb.data ```js -{{name}}.data({a: 'b'}); +verb.data({a: 'b'}); // or -{{name}}.data('a', 'b'); -console.log({{name}}.cache.data); +verb.data('a', 'b'); +console.log(verb.cache.data); //=> {a: 'b'} ``` -The `{{name}}.data` method is powered by the [base-data][] plugin. Visit [that project][base-data] to see all available features and options. +The `verb.data` method is powered by the [base-data][] plugin. Visit [that project][base-data] to see all available features and options. -## {{name}}.store +## verb.store ```js -{{name}}.store.set({a: 'b'}); +verb.store.set({a: 'b'}); // or -{{name}}.store.set('a', 'b'); +verb.store.set('a', 'b'); -console.log({{name}}.store.data); +console.log(verb.store.data); //=> {a: 'b'} ``` -The `{{name}}.store` object is powered by the [base-store][] plugin. Visit [that project][base-store] to see all available features and options. +The `verb.store` object is powered by the [base-store][] plugin. Visit [that project][base-store] to see all available features and options. From 366bc911f8a515765a422b5c1957cfcaa91640f8 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 20 Feb 2016 03:38:14 -0500 Subject: [PATCH 176/282] lint tests --- test/app.generator.js | 6 +++--- test/app.invoke.js | 2 +- test/app.register.js | 21 +++++++++++++-------- test/app.resolve.js | 11 ++++++++--- test/app.store.js | 4 ---- test/app.toAlias.js | 13 +++++++------ test/env.js | 10 +++++----- test/partials.js | 2 +- test/store.js | 2 -- 9 files changed, 38 insertions(+), 33 deletions(-) diff --git a/test/app.generator.js b/test/app.generator.js index 674ed635..b9472ee0 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -15,14 +15,14 @@ describe('.generator', function() { }); describe('register > function', function() { - it('should register a generator function by name', function() { + it('should register a generator function by alias', function() { verb.generator('foo', function() {}); assert(verb.generators.hasOwnProperty('foo')); }); - it('should register a generator function by alias', function() { + it('should register a generator function by full name', function() { verb.generator('verb-abc-generator', function() {}); - assert(verb.generators.hasOwnProperty('abc')); + assert(verb.generators.hasOwnProperty('verb-abc-generator')); }); }); diff --git a/test/app.invoke.js b/test/app.invoke.js index e9f94e3b..cd6cfc33 100644 --- a/test/app.invoke.js +++ b/test/app.invoke.js @@ -109,7 +109,7 @@ describe('.invoke', function() { cb(); }); - verb.register('generate-qux', function(app) { + verb.register('verb-qux-generator', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); diff --git a/test/app.register.js b/test/app.register.js index 194d0c6a..4228d25c 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -18,7 +18,6 @@ describe('.register plugin', function() { describe('.register', function() { beforeEach(function() { app = new Verb(); - app.initVerb({}); }); describe('properties', function() { @@ -149,7 +148,13 @@ describe('.register', function() { app.getGenerator('bar'); }); }); +}); +describe('naming', function() { + beforeEach(function() { + app = new Verb(); + }); + describe('alias', function() { it('should use a custom function to create the alias', function() { app.option('alias', function(name) { @@ -157,7 +162,7 @@ describe('.register', function() { }); app.register('base-abc-xyz', function() {}); - assert(app.generators.hasOwnProperty('xyz')); + assert(app.generators.hasOwnProperty('base-abc-xyz')); }); }); @@ -167,9 +172,9 @@ describe('.register', function() { assert(app.generators.hasOwnProperty('foo')); }); - it('should register a generator function by alias', function() { + it('should register a generator function by its given name', function() { app.register('verb-generate-abc', function() {}); - assert(app.generators.hasOwnProperty('abc')); + assert(app.generators.hasOwnProperty('verb-generate-abc')); }); it('should register a generator by dirname', function() { @@ -177,9 +182,9 @@ describe('.register', function() { assert(app.generators.hasOwnProperty('a')); }); - it('should register a generator from a configfile filepath', function() { + it('should register a generator filepath by the given name', function() { app.register('verb-generate-abc', fixtures('generators/a/verbfile.js')); - assert(app.generators.hasOwnProperty('abc')); + assert(app.generators.hasOwnProperty('verb-generate-abc')); }); it('should throw when a generator does not expose the instance', function(cb) { @@ -194,9 +199,9 @@ describe('.register', function() { }); describe('instance', function() { - it('should register an instance', function() { + it('should register an instance by the given name', function() { app.register('verb-generate-inst', new Verb()); - assert(app.generators.hasOwnProperty('inst')); + assert(app.generators.hasOwnProperty('verb-generate-inst')); }); it('should get a generator that was registered as an instance', function() { diff --git a/test/app.resolve.js b/test/app.resolve.js index 10ab74d1..432809b6 100644 --- a/test/app.resolve.js +++ b/test/app.resolve.js @@ -62,9 +62,14 @@ describe('.resolve', function() { assert.equal(typeof fp, 'undefined'); }); - it('should return undefined when a generator is not found at the given cwd', function() { - var actual = verb.resolve('bar', {cwd: fixtures()}); - assert.equal(typeof actual, 'undefined'); + it('should throw an error when a generator is not found at the given cwd', function(cb) { + try { + verb.resolve('foofof', {cwd: fixtures()}); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.code, 'ENOENT'); + cb(); + } }); }); }); diff --git a/test/app.store.js b/test/app.store.js index 76f0de78..c80f5001 100644 --- a/test/app.store.js +++ b/test/app.store.js @@ -5,7 +5,6 @@ require('should'); var fs = require('fs'); var path = require('path'); var assert = require('assert'); -var store = require('base-store'); var support = require('./support'); var verb = support.resolve(); var app; @@ -14,7 +13,6 @@ describe('store', function() { describe('methods', function() { beforeEach(function() { app = verb({cli: true}); - app.use(store()); app.store.create('app-data-tests'); }); @@ -155,7 +153,6 @@ describe('store', function() { describe('create', function() { beforeEach(function() { app = verb({cli: true}); - app.use(store()); app.store.create('abc'); // init the actual store json file @@ -226,7 +223,6 @@ describe('create', function() { describe('events', function() { beforeEach(function() { app = verb({cli: true}); - app.use(store()); app.store.create('abc'); }); diff --git a/test/app.toAlias.js b/test/app.toAlias.js index 1e77b77f..4e16ee64 100644 --- a/test/app.toAlias.js +++ b/test/app.toAlias.js @@ -3,24 +3,25 @@ require('mocha'); var assert = require('assert'); require('generate-foo/generator.js'); -var Generate = require('..'); -var generate; +var Verb = require('..'); +var verb; describe('.toAlias', function() { beforeEach(function() { - generate = new Generate(); + verb = new Verb(); + delete verb.toAlias; }); it('should not create an alias from a name without a prefix', function() { - assert.equal(generate.toAlias('foo-bar'), 'foo-bar'); + assert.equal(verb.toAlias('foo-bar'), 'foo-bar'); }); it('should create an alias using the given prefix', function() { - assert.equal(generate.toAlias('foo-bar', {prefix: 'foo'}), 'bar'); + assert.equal(verb.toAlias('foo-bar', {prefix: 'foo'}), 'bar'); }); it('should create an alias using the given alias function', function() { - var alias = generate.toAlias('one-two-three', { + var alias = verb.toAlias('one-two-three', { alias: function(name) { return name.slice(name.lastIndexOf('-') + 1); } diff --git a/test/env.js b/test/env.js index 11d24996..e8391e54 100644 --- a/test/env.js +++ b/test/env.js @@ -40,24 +40,24 @@ describe('env', function() { assert.equal(verb.env.alias, 'foo'); }); - it('should use the given prefix', function() { + it('should not use prefix when a function is passed', function() { var fn = function() {}; verb.prefix = 'generate'; verb.createEnv('foo', {}, fn); - assert.equal(verb.env.name, 'generate-foo'); + assert.equal(verb.env.name, 'foo'); }); - it('should use `prefix` to add a full name to the env object', function() { + it('should not prefix a name when a function is registered', function() { var fn = function() {}; verb.prefix = 'whatever'; verb.createEnv('foo', {}, fn); - assert.equal(verb.env.name, 'whatever-foo'); + assert.equal(verb.env.name, 'foo'); }); it('should try to resolve a path passed as the second arg', function() { verb.createEnv('foo', 'generate-foo/verbfile.js'); assert.equal(verb.env.alias, 'foo'); - assert.equal(verb.env.name, 'verb-generate-foo'); + assert.equal(verb.env.name, 'verb-foo-generator'); }); it('should throw an error when the path is not resolved', function(cb) { diff --git a/test/partials.js b/test/partials.js index 72b719d8..aea0141d 100644 --- a/test/partials.js +++ b/test/partials.js @@ -56,7 +56,7 @@ describe('partials', function() { pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{> base }} c'}); var page = pages.getView('a.tmpl'); - var locals = app.mergePartials(this.options); + var locals = app.mergePartialsSync(this.options); pages.render(page, locals, function(err, view) { if (err) return cb(err); diff --git a/test/store.js b/test/store.js index 260f2bff..9664c5f6 100644 --- a/test/store.js +++ b/test/store.js @@ -4,7 +4,6 @@ require('mocha'); require('should'); var fs = require('fs'); var path = require('path'); -var Store = require('data-store'); var assert = require('assert'); var App = require('../'); var app; @@ -160,7 +159,6 @@ describe('store', function() { describe('events', function() { beforeEach(function() { app = new App(); - app.store = new Store('verb-tests'); }); afterEach(function(cb) { From 88289e8d0da3b2febb4908df06a023b8ed529938 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 20 Feb 2016 03:38:30 -0500 Subject: [PATCH 177/282] run update --- .eslintrc.json | 5 ++--- .gitignore | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7b5d047f..3ab3bea5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -49,7 +49,7 @@ "no-dupe-keys": 2, "no-duplicate-case": 2, "no-empty-character-class": 2, - "no-empty-label": 2, + "no-labels": 2, "no-eval": 2, "no-ex-assign": 2, "no-extend-native": 2, @@ -108,10 +108,9 @@ "radix": 2, "semi": [2, "always"], "semi-spacing": [2, { "before": false, "after": true }], - "space-after-keywords": [2, "always"], + "keyword-spacing": [2, { "before": true, "after": true }], "space-before-blocks": [2, "always"], "space-before-function-paren": [2, "never"], - "space-before-keywords": [2, "always"], "space-in-parens": [2, "never"], "space-infix-ops": 2, "space-return-throw-case": 2, diff --git a/.gitignore b/.gitignore index 79881544..37bcd361 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,3 @@ bower_components vendor temp tmp -TODO.md From 0c5aa1badc17c62cfe1d88ee17c4a336bf3e3120 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 20 Feb 2016 03:44:50 -0500 Subject: [PATCH 178/282] tweak aliasFn and fullnameFn --- index.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 063ee518..50e446ec 100644 --- a/index.js +++ b/index.js @@ -8,8 +8,8 @@ 'use strict'; var debug = require('debug')('base:verb'); +var utils = require('generator-util'); var Generate = require('generate'); -var utils = require('./lib/utils'); var pkg = require('./package'); /** @@ -47,17 +47,24 @@ Verb.prototype.initVerb = function(opts) { this.is('verb'); this.name = 'verb'; + this.isApp = true; + this.data({runner: pkg}); this.data({verb: {related: {}, reflinks: []}}); + var aliasRegex = /(^verb-?|-?generat(e|or)-?)/g; // temporary, there is an easier way to do this this.toFullname = function(str) { - var re = /(^verb-?|-?generat(e|or)-?)/g; - var alias = str.replace(re, ''); - return 'verb-' + alias + '-generator'; + return 'verb-' + this.toAlias(str) + '-generator'; + }; + + // temporary, there is an easier way to do this + this.toAlias = function(str) { + return str.replace(aliasRegex, ''); }; if (process.env.GENERATE_CLI) { + this.use(utils.create(this.options)); this.create('files'); this.create('docs'); } From e7e45ddd82cead07cd78b24b2e1e4cde4975ca4f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 20 Feb 2016 03:45:07 -0500 Subject: [PATCH 179/282] update deps --- package.json | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 759c9cfc..90fe0016 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "verb", - "description": "Documentation generator for GitHub projects.", - "version": "0.1.0", + "description": "Documentation system for GitHub projects.", + "version": "0.9.0", "homepage": "https://github.com/verbose/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", "repository": "verbose/verb", @@ -26,19 +26,16 @@ "verb": "bin/verb.js" }, "dependencies": { - "base-questions": "^0.3.2", "debug": "^2.2.0", - "generate": "^0.4.0", - "generator-util": "^0.2.5", - "is-affirmative": "^0.1.0" + "generate": "^0.4.5", + "generator-util": "^0.2.7" }, "devDependencies": { "async": "^1.5.2", - "base-config": "^0.4.0", - "base-store": "^0.3.6", + "base-config": "^0.4.2", + "base-questions": "^0.3.2", "buffer-equal": "^1.0.0", "consolidate": "^0.14.0", - "data-store": "^0.14.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", "engine-handlebars": "^0.8.0", @@ -48,18 +45,18 @@ "global-modules": "^0.2.0", "graceful-fs": "^4.1.3", "gulp": "^3.9.1", - "gulp-eslint": "^1.1.1", - "gulp-format-md": "^0.1.5", + "gulp-eslint": "^2.0.0", + "gulp-format-md": "^0.1.7", "gulp-istanbul": "^0.10.3", "gulp-mocha": "^2.2.0", "is-buffer": "^1.1.2", "kind-of": "^3.0.2", "load-pkg": "^3.0.1", "matched": "^0.4.1", - "mocha": "*", + "mocha": "^2.4.5", "parser-front-matter": "^1.3.0", "resolve-glob": "^0.1.8", - "rimraf": "^2.5.1", + "rimraf": "^2.5.2", "should": "^8.2.2", "sinon": "^1.17.3", "spawn-commands": "^0.3.1", @@ -68,7 +65,8 @@ "vinyl": "^1.1.1" }, "verb": { - "toc": true, + "run": true, + "toc": false, "layout": "default", "tasks": [ "readme" @@ -87,10 +85,14 @@ "assemble", "base", "generate", - "verb" - ] + "verb", + "update" + ], + "lint": { + "reflinks": true + } }, "lintDeps": { "ignore": [] } -} +} \ No newline at end of file From 97e0f70833ecc8b128cf9fed23a280d59da5a666 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 20 Feb 2016 03:45:22 -0500 Subject: [PATCH 180/282] remove junk from lib --- lib/generators/generator.js | 19 ---------- lib/generators/init.js | 57 ---------------------------- lib/generators/verbfile.js | 30 --------------- lib/generators/verbfiles.js | 15 -------- lib/generators/verbmd.js | 31 --------------- lib/utils.js | 75 ------------------------------------- verbfile.js | 14 ------- 7 files changed, 241 deletions(-) delete mode 100644 lib/generators/generator.js delete mode 100644 lib/generators/init.js delete mode 100644 lib/generators/verbfile.js delete mode 100644 lib/generators/verbfiles.js delete mode 100644 lib/generators/verbmd.js delete mode 100644 lib/utils.js delete mode 100644 verbfile.js diff --git a/lib/generators/generator.js b/lib/generators/generator.js deleted file mode 100644 index 0365d1e6..00000000 --- a/lib/generators/generator.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var utils = require('../utils'); - -module.exports = function(verb) { - verb.task('generator', function(cb) { - verb.src('templates/generator.js', {cwd: __dirname}) - .pipe(verb.dest(function(file) { - file.basename = verb.options.f || file.basename; - return verb.cwd; - })) - .on('end', function() { - console.log('created file'); - cb(); - }); - }); - - verb.task('default', ['generator']); -}; diff --git a/lib/generators/init.js b/lib/generators/init.js deleted file mode 100644 index 0389fb1c..00000000 --- a/lib/generators/init.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -var debug = require('debug')('verb:generator'); -var isYes = require('is-affirmative'); - -module.exports = function(app, base) { - var store = app.store; - - app.questions - .set('setTasks', { - message: 'Default tasks', - type: 'input', - force: true - }) - .set('run', { - message: 'Do you want to run tasks when arbitrary flags are passed?', - type: 'input', - force: true - }) - .set('run.before', { - message: 'Enter the names of the generators and tasks to run:', - type: 'input', - force: true - }) - .set('run.after', { - message: 'Enter the names of one or more tasks, generators, or generators and tasks to run after `end` is emitted:', - type: 'input', - force: true - }); - - app.task('default', function(cb) { - console.log('Welcome to verb! Since this is your first time running verb,'); - console.log('you can specify the names of tasks and/or generators to run'); - console.log('every time you run verb. Tips:'); - console.log('- to run the `readme` generator, just enter "readme"') - console.log('- to run the `default` generator, just enter "default"') - console.log('- search npm for verb generators, and enter the names of the generators to run'); - app.ask('setTasks', function(err, answers) { - if (err) return cb(err); - var tasks = answers.setTasks; - store.set('tasks', tasks); - console.log('Tasks "%s" will now run with the verb command', tasks); - cb(); - }); - }); - - app.task('run', function(cb) { - app.ask('run', function(err, answers) { - answers.run.before = answers.run.before.split(' '); - answers.run.after = answers.run.after.split(' '); - var res = answers.run; - console.log('implement me!'); - console.log(res); - cb(); - }); - }); -}; diff --git a/lib/generators/verbfile.js b/lib/generators/verbfile.js deleted file mode 100644 index ed6bcb3d..00000000 --- a/lib/generators/verbfile.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var utils = require('../utils'); - -module.exports = function(verb) { - verb.task('verbfile', function(cb) { - var opts = { cwd: verb.cwd, filename: 'verbfile.js' }; - utils.conflict(verb, opts, function(err, conflict) { - if (err) { - cb(err); - return; - } - - if (conflict === true) { - console.log('keeping existing verbfile.js'); - cb(); - return; - } - - verb.src('templates/verbfile.js', {cwd: __dirname}) - .pipe(verb.dest(verb.cwd)) - .on('end', function() { - console.log('created verbfile.js'); - cb(); - }); - }); - }); - - verb.task('default', ['verbfile']); -}; diff --git a/lib/generators/verbfiles.js b/lib/generators/verbfiles.js deleted file mode 100644 index ba1c5c64..00000000 --- a/lib/generators/verbfiles.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('../utils'); - -module.exports = function(verb) { - verb.register('verbfile', require('./verbfile')); - verb.register('verbmd', require('./verbmd')); - - verb.task('verbfiles', function(cb) { - utils.ask(verb, '.verb.md', cb); - }); - - verb.task('default', ['verbfiles']); -}; diff --git a/lib/generators/verbmd.js b/lib/generators/verbmd.js deleted file mode 100644 index 5b7c2cb0..00000000 --- a/lib/generators/verbmd.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var utils = require('../utils'); - -module.exports = function(verb) { - - verb.task('verbmd', function(cb) { - var opts = { cwd: verb.cwd, filename: '.verb.md' }; - utils.conflict(verb, opts, function(err, conflict) { - if (err) { - cb(err); - return; - } - - if (conflict === true) { - console.log('keeping existing .verb.md'); - cb(); - return; - } - - verb.src('templates/.verb.md', {cwd: __dirname}) - .pipe(verb.dest(verb.cwd)) - .on('end', function() { - console.log('created .verb.md'); - cb(); - }); - }); - }); - - verb.task('default', ['verbmd']); -}; diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index c338198b..00000000 --- a/lib/utils.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; - -var path = require('path'); -var utils = require('generator-util'); -var fn = require; -require = utils; - -/** - * Utils - */ - -require('is-affirmative'); -require = fn; - -utils.conflict = function(app, options, cb) { - if (typeof cb !== 'function') { - throw new TypeError('expected a callback function'); - } - options = options || {}; - if (typeof options.filename !== 'string') { - throw new TypeError('expected options.filename to be a string'); - } - var filename = options.filename; - var fp = path.resolve(options.cwd || process.cwd(), filename); - app.questions.set('conflict', filename + ' exists, want to overwrite it?', {save: false}); - if (utils.exists(fp)) { - app.ask('conflict', function(err, answers) { - if (err) return cb(err); - - cb(null, !utils.isAffirmative(answers.conflict)); - }); - } else { - cb(null, false); - } -}; - -utils.ask = function(app, filename, cb) { - if (app.disabled('force') && utils.exists(filename)) { - cb(); - return; - } - - // create a question - app.questions.set('writefile', 'Can\'t find "' + filename + '", want to add one?', { - save: false - }); - - // ask the question created above - app.ask('writefile', function(err, answers) { - if (err) { - cb(err); - return; - } - - // if the answer is falsey, we're cb - if (!utils.isAffirmative(answers.writefile)) { - cb(); - return; - } - - // if the answer is "affirmative", write the file - app.src(filename, {cwd: path.resolve(__dirname, 'generators/templates')}) - .pipe(app.dest(app.cwd)) - .on('end', function() { - console.log('created', filename); - cb(); - }); - }); -}; - -/** - * Expose utils - */ - -module.exports = utils; diff --git a/verbfile.js b/verbfile.js deleted file mode 100644 index a13d3518..00000000 --- a/verbfile.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -/** - * Example verbfile.js, extends `verb-readme-generator`, which - * provides the `readme` task for building the readme. - */ - -module.exports = function(verb) { - verb.extendWith('verb-readme-generator'); - - // do anything here! - - verb.task('default', ['readme']); -}; From 79f53d3032bcb3e7ffa54b57fd0db0a5bc9ec7f5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 23 Feb 2016 18:05:03 -0500 Subject: [PATCH 181/282] reorganize init code to ensure objects exist when they need to --- index.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 50e446ec..c612df10 100644 --- a/index.js +++ b/index.js @@ -27,8 +27,15 @@ function Verb(options) { if (!(this instanceof Verb)) { return new Verb(options); } - this.options = this.verbDefaults(options); + + this.options = this.options || {}; + this.verbDefaults(options); + Generate.call(this, this.options); + + this.is('verb'); + this.define('isApp', true); + this.initVerb(this.options); } @@ -43,11 +50,7 @@ Generate.extend(Verb); */ Verb.prototype.initVerb = function(opts) { - debug('initializing verb data'); - - this.is('verb'); - this.name = 'verb'; - this.isApp = true; + this.debug('initializing verb data'); this.data({runner: pkg}); this.data({verb: {related: {}, reflinks: []}}); @@ -77,7 +80,7 @@ Verb.prototype.initVerb = function(opts) { Verb.prototype.verbDefaults = function(options) { debug('initializing verb defaults'); var defaults = { prefix: 'verb', configfile: 'verbfile.js' }; - return utils.extend({}, defaults, this.options, options); + this.options = utils.extend(defaults, this.options, options); }; /** From d605db1917ffb5069f2516e0028b9fbcf73cc231 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 23 Feb 2016 18:06:18 -0500 Subject: [PATCH 182/282] questions tests --- test/questions.js | 316 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 250 insertions(+), 66 deletions(-) diff --git a/test/questions.js b/test/questions.js index ac9272a8..c60762da 100644 --- a/test/questions.js +++ b/test/questions.js @@ -1,93 +1,277 @@ 'use strict'; +process.env.NODE_ENV = 'test'; + require('mocha'); -require('should'); var fs = require('fs'); var assert = require('assert'); -var support = require('./support'); -var config = require('base-config'); -var ask = require('base-questions'); -var App = support.resolve(); -var app; - -describe('content', function() { - beforeEach(function() { - app = new App(); - app.use(config()); - app.use(ask()); - }); +var store = require('base-store'); +var App = require('..'); +var app, base, site; - afterEach(function(cb) { - app.questions.del('a'); - cb(); - }); +describe('app.questions', function() { + describe('plugin', function() { + beforeEach(function() { + base = new App(); + base.use(store('base-questions-tests/base')); + + app = new App(); + app.use(store('base-questions-tests/app')); + }); + + it('should expose a `questions` object on "app"', function() { + assert.equal(typeof app.questions, 'object'); + }); + + it('should expose a `set` method on "app.questions"', function() { + assert.equal(typeof app.questions.set, 'function'); + }); + it('should expose a `get` method on "app.questions"', function() { + assert.equal(typeof app.questions.get, 'function'); + }); + it('should expose an `ask` method on "app.questions"', function() { + assert.equal(typeof app.questions.ask, 'function'); + }); - it('should store a question:', function() { - app.question('a', 'b'); - assert(app.questions); - assert(app.questions.cache); - assert(app.questions.cache.a); - assert(app.questions.cache.a.name === 'a'); - assert(app.questions.cache.a.options.message === 'b'); + it('should expose an `ask` method on "app"', function() { + assert.equal(typeof app.ask, 'function'); + }); + it('should expose a `question` method on "app"', function() { + assert.equal(typeof app.question, 'function'); + }); }); - it('should ask a question and use data value to answer:', function(cb) { - app.question('a', 'b'); - app.data('a', 'b'); + describe('app.ask', function() { + beforeEach(function() { + app = new App(); + app.use(store('base-questions-tests/ask')); + }); - app.ask('a', function(err, answers) { - assert(!err); - assert(answers.a === 'b'); + afterEach(function() { + app.store.del({force: true}); + app.questions.clearCache(); + app.cache.data = {}; + }); + + it.skip('should force all questions to be asked', function(cb) { + app.questions.option('init', 'author'); + app.ask({force: true}, function(err, answers) { + console.log(answers) + cb(); + }); + }); - app.data('a', 'zzz'); + it('should store a question:', function() { + app.question('a', 'b'); + assert(app.questions); + assert(app.questions.cache); + assert(app.questions.cache.a); + assert.equal(app.questions.cache.a.name, 'a'); + assert.equal(app.questions.cache.a.options.message, 'b'); + }); + + it.skip('should re-init a specific question:', function(cb) { + this.timeout(20000); + app.question('a', 'b'); + app.question('c', 'd'); + app.question('e', 'f'); + app.data({a: 'b'}); + + app.questions.get('e') + .force() + + app.ask(function(err, answers) { + console.log(answers); + cb(); + }); + }); + + it('should ask a question defined on `ask`', function(cb) { + app.data('name', 'Brian Woodward'); + + app.ask('name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.name, 'Brian Woodward'); + cb(); + }); + }); + + it('should ask a question and use a `cache.data` value to answer:', function(cb) { + app.question('a', 'this is a question'); + app.data('a', 'b'); + + app.ask('a', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.a, 'b'); + + app.data('a', 'zzz'); + app.ask('a', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.a, 'zzz'); + cb(); + }) + }); + }); + + it('should ask a question and use a `store.data` value to answer:', function(cb) { + app.question('a', 'this is another question'); + app.store.set('a', 'c'); app.ask('a', function(err, answers) { - assert(!err); - assert(answers.a === 'zzz'); + if (err) return cb(err); + assert(answers); + assert.equal(answers.a, 'c'); cb(); }) }); - }); - it('should ask a question and use store value to answer:', function(cb) { - app.question('a', 'b'); - app.store.set('a', 'c'); - - app.ask('a', function(err, answers) { - assert(!err); - assert(answers); - assert(answers.a === 'c'); - app.store.del('a'); - cb(); - }) - }); + it('should ask a question and use a config value to answer:', function(cb) { + app.question('a', 'b'); + app.config.process({data: {a: 'foo'}}, function(err) { + if (err) return cb(err); + + app.store.set('a', 'c'); - it('should ask a question and use config value to answer:', function(cb) { - app.question('a', 'b'); - app.config.process({data: {a: 'zzz'}}, function(err) { - if (err) cb(err); + app.ask('a', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.a, 'foo'); + cb(); + }); + }); }); - app.ask('a', function(err, answer) { - assert(!err); - assert(answer); - assert(answer.a === 'zzz'); - cb(); + it('should prefer `cache.data` to `store.data`', function(cb) { + app.question('a', 'b'); + app.data('a', 'b'); + app.store.set('a', 'c'); + + app.ask('a', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.a, 'b'); + cb(); + }) + }); + + it('should update data with data loaded by config', function(cb) { + app.question('a', 'this is a question'); + app.data('a', 'b'); + + app.config.process({data: {a: 'foo'}}, function(err) { + if (err) return cb(err); + + app.ask('a', function(err, answer) { + if (err) return cb(err); + + assert(answer); + assert.equal(answer.a, 'foo'); + cb(); + }); + }); }); }); - it('should prefer data from config over store.data', function(cb) { - app.question('a', 'b'); - app.config.process({data: {a: 'zzz'}}, function(err) { - if (err) cb(err); + describe('session data', function() { + before(function() { + site = new App(); + site.use(store('base-questions-tests/site')); + + app = new App(); + app.use(store('base-questions-tests/ask')); + }); + + after(function() { + site.store.del({force: true}); + site.questions.clearCache(); + + app.store.del({force: true}); + app.questions.clearCache(); + }); + + it('[app] should ask a question and use a `cache.data` value to answer:', function(cb) { + app.question('package.name', 'this is a question'); + app.data('package.name', 'base-questions'); + + app.ask('package.name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.package.name, 'base-questions'); + + app.data('package.name', 'foo-bar-baz'); + app.ask('package.name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.package.name, 'foo-bar-baz'); + cb(); + }) + }); }); - app.store.set('a', 'c'); - app.ask('a', function(err, answer) { - assert(!err); - assert(answer); - assert(answer.a === 'zzz'); - app.store.del('a'); - cb(); + it('[site] should ask a question and use a `cache.data` value to answer:', function(cb) { + site.question('package.name', 'this is a question'); + site.data('package.name', 'base-questions'); + + site.ask('package.name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.package.name, 'base-questions'); + + site.data('package.name', 'foo-bar-baz'); + site.ask('package.name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.package.name, 'foo-bar-baz'); + cb(); + }) + }); + }); + + it('[app] should ask a question and use a `store.data` value to answer:', function(cb) { + app.question('author.name', 'this is another question'); + app.store.set('author.name', 'Brian Woodward'); + app.ask('author.name', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.author.name, 'Brian Woodward'); + cb(); + }) + }); + + it('[site] should ask a question and use a `store.data` value to answer:', function(cb) { + site.question('author.name', 'this is another question'); + site.store.set('author.name', 'Jon Schlinkert'); + site.ask('author.name', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.author.name, 'Brian Woodward'); + cb(); + }) + }); + + it('[app] should ask a question and use a config value to answer:', function(cb) { + app.question('foo', 'Username?'); + app.config.process({data: {foo: 'jonschlinkert'}}, function(err) { + if (err) return cb(err); + + app.store.set('foo', 'doowb'); + + app.ask('foo', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.foo, 'jonschlinkert'); + cb(); + }); + }); + }); + + it('[site] should ask a question and use a config value to answer:', function(cb) { + site.question('foo', 'Username?'); + site.config.process({data: {foo: 'doowb'}}, function(err) { + if (err) return cb(err); + + site.ask('foo', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.foo, 'doowb'); + cb(); + }); + }); }); }); }); From 7366ff4d564ba904f1f0f8e1734e41e04cc49684 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 28 Feb 2016 14:20:44 -0500 Subject: [PATCH 183/282] clean up lib --- bin/verb.js | 2 +- lib/{generators/default.js => generator.js} | 2 +- lib/generators/index.js | 3 -- lib/generators/templates/.verb.md | 34 --------------------- lib/generators/templates/generator.js | 10 ------ lib/generators/templates/verbfile.js | 13 -------- 6 files changed, 2 insertions(+), 62 deletions(-) rename lib/{generators/default.js => generator.js} (52%) delete mode 100644 lib/generators/index.js delete mode 100644 lib/generators/templates/.verb.md delete mode 100644 lib/generators/templates/generator.js delete mode 100644 lib/generators/templates/verbfile.js diff --git a/bin/verb.js b/bin/verb.js index 0d1c1e28..d307f976 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,7 +1,7 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; -var generator = require('../lib/generators'); +var generator = require('../lib/generator'); var verb = require('..'); /** diff --git a/lib/generators/default.js b/lib/generator.js similarity index 52% rename from lib/generators/default.js rename to lib/generator.js index 2cd9ed7a..2bc5ef28 100644 --- a/lib/generators/default.js +++ b/lib/generator.js @@ -2,7 +2,7 @@ module.exports = function verbDefault(verb) { verb.task('default', function(cb) { - console.log('No tasks were defined, doing nothing.'); + console.log('No tasks were defined, try running `verb readme` if verb-readme-generator is installed'); cb(); }); }; diff --git a/lib/generators/index.js b/lib/generators/index.js deleted file mode 100644 index 6356a28c..00000000 --- a/lib/generators/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('./default'); diff --git a/lib/generators/templates/.verb.md b/lib/generators/templates/.verb.md deleted file mode 100644 index 87cfe7bf..00000000 --- a/lib/generators/templates/.verb.md +++ /dev/null @@ -1,34 +0,0 @@ -# {%= name %} {%= badge('npm') %} {%= badge('travis') %} - -> {%= description %} - -## Install -{%= include('install-npm', {save: true}) %} - -## Usage - -```js -var {%= alias %} = require('{%= name %}'); -``` - -## Running tests -{%= include("tests") %} - -## Related projects -{%= related(verb.related.list) %} - -## Contributing -{%= include("contributing") %} - -## Author -{%= include("author") %} - -## License -{%= copyright({linkify: true}) %} -{%= license %} - -*** - -{%= include("footer") %} - -{%= reflinks(verb.reflinks) %} diff --git a/lib/generators/templates/generator.js b/lib/generators/templates/generator.js deleted file mode 100644 index 5a1810bc..00000000 --- a/lib/generators/templates/generator.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = function(verb) { - verb.task('<%= alias %>', function(cb) { - console.log('verb generator > default task'); - cb(); - }); - - verb.task('default', ['<%= alias %>']); -}; diff --git a/lib/generators/templates/verbfile.js b/lib/generators/templates/verbfile.js deleted file mode 100644 index 5e8b9abf..00000000 --- a/lib/generators/templates/verbfile.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -var extend = require('extend-shallow'); - -module.exports = function(verb) { - verb.extendWith('verb-generate-readme'); - - verb.helper('foo', function(name, locals) { - var ctx = extend({}, this.context, locals); - }); - - verb.task('default', ['readme']); -}; From 3d0c388b3200b237c35bf8bb204b94a9a9988362 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 28 Feb 2016 14:21:10 -0500 Subject: [PATCH 184/282] adds debug --- index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index c612df10..f974efd8 100644 --- a/index.js +++ b/index.js @@ -7,9 +7,9 @@ 'use strict'; -var debug = require('debug')('base:verb'); var utils = require('generator-util'); var Generate = require('generate'); +var debug = Generate.debug; var pkg = require('./package'); /** @@ -29,13 +29,13 @@ function Verb(options) { } this.options = this.options || {}; - this.verbDefaults(options); - Generate.call(this, this.options); this.is('verb'); this.define('isApp', true); + debug(this); + this.verbDefaults(options); this.initVerb(this.options); } @@ -78,7 +78,7 @@ Verb.prototype.initVerb = function(opts) { */ Verb.prototype.verbDefaults = function(options) { - debug('initializing verb defaults'); + this.debug('initializing verb defaults'); var defaults = { prefix: 'verb', configfile: 'verbfile.js' }; this.options = utils.extend(defaults, this.options, options); }; From ee8d388e0cb0e1a46733b74b1fbf9b2bf2994b5e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 28 Feb 2016 14:21:23 -0500 Subject: [PATCH 185/282] update deps --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 90fe0016..264e5511 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,12 @@ }, "dependencies": { "debug": "^2.2.0", - "generate": "^0.4.5", + "generate": "^0.4.7", "generator-util": "^0.2.7" }, "devDependencies": { "async": "^1.5.2", - "base-config": "^0.4.2", - "base-questions": "^0.3.2", + "base-store": "^0.3.6", "buffer-equal": "^1.0.0", "consolidate": "^0.14.0", "define-property": "^0.2.5", @@ -66,7 +65,7 @@ }, "verb": { "run": true, - "toc": false, + "toc": true, "layout": "default", "tasks": [ "readme" From 1d524356ff6fe886a7f183f02de6764de13616ed Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 28 Feb 2016 14:21:29 -0500 Subject: [PATCH 186/282] generate docs --- .verb.md | 38 +++++++++++++++++++++++--------- readme.md | 66 ++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/.verb.md b/.verb.md index ad821b1c..cdbb2fb4 100644 --- a/.verb.md +++ b/.verb.md @@ -63,6 +63,34 @@ module.exports = function(verb) { ## CLI Commands +There are several generic flags for setting options from the command line: `option`, `data`, `config` and `save`. Beyond these there are also a number of specialized flags described below. + +- `--option`: set options in memory +- `--data`: set data to be passed to templates in memory +- `--config`: persist options to package.json +- `--save`: persist options to a global config store + +### config + +Persist configuration settings to the `verb` object in `package.json`. + +```sh +$ verb --config +``` + +Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. + + +### save + +Persist options values to the global data store for `app` ([verb][], [assemble][], [generate][], [update][], etc) + +```sh +$ verb --save +``` + +Most of the above CLI commands can be prefixed with `--save` to persist the value to the global config store. + ### file Specify the file to use instead of `verbfile.js`. @@ -97,16 +125,6 @@ Display the currently defined cwd: $ verb --cwd ``` -### config - -Persist configuration settings to the `verb` object in `package.json`. - -```sh -$ verb --config=[options] -``` - -Most of the above CLI commands can be prefixed with `config=` to persist the value to package.json. - #### tasks Set the default tasks to run for a project: diff --git a/readme.md b/readme.md index f095a0ee..74ff6a67 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg)](https://travis-ci.org/verbose/verb) -> Documentation generator for GitHub projects. +> Documentation system for GitHub projects. ## TOC @@ -9,24 +9,27 @@ - [Quickstart](#quickstart) - [verbfile](#verbfile) - [CLI Commands](#cli-commands) + * [config](#config) + * [save](#save) * [file](#file) * [cwd](#cwd) - * [config](#config) + [tasks](#tasks) - [API](#api) - [Related projects](#related-projects) -- [Generate docs](#generate-docs) -- [Running tests](#running-tests) - [Contributing](#contributing) +- [Building docs](#building-docs) +- [Running tests](#running-tests) - [Author](#author) - [License](#license) +_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ + ## Install Install with [npm](https://www.npmjs.com/): ```sh -$ npm i verb --save +$ npm install verb --save ``` ## What is verb? @@ -93,6 +96,33 @@ module.exports = function(verb) { ## CLI Commands +There are several generic flags for setting options from the command line: `option`, `data`, `config` and `save`. Beyond these there are also a number of specialized flags described below. + +* `--option`: set options in memory +* `--data`: set data to be passed to templates in memory +* `--config`: persist options to package.json +* `--save`: persist options to a global config store + +### config + +Persist configuration settings to the `verb` object in `package.json`. + +```sh +$ verb --config +``` + +Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. + +### save + +Persist options values to the global data store for `app` ([verb](https://github.com/verbose/verb), [assemble](https://github.com/assemble/assemble), [generate](https://github.com/generate/generate), [update](https://github.com/update/update), etc) + +```sh +$ verb --save +``` + +Most of the above CLI commands can be prefixed with `--save` to persist the value to the global config store. + ### file Specify the file to use instead of `verbfile.js`. @@ -127,16 +157,6 @@ Display the currently defined cwd: $ verb --cwd ``` -### config - -Persist configuration settings to the `verb` object in `package.json`. - -```sh -$ verb --config=[options] -``` - -Most of the above CLI commands can be prefixed with `config=` to persist the value to package.json. - #### tasks Set the default tasks to run for a project: @@ -178,12 +198,16 @@ var app = verb(); * [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base) | [homepage](https://github.com/node-base/base) * [generate](https://www.npmjs.com/package/generate): Fast, composable, highly extendable project generator with a user-friendly and expressive API. | [homepage](https://github.com/generate/generate) -## Generate docs +## Contributing + +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). + +## Building docs Generate readme and API documentation with [verb](https://github.com/verbose/verb): ```sh -$ npm i -d && npm run docs +$ npm install verb && npm run docs ``` Or, if [verb](https://github.com/verbose/verb) is installed globally: @@ -197,13 +221,9 @@ $ verb Install dev dependencies: ```sh -$ npm i -d && npm test +$ npm install -d && npm test ``` -## Contributing - -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). - ## Author **Jon Schlinkert** @@ -218,4 +238,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 12, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 28, 2016._ \ No newline at end of file From a5d0de65359b3aa6e457c0b9575da2012a10616c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:02:45 -0500 Subject: [PATCH 187/282] update tests --- lib/generator.js | 4 +- test/app.dest.js | 273 +++++++------- test/app.doc.js | 4 +- test/app.docs.js | 4 +- test/app.env.js | 72 ++++ test/app.extendWith.js | 414 ++++++++++++++++++++- test/app.generate.js | 260 +++++++++---- test/app.generateEach.js | 7 +- test/app.generator.js | 163 +++++--- test/app.getGenerator.js | 50 +-- test/app.include.js | 4 +- test/app.includes.js | 4 +- test/app.invoke.js | 117 +++--- test/app.js | 10 - test/app.mergePartials.js | 12 +- test/{questions.js => app.questions.js} | 8 +- test/app.register.js | 179 +++++---- test/app.resolve.js | 27 +- test/app.store.js | 14 +- test/app.task.js | 86 ++--- test/app.toAlias.js | 31 -- test/env.js | 47 ++- test/fixtures/def-gen.js | 8 + test/fixtures/generator.js | 49 +++ test/fixtures/generators/a/generator.js | 10 + test/fixtures/generators/c/generator.js | 39 ++ test/fixtures/generators/e/generator.js | 43 +++ test/fixtures/generators/qux/generator.js | 11 + test/fixtures/generators2.js | 2 +- test/fixtures/one/package.json | 2 +- test/fixtures/three/four/five/generator.js | 12 +- test/fixtures/three/four/five/package.json | 2 +- test/fixtures/three/four/generator.js | 12 +- test/fixtures/three/four/package.json | 2 +- test/fixtures/three/generator.js | 12 +- test/fixtures/three/package.json | 2 +- test/fixtures/two/generate.js | 18 +- test/fixtures/two/package.json | 2 +- test/fixtures/verbfile.js | 2 +- test/generate.js | 8 +- test/generators.cache.js | 223 +++++++++++ test/generators.env.js | 117 ++++++ test/generators.events.js | 169 +++++++++ test/partials.js | 2 +- test/store.js | 6 +- test/support/spy.js | 9 +- test/view.methods.js | 25 +- test/views.js | 1 - 48 files changed, 1943 insertions(+), 635 deletions(-) create mode 100644 test/app.env.js rename test/{questions.js => app.questions.js} (97%) delete mode 100644 test/app.toAlias.js create mode 100644 test/fixtures/def-gen.js create mode 100644 test/fixtures/generator.js create mode 100644 test/fixtures/generators/a/generator.js create mode 100644 test/fixtures/generators/c/generator.js create mode 100644 test/fixtures/generators/e/generator.js create mode 100644 test/fixtures/generators/qux/generator.js create mode 100644 test/generators.cache.js create mode 100644 test/generators.env.js create mode 100644 test/generators.events.js diff --git a/lib/generator.js b/lib/generator.js index 2bc5ef28..414c565e 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,8 +1,10 @@ 'use strict'; +var wrap = require('word-wrap'); + module.exports = function verbDefault(verb) { verb.task('default', function(cb) { - console.log('No tasks were defined, try running `verb readme` if verb-readme-generator is installed'); + verb.log('No tasks were defined, try running `verb readme` if verb-readme-generator is installed'); cb(); }); }; diff --git a/test/app.dest.js b/test/app.dest.js index 97fc937e..d2c58ef0 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -1,17 +1,23 @@ -var spies = require('./support/spy'); -var chmodSpy = spies.chmodSpy; -var statSpy = spies.statSpy; +'use strict'; + +var os = require('os'); +var path = require('path'); +var fs = require('graceful-fs'); +var rimraf = require('rimraf'); +var isWindows = (os.platform() === 'win32'); require('mocha'); var should = require('should'); var assert = require('assert'); +var expect = require('expect'); + +var spies = require('./support/spy'); +var chmodSpy = spies.chmodSpy; +var statSpy = spies.statSpy; + var App = require('..'); var app; -var path = require('path'); -var fs = require('graceful-fs'); -var rimraf = require('rimraf'); - var bufferStream; var bufEqual = require('buffer-equal'); var through = require('through2'); @@ -21,10 +27,11 @@ var actual = path.join(__dirname, 'actual'); var wipeOut = function(cb) { app = new App(); - rimraf(path.join(__dirname, 'actual/'), cb); spies.setError('false'); statSpy.reset(); chmodSpy.reset(); + expect.restoreSpies(); + rimraf(path.join(__dirname, 'actual/'), cb); }; var dataWrap = function(fn) { @@ -34,9 +41,11 @@ var dataWrap = function(fn) { }; }; -var realMode = function(n) { - return n & 07777; -}; +var MASK_MODE = parseInt('777', 8); + +function masked(mode) { + return mode & MASK_MODE; +} describe('dest stream', function() { beforeEach(wipeOut); @@ -46,7 +55,7 @@ describe('dest stream', function() { var stream; try { stream = app.dest(); - } catch (err) { + }catch (err) { assert(err && typeof err === 'object'); should.not.exist(stream); cb(); @@ -57,7 +66,7 @@ describe('dest stream', function() { var stream; try { stream = app.dest(''); - } catch (err) { + }catch (err) { assert(err && typeof err === 'object'); should.not.exist(stream); cb(); @@ -74,7 +83,7 @@ describe('dest stream', function() { contents: null }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); cb(); @@ -99,7 +108,7 @@ describe('dest stream', function() { contents: null }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); cb(); @@ -128,7 +137,7 @@ describe('dest stream', function() { contents: null }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); @@ -162,7 +171,7 @@ describe('dest stream', function() { contents: expectedContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); @@ -197,7 +206,7 @@ describe('dest stream', function() { contents: expectedContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); @@ -208,7 +217,7 @@ describe('dest stream', function() { cb(); }; - var stream = app.dest(function(file){ + var stream = app.dest(function(file) { should.exist(file); file.should.equal(expectedFile); return './actual'; @@ -228,7 +237,7 @@ describe('dest stream', function() { var expectedContents = fs.readFileSync(inputPath); var expectedCwd = __dirname; var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0655; + var expectedMode = parseInt('655', 8); var expectedFile = new File({ base: inputBase, @@ -240,7 +249,7 @@ describe('dest stream', function() { } }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); @@ -248,7 +257,7 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); cb(); }; @@ -268,7 +277,7 @@ describe('dest stream', function() { var expectedContents = fs.readFileSync(inputPath); var expectedCwd = __dirname; var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0655; + var expectedMode = parseInt('655', 8); var contentStream = through.obj(); var expectedFile = new File({ @@ -281,7 +290,7 @@ describe('dest stream', function() { } }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); @@ -289,7 +298,7 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); cb(); }; @@ -299,7 +308,7 @@ describe('dest stream', function() { bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); stream.pipe(bufferStream); stream.write(expectedFile); - setTimeout(function(){ + setTimeout(function() { contentStream.write(expectedContents); contentStream.end(); }, 100); @@ -312,7 +321,7 @@ describe('dest stream', function() { var expectedPath = path.join(__dirname, 'actual/test'); var expectedCwd = __dirname; var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0655; + var expectedMode = parseInt('655', 8); var expectedFile = new File({ base: inputBase, @@ -320,14 +329,14 @@ describe('dest stream', function() { path: inputPath, contents: null, stat: { - isDirectory: function(){ + isDirectory: function() { return true; }, mode: expectedMode } }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); @@ -335,7 +344,7 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); fs.lstatSync(expectedPath).isDirectory().should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); cb(); }; @@ -391,20 +400,20 @@ describe('dest stream', function() { var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedMode = 0666 & (~process.umask()); + var expectedMode = parseInt('0666', 8) & (~process.umask()); var expectedFile = new File({ base: inputBase, cwd: __dirname, path: inputPath, - contents: expectedContents, + contents: expectedContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); cb(); }; @@ -424,25 +433,25 @@ describe('dest stream', function() { var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedMode = 0744; + var expectedMode = parseInt('744', 8); var expectedFile = new File({ base: inputBase, cwd: __dirname, path: inputPath, - contents: expectedContents, + contents: expectedContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); cb(); }; chmodSpy.reset(); - var stream = app.dest('./actual/', {cwd: __dirname, mode:expectedMode}); + var stream = app.dest('./actual/', { cwd: __dirname, mode: expectedMode }); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -453,13 +462,18 @@ describe('dest stream', function() { }); it('should update file mode to match the vinyl mode', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var startMode = 0655; - var expectedMode = 0722; + var expectedBase = path.join(__dirname, './actual'); + var startMode = parseInt('0655', 8); + var expectedMode = parseInt('0722', 8); var expectedFile = new File({ base: inputBase, @@ -471,12 +485,8 @@ describe('dest stream', function() { } }); - var onEnd = function(){ - assert(chmodSpy.called); - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + var onEnd = function() { + assert.equal(masked(fs.lstatSync(expectedPath).mode), expectedMode); cb(); }; @@ -484,13 +494,8 @@ describe('dest stream', function() { fs.closeSync(fs.openSync(expectedPath, 'w')); fs.chmodSync(expectedPath, startMode); - chmodSpy.reset(); - var stream = app.dest('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); + var stream = app.dest('actual/', { cwd: __dirname }); + stream.on('end', onEnd); stream.write(expectedFile); stream.end(); }); @@ -499,8 +504,8 @@ describe('dest stream', function() { var inputBase = path.join(__dirname, 'fixtures/vinyl'); var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); var expectedBase = path.join(__dirname, 'actual/wow'); - var expectedDirMode = 0755; - var expectedFileMode = 0655; + var expectedDirMode = parseInt('755', 8); + var expectedFileMode = parseInt('655', 8); var firstFile = new File({ base: inputBase, @@ -509,9 +514,9 @@ describe('dest stream', function() { stat: fs.statSync(inputPath) }); - var onEnd = function(){ - realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); - realMode(buffered[0].stat.mode).should.equal(expectedFileMode); + var onEnd = function() { + masked(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); + masked(buffered[0].stat.mode).should.equal(expectedFileMode); cb(); }; @@ -539,7 +544,7 @@ describe('dest stream', function() { stat: fs.statSync(inputPath) }); - var onEnd = function(){ + var onEnd = function() { buffered[0].base.should.equal(inputBase); cb(); }; @@ -574,7 +579,7 @@ describe('dest stream', function() { var stream = app.dest('./actual/', { cwd: __dirname, - base: function(file){ + base: function(file) { should.exist(file); file.path.should.equal(inputPath); return inputBase; @@ -595,7 +600,7 @@ describe('dest stream', function() { var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0722; + var expectedMode = parseInt('722', 8); var expectedFile = new File({ base: inputBase, @@ -625,7 +630,7 @@ describe('dest stream', function() { var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0722; + var expectedMode = parseInt('722', 8); var expectedFile = new File({ base: inputBase, @@ -641,7 +646,7 @@ describe('dest stream', function() { fs.closeSync(fs.openSync(expectedPath, 'w')); spies.setError(function(mod, fn) { - if (fn === 'stat' && arguments[2] === expectedPath) { + if (fn === 'fstat' && typeof arguments[2] === 'number') { return new Error('stat error'); } }); @@ -654,13 +659,23 @@ describe('dest stream', function() { stream.write(expectedFile); }); - it('should report chmod errors', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + it('should report fchmod errors', function(cb) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0722; + var expectedMode = parseInt('722', 8); + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function() { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + }); var expectedFile = new File({ base: inputBase, @@ -668,34 +683,35 @@ describe('dest stream', function() { path: inputPath, contents: expectedContents, stat: { - mode: expectedMode - } + mode: expectedMode, + }, }); fs.mkdirSync(expectedBase); fs.closeSync(fs.openSync(expectedPath, 'w')); - spies.setError(function(mod, fn) { - if (fn === 'chmod' && arguments[2] === expectedPath) { - return new Error('chmod error'); - } - }); - - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', { cwd: __dirname }); stream.on('error', function(err) { - err.message.should.equal('chmod error'); + expect(err).toExist(); + expect(fchmodSpy.calls.length).toEqual(1); cb(); }); stream.write(expectedFile); }); - it('should not chmod a matching file', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + it('should not fchmod a matching file', function(cb) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 0722; + var expectedMode = parseInt('711', 8); var expectedFile = new File({ base: inputBase, @@ -707,44 +723,31 @@ describe('dest stream', function() { } }); - var expectedCount = 0; - spies.setError(function(mod, fn) { - if (fn === 'stat' && arguments[2] === expectedPath) { - expectedCount++; - } - }); - - var onEnd = function(){ - expectedCount.should.equal(1); - assert(!chmodSpy.called); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + var stream = app.dest('actual/', { cwd: __dirname }); + stream.on('end', function() { + assert.equal(fchmodSpy.calls.length, 0); + assert.equal(masked(fs.lstatSync(expectedPath).mode), expectedMode); cb(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - statSpy.reset(); - chmodSpy.reset(); - var stream = app.dest('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); + }); stream.write(expectedFile); stream.end(); }); it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = 03722; - var normalMode = 0722; + var expectedMode = parseInt('3722', 8); + var normalMode = parseInt('722', 8); var expectedFile = new File({ base: inputBase, @@ -752,20 +755,12 @@ describe('dest stream', function() { path: inputPath, contents: expectedContents, stat: { - mode: normalMode - } - }); - - var expectedCount = 0; - spies.setError(function(mod, fn) { - if (fn === 'stat' && arguments[2] === expectedPath) { - expectedCount++; - } + mode: normalMode, + }, }); - var onEnd = function(){ - expectedCount.should.equal(1); - assert(!chmodSpy.called); + var onEnd = function() { + expect(fchmodSpy.calls.length).toEqual(0); cb(); }; @@ -773,14 +768,8 @@ describe('dest stream', function() { fs.closeSync(fs.openSync(expectedPath, 'w')); fs.chmodSync(expectedPath, expectedMode); - statSpy.reset(); - chmodSpy.reset(); - var stream = app.dest('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); + var stream = app.dest('actual/', { cwd: __dirname }); + stream.on('end', onEnd); stream.write(expectedFile); stream.end(); }); @@ -801,7 +790,7 @@ describe('dest stream', function() { contents: inputContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); cb(); @@ -836,7 +825,7 @@ describe('dest stream', function() { contents: inputContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); cb(); @@ -866,13 +855,13 @@ describe('dest stream', function() { base: inputBase, cwd: __dirname, path: inputPath, - contents: null, //'' + contents: null }); // `src()` adds this side-effect with `keepSymlinks` option set to false inputFile.symlink = inputRelativeSymlinkPath; - var onEnd = function(){ + var onEnd = function() { fs.readlink(buffered[0].path, function() { buffered[0].symlink.should.equal(inputFile.symlink); buffered[0].path.should.equal(expectedPath); @@ -900,7 +889,7 @@ describe('dest stream', function() { var file = new File({ path: srcPath, cwd: __dirname, - contents: new Buffer("1234567890") + contents: new Buffer('1234567890') }); stream.write(file); @@ -1087,7 +1076,7 @@ describe('dest', function() { // data should be re-emitted correctly should.exist(file); should.exist(file.path); - path.join(file.path,'').should.equal(path.join(actual, 'generic')); + path.join(file.path, '').should.equal(path.join(actual, 'generic')); }); outstream.on('end', function() { diff --git a/test/app.doc.js b/test/app.doc.js index e89198d6..abf08797 100644 --- a/test/app.doc.js +++ b/test/app.doc.js @@ -3,12 +3,12 @@ require('mocha'); require('should'); var assert = require('assert'); -var verb = require('..'); +var generate = require('..'); var app; describe('app', function() { beforeEach(function() { - app = verb(); + app = generate(); app.create('docs'); }); diff --git a/test/app.docs.js b/test/app.docs.js index d392f723..4f7a96cc 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -3,12 +3,12 @@ require('mocha'); require('should'); var assert = require('assert'); -var verb = require('..'); +var generate = require('..'); var app; describe('app', function() { beforeEach(function() { - app = verb(); + app = generate(); app.create('docs'); }); diff --git a/test/app.env.js b/test/app.env.js new file mode 100644 index 00000000..23682a1f --- /dev/null +++ b/test/app.env.js @@ -0,0 +1,72 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); + +describe('env', function() { + describe('createEnv', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should add an env object to the instance', function() { + var fn = function() {}; + generate.createEnv('foo', fn); + assert(generate.env); + }); + + it('should take options as the second arg', function() { + var fn = function() {}; + generate.createEnv('foo', {}, fn); + assert(generate.env); + }); + + it('should prime `env` if it doesn\'t exist', function() { + var fn = function() {}; + delete generate.env; + generate.createEnv('foo', {}, fn); + assert(generate.env); + }); + + it('should add an alias to the env object', function() { + var fn = function() {}; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.alias, 'foo'); + }); + + it('should not use `prefix` when function is passed', function() { + var fn = function() {}; + delete generate.prefix; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.name, 'foo'); + }); + + it('should use not use custom `prefix` when function is passed', function() { + var fn = function() {}; + generate.prefix = 'whatever'; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.name, 'foo'); + }); + + it('should try to resolve a path passed as the second arg', function() { + generate.createEnv('generate-foo', fixtures('verbfile.js')); + assert.equal(generate.env.alias, 'foo'); + assert.equal(generate.env.name, 'generate-foo'); + }); + + it('should throw an error when the path is not resolved', function(cb) { + try { + generate.createEnv('foo', fixtures('whatever.js')); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); + cb(); + } + }); + }); +}); diff --git a/test/app.extendWith.js b/test/app.extendWith.js index 8b9893ac..29e27b89 100644 --- a/test/app.extendWith.js +++ b/test/app.extendWith.js @@ -1,12 +1,33 @@ + 'use strict'; require('mocha'); -require('generate-foo/verbfile.js'); +var path = require('path'); var assert = require('assert'); +var gm = require('global-modules'); +var commands = require('spawn-commands'); +var utils = require('generator-util'); +require('generate-foo/verbfile.js'); var Generate = require('..'); var generate; +var fixture = path.resolve.bind(path, __dirname, 'fixtures/generators'); +function install(name, cb) { + commands({ + args: ['install', '-g', '--silent', name], + cmd: 'npm' + }, cb); +} + describe('.extendWith', function() { + before(function(cb) { + if (!utils.exists(path.resolve(gm, 'generate-bar'))) { + install('generate-bar', cb); + } else { + cb(); + } + }); + beforeEach(function() { generate = new Generate(); }); @@ -75,4 +96,395 @@ describe('.extendWith', function() { generate.getGenerator('foo'); }); + + describe('invoke generators', function(cb) { + it('should invoke an instance', function(cb) { + generate.register('foo', function(app) { + var bar = app.getGenerator('bar'); + app.extendWith(bar); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + generate.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke a named generator', function(cb) { + generate.register('foo', function(app) { + app.extendWith('bar'); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + generate.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + }); + + describe('extend generators', function(cb) { + it('should extend a generator with a generator invoked by name', function(cb) { + generate.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('bar'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.register('bar', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + + it.only('should extend a generator with a generator invoked by alias', function(cb) { + generate.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('qux'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.register('generate-qux', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + + it('should extend with a generator invoked by filepath', function(cb) { + generate.register('foo', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith(fixture('qux')); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.getGenerator('foo'); + }); + + it('should extend with a generator invoked from node_modules by name', function(cb) { + generate.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('generate-foo'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.getGenerator('abc'); + }); + + it('should extend with a generator invoked from node_modules by alias', function(cb) { + generate.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('foo'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.getGenerator('abc'); + }); + + it('should extend with a generator invoked from global modules by name', function(cb) { + generate.register('zzz', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + app.extendWith('generate-bar'); + + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.getGenerator('zzz'); + }); + + it('should extend with a generator invoked from global modules by alias', function(cb) { + generate.register('zzz', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('bar'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.getGenerator('zzz'); + }); + }); + + describe('sub-generators', function(cb) { + it('should invoke sub-generators', function(cb) { + generate.register('foo', function(app) { + app.register('one', function(app) { + app.task('a', function() {}); + }); + app.register('two', function(app) { + app.task('b', function() {}); + }); + + app.extendWith('one'); + app.extendWith('two'); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke a sub-generator on the base instance', function(cb) { + generate.register('foo', function(app) { + app.extendWith('bar.sub'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + generate.register('bar', function(app) { + app.register('sub', function(sub) { + sub.task('a', function() {}); + sub.task('b', function() {}); + sub.task('c', function() {}); + }); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke a sub-generator from node_modules by name', function(cb) { + generate.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('xyz'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.register('xyz', function(app) { + app.extendWith('generate-foo'); + }); + + generate.getGenerator('abc'); + }); + + it('should invoke a sub-generator from node_modules by alias', function(cb) { + generate.register('abc', function(app) { + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('xyz'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + cb(); + }); + + generate.register('xyz', function(app) { + app.extendWith('foo'); + }); + + generate.getGenerator('abc'); + }); + + it('should invoke an array of sub-generators', function(cb) { + generate.register('foo', function(app) { + app.register('one', function(app) { + app.task('a', function() {}); + }); + app.register('two', function(app) { + app.task('b', function() {}); + }); + + app.extendWith(['one', 'two']); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke sub-generators from sub-generators', function(cb) { + generate.register('foo', function(app) { + app.register('one', function(sub) { + sub.register('a', function(a) { + a.task('a', function() {}); + }); + }); + + app.register('two', function(sub) { + sub.register('a', function(a) { + a.task('b', function() {}); + }); + }); + + app.extendWith('one.a'); + app.extendWith('two.a'); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke an array of sub-generators from sub-generators', function(cb) { + generate.register('foo', function(app) { + app.register('one', function(sub) { + sub.register('a', function(a) { + a.task('a', function() {}); + }); + }); + + app.register('two', function(sub) { + sub.register('a', function(a) { + a.task('b', function() {}); + }); + }); + + app.extendWith(['one.a', 'two.a']); + + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + cb(); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke sub-generator that invokes another generator', function(cb) { + generate.register('foo', function(app) { + app.extendWith('bar'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + generate.register('bar', function(app) { + app.extendWith('baz'); + }); + + generate.register('baz', function(app) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke sub-generator that invokes another sub-generator', function(cb) { + generate.register('foo', function(app) { + app.extendWith('bar.sub'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + generate.register('bar', function(app) { + app.register('sub', function(sub) { + sub.extendWith('baz.sub'); + }); + }); + + generate.register('baz', function(app) { + app.register('sub', function(sub) { + sub.task('a', function() {}); + sub.task('b', function() {}); + sub.task('c', function() {}); + }); + }); + + generate.getGenerator('foo'); + }); + + it('should invoke sub-generator that invokes another sub-generator', function(cb) { + generate.register('foo', function(app) { + app.extendWith('bar.sub'); + assert(app.tasks.hasOwnProperty('a')); + assert(app.tasks.hasOwnProperty('b')); + assert(app.tasks.hasOwnProperty('c')); + cb(); + }); + + generate.register('bar', function(app) { + app.register('sub', function(sub) { + sub.extendWith('baz.sub'); + }); + }); + + generate.register('baz', function(app) { + app.register('sub', function(sub) { + sub.task('a', function() {}); + sub.task('b', function() {}); + sub.task('c', function() {}); + }); + }); + + generate.getGenerator('foo'); + }); + }); }); diff --git a/test/app.generate.js b/test/app.generate.js index cdeb5da7..793e06dd 100644 --- a/test/app.generate.js +++ b/test/app.generate.js @@ -2,17 +2,17 @@ require('mocha'); var assert = require('assert'); -var Verb = require('..'); -var verb; +var Generate = require('..'); +var generate; describe('.generate', function() { beforeEach(function() { - verb = new Verb(); + generate = new Generate(); }); describe('generators', function(cb) { it('should throw an error when a generator is not found', function(cb) { - verb.generate('fdsslsllsfjssl', function(err) { + generate.generate('fdsslsllsfjssl', function(err) { assert(err); assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); cb(); @@ -21,95 +21,181 @@ describe('.generate', function() { // special case it('should throw an error when a generator is not found in argv.cwd', function(cb) { - verb.option('cwd', 'foo/bar/baz'); - verb.generate('sflsjljskksl', function(err) { + generate.option('cwd', 'foo/bar/baz'); + generate.generate('sflsjljskksl', function(err) { assert(err); assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); cb(); }); }); - it('should throw an error when a stringified task is not found', function(cb) { - verb.register('fdsslsllsfjssl', function() {}); - verb.generate('fdsslsllsfjssl:foo', function(err) { + it('should not reformat error messages that are not about invalid tasks', function(cb) { + generate.task('default', function(cb) { + cb(new Error('whatever')); + }); + + generate.generate('default', function(err) { assert(err); - assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + assert.equal(err.message, 'whatever'); cb(); }); }); it('should throw an error when a task is not found', function(cb) { - verb.register('fdsslsllsfjssl', function() {}); - verb.generate('fdsslsllsfjssl', ['foo'], function(err) { + generate.register('fdsslsllsfjssl', function() {}); + generate.generate('fdsslsllsfjssl', ['foo'], function(err) { assert(err); assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); cb(); }); }); - it('should not reformat error messages that are not about invalid tasks', function(cb) { - verb.task('default', function(cb) { - cb(new Error('whatever')); - }); + it('should not throw an error when the default task is not defined', function(cb) { + generate.register('foo', function() {}); + generate.register('bar', function() {}); + generate.generate('foo', ['default'], function(err) { + if (err) return cb(err); - verb.generate('default', function(err) { - assert(err); - assert.equal(err.message, 'whatever'); - cb(); + generate.generate('bar', function(err) { + if (err) return cb(err); + + cb(); + }); }); }); it('should run a task on the instance', function(cb) { - verb.task('foo', function(next) { + generate.task('abc123', function(next) { next(); }); - verb.generate('foo', function(err) { + generate.generate('abc123', function(err) { assert(!err); cb(); }); }); - it('should run a task instead of a generator of the same name', function(cb) { - verb.register('foo', function(app) { + it('should run same-named task instead of a generator', function(cb) { + generate.register('123xyz', function(app) { + cb(new Error('expected the task to run first')); + }); + + generate.task('123xyz', function() { + cb(); + }); + + generate.generate('123xyz', function(err) { + assert(!err); + }); + }); + + it('should run a task instead of a generator with a default task', function(cb) { + generate.register('123xyz', function(app) { app.task('default', function() { cb(new Error('expected the task to run first')); }); }); - - verb.task('foo', function() { + generate.task('123xyz', function() { cb(); }); + generate.generate('123xyz', function(err) { + assert(!err); + }); + }); + + it('should run a task on a same-named generator when the task is specified', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.task('foo', function() { + cb(new Error('expected the generator to run')); + }); - verb.generate('foo', function(err) { + generate.generate('foo:default', function(err) { assert(!err); + assert.equal(count, 1); + cb(); }); }); - it('should run a task on a generator with the same name when specified', function(cb) { - verb.register('foo', function(app) { - app.task('default', function() { - cb(); + it('should run an array of tasks that includes a same-named generator', function(cb) { + var count = 0; + generate.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + generate.register('bar', function(app) { + app.task('baz', function(next) { + count++; + next(); }); }); - verb.task('foo', function() { + generate.task('foo', function() { cb(new Error('expected the generator to run')); }); - verb.generate('foo:default', function(err) { + generate.generate(['foo:default', 'bar:baz'], function(err) { assert(!err); + assert.equal(count, 2); + cb(); }); }); + it('should run a generator from a task with the same name', function(cb) { + generate.register('foo', function(app) { + app.task('default', function() { + cb(); + }); + }); + + generate.task('foo', function(cb) { + generate.generate('foo', cb); + }); + + generate.build('foo', function(err) { + if (err) cb(err); + }) + }); + it('should run the default task on a generator', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('default', function(next) { next(); }); }); - verb.generate('foo', function(err) { + generate.generate('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run a stringified array of tasks on the instance', function(cb) { + var count = 0; + generate.task('a', function(next) { + count++; + next(); + }); + generate.task('b', function(next) { + count++; + next(); + }); + generate.task('c', function(next) { + count++; + next(); + }); + + generate.generate('a,b,c', function(err) { + assert.equal(count, 3); assert(!err); cb(); }); @@ -117,20 +203,52 @@ describe('.generate', function() { it('should run an array of tasks on the instance', function(cb) { var count = 0; - verb.task('a', function(next) { + generate.task('a', function(next) { count++; next(); }); - verb.task('b', function(next) { + generate.task('b', function(next) { count++; next(); }); - verb.task('c', function(next) { + generate.task('c', function(next) { count++; next(); }); - verb.generate('a,b,c', function(err) { + generate.generate(['a', 'b', 'c'], function(err) { + if (err) return cb(err); + assert.equal(count, 3); + assert(!err); + cb(); + }); + }); + + it('should run the default tasks on an array of generators', function(cb) { + var count = 0; + generate.register('a', function(app) { + this.task('default', function(cb) { + count++; + cb(); + }); + }); + + generate.register('b', function(app) { + this.task('default', function(cb) { + count++; + cb(); + }); + }); + + generate.register('c', function(app) { + this.task('default', function(cb) { + count++; + cb(); + }); + }); + + generate.generate(['a', 'b', 'c'], function(err) { + if (err) return cb(err); assert.equal(count, 3); assert(!err); cb(); @@ -139,14 +257,14 @@ describe('.generate', function() { it('should run the default task on the default generator', function(cb) { var count = 0; - verb.register('default', function(app) { + generate.register('default', function(app) { app.task('default', function(next) { count++; next(); }); }); - verb.generate(function(err) { + generate.generate(function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -155,14 +273,14 @@ describe('.generate', function() { it('should run the default task on a registered generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - verb.generate('foo', function(err) { + generate.generate('foo', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -171,7 +289,7 @@ describe('.generate', function() { it('should run the specified task on a registered generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -183,7 +301,7 @@ describe('.generate', function() { }); }); - verb.generate('foo', ['abc'], function(err) { + generate.generate('foo', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -192,7 +310,7 @@ describe('.generate', function() { it('should run an array of tasks on a registered generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -214,7 +332,7 @@ describe('.generate', function() { }); }); - verb.generate('foo', 'a,b,c', function(err) { + generate.generate('foo', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -222,10 +340,10 @@ describe('.generate', function() { }); }); - describe('sub-generators', function(cb) { + describe('generate sub-generators', function(cb) { it('should run the default task on a registered sub-generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -239,7 +357,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', function(err) { + generate.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -248,7 +366,7 @@ describe('.generate', function() { it('should run the specified task on a registered sub-generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -262,7 +380,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', ['abc'], function(err) { + generate.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -271,7 +389,7 @@ describe('.generate', function() { it('should run an array of tasks on a registered sub-generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -295,7 +413,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.bar', ['a', 'b', 'c'], function(err) { + generate.generate('foo.bar', ['a', 'b', 'c'], function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -304,7 +422,7 @@ describe('.generate', function() { it('should run an multiple tasks on a registered sub-generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -328,7 +446,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.bar', 'a,b,c', function(err) { + generate.generate('foo.bar', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -340,16 +458,16 @@ describe('.generate', function() { it('should run a generator from another generator', function(cb) { var res = ''; - verb.register('foo', function(app, two) { + generate.register('foo', function(app, two) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'foo > sub > default '; - verb.generate('bar.sub', next); + generate.generate('bar.sub', next); }); }); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'bar > sub > default '; @@ -358,7 +476,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', function(err) { + generate.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(res, 'foo > sub > default bar > sub > default '); cb(); @@ -367,7 +485,7 @@ describe('.generate', function() { it('should run the specified task on a registered sub-generator', function(cb) { var count = 0; - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -381,7 +499,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', ['abc'], function(err) { + generate.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -391,11 +509,11 @@ describe('.generate', function() { describe('events', function(cb) { it('should emit generate', function(cb) { - verb.on('generate', function() { + generate.on('generate', function() { cb(); }); - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -407,18 +525,18 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', ['abc'], function(err) { + generate.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); it('should expose the generator alias as the first parameter', function(cb) { - verb.on('generate', function(name) { + generate.on('generate', function(name) { assert.equal(name, 'sub'); cb(); }); - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -430,18 +548,18 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', ['abc'], function(err) { + generate.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); it('should expose the tasks array as the second parameter', function(cb) { - verb.on('generate', function(name, tasks) { + generate.on('generate', function(name, tasks) { assert.deepEqual(tasks, ['abc']); cb(); }); - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -453,7 +571,7 @@ describe('.generate', function() { }); }); - verb.generate('foo.sub', ['abc'], function(err) { + generate.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); diff --git a/test/app.generateEach.js b/test/app.generateEach.js index 31157f96..9a37a362 100644 --- a/test/app.generateEach.js +++ b/test/app.generateEach.js @@ -2,13 +2,12 @@ require('mocha'); var assert = require('assert'); -var Verb = require('..'); +var Generate = require('..'); var generate; -describe('.generate', function() { +describe('.generateEach', function() { beforeEach(function() { - generate = new Verb(); - generate.initVerb({}); + generate = new Generate(); }); describe('generators', function(cb) { diff --git a/test/app.generator.js b/test/app.generator.js index b9472ee0..83da725b 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -3,33 +3,55 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var verb; +var Generate = require('..'); +var generate; var fixtures = path.resolve.bind(path, __dirname, 'fixtures'); describe('.generator', function() { beforeEach(function() { - verb = new Verb(); - verb.initVerb({}); + generate = new Generate(); + }); + + describe('get generator', function() { + it('should get a generator by full name name', function() { + var gen = generate.getGenerator('generate-mocha'); + assert(gen); + assert.equal(gen.env.alias, 'mocha'); + assert.equal(gen.env.name, 'generate-mocha'); + }); + + it('should get a generator by aliased name', function() { + var gen = generate.getGenerator('generate-mocha'); + assert(gen); + assert.equal(gen.env.alias, 'mocha'); + assert.equal(gen.env.name, 'generate-mocha'); + }); + + it('should get a generator by alias', function() { + var gen = generate.getGenerator('generate-mocha'); + assert(gen); + assert.equal(gen.env.alias, 'mocha'); + assert.equal(gen.env.name, 'generate-mocha'); + }); }); describe('register > function', function() { - it('should register a generator function by alias', function() { - verb.generator('foo', function() {}); - assert(verb.generators.hasOwnProperty('foo')); + it('should register a generator function by name', function() { + generate.generator('foo', function() {}); + assert(generate.generators.hasOwnProperty('foo')); }); - it('should register a generator function by full name', function() { - verb.generator('verb-abc-generator', function() {}); - assert(verb.generators.hasOwnProperty('verb-abc-generator')); + it('should register a generator function by alias', function() { + generate.generator('generate-abc', function() {}); + assert(generate.generators.hasOwnProperty('generate-abc')); }); }); describe('get > alias', function() { it('should get a generator by alias', function() { - verb.generator('verb-abc-generator', function() {}); - var abc = verb.generator('abc'); + generate.generator('generate-abc', function() {}); + var abc = generate.generator('abc'); assert(abc); assert.equal(typeof abc, 'object'); }); @@ -37,8 +59,8 @@ describe('.generator', function() { describe('get > name', function() { it('should get a generator by name', function() { - verb.generator('verb-abc-generator', function() {}); - var abc = verb.generator('verb-abc-generator'); + generate.generator('generate-abc', function() {}); + var abc = generate.generator('generate-abc'); assert(abc); assert.equal(typeof abc, 'object'); }); @@ -46,15 +68,62 @@ describe('.generator', function() { describe('generators', function() { it('should invoke a registered generator when `getGenerator` is called', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('default', function() {}); cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); + }); + + it('should expose the generator instance on `app`', function(cb) { + generate.register('foo', function(app) { + app.task('default', function(next) { + assert.equal(app.get('a'), 'b'); + next(); + }) + }); + + var foo = generate.getGenerator('foo'); + foo.set('a', 'b'); + foo.build('default', function(err) { + if (err) return cb(err); + cb() + }); + }); + + it('should expose the "base" instance on `base`', function(cb) { + generate.set('x', 'z'); + generate.register('foo', function(app, base) { + app.task('default', function(next) { + assert.equal(generate.get('x'), 'z'); + next(); + }) + }); + + var foo = generate.getGenerator('foo'); + foo.set('a', 'b'); + foo.build('default', function(err) { + if (err) return cb(err); + cb() + }); + }); + + it('should expose the "env" object on `env`', function(cb) { + generate.register('foo', function(app, base, env) { + app.task('default', function(next) { + assert.equal(env.alias, 'foo'); + next(); + }) + }); + + generate.getGenerator('foo').build('default', function(err) { + if (err) return cb(err); + cb() + }); }); it('should expose an app\'s generators on app.generators', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('a', function() {}); app.register('b', function() {}); @@ -63,45 +132,45 @@ describe('.generator', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); - it('should expose all root generators on verb.generators', function(cb) { - verb.register('foo', function(app, b) { + it('should expose all root generators on generate.generators', function(cb) { + generate.register('foo', function(app, b) { b.generators.hasOwnProperty('foo'); b.generators.hasOwnProperty('bar'); b.generators.hasOwnProperty('baz'); cb(); }); - verb.register('bar', function(app, verb) {}); - verb.register('baz', function(app, verb) {}); - verb.getGenerator('foo'); + generate.register('bar', function(app, base) {}); + generate.register('baz', function(app, base) {}); + generate.getGenerator('foo'); }); }); describe('cross-generators', function() { it('should get a generator from another generator', function(cb) { - verb.register('foo', function(app, b) { + generate.register('foo', function(app, b) { var bar = b.getGenerator('bar'); assert(bar); cb(); }); - verb.register('bar', function(app, verb) {}); - verb.register('baz', function(app, verb) {}); - verb.getGenerator('foo'); + generate.register('bar', function(app, base) {}); + generate.register('baz', function(app, base) {}); + generate.getGenerator('foo'); }); it('should set options on another generator instance', function(cb) { - verb.generator('foo', function(app) { + generate.generator('foo', function(app) { app.task('default', function(next) { assert.equal(app.option('abc'), 'xyz'); next(); }); }); - verb.generator('bar', function(app, b) { + generate.generator('bar', function(app, b) { var foo = b.getGenerator('foo'); foo.option('abc', 'xyz'); foo.build(function(err) { @@ -114,28 +183,30 @@ describe('.generator', function() { describe('generators > filepath', function() { it('should register a generator function from a file path', function() { - var one = verb.generator('one', fixtures('one/verbfile.js')); - assert(verb.generators.hasOwnProperty('one')); - assert(typeof verb.generators.one === 'object'); - assert.deepEqual(verb.generators.one, one); + var one = generate.generator('one', fixtures('one/verbfile.js')); + assert(generate.generators.hasOwnProperty('one')); + assert(typeof generate.generators.one === 'object'); + assert.deepEqual(generate.generators.one, one); }); - it('should register a Verb instance from a file path', function() { - var two = verb.generator('two', fixtures('two/verbfile.js')); - assert(verb.generators.hasOwnProperty('two')); - assert(typeof verb.generators.two === 'object'); - assert.deepEqual(verb.generators.two, two); + it('should register a Generate instance from a file path', function() { + var two = generate.generator('two', fixtures('two/generate.js')); + assert(generate.generators.hasOwnProperty('two')); + assert(typeof generate.generators.two === 'object'); + assert.deepEqual(generate.generators.two, two); }); it('should get a registered generator by name', function() { - var one = verb.generator('one', fixtures('one/verbfile.js')); - assert.deepEqual(verb.generator('one'), one); + var one = generate.generator('one', fixtures('one/verbfile.js')); + var two = generate.generator('two', fixtures('two/generate.js')); + assert.deepEqual(generate.generator('one'), one); + assert.deepEqual(generate.generator('two'), two); }); }); describe('tasks', function() { it('should expose a generator\'s tasks on app.tasks', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('a', function() {}); app.task('b', function() {}); assert(app.tasks.a); @@ -143,22 +214,22 @@ describe('.generator', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should get tasks from another generator', function(cb) { - verb.register('foo', function(app, b) { + generate.register('foo', function(app, b) { var baz = b.getGenerator('baz'); var task = baz.tasks.aaa; assert(task); cb(); }); - verb.register('bar', function(app, verb) {}); - verb.register('baz', function(app, verb) { + generate.register('bar', function(app, base) {}); + generate.register('baz', function(app, base) { app.task('aaa', function() {}); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); }); }); diff --git a/test/app.getGenerator.js b/test/app.getGenerator.js index 7fd2c9fd..b6c41d3c 100644 --- a/test/app.getGenerator.js +++ b/test/app.getGenerator.js @@ -2,40 +2,40 @@ var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var verb; +var Generate = require('..'); +var generate var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); describe('.generator', function() { beforeEach(function() { - verb = new Verb(); + generate = new Generate(); }); - it('should get a generator from the verb instance', function() { - verb.register('abc', function() {}); - var generator = verb.getGenerator('abc'); + it('should get a generator from the base instance', function() { + generate.register('abc', function() {}); + var generator = generate.getGenerator('abc'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'abc'); }); - it('should get a generator from the verb instance from a nested generator', function() { - verb.register('abc', function() {}); - verb.register('xyz', function(app) { + it('should get a generator from the base instance from a nested generator', function() { + generate.register('abc', function() {}); + generate.register('xyz', function(app) { app.register('sub', function(sub) { - var generator = verb.getGenerator('abc'); + var generator = generate.getGenerator('abc'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'abc'); }); }); - verb.getGenerator('xyz'); + generate.getGenerator('xyz'); }); - it('should get a generator from the verb instance using `this`', function() { - verb.register('abc', function() {}); - verb.register('xyz', function(app) { + it('should get a generator from the base instance using `this`', function() { + generate.register('abc', function() {}); + generate.register('xyz', function(app) { app.register('sub', function(sub) { var generator = this.getGenerator('abc'); assert(generator); @@ -43,12 +43,12 @@ describe('.generator', function() { assert.equal(generator.name, 'abc'); }); }); - verb.getGenerator('xyz'); + generate.getGenerator('xyz'); }); - it('should get a verb generator from "app" from a nested generator', function() { - verb.register('abc', function() {}); - verb.register('xyz', function(app) { + it('should get a base generator from "app" from a nested generator', function() { + generate.register('abc', function() {}); + generate.register('xyz', function(app) { app.register('sub', function(sub) { var generator = app.getGenerator('abc'); assert(generator); @@ -56,22 +56,22 @@ describe('.generator', function() { assert.equal(generator.name, 'abc'); }); }); - verb.getGenerator('xyz'); + generate.getGenerator('xyz'); }); it('should get a nested generator', function() { - verb.register('abc', function(abc) { + generate.register('abc', function(abc) { abc.register('def', function() {}); }); - var generator = verb.getGenerator('abc.def'); + var generator = generate.getGenerator('abc.def'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'def'); }); it('should get a deeply nested generator', function() { - verb.register('abc', function(abc) { + generate.register('abc', function(abc) { abc.register('def', function(def) { def.register('ghi', function(ghi) { ghi.register('jkl', function(jkl) { @@ -81,15 +81,15 @@ describe('.generator', function() { }); }); - var generator = verb.getGenerator('abc.def.ghi.jkl.mno'); + var generator = generate.getGenerator('abc.def.ghi.jkl.mno'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'mno'); }); it('should get a generator that was registered by path', function() { - verb.register('a', fixtures('generators/a')); - var generator = verb.getGenerator('a'); + generate.register('a', fixtures('generators/a')); + var generator = generate.getGenerator('a'); assert(generator); assert(generator.tasks); assert(generator.tasks.hasOwnProperty('default')); diff --git a/test/app.include.js b/test/app.include.js index 75e78285..cdf58bbf 100644 --- a/test/app.include.js +++ b/test/app.include.js @@ -3,12 +3,12 @@ require('mocha'); require('should'); var assert = require('assert'); -var verb = require('..'); +var generate = require('..'); var app, len; describe('app', function() { beforeEach(function() { - app = verb(); + app = generate(); if (typeof app.include === 'undefined') { app.create('include'); } diff --git a/test/app.includes.js b/test/app.includes.js index 2eaff36a..390660d6 100644 --- a/test/app.includes.js +++ b/test/app.includes.js @@ -3,12 +3,12 @@ require('mocha'); require('should'); var assert = require('assert'); -var verb = require('..'); +var generate = require('..'); var app, len; describe('app', function() { beforeEach(function() { - app = verb(); + app = generate(); if (typeof app.include === 'undefined') { app.create('include'); } diff --git a/test/app.invoke.js b/test/app.invoke.js index cd6cfc33..19df0771 100644 --- a/test/app.invoke.js +++ b/test/app.invoke.js @@ -6,9 +6,9 @@ var assert = require('assert'); var gm = require('global-modules'); var commands = require('spawn-commands'); var utils = require('generator-util'); -require('generate-foo/generator.js'); -var Verb = require('..'); -var verb; +require('generate-foo/verbfile.js'); +var Generate = require('..'); +var generate; var fixture = path.resolve.bind(path, __dirname, 'fixtures/generators'); function install(name, cb) { @@ -28,13 +28,12 @@ describe('.invoke', function() { }); beforeEach(function() { - verb = new Verb({prefix: 'generate'}); - verb.prefix = 'generate'; + generate = new Generate(); }); - + describe('invoke generators', function(cb) { it('should invoke an instance', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { var bar = app.getGenerator('bar'); app.invoke(bar); @@ -44,17 +43,17 @@ describe('.invoke', function() { cb(); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should invoke a named generator', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.invoke('bar'); assert(app.tasks.hasOwnProperty('a')); @@ -63,19 +62,19 @@ describe('.invoke', function() { cb(); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); }); describe('extend generators', function(cb) { it('should extend a generator with a generator invoked by name', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -87,17 +86,17 @@ describe('.invoke', function() { cb(); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should extend a generator with a generator invoked by alias', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -109,17 +108,17 @@ describe('.invoke', function() { cb(); }); - verb.register('verb-qux-generator', function(app) { + generate.register('generate-qux', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should extend with a generator invoked by filepath', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -131,11 +130,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should extend with a generator invoked from node_modules by name', function(cb) { - verb.register('abc', function(app) { + generate.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -147,14 +146,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('abc'); + generate.getGenerator('abc'); }); - - it('should extend with a generator invoked from node_modules by alias', function(cb) { - verb.prefix = 'generate'; - verb.register('abc', function(app) { - app.prefix = 'generate'; + it('should extend with a generator invoked from node_modules by alias', function(cb) { + generate.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -166,11 +162,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('abc'); + generate.getGenerator('abc'); }); it('should extend with a generator invoked from global modules by name', function(cb) { - verb.register('zzz', function(app) { + generate.register('zzz', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -182,13 +178,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('zzz'); + generate.getGenerator('zzz'); }); it('should extend with a generator invoked from global modules by alias', function(cb) { - verb.register('zzz', function(app) { - app.prefix = 'generate'; - + generate.register('zzz', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -200,13 +194,13 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('zzz'); + generate.getGenerator('zzz'); }); }); describe('sub-generators', function(cb) { it('should invoke sub-generators', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('one', function(app) { app.task('a', function() {}); }); @@ -222,11 +216,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); - - it('should invoke a sub-generator on the verb instance', function(cb) { - verb.register('foo', function(app) { + + it('should invoke a sub-generator on the base instance', function(cb) { + generate.register('foo', function(app) { app.invoke('bar.sub'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -234,7 +228,7 @@ describe('.invoke', function() { cb(); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.register('sub', function(sub) { sub.task('a', function() {}); sub.task('b', function() {}); @@ -242,11 +236,11 @@ describe('.invoke', function() { }); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should invoke a sub-generator from node_modules by name', function(cb) { - verb.register('abc', function(app) { + generate.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -258,15 +252,15 @@ describe('.invoke', function() { cb(); }); - verb.register('xyz', function(app) { + generate.register('xyz', function(app) { app.invoke('generate-foo'); }); - verb.getGenerator('abc'); + generate.getGenerator('abc'); }); it('should invoke a sub-generator from node_modules by alias', function(cb) { - verb.register('abc', function(app) { + generate.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -278,16 +272,15 @@ describe('.invoke', function() { cb(); }); - verb.register('xyz', function(app) { - app.prefix = 'generate'; + generate.register('xyz', function(app) { app.invoke('foo'); }); - verb.getGenerator('abc'); + generate.getGenerator('abc'); }); it('should invoke an array of sub-generators', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('one', function(app) { app.task('a', function() {}); }); @@ -302,11 +295,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should invoke sub-generators from sub-generators', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('one', function(sub) { sub.register('a', function(a) { a.task('a', function() {}); @@ -327,11 +320,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should invoke an array of sub-generators from sub-generators', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.register('one', function(sub) { sub.register('a', function(a) { a.task('a', function() {}); @@ -351,11 +344,11 @@ describe('.invoke', function() { cb(); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should invoke sub-generator that invokes another generator', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.invoke('bar'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -363,21 +356,21 @@ describe('.invoke', function() { cb(); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.invoke('baz'); }); - verb.register('baz', function(app) { + generate.register('baz', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); it('should invoke sub-generator that invokes another sub-generator', function(cb) { - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.invoke('bar.sub'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -385,13 +378,13 @@ describe('.invoke', function() { cb(); }); - verb.register('bar', function(app) { + generate.register('bar', function(app) { app.register('sub', function(sub) { sub.invoke('baz.sub'); }); }); - verb.register('baz', function(app) { + generate.register('baz', function(app) { app.register('sub', function(sub) { sub.task('a', function() {}); sub.task('b', function() {}); @@ -399,7 +392,7 @@ describe('.invoke', function() { }); }); - verb.getGenerator('foo'); + generate.getGenerator('foo'); }); }); }); diff --git a/test/app.js b/test/app.js index f63b8a61..7aa51ce4 100644 --- a/test/app.js +++ b/test/app.js @@ -96,16 +96,6 @@ describe('app', function() { assert.equal(app.get('Group'), MyGroup); }); - it('should mixin prototype methods defined on options:', function() { - app = new App({ - mixins: { - foo: function() {} - } - }); - assert(typeof app.foo === 'function'); - delete App.prototype.foo; - }); - it('should expose `_` on app:', function() { app = new App(); assert(typeof app._ === 'object'); diff --git a/test/app.mergePartials.js b/test/app.mergePartials.js index 7d4a1114..edd65c44 100644 --- a/test/app.mergePartials.js +++ b/test/app.mergePartials.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('app.mergePartialsSync', function() { +describe('app.mergePartials', function() { beforeEach(function() { app = new App(); }); @@ -20,7 +20,7 @@ describe('app.mergePartialsSync', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartialsSync(); + var actual = app.mergePartials(); actual.should.have.property('partials'); actual.partials.should.have.properties(['a', 'b', 'c']); }); @@ -35,7 +35,7 @@ describe('app.mergePartialsSync', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartialsSync({mergePartials: false}); + var actual = app.mergePartials({mergePartials: false}); actual.should.not.have.property('partials'); actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); }); @@ -55,7 +55,7 @@ describe('app.mergePartialsSync', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartialsSync({mergePartials: false}); + var actual = app.mergePartials({mergePartials: false}); actual.should.not.have.property('partials'); actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); arr.should.eql(['aaa', 'bbb', 'ccc']); @@ -76,7 +76,7 @@ describe('app.mergePartialsSync', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartialsSync({mergePartials: false}); + var actual = app.mergePartials({mergePartials: false}); actual.should.eql({ foos: {a: 'aaa onMerge'}, bars: {b: 'bbb onMerge'}, @@ -100,7 +100,7 @@ describe('app.mergePartialsSync', function() { app.bar('b', {path: 'b', content: 'bbb'}); app.baz('c', {path: 'c', content: 'ccc'}); - var actual = app.mergePartialsSync({mergePartials: false}); + var actual = app.mergePartials({mergePartials: false}); actual.should.eql({ bazs: { c: 'ccc' } }); }); }); diff --git a/test/questions.js b/test/app.questions.js similarity index 97% rename from test/questions.js rename to test/app.questions.js index c60762da..609ff78f 100644 --- a/test/questions.js +++ b/test/app.questions.js @@ -49,7 +49,7 @@ describe('app.questions', function() { afterEach(function() { app.store.del({force: true}); - app.questions.clearCache(); + app.questions.clear(); app.cache.data = {}; }); @@ -67,7 +67,7 @@ describe('app.questions', function() { assert(app.questions.cache); assert(app.questions.cache.a); assert.equal(app.questions.cache.a.name, 'a'); - assert.equal(app.questions.cache.a.options.message, 'b'); + assert.equal(app.questions.cache.a.message, 'b'); }); it.skip('should re-init a specific question:', function(cb) { @@ -182,10 +182,10 @@ describe('app.questions', function() { after(function() { site.store.del({force: true}); - site.questions.clearCache(); + site.questions.clear(); app.store.del({force: true}); - app.questions.clearCache(); + app.questions.clear(); }); it('[app] should ask a question and use a `cache.data` value to answer:', function(cb) { diff --git a/test/app.register.js b/test/app.register.js index 4228d25c..57c6a260 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -3,79 +3,112 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var app; +var Generate = require('..'); +var base; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); describe('.register plugin', function() { it('should register as a plugin', function() { - var app = new Verb(); - assert(app.registered.hasOwnProperty('base-generators')); + var base = new Generate(); + assert(base.registered.hasOwnProperty('base-generators')); }); }); describe('.register', function() { beforeEach(function() { - app = new Verb(); + base = new Generate(); }); describe('properties', function() { it('should expose a configfile getter/setter', function() { - assert.equal(typeof app.configfile, 'string'); + assert.equal(typeof base.configfile, 'string'); }); it('should set configfile to verbfile.js by default', function() { - assert.equal(app.configfile, 'verbfile.js'); + assert.equal(base.configfile, 'verbfile.js'); }); it('should set configfile', function() { - app.configfile = 'foo.js'; - assert.equal(app.configfile, 'foo.js'); + base.configfile = 'foo.js'; + assert.equal(base.configfile, 'foo.js'); + }); + }); + + describe('configname', function() { + it('should expose a configname getter/setter', function() { + assert.equal(typeof base.configname, 'string'); + }); + + it('should set configname to generator by default', function() { + assert.equal(base.configname, 'generator'); + }); + + it('should set configname', function() { + base.configname = 'foo'; + assert.equal(base.configname, 'foo'); + }); + }); + + describe('configpath', function() { + it('should expose a configpath getter/setter', function() { + assert.equal(typeof base.configpath, 'string'); + }); + + it('should use configfile as basename of configpath', function() { + base.cwd = __dirname; + base.configfile = 'whatever.js'; + assert.equal(path.basename(base.configpath), 'whatever.js'); + }); + + it('should resolve configpath from base.cwd and base.configfile', function() { + base.cwd = __dirname; + base.configfile = 'whatever.js'; + assert.equal(base.configpath, path.resolve(__dirname, base.configfile)); }); }); describe('function', function() { it('should get a generator registered as a function', function() { - app.register('foo', function() {}); - var foo = app.getGenerator('foo'); + base.register('foo', function() {}); + var foo = base.getGenerator('foo'); assert(foo); assert.equal(foo.env.alias, 'foo'); }); it('should get a task from a generator registered as a function', function() { - app.register('foo', function(foo) { + base.register('foo', function(foo) { foo.task('default', function() {}); }); - var generator = app.getGenerator('foo'); + var generator = base.getGenerator('foo'); assert(generator); assert(generator.tasks); assert(generator.tasks.hasOwnProperty('default')); }); it('should get a sub-generator from a generator registered as a function', function() { - app.register('foo', function(foo) { + base.register('foo', function(foo) { foo.register('bar', function(bar) {}); }); - var bar = app.getGenerator('foo.bar'); + var bar = base.getGenerator('foo.bar'); assert(bar); assert.equal(bar.env.alias, 'bar'); }); it('should get a sub-generator from a generator registered as a function', function() { - app.register('foo', function(foo) { + base.register('foo', function(foo) { foo.register('bar', function(bar) { bar.task('something', function() {}); }); }); - var bar = app.getGenerator('foo.bar'); + var bar = base.getGenerator('foo.bar'); assert(bar); assert(bar.tasks); assert(bar.tasks.hasOwnProperty('something')); }); it('should get a deeply-nested sub-generator registered as a function', function() { - app.register('foo', function(foo) { + base.register('foo', function(foo) { foo.register('bar', function(bar) { bar.register('baz', function(baz) { baz.register('qux', function(qux) { @@ -85,14 +118,14 @@ describe('.register', function() { }); }); - var qux = app.getGenerator('foo.bar.baz.qux'); + var qux = base.getGenerator('foo.bar.baz.qux'); assert(qux); assert(qux.tasks); assert(qux.tasks.hasOwnProperty('qux-one')); }); it('should expose the instance from each generator', function() { - app.register('foo', function(foo) { + base.register('foo', function(foo) { foo.register('bar', function(bar) { bar.register('baz', function(baz) { baz.register('qux', function(qux) { @@ -102,7 +135,7 @@ describe('.register', function() { }); }); - var qux = app + var qux = base .getGenerator('foo') .getGenerator('bar') .getGenerator('baz') @@ -114,7 +147,7 @@ describe('.register', function() { }); it('should fail when the wrong generator name is given', function() { - app.register('foo', function(foo) { + base.register('foo', function(foo) { foo.register('bar', function(bar) { bar.register('baz', function(baz) { baz.register('qux', function(qux) { @@ -122,74 +155,68 @@ describe('.register', function() { }); }); }); - var fez = app.getGenerator('foo.bar.fez'); + var fez = base.getGenerator('foo.bar.fez'); assert.equal(typeof fez, 'undefined'); }); it('should expose the `base` instance as the second param', function(cb) { - app.register('foo', function(foo, base) { - assert(app.generators.hasOwnProperty('foo')); + base.register('foo', function(foo, base) { + assert(base.generators.hasOwnProperty('foo')); cb(); }); - app.getGenerator('foo'); + base.getGenerator('foo'); }); it('should expose sibling generators on the `base` instance', function(cb) { - app.register('foo', function(foo, base) { + base.register('foo', function(foo, base) { foo.task('abc', function() {}); }); - app.register('bar', function(bar, base) { - assert(app.generators.hasOwnProperty('foo')); - assert(app.generators.hasOwnProperty('bar')); + base.register('bar', function(bar, base) { + assert(base.generators.hasOwnProperty('foo')); + assert(base.generators.hasOwnProperty('bar')); cb(); }); - app.getGenerator('foo'); - app.getGenerator('bar'); + base.getGenerator('foo'); + base.getGenerator('bar'); }); }); -}); - -describe('naming', function() { - beforeEach(function() { - app = new Verb(); - }); describe('alias', function() { it('should use a custom function to create the alias', function() { - app.option('alias', function(name) { + base.option('alias', function(name) { return name.slice(name.lastIndexOf('-') + 1); }); - app.register('base-abc-xyz', function() {}); - assert(app.generators.hasOwnProperty('base-abc-xyz')); + base.register('generate-abc-xyz', function() {}); + assert(base.generators.hasOwnProperty('generate-abc-xyz')); }); }); describe('path', function() { it('should register a generator function by name', function() { - app.register('foo', function() {}); - assert(app.generators.hasOwnProperty('foo')); + base.register('foo', function() {}); + assert(base.generators.hasOwnProperty('foo')); }); - it('should register a generator function by its given name', function() { - app.register('verb-generate-abc', function() {}); - assert(app.generators.hasOwnProperty('verb-generate-abc')); + it('should register a generator function by alias', function() { + base.register('generate-abc', function() {}); + assert(base.generators.hasOwnProperty('generate-abc')); }); it('should register a generator by dirname', function() { - app.register('a', fixtures('generators/a')); - assert(app.generators.hasOwnProperty('a')); + base.register('a', fixtures('generators/a')); + assert(base.generators.hasOwnProperty('a')); }); - it('should register a generator filepath by the given name', function() { - app.register('verb-generate-abc', fixtures('generators/a/verbfile.js')); - assert(app.generators.hasOwnProperty('verb-generate-abc')); + it('should register a generator from a configfile filepath', function() { + base.register('generate-abc', fixtures('generators/a/verbfile.js')); + assert(base.generators.hasOwnProperty('generate-abc')); }); it('should throw when a generator does not expose the instance', function(cb) { try { - app.register('not-exposed', require(fixtures('not-exposed.js'))); + base.register('not-exposed', require(fixtures('not-exposed.js'))); cb(new Error('expected an error')); } catch (err) { assert.equal(err.message, 'generator instances must be exposed with module.exports'); @@ -199,54 +226,54 @@ describe('naming', function() { }); describe('instance', function() { - it('should register an instance by the given name', function() { - app.register('verb-generate-inst', new Verb()); - assert(app.generators.hasOwnProperty('verb-generate-inst')); + it('should register an instance', function() { + base.register('generate-inst', new Generate()); + assert(base.generators.hasOwnProperty('generate-inst')); }); it('should get a generator that was registered as an instance', function() { - var foo = new Verb(); + var foo = new Generate(); foo.task('default', function() {}); - app.register('foo', foo); - assert(app.getGenerator('foo')); + base.register('foo', foo); + assert(base.getGenerator('foo')); }); it('should register multiple instances', function() { - var foo = new Verb(); - var bar = new Verb(); - var baz = new Verb(); - app.register('foo', foo); - app.register('bar', bar); - app.register('baz', baz); - assert(app.getGenerator('foo')); - assert(app.getGenerator('bar')); - assert(app.getGenerator('baz')); + var foo = new Generate(); + var bar = new Generate(); + var baz = new Generate(); + base.register('foo', foo); + base.register('bar', bar); + base.register('baz', baz); + assert(base.getGenerator('foo')); + assert(base.getGenerator('bar')); + assert(base.getGenerator('baz')); }); it('should get tasks from a generator that was registered as an instance', function() { - var foo = new Verb(); + var foo = new Generate(); foo.task('default', function() {}); - app.register('foo', foo); - var generator = app.getGenerator('foo'); + base.register('foo', foo); + var generator = base.getGenerator('foo'); assert(generator.tasks); assert(generator.tasks.hasOwnProperty('default')); }); it('should get sub-generators from a generator registered as an instance', function() { - var foo = new Verb(); + var foo = new Generate(); foo.register('bar', function() {}); - app.register('foo', foo); - var generator = app.getGenerator('foo.bar'); + base.register('foo', foo); + var generator = base.getGenerator('foo.bar'); assert(generator); }); it('should get tasks from sub-generators registered as an instance', function() { - var foo = new Verb(); + var foo = new Generate(); foo.register('bar', function(bar) { bar.task('whatever', function() {}); }); - app.register('foo', foo); - var generator = app.getGenerator('foo.bar'); + base.register('foo', foo); + var generator = base.getGenerator('foo.bar'); assert(generator.tasks); assert(generator.tasks.hasOwnProperty('whatever')); }); diff --git a/test/app.resolve.js b/test/app.resolve.js index 432809b6..4fa54fe3 100644 --- a/test/app.resolve.js +++ b/test/app.resolve.js @@ -4,67 +4,66 @@ require('mocha'); var path = require('path'); var assert = require('assert'); var gm = require('global-modules'); -var Verb = require('..'); -var verb; +var Generate = require('..'); +var generate; var fixtures = path.resolve.bind(path, __dirname, 'fixtures/generators'); describe('.resolve', function() { beforeEach(function() { - verb = new Verb(); - verb.prefix = 'generate'; + generate = new Generate(); }); describe('method', function() { it('should expose a `resolve` method', function() { - assert.equal(typeof verb.resolve, 'function'); + assert.equal(typeof generate.resolve, 'function'); }); }); describe('local', function() { it('should resolve a local generator path', function() { - var fp = verb.resolve(fixtures('a')); + var fp = generate.resolve(fixtures('a')); assert.equal(typeof fp, 'string'); }); it('should resolve a generator path from a cwd', function() { - assert(verb.resolve('a', {cwd: fixtures()})); + assert(generate.resolve('a', {cwd: fixtures()})); }); it('should resolve a generator path from a generator name', function() { - assert(verb.resolve('a', {cwd: fixtures()})); + assert(generate.resolve('a', {cwd: fixtures()})); }); it('should resolve the path to a local config file', function() { - var fp = verb.resolve('a', {cwd: fixtures()}); + var fp = generate.resolve('a', {cwd: fixtures()}); assert.equal(typeof fp, 'string'); }); }); describe('global', function() { it('should resolve a global generator path', function() { - var fp = verb.resolve('bar', gm); + var fp = generate.resolve('bar', gm); assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); }); it('should resolve a global generator path without a cwd', function() { - var fp = verb.resolve('bar'); + var fp = generate.resolve('bar'); assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); }); it('should resolve a global generator by full name', function() { - var fp = verb.resolve('generate-bar'); + var fp = generate.resolve('generate-bar'); assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); }); it('should return undefined when a generator is not found', function() { - var fp = verb.resolve('foo-bar'); + var fp = generate.resolve('foo-bar'); assert.equal(typeof fp, 'undefined'); }); it('should throw an error when a generator is not found at the given cwd', function(cb) { try { - verb.resolve('foofof', {cwd: fixtures()}); + generate.resolve('foofof', {cwd: fixtures()}); cb(new Error('expected an error')); } catch (err) { assert.equal(err.code, 'ENOENT'); diff --git a/test/app.store.js b/test/app.store.js index c80f5001..f2382e36 100644 --- a/test/app.store.js +++ b/test/app.store.js @@ -5,14 +5,16 @@ require('should'); var fs = require('fs'); var path = require('path'); var assert = require('assert'); +var store = require('base-store'); var support = require('./support'); -var verb = support.resolve(); +var generate = support.resolve(); var app; describe('store', function() { describe('methods', function() { beforeEach(function() { - app = verb({cli: true}); + app = generate({cli: true}); + app.use(store()); app.store.create('app-data-tests'); }); @@ -152,7 +154,8 @@ describe('store', function() { describe('create', function() { beforeEach(function() { - app = verb({cli: true}); + app = generate({cli: true}); + app.use(store()); app.store.create('abc'); // init the actual store json file @@ -200,7 +203,7 @@ describe('create', function() { app.store.create('foo'); var dir = path.dirname(app.store.path); - assert.equal(app.store.foo.path, path.join(dir, 'verb/foo.json')); + assert.equal(app.store.foo.path, path.join(dir, 'generate/foo.json')); app.store.foo.set('a', 'b'); app.store.foo.del({force: true}); }); @@ -222,7 +225,8 @@ describe('create', function() { describe('events', function() { beforeEach(function() { - app = verb({cli: true}); + app = generate({cli: true}); + app.use(store()); app.store.create('abc'); }); diff --git a/test/app.task.js b/test/app.task.js index 4ae0141e..a34956b0 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -2,47 +2,47 @@ require('mocha'); var assert = require('assert'); -var App = require('..'); -var app; +var Generate = require('..'); +var generate; -describe('task()', function() { +describe('.generate', function() { beforeEach(function() { - app = new App(); + generate = new Generate(); }); it('should register a task', function() { var fn = function(cb) { cb(); }; - app.task('default', fn); - assert.equal(typeof app.tasks.default, 'object'); - assert.equal(app.tasks.default.fn, fn); + generate.task('default', fn); + assert.equal(typeof generate.tasks.default, 'object'); + assert.equal(generate.tasks.default.fn, fn); }); it('should register a task with an array of dependencies', function() { - app.task('default', ['foo', 'bar'], function(cb) { + generate.task('default', ['foo', 'bar'], function(cb) { cb(); }); - assert.equal(typeof app.tasks.default, 'object'); - assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof generate.tasks.default, 'object'); + assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); }); it('should register a task with a list of strings as dependencies', function() { - app.task('default', 'foo', 'bar', function(cb) { + generate.task('default', 'foo', 'bar', function(cb) { cb(); }); - assert.equal(typeof app.tasks.default, 'object'); - assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof generate.tasks.default, 'object'); + assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); }); it('should run a task', function(cb) { var count = 0; - app.task('default', function(cb) { + generate.task('default', function(cb) { count++; cb(); }); - app.build('default', function(err) { + generate.build('default', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -50,22 +50,16 @@ describe('task()', function() { }); it('should throw an error when a task with unregistered dependencies is run', function(cb) { - var count = 0; - app.task('default', ['foo', 'bar'], function(cb) { - count++; - cb(); - }); - - app.build('default', function(err) { - if (!err) return cb(new Error('Expected an error to be thrown.')); - assert.equal(count, 0); + generate.task('default', ['foo', 'bar']); + generate.build('default', function(err) { + assert(err); cb(); }); }); it('should throw an error when `.build` is called without a callback function.', function() { try { - app.build('default'); + generate.build('default'); throw new Error('Expected an error to be thrown.'); } catch (err) { } @@ -73,24 +67,23 @@ describe('task()', function() { it('should emit task events', function(cb) { var events = []; - app.on('task:starting', function(task) { + generate.on('task:starting', function(task) { events.push('starting.' + task.name); }); - app.on('task:finished', function(task) { + generate.on('task:finished', function(task) { events.push('finished.' + task.name); }); - app.on('task:error', function(err, task) { + generate.on('task:error', function(e, task) { events.push('error.' + task.name); }); - - app.task('foo', function(cb) { + generate.task('foo', function(cb) { cb(); }); - app.task('bar', ['foo'], function(cb) { + generate.task('bar', ['foo'], function(cb) { cb(); }); - app.task('default', ['bar']); - app.build('default', function(err) { + generate.task('default', ['bar']); + generate.build('default', function(err) { if (err) return cb(err); assert.deepEqual(events, [ 'starting.default', @@ -105,52 +98,49 @@ describe('task()', function() { }); it('should emit an error event when an error is passed back in a task', function(cb) { - app.on('error', function(err) { + generate.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - app.task('default', function(cb) { + generate.task('default', function(cb) { return cb(new Error('This is an error')); }); - app.build('default', function(err) { + generate.build('default', function(err) { if (err) return cb(); cb(new Error('Expected an error')); }); }); it('should emit an error event when an error is thrown in a task', function(cb) { - var errors = 0; - app.on('error', function(err) { - errors++; + generate.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - app.task('default', function(cb) { + generate.task('default', function(cb) { cb(new Error('This is an error')); }); - app.build('default', function(err) { - assert.equal(errors, 1); - if (err) return cb(); - cb(new Error('Expected an error')); + generate.build('default', function(err) { + assert(err); + cb(); }); }); it('should run dependencies before running the dependent task.', function(cb) { var seq = []; - app.task('foo', function(cb) { + generate.task('foo', function(cb) { seq.push('foo'); cb(); }); - app.task('bar', function(cb) { + generate.task('bar', function(cb) { seq.push('bar'); cb(); }); - app.task('default', ['foo', 'bar'], function(cb) { + generate.task('default', ['foo', 'bar'], function(cb) { seq.push('default'); cb(); }); - app.build('default', function(err) { + generate.build('default', function(err) { if (err) return cb(err); assert.deepEqual(seq, ['foo', 'bar', 'default']); cb(); diff --git a/test/app.toAlias.js b/test/app.toAlias.js deleted file mode 100644 index 4e16ee64..00000000 --- a/test/app.toAlias.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -require('generate-foo/generator.js'); -var Verb = require('..'); -var verb; - -describe('.toAlias', function() { - beforeEach(function() { - verb = new Verb(); - delete verb.toAlias; - }); - - it('should not create an alias from a name without a prefix', function() { - assert.equal(verb.toAlias('foo-bar'), 'foo-bar'); - }); - - it('should create an alias using the given prefix', function() { - assert.equal(verb.toAlias('foo-bar', {prefix: 'foo'}), 'bar'); - }); - - it('should create an alias using the given alias function', function() { - var alias = verb.toAlias('one-two-three', { - alias: function(name) { - return name.slice(name.lastIndexOf('-') + 1); - } - }); - assert.equal(alias, 'three'); - }); -}); diff --git a/test/env.js b/test/env.js index e8391e54..7dae91bb 100644 --- a/test/env.js +++ b/test/env.js @@ -3,66 +3,65 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var verb; +var Generate = require('..'); +var generate; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); describe('env', function() { describe('createEnv', function() { beforeEach(function() { - verb = new Verb(); - verb.initVerb({}); + generate = new Generate(); }); it('should add an env object to the instance', function() { var fn = function() {}; - verb.createEnv('foo', fn); - assert(verb.env); + generate.createEnv('foo', fn); + assert(generate.env); }); it('should take options as the second arg', function() { var fn = function() {}; - verb.createEnv('foo', {}, fn); - assert(verb.env); + generate.createEnv('foo', {}, fn); + assert(generate.env); }); it('should prime `env` if it doesn\'t exist', function() { var fn = function() {}; - delete verb.env; - verb.createEnv('foo', {}, fn); - assert(verb.env); + delete generate.env; + generate.createEnv('foo', {}, fn); + assert(generate.env); }); it('should add an alias to the env object', function() { var fn = function() {}; - verb.createEnv('foo', {}, fn); - assert.equal(verb.env.alias, 'foo'); + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.alias, 'foo'); }); it('should not use prefix when a function is passed', function() { var fn = function() {}; - verb.prefix = 'generate'; - verb.createEnv('foo', {}, fn); - assert.equal(verb.env.name, 'foo'); + generate.prefix = 'generate'; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.name, 'foo'); }); - it('should not prefix a name when a function is registered', function() { + it('should not use custom prefix when a function is passed', function() { var fn = function() {}; - verb.prefix = 'whatever'; - verb.createEnv('foo', {}, fn); - assert.equal(verb.env.name, 'foo'); + generate.prefix = 'whatever'; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.name, 'foo'); }); it('should try to resolve a path passed as the second arg', function() { - verb.createEnv('foo', 'generate-foo/verbfile.js'); - assert.equal(verb.env.alias, 'foo'); - assert.equal(verb.env.name, 'verb-foo-generator'); + generate.createEnv('foo', 'generate-foo/verbfile.js'); + assert.equal(generate.env.alias, 'foo'); + assert.equal(generate.env.name, 'generate-foo'); }); it('should throw an error when the path is not resolved', function(cb) { try { - verb.createEnv('foo', fixtures('whatever.js')); + generate.createEnv('foo', fixtures('whatever.js')); cb(new Error('expected an error')); } catch (err) { assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); diff --git a/test/fixtures/def-gen.js b/test/fixtures/def-gen.js new file mode 100644 index 00000000..9955ea4a --- /dev/null +++ b/test/fixtures/def-gen.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function(app) { + app.task('default', function(cb) { + console.log('default'); + cb(); + }); +}; diff --git a/test/fixtures/generator.js b/test/fixtures/generator.js new file mode 100644 index 00000000..060e8248 --- /dev/null +++ b/test/fixtures/generator.js @@ -0,0 +1,49 @@ +'use strict'; + +module.exports = function(app) { + app.register('generate-aaa', function(app) { + app.task('default', function(cb) { + console.log('generate > default'); + cb(); + }); + + app.register('sub', function(sub) { + sub.task('default', function(cb) { + console.log('aaa > sub > default'); + cb(); + }); + + sub.register('bbb', function(bbb) { + bbb.task('default', function(cb) { + console.log('aaa > sub > bbb > default'); + cb(); + }); + }); + }); + }); + + app.register('generate-abc', 'test/fixtures/generators/a/verbfile.js'); + + app.register('generate-bbb', function(app) { + app.task('default', function(cb) { + app.generate('aaa.sub.bbb', 'default', cb); + }); + }); + + app.register('generate-ccc', function(app) { + app.task('default', function(cb) { + app.generate('abc', 'default', cb); + }); + }); + + app.register('generate-ddd', function(app) { + app.task('default', function(cb) { + app.generate('abc.docs', 'x', cb); + }); + }); + + app.generate('aaa.sub', ['default'], function(err) { + if (err) throw err; + console.log('done'); + }); +}; diff --git a/test/fixtures/generators/a/generator.js b/test/fixtures/generators/a/generator.js new file mode 100644 index 00000000..c3f4d75e --- /dev/null +++ b/test/fixtures/generators/a/generator.js @@ -0,0 +1,10 @@ +'use strict'; + +var path = require('path'); + +module.exports = function(app) { + app.task('default', function(cb) { + console.log('fixtures/a > default'); + cb(); + }); +}; diff --git a/test/fixtures/generators/c/generator.js b/test/fixtures/generators/c/generator.js new file mode 100644 index 00000000..2c448ee6 --- /dev/null +++ b/test/fixtures/generators/c/generator.js @@ -0,0 +1,39 @@ +'use strict'; + +module.exports = function(app) { + app.task('default', function(cb) { + console.log('c > default'); + cb(); + }); + app.task('c_a', function(cb) { + console.log('c > a'); + cb(); + }); + app.task('c_b', function(cb) { + console.log('c > b'); + cb(); + }); + app.task('c_c', function(cb) { + console.log('c > c'); + cb(); + }); + + app.register('docs', function(docs, base) { + docs.task('default', function(cb) { + console.log('c > x'); + cb(); + }); + docs.task('c_x', function(cb) { + console.log('c > x'); + cb(); + }); + docs.task('c_y', function(cb) { + console.log('c > y'); + cb(); + }); + docs.task('c_z', function(cb) { + console.log('c > z'); + cb(); + }); + }); +}; diff --git a/test/fixtures/generators/e/generator.js b/test/fixtures/generators/e/generator.js new file mode 100644 index 00000000..fc333ec2 --- /dev/null +++ b/test/fixtures/generators/e/generator.js @@ -0,0 +1,43 @@ +'use strict'; + +/** + * testing... + */ + +module.exports = function(app, base, env) { + app.task('default', function(cb) { + console.log('e > default'); + cb(); + }); + app.task('one', function(cb) { + console.log('e > one'); + cb(); + }); + app.task('two', function(cb) { + console.log('e > two'); + cb(); + }); + + // console.log(app) + app.task('three', function(cb) { + base.data({title: 'three'}); + base.build('readme', cb); + }); + + app.register('eee', '../c'); + + app.register('e_docs', function(docs) { + docs.task('e_x', function(cb) { + console.log('e > x'); + cb(); + }); + docs.task('e_y', function(cb) { + console.log('e > y'); + cb(); + }); + docs.task('e_z', function(cb) { + console.log('e > z'); + cb(); + }); + }); +}; diff --git a/test/fixtures/generators/qux/generator.js b/test/fixtures/generators/qux/generator.js new file mode 100644 index 00000000..28979eea --- /dev/null +++ b/test/fixtures/generators/qux/generator.js @@ -0,0 +1,11 @@ +'use strict'; + +/** + * testing... + */ + +module.exports = function(app, base, env) { + app.task('a', function() {}); + app.task('b', function() {}); + app.task('c', function() {}); +}; diff --git a/test/fixtures/generators2.js b/test/fixtures/generators2.js index e2ddbb04..294b58ff 100644 --- a/test/fixtures/generators2.js +++ b/test/fixtures/generators2.js @@ -5,7 +5,7 @@ var app = new Generate({foo: 'bar'}); app.register('a', function(a, base) { a.register('a1', function(aa) { - aa.register('aaa', require('./generators/f/generator.js')); + aa.register('aaa', require('./generators/f/verbfile.js')); aa.task('default', function(cb) { console.log('aa > default'); cb(); diff --git a/test/fixtures/one/package.json b/test/fixtures/one/package.json index d1d46764..363625fd 100644 --- a/test/fixtures/one/package.json +++ b/test/fixtures/one/package.json @@ -1,6 +1,6 @@ { "name": "one", - "description": "The most interesting project in the world > Verb", + "description": "The most interesting project in the world > Generate", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/one", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/three/four/five/generator.js b/test/fixtures/three/four/five/generator.js index 607c7ebe..61f8bf67 100644 --- a/test/fixtures/three/four/five/generator.js +++ b/test/fixtures/three/four/five/generator.js @@ -1,12 +1,12 @@ 'use strict'; -module.exports = function(verb, base) { - verb.task('default', function() {}); - verb.task('a', function() {}); - verb.task('b', function() {}); - verb.task('c', function() {}); +module.exports = function(generate, base) { + generate.task('default', function() {}); + generate.task('a', function() {}); + generate.task('b', function() {}); + generate.task('c', function() {}); - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('x', function() {}); app.task('y', function() {}); app.task('z', function() {}); diff --git a/test/fixtures/three/four/five/package.json b/test/fixtures/three/four/five/package.json index 984f0f6b..4bc4c94a 100644 --- a/test/fixtures/three/four/five/package.json +++ b/test/fixtures/three/four/five/package.json @@ -1,6 +1,6 @@ { "name": "five", - "description": "The most interesting project in the world > Verb", + "description": "The most interesting project in the world > Generate", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/five", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/three/four/generator.js b/test/fixtures/three/four/generator.js index 607c7ebe..61f8bf67 100644 --- a/test/fixtures/three/four/generator.js +++ b/test/fixtures/three/four/generator.js @@ -1,12 +1,12 @@ 'use strict'; -module.exports = function(verb, base) { - verb.task('default', function() {}); - verb.task('a', function() {}); - verb.task('b', function() {}); - verb.task('c', function() {}); +module.exports = function(generate, base) { + generate.task('default', function() {}); + generate.task('a', function() {}); + generate.task('b', function() {}); + generate.task('c', function() {}); - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('x', function() {}); app.task('y', function() {}); app.task('z', function() {}); diff --git a/test/fixtures/three/four/package.json b/test/fixtures/three/four/package.json index 3f25912f..2662ab91 100644 --- a/test/fixtures/three/four/package.json +++ b/test/fixtures/three/four/package.json @@ -1,6 +1,6 @@ { "name": "four", - "description": "The most interesting project in the world > Verb", + "description": "The most interesting project in the world > Generate", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/four", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/three/generator.js b/test/fixtures/three/generator.js index 607c7ebe..61f8bf67 100644 --- a/test/fixtures/three/generator.js +++ b/test/fixtures/three/generator.js @@ -1,12 +1,12 @@ 'use strict'; -module.exports = function(verb, base) { - verb.task('default', function() {}); - verb.task('a', function() {}); - verb.task('b', function() {}); - verb.task('c', function() {}); +module.exports = function(generate, base) { + generate.task('default', function() {}); + generate.task('a', function() {}); + generate.task('b', function() {}); + generate.task('c', function() {}); - verb.register('foo', function(app) { + generate.register('foo', function(app) { app.task('x', function() {}); app.task('y', function() {}); app.task('z', function() {}); diff --git a/test/fixtures/three/package.json b/test/fixtures/three/package.json index 12b5fac3..af82c885 100644 --- a/test/fixtures/three/package.json +++ b/test/fixtures/three/package.json @@ -1,6 +1,6 @@ { "name": "three", - "description": "The most interesting project in the world > Verb", + "description": "The most interesting project in the world > Generate", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/three", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/two/generate.js b/test/fixtures/two/generate.js index 8e487499..15cf2281 100644 --- a/test/fixtures/two/generate.js +++ b/test/fixtures/two/generate.js @@ -1,21 +1,21 @@ 'use strict'; -var Verb = require('../..'); -var verb = new Verb(); +var Generate = require('../..'); +var generate = new Generate(); -verb.task('default', function() {}); -verb.task('a', function() {}); -verb.task('b', function() {}); -verb.task('c', function() {}); +generate.task('default', function() {}); +generate.task('a', function() {}); +generate.task('b', function() {}); +generate.task('c', function() {}); -verb.register('foo', function(app) { +generate.register('foo', function(app) { app.task('x', function() {}); app.task('y', function() {}); app.task('z', function() {}); }); /** - * Expose this instance of `Verb` + * Expose this instance of `Generate` */ -module.exports = verb; \ No newline at end of file +module.exports = generate; \ No newline at end of file diff --git a/test/fixtures/two/package.json b/test/fixtures/two/package.json index 8a02df01..55e28acb 100644 --- a/test/fixtures/two/package.json +++ b/test/fixtures/two/package.json @@ -1,6 +1,6 @@ { "name": "two", - "description": "The most interesting project in the world > Verb", + "description": "The most interesting project in the world > Generate", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/two", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/verbfile.js b/test/fixtures/verbfile.js index 3dbcc451..060e8248 100644 --- a/test/fixtures/verbfile.js +++ b/test/fixtures/verbfile.js @@ -22,7 +22,7 @@ module.exports = function(app) { }); }); - app.register('generate-abc', 'test/fixtures/generators/a/generator.js'); + app.register('generate-abc', 'test/fixtures/generators/a/verbfile.js'); app.register('generate-bbb', function(app) { app.task('default', function(cb) { diff --git a/test/generate.js b/test/generate.js index 4bda7952..84f46689 100644 --- a/test/generate.js +++ b/test/generate.js @@ -3,13 +3,13 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Verb = require('..'); +var Generate = require('..'); var generate; describe('generate', function() { describe('cwd', function() { beforeEach(function() { - generate = new Verb(); + generate = new Generate(); }); it('should get the current working directory', function() { @@ -24,11 +24,11 @@ describe('generate', function() { describe('generator', function() { beforeEach(function() { - generate = new Verb(); + generate = new Generate(); }); it('should register the default generator', function() { - generate.register('default', require('../lib/generators/default')); + generate.register('default', require('./fixtures/def-gen')); assert(generate.hasGenerator('default')); }); }); diff --git a/test/generators.cache.js b/test/generators.cache.js new file mode 100644 index 00000000..9f951ef4 --- /dev/null +++ b/test/generators.cache.js @@ -0,0 +1,223 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var option = require('base-option'); +var Generate = require('..'); +var generate; + +describe('cache', function() { + describe('set', function() { + beforeEach(function() { + generate = new Generate(); + generate.use(option()); + }); + + it('should set an instance', function() { + generate.generators.set('foo', function() {}); + assert(generate.generators.hasOwnProperty('foo')); + }); + + it('should set an instance with a parent instance', function() { + generate.generators.set('foo', function() {}); + assert(generate.generators.hasOwnProperty('foo')); + }); + + it('should set options from the parent instance on new instances', function() { + generate = new Generate({a: 'b'}); + assert.equal(generate.options.a, 'b'); + + generate.generators.set('foo', function() {}); + assert(generate.generators.hasOwnProperty('foo')); + assert.equal(generate.generators.foo.options.a, 'b'); + }); + + it('should set options from the parent instance on sub-generators', function(cb) { + generate = new Generate({a: 'b'}); + assert.equal(generate.options.a, 'b'); + + generate.generators.set('foo', function(app) { + app.generators.set('bar', function(bar) { + assert.equal(bar.options.a, 'b'); + cb(); + }); + }); + + generate.getGenerator('foo.bar'); + }); + + it('should set parent options on deeply nested sub-generators', function(cb) { + generate = new Generate({a: 'b'}); + generate.use(option()); + + assert.equal(generate.options.a, 'b'); + + generate.option('c', 'd'); + + generate.generators.set('foo', function(foo) { + assert.equal(foo.options.a, 'b'); + + foo.generators.set('bar', function(bar) { + assert.equal(bar.options.a, 'b'); + + bar.generators.set('baz', function(baz) { + assert.equal(baz.options.a, 'b'); + + baz.generators.set('qux', function(qux) { + assert.equal(qux.options.a, 'b'); + + qux.generators.set('fez', function(fez) { + assert.equal(fez.options.a, 'b'); + + cb(); + }); + }); + }); + }); + }); + generate.getGenerator('foo.bar.baz.qux.fez'); + }); + + it('should expose the base instance as the second arg', function(cb) { + generate = new Generate({a: 'b'}); + generate.use(option()); + + assert.equal(generate.options.a, 'b'); + + generate.option('c', 'd'); + + generate.generators.set('foo', function(foo, fooBase) { + foo.generators.set('bar', function(bar, barBase) { + bar.generators.set('baz', function(baz, bazBase) { + baz.generators.set('qux', function(qux, quxBase) { + qux.generators.set('fez', function(fez, fezBase) { + assert(fezBase.hasGenerator('foo')); + assert(!fezBase.hasGenerator('bar')); + assert(fezBase.hasGenerator('foo.bar')); + cb(); + }); + }); + }); + }); + }); + generate.getGenerator('foo.bar.baz.qux.fez'); + }); + + it('should not merge generator options back upstream', function() { + generate = new Generate({a: 'b'}); + generate.use(option()); + + assert.equal(generate.options.a, 'b'); + + generate.generators.set('foo', function() {}); + generate.option('one', 'two'); + + var foo = generate.getGenerator('foo'); + foo.option('x', 'z'); + + assert.equal(foo.options.a, 'b'); + assert.equal(foo.options.x, 'z'); + assert.equal(typeof generate.options.x, 'undefined'); + }); + + it('should break the options reference after instantiation', function() { + generate = new Generate({a: 'b'}); + generate.use(option()); + + assert.equal(generate.options.a, 'b'); + + generate.generators.set('foo', function() {}); + generate.option('one', 'two'); + + var foo = generate.getGenerator('foo'); + assert.equal(foo.options.a, 'b'); + assert.equal(typeof foo.options.one, 'undefined'); + }); + }); + + describe('get', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should get an instance from app.generators', function() { + generate.generators.set('foo', function() {}); + var foo = generate.generators.get('foo'); + assert(foo); + assert(foo.isGenerator); + }); + }); + + describe('plugins', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should add plugins from a parent generator to child generators', function() { + generate.use(option()); + + generate.generators.set('foo', function() {}); + var foo = generate.generators.get('foo'); + assert(foo); + assert(foo.hasOwnProperty('option')); + assert.equal(typeof foo.option, 'function'); + }); + + it('should add parent plugins to deeply nested generators', function(cb) { + generate.use(option()); + + generate.generators.set('foo', function(foo) { + foo.use(function fn() { + this.aaa = 'aaa'; + return fn; + }); + + assert.equal(this.aaa, 'aaa'); + assert.equal(typeof this.option, 'function'); + + foo.generators.set('bar', function(bar) { + bar.use(function() { + this.bbb = 'bbb'; + }); + + assert.equal(this.aaa, 'aaa'); + assert.equal(typeof this.option, 'function'); + + bar.generators.set('baz', function(baz) { + baz.use(function fn() { + this.ccc = 'ccc'; + return fn; + }); + + assert.equal(this.aaa, 'aaa'); + assert.equal(typeof this.option, 'function'); + + baz.generators.set('qux', function(qux) { + qux.use(function() { + this.ddd = 'ddd'; + }); + + assert.equal(this.ccc, 'ccc'); + assert.equal(this.aaa, 'aaa'); + assert.equal(typeof this.option, 'function'); + + qux.generators.set('fez', function(fez) { + fez.use(function() { + this.eee = 'eee'; + }); + + assert.equal(this.ccc, 'ccc'); + assert.equal(this.aaa, 'aaa'); + assert.equal(typeof this.option, 'function'); + + cb(); + }); + }); + }); + }); + }); + + generate.getGenerator('foo.bar.baz.qux.fez'); + }); + }); +}); diff --git a/test/generators.env.js b/test/generators.env.js new file mode 100644 index 00000000..b9079eee --- /dev/null +++ b/test/generators.env.js @@ -0,0 +1,117 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); + +describe('env', function() { + describe('createEnv paths', function() { + beforeEach(function() { + generate = new Generate(); + }); + + describe('alias and function', function() { + it('should make the alias the exact name when the second arg is a function', function() { + var fn = function() {}; + generate.createEnv('foo-bar-baz', fn); + assert(generate.env); + assert(generate.env.alias); + assert.equal(generate.env.alias, 'foo-bar-baz'); + }); + + it('should not change the name when the second arg is a function', function() { + var fn = function() {}; + generate.createEnv('foo-bar-baz', fn); + assert(generate.env); + assert(generate.env.name); + assert.equal(generate.env.name, 'foo-bar-baz'); + }); + }); + + describe('alias and path', function() { + it('should set the name to the basename of a generator\'s dirname', function() { + generate.createEnv('foo', 'generate-foo/verbfile.js'); + assert.equal(generate.env.name, 'generate-foo'); + }); + + it('should not change the name when the second arg is a function', function() { + var fn = function() {}; + generate.createEnv('foo-bar-baz', fn); + assert(generate.env); + assert(generate.env.name); + assert.equal(generate.env.name, 'foo-bar-baz'); + }); + }); + }); + + describe('createEnv', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should add an env object to the instance', function() { + var fn = function() {}; + generate.createEnv('foo', fn); + assert(generate.env); + }); + + it('should take options as the second arg', function() { + var fn = function() {}; + generate.createEnv('foo', {}, fn); + assert(generate.env); + }); + + it('should prime `env` if it doesn\'t exist', function() { + var fn = function() {}; + delete generate.env; + generate.createEnv('foo', {}, fn); + assert(generate.env); + }); + + it('should add an alias to the env object', function() { + var fn = function() {}; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.alias, 'foo'); + }); + + it('should not prefix the alias when a function is passed', function() { + var fn = function() {}; + delete generate.prefix; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.name, 'foo'); + }); + + it('should not prefix a custom alias when a function is passed', function() { + var fn = function() {}; + generate.prefix = 'whatever'; + generate.createEnv('foo', {}, fn); + assert.equal(generate.env.name, 'foo'); + }); + + it('should try to resolve a path passed as the second arg', function() { + generate.createEnv('generate-foo', fixtures('verbfile.js')); + assert.equal(generate.env.alias, 'foo'); + assert.equal(generate.env.name, 'generate-foo'); + }); + + it('should try to resolve a path passed as the second arg', function() { + generate.createEnv('generate-foo', 'generate-foo/verbfile.js'); + assert.equal(generate.env.alias, 'foo'); + assert.equal(generate.env.name, 'generate-foo'); + }); + + it('should throw an error when the path is not resolved', function(cb) { + try { + generate.createEnv('foo', fixtures('whatever.js')); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); + cb(); + } + }); + }); +}); diff --git a/test/generators.events.js b/test/generators.events.js new file mode 100644 index 00000000..eed598ec --- /dev/null +++ b/test/generators.events.js @@ -0,0 +1,169 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Generate = require('..'); +var generate; + +describe('generators events', function() { + describe('generator', function() { + beforeEach(function() { + generate = new Generate(); + }); + + it('should emit generator.set when a generator is registered', function(cb) { + generate = new Generate(); + generate.on('generator.set', function(generator) { + assert.equal(generator.env.alias, 'foo'); + cb(); + }); + + generate.register('foo', function() {}); + }); + + it('should emit generator when a generator is registered', function(cb) { + generate = new Generate(); + generate.on('generator', function(method, generator) { + assert.equal(method, 'set'); + assert.equal(generator.env.alias, 'foo'); + cb(); + }); + + generate.register('foo', function() {}); + }); + + it('should emit generator when generate.generators.get is called', function(cb) { + generate = new Generate(); + generate.register('foo', function() {}); + + generate.on('generator', function(method, generator) { + assert.equal(method, 'get'); + assert.equal(generator.env.alias, 'foo'); + cb(); + }); + + generate.generators.get('foo'); + }); + + it('should emit generator.get when generate.generators.get is called', function(cb) { + generate = new Generate(); + generate.on('generator.get', function(generator) { + assert.equal(generator.env.alias, 'foo'); + cb(); + }); + + generate.register('foo', function() {}); + generate.generators.get('foo'); + }); + + it('should emit error on base when a base generator emits an error', function(cb) { + generate = new Generate(); + var called = 0; + + generate.on('error', function(err) { + assert.equal(err.message, 'whatever'); + called++; + }); + + generate.register('foo', function(app) { + app.emit('error', new Error('whatever')); + }); + + generate.getGenerator('foo'); + assert.equal(called, 1); + cb(); + }); + + it('should emit error on base when a base generator throws an error', function(cb) { + generate = new Generate(); + var called = 0; + + generate.on('error', function(err) { + assert.equal(err.message, 'whatever'); + called++; + }); + + generate.register('foo', function(app) { + app.task('default', function(cb) { + cb(new Error('whatever')); + }); + }); + + generate.getGenerator('foo') + .build(function(err) { + assert.equal(called, 1); + cb(); + }); + + }); + + it('should emit errors on base from deeply nested generators', function(cb) { + generate = new Generate(); + var called = 0; + + generate.on('error', function(err) { + assert.equal(err.message, 'whatever'); + called++; + }); + + generate.register('a', function() { + this.register('b', function() { + this.register('c', function() { + this.register('d', function() { + this.task('default', function(cb) { + cb(new Error('whatever')); + }); + }); + }); + }); + }); + + generate.getGenerator('a.b.c.d') + .build(function(err) { + assert.equal(called, 1); + cb(); + }); + + }); + + it('should bubble up errors to all parent generators', function(cb) { + generate = new Generate(); + var called = 0; + + generate.on('error', function(err) { + assert.equal(err.message, 'whatever'); + called++; + }); + + function count(err) { + called++; + } + + generate.register('a', function() { + this.on('error', count); + + this.register('b', function() { + this.on('error', count); + + this.register('c', function() { + this.on('error', count); + + this.register('d', function() { + this.on('error', count); + + this.task('default', function(cb) { + cb(new Error('whatever')); + }); + }); + }); + }); + }); + + generate.getGenerator('a.b.c.d') + .build(function(err) { + assert.equal(called, 5); + cb(); + }); + }); + }); +}); diff --git a/test/partials.js b/test/partials.js index aea0141d..72b719d8 100644 --- a/test/partials.js +++ b/test/partials.js @@ -56,7 +56,7 @@ describe('partials', function() { pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{> base }} c'}); var page = pages.getView('a.tmpl'); - var locals = app.mergePartialsSync(this.options); + var locals = app.mergePartials(this.options); pages.render(page, locals, function(err, view) { if (err) return cb(err); diff --git a/test/store.js b/test/store.js index 9664c5f6..037f338f 100644 --- a/test/store.js +++ b/test/store.js @@ -21,16 +21,16 @@ describe('store', function() { it('should create a store at the given `cwd`', function() { app = new App({store: {cwd: __dirname + '/actual'}}); app.store.set('foo', 'bar'); - assert(path.basename(app.store.path) === 'verb.json'); + assert(path.basename(app.store.path) === 'generate.json'); assert(app.store.data.hasOwnProperty('foo')); assert(app.store.data.foo === 'bar'); - assert(fs.existsSync(path.join(__dirname, 'actual', 'verb.json'))); + assert(fs.existsSync(path.join(__dirname, 'actual', 'generate.json'))); }); it('should create a store using the given `indent` value', function() { app = new App({store: {cwd: __dirname + '/actual', indent: 0}}); app.store.set('foo', 'bar'); - var contents = fs.readFileSync(path.join(__dirname, 'actual', 'verb.json'), 'utf8'); + var contents = fs.readFileSync(path.join(__dirname, 'actual', 'generate.json'), 'utf8'); assert(contents === '{"foo":"bar"}'); }); diff --git a/test/support/spy.js b/test/support/spy.js index e14512b2..f64b6cec 100644 --- a/test/support/spy.js +++ b/test/support/spy.js @@ -1,4 +1,6 @@ -var fs = require('fs'); +'use strict'; + +var fs = require('graceful-fs'); var sinon = require('sinon'); var errorfn = false; @@ -23,5 +25,8 @@ module.exports = { errorfn = fn; }, chmodSpy: maybeCallAsync(fs, 'chmod'), - statSpy: maybeCallAsync(fs, 'stat') + fchmodSpy: maybeCallAsync(fs, 'fchmod'), + futimesSpy: maybeCallAsync(fs, 'futimes'), + statSpy: maybeCallAsync(fs, 'stat'), + fstatSpy: maybeCallAsync(fs, 'fstat'), }; diff --git a/test/view.methods.js b/test/view.methods.js index bb120c69..72ea95c1 100644 --- a/test/view.methods.js +++ b/test/view.methods.js @@ -1,23 +1,24 @@ +'use strict'; + require('should'); var support = require('./support'); var App = support.resolve(); var app; -describe('view.option()', function () { - beforeEach(function () { +describe('view.option()', function() { + beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('page'); }); - - describe('.use', function () { - it('should expose `.use` for running plugins on a view:', function () { + describe('.use', function() { + it('should expose `.use` for running plugins on a view:', function() { app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) - .use(function () { + .use(function() { this.options.foo = 'bar'; }) - .use(function () { + .use(function() { this.options.bar = 'baz'; }); @@ -27,13 +28,13 @@ describe('view.option()', function () { }); }); - describe('.render:', function () { - it('should expose `.render` for rendering a view:', function (done) { + describe('.render:', function() { + it('should expose `.render` for rendering a view:', function(cb) { app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', locals: {a: 'bbb'}}) - .render({}, function (err, res) { - if (err) return done(err); + .render({}, function(err, res) { + if (err) return cb(err); res.contents.toString().should.equal('bbb'); - done(); + cb(); }); }); }); diff --git a/test/views.js b/test/views.js index 16a50b68..aa5d7d49 100644 --- a/test/views.js +++ b/test/views.js @@ -457,7 +457,6 @@ describe('options', function() { }); }); - describe('queue', function() { beforeEach(function() { collection = new Views(); From a06a359b59d39c0566e4c4ceb67b56c62c42a2b4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:03:23 -0500 Subject: [PATCH 188/282] adds `isGeneratorPath` --- index.js | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index f974efd8..5ea339ac 100644 --- a/index.js +++ b/index.js @@ -28,23 +28,34 @@ function Verb(options) { return new Verb(options); } - this.options = this.options || {}; + this.options = utils.extend({}, this.options, options); Generate.call(this, this.options); this.is('verb'); this.define('isApp', true); debug(this); + this.debug('initializing'); this.verbDefaults(options); this.initVerb(this.options); } /** - * Extend `Generate` + * Extend `Verb` */ Generate.extend(Verb); +/** + * Initialize verb defaults + */ + +Verb.prototype.verbDefaults = function(options) { + this.debug('initializing verb defaults'); + var defaults = { prefix: 'verb', configfile: 'verbfile.js' }; + this.options = utils.extend(defaults, this.options, options); +}; + /** * Initialize verb data */ @@ -56,16 +67,19 @@ Verb.prototype.initVerb = function(opts) { this.data({verb: {related: {}, reflinks: []}}); var aliasRegex = /(^verb-?|-?generat(e|or)-?)/g; - // temporary, there is an easier way to do this - this.toFullname = function(str) { - return 'verb-' + this.toAlias(str) + '-generator'; + this.toFullname = function(name) { + return 'verb-' + this.toAlias(name) + '-generator'; + }; + + this.isGeneratorPath = function(fp) { + return aliasRegex.test(fp) || /^generate-/.test(fp); }; - // temporary, there is an easier way to do this - this.toAlias = function(str) { - return str.replace(aliasRegex, ''); + this.toAlias = function(name) { + return name.replace(aliasRegex, ''); }; + // if run via CLI, add defaults if (process.env.GENERATE_CLI) { this.use(utils.create(this.options)); this.create('files'); @@ -73,16 +87,6 @@ Verb.prototype.initVerb = function(opts) { } }; -/** - * Initialize verb defaults - */ - -Verb.prototype.verbDefaults = function(options) { - this.debug('initializing verb defaults'); - var defaults = { prefix: 'verb', configfile: 'verbfile.js' }; - this.options = utils.extend(defaults, this.options, options); -}; - /** * Expose static `is*` methods from Templates */ From 0282bbbb6faa7e7cbd6ac40d7689d6377c8120ff Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:03:32 -0500 Subject: [PATCH 189/282] generate docs --- .verb.md | 3 +++ readme.md | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/.verb.md b/.verb.md index cdbb2fb4..526f541d 100644 --- a/.verb.md +++ b/.verb.md @@ -145,3 +145,6 @@ $ verb --config=tasks:foo,bar,baz ## API {%= apidocs("index.js") %} + +## Upgrading +{%= include("upgrading") %} \ No newline at end of file diff --git a/readme.md b/readme.md index 74ff6a67..971cc161 100644 --- a/readme.md +++ b/readme.md @@ -15,6 +15,7 @@ * [cwd](#cwd) + [tasks](#tasks) - [API](#api) +- [Upgrading](#upgrading) - [Related projects](#related-projects) - [Contributing](#contributing) - [Building docs](#building-docs) @@ -192,6 +193,16 @@ var verb = require('verb'); var app = verb(); ``` +## Upgrading + +**Clear your cache and re-install** + +If you're currently running verb v0.8.0 or lower, please do the following to clear out old versions of verb, so that the latest version of verb will install properly: + +```bash +$ npm cache clean && npm i -g verb +``` + ## Related projects * [assemble](https://www.npmjs.com/package/assemble): Assemble is a powerful, extendable and easy to use static site generator for node.js. Used… [more](https://www.npmjs.com/package/assemble) | [homepage](https://github.com/assemble/assemble) From b0885221831da30cf2c236f4f9de1e143eeedf76 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:04:32 -0500 Subject: [PATCH 190/282] start scaffolding out some docs for middleware --- docs/src/content/fs.md | 2 +- docs/src/content/middleware.md | 247 ++++++++++++++++++++++++++++++++- docs/src/content/plugins.md | 66 +++++++-- 3 files changed, 298 insertions(+), 17 deletions(-) diff --git a/docs/src/content/fs.md b/docs/src/content/fs.md index d3b36007..bd858fd1 100644 --- a/docs/src/content/fs.md +++ b/docs/src/content/fs.md @@ -7,4 +7,4 @@ _(TODO)_ ```js app.src('*.hbs') .pipe(app.dest('site/')) -``` \ No newline at end of file +``` diff --git a/docs/src/content/middleware.md b/docs/src/content/middleware.md index 2878369c..98d1aa32 100644 --- a/docs/src/content/middleware.md +++ b/docs/src/content/middleware.md @@ -7,18 +7,253 @@ related: ['en-route'] reflinks: ['en-route'] --- -While plugins and middleware are both used to "extend" {{name}}, they serve very different purposes, are used in completely different ways, and have access to different objects at runtime. - ## What is middleware? -(Some verbiage was borrowed from [express's middleware docs][express]) - -Middleware functions are functions that have access to the `file` object (or in {{name}}'s case, the `view` object), and a callback function that represents the `next` middleware in the application’s build cycle. +A "middleware" is a function that has access to the [view](view.md) object, and a callback function that represents the `next` middleware in the application’s build cycle. Middleware functions can perform the following tasks: - Execute any code. -- Make changes to the `file` object. +- Make changes to the [view](view.md) object. - Call the `next` middleware function in the stack. +### Router methods and handlers + +- **Router methods**: similar to the router METHODS in [express][], but instead of representing [HTTP METHODS][verbs], here router methods represent significant points during the build cycle. + +- **Handler**: The `.handle` method + + +- middleware is "registered" by a router method + + + +## Build lifecycle + +The build lifecycle describes the various stages, events and tranformations that occur from start to finish. + +## Built-in handlers + +- `onLoad` +- `preRender` +- `preCompile` +- `preLayout` +- `onLayout` +- `postLayout` +- `onMerge` +- `postCompile` +- `postRender` + +### Pipeline handlers + +Assemble adds the following handlers, called after a `view` enters assemble's ([vinyl][]) `.src` stream: + +- `onStream`: called by the `.toStream` method upon adding a view to the `.src` stream +- `preWrite`: called by the `.dest` method before writing `view.contents` to the file system +- `postWrite`: called by the `.dest` method after writing `view.contents` to the file system + +### Custom handlers + +... + + +## Caveats + +Their are many advantages to using middleware as a part of your application's build cycle, but there is also at least one common potential pitfalls you should be aware of: + +**A middleware will only when called by its handler** + +This means, for instance, that if you compile a view using the `.compile` method, and you never call the `.render` method, then the `.render` middleware handler will **not be called**, thus any custom `.preRender` or `.postRender` middleware you're registered will not be run. + +In most cases, this should be acceptable and considered correct. However, there will be cases where this behavior causes confusion, but we know of at least a couple of ways to work around it. + +### Workarounds + +One solution to the "why wasn't my middleware called?" problem is to call the "missing" handlers directly. For instance, continuing with the previous example, if you need a `.render` middleware to be called, you could: + +- Call the missing handlers directly, or +- Create noops to fill in build cycle gaps and trigger the handlers + +**Call the missing handlers directly** + +```js +app.preCompile(/./, function(view, next) { + app.handle('preRender', view, next); +}); + +app.postCompile(/./, function(view, next) { + app.handle('postRender', view, next); +}); +``` + +This solution brings its own set of complications. In particular, now the `.preRender` method is being called after `.preCompile`, when `.preRender` should be called first. Since middleware are executed in the order in which they are defined, as long as you register all other `.preCompile` middleware after the one in the example, you should be okay. But the following workaround might be a better solution whenever possible. + +**Create noops to trigger handlers** + +A more reliable solution is to create noops that will trigger missing handlers, ideally without any negative side effects. + +For example, instead of skipping `.render` altogether (because we only want to `.compile` a view), we could create a basic custom engine, or override the render method on a registered engine so that `.render` will not alter `view.contents`, allowing us to call `.render` instead of `.compile`. + +For example, a basic noop engine might look like this: + +```js +app.engine('foo', function(view, locals, cb) { + cb(null, view); +}); +``` + +Or, to override the `.render` method on a registered engine: + +```js +var engine = app.engine('foo'); +engine.render = function(view, locals, cb) { + cb(null, view); +}; +``` + +**** + +## Middleware guidelines + +Guidelines for authoring durable, reliable middleware: + +- middleware should do one thing, and do it well +- middleware should not rely on other middleware, with rare exception +- middleware should be able to run before or after any other middleware called by the same handler. For example, if two different `.onLoad` middleware are registered, either should be able to run first or last, without effecting the results of either middleware. + + +## Router methods + +Router methods are similar to the router METHODS in [express][], but instead of representing [HTTP METHODS][verbs], the router methods here represent significant points or "stages" during the build. + +**Summary** + +- `onLoad`: Immediately after a view is loaded, as a last step just before adding the view to a collection. +- `preLayout`: Immediately before the first [layout][] in a [layout-stack][] is applied to a view. +- `onLayout`: Called after each [layout][] in a [layout-stack][] is applied. +- `postLayout`: Called after all [layouts][] have been applied to a view. +- `onMerge`: Called directly before [partials][] collections are merged onto the [context][]. +- `preCompile`: Called before compiling a view. +- `postCompile`: Called after compiling a view. +- `preRender`: Called before rendering a view. +- `postRender`: Called after rendering a view. + + +## Methods + +### onLoad + +Immediately after a view is loaded, as a last step just before adding the view to a collection. + +**Example** + +Parse [YAML Front Matter][yaml] and add the parsed data object to `view.data`: + +```js +var matter = require('parser-front-matter'); +app.onLoad(/\.hbs$/, function(view, next) { + matter.parse(view, next); +}); +``` + +### preLayout + +Immediately before the first [layout][] in a [layout-stack][] is applied to a view. + +```js +app.preLayout(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### onLayout + +Called after each [layout][] in a [layout-stack][] is applied. + +```js +app.onLayout(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### postLayout + +Called after all [layouts][] have been applied to a view. + +```js +app.postLayout(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### onMerge + +Called directly before [partials][] collections are merged onto the [context][]. + +```js +app.onMerge(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### preCompile + +Called before compiling a view. + +```js +app.preCompile(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### postCompile + +Called after compiling a view. + +```js +app.postCompile(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### preRender + +Called before rendering a view. + +```js +app.preRender(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + +### postRender + +Called after rendering a view. + +```js +app.postRender(/\.hbs$/, function(view, next) { + // do something with `view` + next(); +}); +``` + + +[yaml]: https://en.wikipedia.org/wiki/YAML +[verbs]: http://expressjs.com/api.html#router.METHOD + +{%= reflinks(['express']) %} + + + + + +(Some wording in this document was borrowed from [express's middleware docs][express]) + [express]: http://expressjs.com/en/guide/using-middleware.html \ No newline at end of file diff --git a/docs/src/content/plugins.md b/docs/src/content/plugins.md index a4530434..cb1ff3b0 100644 --- a/docs/src/content/plugins.md +++ b/docs/src/content/plugins.md @@ -9,19 +9,21 @@ reflinks: [] ## Plugin types -- **instance plugins**: Instance plugins are registered with the `.use()` method and are called immediately upon instantiation. The only parameter exposed to an instance plugin is the instance of `app` ({{name}}), `collection`, or `view`. -- **pipeline plugins**: Pipeline plugins are registered with `.pipe()` and are used on vinyl `file` objects in a stream (note that all {{name}} "views" are instances of vinyl files) +- **instance plugins**: Instance plugins are functions that are invoked by the `.use()` method and have access to `app`, `collection`, or `view`, depending on where and how the plugin is registered. +- **pipeline plugins**: Pipeline plugins are registered with `.pipe()` and are used on vinyl `file` objects in a stream (note that all verb "views" are instances of vinyl files) ### Instance plugins + + **Example instance plugin** ```js -var {{name}} = require('{{name}}'); -var app = {{name}}(); +var verb = require('verb'); +var app = verb(); app.use(function(app) { - // do stuff to app or `this`, which is the {{name}} instance + // do stuff to app (verb instance, also exposed as `this) }); ``` @@ -32,12 +34,12 @@ Collections themselves are like mini-application instances, and collection plugi **Example collection plugin** ```js -var {{name}} = require('{{name}}'); -var app = {{name}}(); +var verb = require('verb'); +var app = verb(); // register a plugin to be used on all collections app.use(function(app) { - // do stuff to app or `this` (the {{name}} instance) + // do stuff to app or `this` (the verb instance) // return a function to be use used as a collection plugin return function(collection) { @@ -69,8 +71,8 @@ app.create('posts') **Example view plugin** ```js -var {{name}} = require('{{name}}'); -var app = {{name}}(); +var verb = require('verb'); +var app = verb(); // register a plugin to be used on all views, from all collections app.use(function(app) { @@ -121,3 +123,47 @@ Here are just a few examples - `pagination` - `groups` and `lists` - `sorting` + +_(this is for the `templates` docs, but it will help explain how plugins work in `base`. you obviously already know a lot about plugins, so this is for anyone who might find it useful)_ + +# Plugins + +> Overview of how `Templates` plugins work + +## App + +The following example shows a `plugin` that will be invoked by the `.use` method. Example: `app.use(plugin)`. When invoked, the plugin function is passed the application instance (`app`) as its only argument. + +![plugins-app](https://cloud.githubusercontent.com/assets/383994/13402852/311b9d88-dee0-11e5-944b-200ba56f42fe.png) + +The plugin stops there and will not be called again, **unless the plugin returns a function**. If a function is returned it will be pushed onto the `app.fns` array and then called on each collection that is created using the `app.create`, `app.collection`, or `app.list` methods. + +In the next example, a function is returned from the plugin so that it can be called again later. + +## Collection + +![plugins-collection](https://cloud.githubusercontent.com/assets/383994/13402856/34dd996c-dee0-11e5-8def-f0b739ff10ca.png) + +The plugin stops there and will not be called again, **unless the plugin returns a function**. If a function is returned it will be pushed onto the `collection.fns` array and then called on each view that is added to the collection. + +In the next example, we'll return a function inside the `collection` plugin, so that it can be called again later. + +## View + +![plugins-view](https://cloud.githubusercontent.com/assets/383994/13402861/37fc7032-dee0-11e5-8489-04f392cb9370.png) + +End of the line... + +# Short-circuit: "smart plugins" + +> Don't like all the function nesting? Want to register your plugin with `app` but only have it run on specific objects? No problem, just short-circuit the plugin! + +Every class has a boolean `.is*` property that is useful for checking the instance name. For example, the `Templates` class has an `isTemplates` property, view collections have `isViews`, and views have `isView`. (all "apps", like `Templates`, `Assemble`, `Generate`, etc. also have `isApp` as a convenience). + +To make your plugins "smarter" and eliminate the nesting, try the following: + +![plugins-short-circuit](https://cloud.githubusercontent.com/assets/383994/13405180/33cea2a4-deeb-11e5-864d-4fbab7510c22.png) + +## Generators + +![plugins-generators](https://cloud.githubusercontent.com/assets/383994/13413383/184bc5de-df18-11e5-815c-b968d5c676a4.png) From 3260c31cc9a16bd8824188be0f5a420d07e41163 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:04:38 -0500 Subject: [PATCH 191/282] test fixtures --- test/out-fixtures/test.coffee | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/out-fixtures/test.coffee diff --git a/test/out-fixtures/test.coffee b/test/out-fixtures/test.coffee new file mode 100644 index 00000000..e69de29b From 10dfa30b4cdc15b346d7195800db1a5fac158c0d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:04:49 -0500 Subject: [PATCH 192/282] start on config docs --- docs/src/content/configuration-and-data.md | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/src/content/configuration-and-data.md diff --git a/docs/src/content/configuration-and-data.md b/docs/src/content/configuration-and-data.md new file mode 100644 index 00000000..f21e396d --- /dev/null +++ b/docs/src/content/configuration-and-data.md @@ -0,0 +1,29 @@ +--- +title: Configuration and data +--- + +_(WIP)_ + +## Overview + +**Persisted** + +* `app.store.data`: global config store +* `app.local.data`: project config store +* `app.pkg.data`: package.json config +* `app.questions[key].answer.data`: every question has its own data store, with one answer per directory, per locale - as well as one default answer per locale. + +**In-memory** + +- `app.answers.data` +- `app.cache.data` + + +## Comparison + +Verb keeps "data" on two different objects, depending on your needs. + +| **Storage object** | **Description** | **Methods** | +| --- | --- | --- | +| `app.cache.data` | in-memory | `app.data()` (method) | +| `app.store.data` | persisted | `app.store` (object with methods) | From de14b157c12580d714ccdf479967fcb1e8009cf3 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:05:00 -0500 Subject: [PATCH 193/282] generate docs --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 971cc161..351d4c2e 100644 --- a/readme.md +++ b/readme.md @@ -249,4 +249,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 28, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on March 04, 2016._ \ No newline at end of file From 8612a80755882f0292e9f2c6d2c8aaa44ca0f690 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 4 Mar 2016 08:05:07 -0500 Subject: [PATCH 194/282] update deps --- package.json | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 264e5511..4a3c25a7 100644 --- a/package.json +++ b/package.json @@ -26,19 +26,21 @@ "verb": "bin/verb.js" }, "dependencies": { - "debug": "^2.2.0", - "generate": "^0.4.7", - "generator-util": "^0.2.7" + "generate": "^0.4.9", + "generator-util": "^0.2.7", + "word-wrap": "^1.1.0" }, "devDependencies": { "async": "^1.5.2", - "base-store": "^0.3.6", + "base-option": "^0.6.1", + "base-store": "^0.4.1", "buffer-equal": "^1.0.0", "consolidate": "^0.14.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", + "expect": "^1.14.0", "generate-foo": "^0.1.5", "get-value": "^2.0.3", "global-modules": "^0.2.0", @@ -94,4 +96,4 @@ "lintDeps": { "ignore": [] } -} \ No newline at end of file +} From 36335d37cc00f10fcc0414252d45e2b3438276a9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Mar 2016 12:02:42 -0500 Subject: [PATCH 195/282] update tests to reflect dependency changes --- test/app.applyLayout.js | 10 +++++----- test/app.create.js | 2 +- test/app.extendWith.js | 2 +- test/app.js | 2 +- test/app.register.js | 2 +- test/list.js | 10 +++++----- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js index 950d64e7..2f1faea3 100644 --- a/test/app.applyLayout.js +++ b/test/app.applyLayout.js @@ -19,7 +19,7 @@ describe('app.applyLayout', function() { beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); - app.create('layout', { viewType: 'layout' }); + app.create('layout', {viewType: 'layout'}); app.create('page'); }); @@ -27,7 +27,7 @@ describe('app.applyLayout', function() { app.layout('fofof.tmpl', {content: '..'}); app.page('a.tmpl', page) .render(function(err) { - assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but cannot be not found (common causes are incorrect glob patterns, renameKey function modifying the key, and typos in search pattern)'); + assert(/layouts/.test(err.message)); cb(); }); }); @@ -35,7 +35,7 @@ describe('app.applyLayout', function() { it('should emit an error when a layout cannot be found:', function(cb) { app.layout('fofof.tmpl', {content: '..'}); app.on('error', function(err) { - assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but cannot be not found (common causes are incorrect glob patterns, renameKey function modifying the key, and typos in search pattern)'); + assert(/layouts/.test(err.message)); cb(); }); @@ -47,14 +47,14 @@ describe('app.applyLayout', function() { it('should throw an error - layout defined but no layouts registered:', function(cb) { app.page('a.tmpl', page) .render(function(err) { - assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but no layouts are registered'); + assert(/layouts/.test(err.message)); cb(); }); }); it('should emit an error - layout defined but no layouts registered:', function(cb) { app.on('error', function(err) { - assert.equal(err.message, 'Templates#layouts layout "default.tmpl" was defined on view "a.tmpl" but no layouts are registered'); + assert(/layouts/.test(err.message)); cb(); }); app.page('a.tmpl', page) diff --git a/test/app.create.js b/test/app.create.js index eaca750b..3f9d091d 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -214,7 +214,7 @@ describe('app.create', function() { }); it('should emit `create` when a collection is created:', function() { - app.on('create', function(collection) { + app.on('postCreate', function(collection) { if (collection.options.plural === 'layouts') { collection.options.foo = 'bar'; } diff --git a/test/app.extendWith.js b/test/app.extendWith.js index 29e27b89..6fa02669 100644 --- a/test/app.extendWith.js +++ b/test/app.extendWith.js @@ -161,7 +161,7 @@ describe('.extendWith', function() { generate.getGenerator('foo'); }); - it.only('should extend a generator with a generator invoked by alias', function(cb) { + it('should extend a generator with a generator invoked by alias', function(cb) { generate.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); diff --git a/test/app.js b/test/app.js index 7aa51ce4..c64f8e76 100644 --- a/test/app.js +++ b/test/app.js @@ -104,7 +104,7 @@ describe('app', function() { it('should not re-add `_` in init:', function() { app = new App(); app._.foo = 'bar'; - app.defaultConfig(); + app.initTemplates(); assert(app._.foo === 'bar'); }); }); diff --git a/test/app.register.js b/test/app.register.js index 57c6a260..9c0706c4 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -41,7 +41,7 @@ describe('.register', function() { }); it('should set configname to generator by default', function() { - assert.equal(base.configname, 'generator'); + assert.equal(base.configname, 'verbfile'); }); it('should set configname', function() { diff --git a/test/list.js b/test/list.js index e601ade3..dace90c6 100644 --- a/test/list.js +++ b/test/list.js @@ -137,7 +137,7 @@ describe('list', function() { }); describe('addItem', function() { - beforeEach(function() { + beforeEach(function() { list = new List(); }); @@ -160,10 +160,10 @@ describe('list', function() { list.addItem('c', {content: '...'}); assert(list.items.length === 3); var a = list.getItem('a'); - list.removeItem(a); + list.deleteItem(a); assert(list.items.length === 2); var c = list.getItem('c'); - list.removeItem(c); + list.deleteItem(c); assert(list.items[0].key === 'b'); }); @@ -172,9 +172,9 @@ describe('list', function() { list.addItem('b', {content: '...'}); list.addItem('c', {content: '...'}); assert(list.items.length === 3); - list.removeItem('c'); + list.deleteItem('c'); assert(list.items.length === 2); - list.removeItem('b'); + list.deleteItem('b'); assert(list.items[0].key === 'a'); }); }); From fffa4f6b60b0335045774196d456175b627b5e18 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Mar 2016 12:03:15 -0500 Subject: [PATCH 196/282] change default generator to display the `--help` menu --- lib/generator.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/generator.js b/lib/generator.js index 414c565e..5f54fc73 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,10 +1,14 @@ 'use strict'; -var wrap = require('word-wrap'); +/** + * Display `--help` when no tasks are defined + */ + +module.exports = function(verb) { + verb.enable('silent'); -module.exports = function verbDefault(verb) { verb.task('default', function(cb) { - verb.log('No tasks were defined, try running `verb readme` if verb-readme-generator is installed'); - cb(); + verb.cli.process({ help: true }, cb); }); }; + From 060bbdbb80e71953344d9d06e822399123c20e42 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Mar 2016 12:03:20 -0500 Subject: [PATCH 197/282] generate docs --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 351d4c2e..63fb78a8 100644 --- a/readme.md +++ b/readme.md @@ -249,4 +249,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on March 04, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on March 05, 2016._ \ No newline at end of file From 12269e27204d60dc1f9baec6629f13161105919c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 5 Mar 2016 12:03:48 -0500 Subject: [PATCH 198/282] update deps --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4a3c25a7..f2b20007 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,8 @@ "verb": "bin/verb.js" }, "dependencies": { - "generate": "^0.4.9", - "generator-util": "^0.2.7", - "word-wrap": "^1.1.0" + "generate": "^0.4.11", + "generator-util": "^0.2.9" }, "devDependencies": { "async": "^1.5.2", @@ -96,4 +95,4 @@ "lintDeps": { "ignore": [] } -} +} \ No newline at end of file From 0831fa8eb17774617adb283011458a839f2c0865 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Mar 2016 01:40:04 -0500 Subject: [PATCH 199/282] don't create docs collection --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 5ea339ac..708d63a1 100644 --- a/index.js +++ b/index.js @@ -83,7 +83,6 @@ Verb.prototype.initVerb = function(opts) { if (process.env.GENERATE_CLI) { this.use(utils.create(this.options)); this.create('files'); - this.create('docs'); } }; From b6b15761468376e4f51b55157841fb67895f9462 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Mar 2016 01:40:26 -0500 Subject: [PATCH 200/282] generate docs --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 63fb78a8..7a41d160 100644 --- a/readme.md +++ b/readme.md @@ -249,4 +249,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on March 05, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on March 06, 2016._ \ No newline at end of file From 894f6f604ba24df17980dd7e6014c4af8226a6ae Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 6 Mar 2016 01:40:32 -0500 Subject: [PATCH 201/282] update deps --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f2b20007..c93c29f2 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,12 @@ "verb": "bin/verb.js" }, "dependencies": { - "generate": "^0.4.11", + "generate": "^0.4.12", "generator-util": "^0.2.9" }, "devDependencies": { "async": "^1.5.2", - "base-option": "^0.6.1", + "base-option": "^0.6.2", "base-store": "^0.4.1", "buffer-equal": "^1.0.0", "consolidate": "^0.14.0", @@ -95,4 +95,4 @@ "lintDeps": { "ignore": [] } -} \ No newline at end of file +} From 5d9fb777c844228784c3418c1e353eff027595fc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 21 Mar 2016 13:19:42 -0400 Subject: [PATCH 202/282] draft docs --- docs/src/content/cli-commands.md | 230 +++++++++++++++++++++++++++ docs/src/content/cli.md | 8 +- docs/src/content/collections.md | 25 ++- docs/src/content/config.md | 265 +++++++++++++++++++++++++++++++ docs/src/content/create.md | 28 ++++ docs/src/content/stores.md | 158 ++++++++++++++++++ 6 files changed, 702 insertions(+), 12 deletions(-) create mode 100644 docs/src/content/cli-commands.md create mode 100644 docs/src/content/config.md create mode 100644 docs/src/content/create.md create mode 100644 docs/src/content/stores.md diff --git a/docs/src/content/cli-commands.md b/docs/src/content/cli-commands.md new file mode 100644 index 00000000..a16a1ee4 --- /dev/null +++ b/docs/src/content/cli-commands.md @@ -0,0 +1,230 @@ +--- +title: CLI Commands +--- + +The following CLI commands are currently supported in verb. + +### --ask + +Force questions that match the given pattern to be asked. The resulting answer data is merged onto `app.cache.data`. + +After questions are answered: + +* Use `app.data('answers')` to get answer data. +* To open the directory where data is persisted, enter `--open answers` in the command line + +**Example** + +```sh +# ask all questions +$ --ask +# ask all `author.*` questions +$ --ask 'author.*' +# ask all `*.name` questions (like `project.name` and `author.name`) +$ --ask '*.name*' +``` + +### --config + +Persist a value to a namespaced config object in package.json. For example, if you're using `verb`, the value would be saved to the `verb` object. + +**Params** + +* **{Object}**: app + +**Example** + +```sh +# display the config +$ --config +# set a boolean for the current project +$ --config=toc +# save the cwd to use for the current project +$ --config=cwd:foo +# save the tasks to run for the current project +$ --config=tasks:readme +``` + +### --cwd + +Set the current working directory. + +**Example** + +```sh +# set working directory to 'foo' +$ --cwd=foo +# display cwd +$ --cwd +``` + +### --data + +Set data on the `app.cache.data` object. This is the API-equivalent of calling `app.data()`. + +**Example** + +```sh +$ --data +# display data object +$ --data=foo +# sets {foo: true} +$ --data=foo:bar +# sets {foo: 'bar'} +$ --data=foo.bar:baz +# sets {foo:{bar: 'baz'}} +``` + +### --disable + +Disable a configuration setting. This is the API-equivalent of calling `app.disable('foo')`, or `app.option('foo', false)`. + +**Example** + +```sh +$ --disable=foo +# sets {foo: false} +``` + +### --emit + +Bind `console.error` to the given event listener, so that when event `name` is emitted, the event arguments will be output in the console. + +**Example** + +```sh +# emit errors +$ --emit error +# emit all views as they're created +$ --emit view +# emit only 'pages' as they're created +$ --emit page +``` + +### --enable + +Enable a configuration setting. This is the API-equivalent of calling `app.enable('foo')`, or `app.option('foo', true)`. + +**Example** + +```sh +$ --enable=foo +# sets {foo: true} +``` + +### --global + +Persist a value to the global config store by prefixing a command line option with `-g` or `--global`. + +**Params** + +* **{Object}**: app + +**Example** + +```sh +# save a boolean +$ -g=toc # saves `{ toc: true }` to global defaults +# save the cwd to use as a global default +$ -g=cwd:foo +# save the tasks to run by default +$ -g=tasks:readme +``` + +### --init + +Ask initialization questions and persist answer data to the global config store. + +**Example** + +```sh +$ --init +``` + +### --open + +Open a directory, or open a file in the default application associated with the file type. + +**Example** + +```sh +# Open the directory where answer data is persisted +$ --open answers +# Open the directory where store data is persisted +$ --open store +``` + +### --option + +Set options on the `app.options` object. This is the API-equivalent of calling `app.option()`. You may also use the plural `--options` flag for identical behavior. + +**Example** + +```sh +$ --option=foo +# sets {foo: true} +$ --option=foo:bar +# sets {foo: 'bar'} +$ --option=foo.bar:baz +# sets {foo:{bar: 'baz'}} +``` + +### --options + +Set in-memory options on the `app.options` object. This is the API-equivalent of calling `app.option()`. You may also use the singular `--option` flag for identical behavior. + +To display currently defined options, pass the `--options` flag with no value. + +**Example** + +```sh +$ --options=foo +# sets {foo: true} +$ --options=foo:bar +# sets {foo: 'bar'} +$ --options=foo.bar:baz +# sets {foo:{bar: 'baz'}} +``` + +### --save + +Persist a value to the global config store by prefixing a command line option with `--save` or `-s`. + +**Params** + +* **{Object}**: app + +**Example** + +```sh +# save a boolean +$ --save=toc # saves `{ toc: true }` to global config +# save the cwd to use as a global default +$ --save=cwd:foo +# save the tasks to run by default +$ --save=tasks:readme +``` + +### --tasks + +Run the given generators and tasks. This flag is unnecessary when used with [base-runner](https://github.com/jonschlinkert/base-runner). + +**Example** + +```sh +# run task 'foo' +$ app --tasks foo +# => {task: ['foo']} +# run generator 'foo', task 'bar' +$ app --tasks foo:bar +# => {task: ['foo:bar']} +``` + +### --show + +Returns true if `val` is true or is an object with `show: true` + +**Params** + +* `val` **{String}** +* `returns` **{Boolean}** diff --git a/docs/src/content/cli.md b/docs/src/content/cli.md index e0ff52c8..834eb028 100644 --- a/docs/src/content/cli.md +++ b/docs/src/content/cli.md @@ -1,13 +1,13 @@ --- -title: <%= upper(name) %> CLI -description: "Using the <%= name %> command line." +title: <%= proper(name) %> CLI +description: "Working with <%= name %> from the command line." related: ["terminology", "tasks", "features"] tags: ['config', 'cli', 'command line', 'options', 'flags'] --- -**FAQ** +**CLI FAQ** -- object paths (dot notation) may be used for most command line arguments +- Dot-notation may be used for most command line arguments - In cases where dots should not be expanded to an object, you may escape the dot with a single backslash diff --git a/docs/src/content/collections.md b/docs/src/content/collections.md index fc90476b..4ea528be 100644 --- a/docs/src/content/collections.md +++ b/docs/src/content/collections.md @@ -1,6 +1,8 @@ -# Collections +--- +title: Collections +--- -> Collections are first-class citizens in Assemble, with three different collection types to choose from. This document describes each collection type and provides the information you need to start using them. +> Collections are first-class citizens in {%= name %}, with three different collection types to choose from. This document describes each collection type and provides the information you need to start using them. - `create` - `collection` @@ -25,13 +27,21 @@ View collections have methods getting, setting and finding views, as well as ass ## Create -The `create` method is used for adding custom "view collections" to verb. A few things happen when the method is used: +The `create` method is used for adding custom "view collections" to verb. -- +**Example** -- This exposed `page` and `pages` methods on verb (verb automatically detects inflections - plural and singular forms) -- A `pages` +To create a "pages" collection: +```js +app.create('pages'); +``` + +A few things happened when the `.create` method was used: + +1. The `page` and `pages` methods were decorated onto verb (verb automatically detects inflections - plural and singular forms) +2. A `pages` object was added to `verb.views` for caching views, so when pages are created they can be found on `verb.views.pages` +3. ~~A `page` helper was created~~ (helpers are only created for `partial` [view types](view-types.md)) ## Collections types @@ -41,7 +51,7 @@ Assemble 0.6.0 supports **3 different collection types**: | --- | --- | --- | ---| | **collections** | `item` | `items` | generic collections, with methods for setting and getting `items` | | **view collections** | `view` | `views` | Methods for working with template collections, like pages, posts, layouts, partials, etc. | -| **lists** | `item` | `items` | Stored as an array, has methods for getting, setting, [sorting](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L359), [grouping](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L333). etc | +| **lists** | `item` | `items` | Stored as an array, has methods for getting, setting, [sorting][], [grouping][]. etc | _(sidenote: views and items are also [vinyl](https://github.com/gulpjs/vinyl) files)_ @@ -64,7 +74,6 @@ Ultimately this gives you full control over how views are named and how lookups Lists are similar to collections but instead of storing an object of views, `items` are stored as an array. - **Nice to know** - You can create [lists from collections][lists-from-collections] diff --git a/docs/src/content/config.md b/docs/src/content/config.md new file mode 100644 index 00000000..ba10322e --- /dev/null +++ b/docs/src/content/config.md @@ -0,0 +1,265 @@ +--- +title: Config +--- + +Define verb configuration settings on the `verb` object in package.json. + +Also see the [CLI commands](cli-commands.md#config) documentation to learn how to set config values from the command line. + +**Example** + +```js +{ + "name": "my-project", + "verb": { + // verb config settings here + } +} +``` + +### .cwd + +Set the current working directory. + +**Example** + +```json +{ + "name": "my-project", + "verb": { + "cwd": "foo/bar" + } +} +``` + +### .data + +Merge data onto the `app.cache.data` object. + +**Example** + +```json +{ + "name": "my-project", + "verb": { + "data": { + "username": "jonschlinkert", + "twitter": "jonschlinkert" + } + } +} +``` + +If the [base-data][] plugin is registered, this is the API-equivalent of calling `app.data()`. + +### .engines + +Register engines to use for rendering templates. Object-keys are used for the engine name, and the value can either be the module name, or an options object with the module name defined on the `name` property. + +**Prerequisites** + +- ~~Requires [templates][], otherwise ignored~~ (included in verb by default) +- Modules must be locally installed and listed in `dependencies` or `devDependencies` + +**Examples** + +Key-value pairs, where the `key` is the name (or file extension) to associate with the engine, and the `value` is the module name to require: + +```json +{ + "name": "my-project", + "verb": { + "engines": { + "*": "engine-base" + } + } +} +``` + +Objects, where the `key` is the name (or file extension) to associate with the engine, and the `value` is an options object to pass to the engine. + +_(When this format is used, the object must include a `name` property with the name of the module to require)_. + +```json +{ + "name": "my-project", + "verb": { + "engines": { + "*": { + "name": "engine-base" + } + } + } +} +``` + +### .engine + +Alias for [engines](#engines) + +### .helpers + +Register helpers to be used in templates. Value can be a string or array of module names, glob patterns, file paths, or an object where each key is a filepath, glob or module name, and the value is an options object to pass to resolved helpers. + +**Prerequisites** + +- ~~Requires [templates][], otherwise ignored~~ (included in verb by default) +- Modules must be locally installed and listed in `dependencies` or `devDependencies` + +**Examples** + +Module names: + +```json +{ + "name": "my-project", + "verb": { + "helpers": { + "helper-foo": {}, + "helper-bar": {} + } + } +} +``` + +Glob of helpers: + +```json +{ + "name": "my-project", + "verb": { + "helpers": ["foo/*.js"] + } +} +``` + +### .options + +Merge options onto the `app.options` object. + +**Example** + +```json +{ + "name": "my-project", + "verb": { + "options": { + "foo": "bar" + } + } +} +``` + +If the [base-option][] plugin is registered, this is the API-equivalent of calling `app.option()`. + +### .plugins + +Load [pipeline plugins][plugins] to be used for transforming files in the `.src` string. + +**Prerequisites** + +- ~~Requires the [base-pipeline][] plugin to be registered~~ (included in verb by default) +- Modules must be locally installed and listed in `dependencies` or `devDependencies` + +**Example** + +Defined as an array of plugin names: + +```json +{ + "name": "my-project", + "verb": { + "plugins": ["gulp-eslint"] + } +} +``` + +Defined as objects: + +```json +{ + "name": "my-project", + "verb": { + "plugins": { + "gulp-format-md": {}, + "gulp-eslint": {} + } + } +} +``` + +### .toc + +Enable or disable Table of Contents rendering, or pass options to the [verb-toc][] library. + +**Prerequisites** + +- Requires the [verb-toc][] plugin to be registered. _This **Not** included in verb by default_. +- Modules must be locally installed and listed in `dependencies` or `devDependencies` + +**Example** + +```json +{ + "name": "my-project", + "verb": { + "toc": true + } +} +``` + +### .use + +Define plugins to load. Value can be a string or array of module names. + +**Prerequisites** + +- Modules must be locally installed and listed in `dependencies` or `devDependencies` + +**Example** + +```json +{ + "verb": { + "use": ["base-option", "base-data"] + } +} +``` + +### .run + +Always run tasks, regardless of additional command line flags that may have been passed. + +**Example** + +```json +{ + "verb": { + "run": true + } +} +``` + +## Related + +**Docs** + +- [CLI commands](cli-commands.md) +- [data](data.md) +- [options](options.md) +- [plugins](plugins.md) + +**Libraries** + +- [base-data][] +- [base-option][] +- [base-pipeline][] +- [templates][] +- [verb-toc][] + + +[base-data]: https://github.com/node-base/base-data +[base-option]: https://github.com/node-base/base-option +[verb-toc]: https://github.com/verbose/verb-toc +[base-pipeline]: https://github.com/jonschlinkert/base-pipeline +[templates]: https://github.com/jonschlinkert/templates \ No newline at end of file diff --git a/docs/src/content/create.md b/docs/src/content/create.md new file mode 100644 index 00000000..f634f1a9 --- /dev/null +++ b/docs/src/content/create.md @@ -0,0 +1,28 @@ +--- +title: create +--- + +The `.create` method is used for creating custom "view collections". + +**Params** + +- `name` **{String}**: the name of the collection to create +- `options` **{Object}**: options to use when creating the collection + +**Example** + +To create a "pages" collection: + +```js +app.create('pages'); +``` + +A few things happened when the `.create` method was used: + +- The `page` and `pages` methods were decorated onto verb (verb automatically detects inflections - plural and singular forms) +- A `pages` object was added to `verb.views` for caching views, so when pages are created they can be found on `verb.views.pages` + +## FAQ + +- View collections are instances of the [Views](Views.md) class. +- When the `.create` method is called, verb invokes the [Views](Views.md) class to create an instance of `Views` \ No newline at end of file diff --git a/docs/src/content/stores.md b/docs/src/content/stores.md new file mode 100644 index 00000000..7cd19e6d --- /dev/null +++ b/docs/src/content/stores.md @@ -0,0 +1,158 @@ +--- +title: Stores +--- + +Verb supports 3 "types" of stores for persisting config values, each is persisted to the `~/.data-store` directory, and all three have API methods for getting/setting data: + +**Store comparison** + +Type | API | Description +--- | --- | --- +Global | `app.globals` | Generic global defaults, persisted to `~/.data-store/globals/defaults.json`. These defaults are shared by `verb`, `assemble`, `update` and `generate`. +Verb | `app.store` | Verb-specific defaults, persisted to `~/.data-store/app/verb.json` +Project | `app.locals` | Project-specific defaults, persisted to `~/.data-store/project/foo-bar.json`. + +**2 bonus types** + +In addition to the stores exposed on the API, there are two `config` stores used for defining options/configuration settings. + +- `package.json`: config object with the name of the "app". `verb`, `assemble` etc +- `verb.json`: where `app` is the name of the app. You should be able to use `verb.json`, this was implemented a while ago. + +## How config works + +Specific data "wins" over general data. + +```sh +global + # "app" wins over "global" + L app + # "project" wins over "app" + L project + # "local" wins over "project" + L local (verb.json) + L local (package.json) + # Anything explicitly passed on the API should win + L API +``` + +### Peristed config + +Config stores are persisted according the following conventions: + +- **globals**: `~/.data-store/globals/defaults.json` +- **app**: `~/.data-store/app/{app-name}.json` +- **app**: `~/.data-store/app/{app-name}/projects/{project-name}.json` + + + +**Examples** + +```sh +# global defaults +~/.data-store/globals/defaults.json + +# app-specific defaults (app => verb) +~/.data-store/app/verb.json + +# verb project configs +~/.data-store/app/verb/projects/foo.json +~/.data-store/app/verb/projects/bar.json +~/.data-store/app/verb/projects/baz.json +``` + + +## Global defaults + +Global defaults are shared across all "apps" and are persisted to: + +```sh +~/.data-store/globals/defaults.json +``` + +**Best for** + +Generic data that can be used in any project, such as "author" `data` to be passed on the context when rendering docs or project readmes: + +```json +{ + "data": { + "username": "jonschlinkert", + "twitter": "jonschlinkert", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + } + } +} +``` + +## Verb defaults + +App-specific defaults are persisted to: + +```sh +~/.data-store/app/verb.json +``` + +**Best for** + +Verb-specific configuration settings. For example, you might have certain preferences that should always be used (unless overridden), like: + +```json +{ + "toc": true, + "layout": "default" +} +``` + +## Project defaults + +Project stores are specific to the "app" being run. In this case, `verb` is the app, so project-specific configuration settings are persisted as follows: + +```sh +# app-specific defaults (app => verb) +~/.data-store/app/verb.json + +# verb project configs +~/.data-store/app/verb/projects/foo.json +~/.data-store/app/verb/projects/bar.json +~/.data-store/app/verb/projects/baz.json +``` + +## Project (local) configs + +### verb.json + +Project defaults may be stored locally in `{app-name}.json`. In this case, `app-name` is verb, but the same applies to [assembe][], [generate][], and [update][]. +For example, the following will enable the Table of Contents for a project (if you're using [verb-readme-generator][]) and will add the `fooo` property to the context at render-time: + +```json +{ + "options": { + "toc": true + }, + "data": { + "fooo": "baaar" + } +} +``` + +### package.json + +The exact same configuration and support as `verb.json`, but the object is stored on the `verb` object in `package.json`: + +```json +{ + "name": "my-project", + "description": "It's a really nice project. seriously, it is. it's nice. reaaaly nice.", + "verb": { + "options": { + "toc": true + }, + "data": { + "fooo": "baaar" + } + } +} +``` From 17538a21aa621fa5b080a6a4e4a23cf500869ffc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 21 Mar 2016 13:22:46 -0400 Subject: [PATCH 203/282] fix label --- docs/src/content/stores.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/src/content/stores.md b/docs/src/content/stores.md index 7cd19e6d..0b960ce3 100644 --- a/docs/src/content/stores.md +++ b/docs/src/content/stores.md @@ -42,8 +42,7 @@ Config stores are persisted according the following conventions: - **globals**: `~/.data-store/globals/defaults.json` - **app**: `~/.data-store/app/{app-name}.json` -- **app**: `~/.data-store/app/{app-name}/projects/{project-name}.json` - +- **project**: `~/.data-store/app/{app-name}/projects/{project-name}.json` **Examples** From c20bbe5f73387e24b7e024e4b8572fe5435e1486 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 21 Mar 2016 13:24:06 -0400 Subject: [PATCH 204/282] format table --- docs/src/content/stores.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/content/stores.md b/docs/src/content/stores.md index 0b960ce3..b327b2b4 100644 --- a/docs/src/content/stores.md +++ b/docs/src/content/stores.md @@ -4,13 +4,13 @@ title: Stores Verb supports 3 "types" of stores for persisting config values, each is persisted to the `~/.data-store` directory, and all three have API methods for getting/setting data: -**Store comparison** +## Store comparison -Type | API | Description +**Type** | **API** | **Description** --- | --- | --- -Global | `app.globals` | Generic global defaults, persisted to `~/.data-store/globals/defaults.json`. These defaults are shared by `verb`, `assemble`, `update` and `generate`. -Verb | `app.store` | Verb-specific defaults, persisted to `~/.data-store/app/verb.json` -Project | `app.locals` | Project-specific defaults, persisted to `~/.data-store/project/foo-bar.json`. +Global defaults | `app.globals` | Generic global defaults, persisted to `~/.data-store/globals/defaults.json`. These defaults are shared by `verb`, `assemble`, `update` and `generate`. +Verb defaults | `app.store` | Verb-specific defaults, persisted to `~/.data-store/app/verb.json` +Project defaults | `app.locals` | Project-specific defaults, persisted to `~/.data-store/project/foo-bar.json`. **2 bonus types** From b94b9dd60456482cdf1344d14060388c3bf17804 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Mon, 21 Mar 2016 13:27:14 -0400 Subject: [PATCH 205/282] clarify config types --- docs/src/content/stores.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/src/content/stores.md b/docs/src/content/stores.md index b327b2b4..7e9839c5 100644 --- a/docs/src/content/stores.md +++ b/docs/src/content/stores.md @@ -2,7 +2,7 @@ title: Stores --- -Verb supports 3 "types" of stores for persisting config values, each is persisted to the `~/.data-store` directory, and all three have API methods for getting/setting data: +Verb supports 4 "types" of stores for persisting config values, each is persisted to the `~/.data-store` directory, and all three have API methods for getting/setting data: ## Store comparison @@ -11,13 +11,7 @@ Verb supports 3 "types" of stores for persisting config values, each is persiste Global defaults | `app.globals` | Generic global defaults, persisted to `~/.data-store/globals/defaults.json`. These defaults are shared by `verb`, `assemble`, `update` and `generate`. Verb defaults | `app.store` | Verb-specific defaults, persisted to `~/.data-store/app/verb.json` Project defaults | `app.locals` | Project-specific defaults, persisted to `~/.data-store/project/foo-bar.json`. - -**2 bonus types** - -In addition to the stores exposed on the API, there are two `config` stores used for defining options/configuration settings. - -- `package.json`: config object with the name of the "app". `verb`, `assemble` etc -- `verb.json`: where `app` is the name of the app. You should be able to use `verb.json`, this was implemented a while ago. +Local configs | N/A | Project-specific configuration settings in `verb.json` or the `verb` object in package.json. ## How config works @@ -119,7 +113,7 @@ Project stores are specific to the "app" being run. In this case, `verb` is the ~/.data-store/app/verb/projects/baz.json ``` -## Project (local) configs +## Local configs ### verb.json From 76ce96a4f701c5c8e0905f37a80e6716a8ce4eaa Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 2 Apr 2016 20:12:27 -0400 Subject: [PATCH 206/282] organize docs --- docs/lang/en/api/events.md | 79 +++++++++++++++++++ .../en}/recipes/auto-load-middleware.md | 0 .../en/recipes/disable-table-of-contents.md} | 0 .../en}/recipes/inspecting-the-context.md | 0 docs/{ => lang/en}/recipes/merging-context.md | 0 docs/{ => lang/en}/recipes/render-a-list.md | 0 docs/lang/en/usage/faq.md | 16 ++++ docs/recipes/generate.js | 44 ----------- docs/src/conflict-reporter.md | 38 +++++++++ docs/src/content/stores.md | 2 + 10 files changed, 135 insertions(+), 44 deletions(-) create mode 100644 docs/lang/en/api/events.md rename docs/{ => lang/en}/recipes/auto-load-middleware.md (100%) rename docs/{recipes/3-ways-to-disable-toc.md => lang/en/recipes/disable-table-of-contents.md} (100%) rename docs/{ => lang/en}/recipes/inspecting-the-context.md (100%) rename docs/{ => lang/en}/recipes/merging-context.md (100%) rename docs/{ => lang/en}/recipes/render-a-list.md (100%) create mode 100644 docs/lang/en/usage/faq.md delete mode 100644 docs/recipes/generate.js create mode 100644 docs/src/conflict-reporter.md diff --git a/docs/lang/en/api/events.md b/docs/lang/en/api/events.md new file mode 100644 index 00000000..3e511347 --- /dev/null +++ b/docs/lang/en/api/events.md @@ -0,0 +1,79 @@ +--- +title: Events +--- + +Verb inherits from [component-emitter][]. + + +_(See the [component-emitter][] docs to see all available methods and features.)_ + +## .on + +The `on` method is used for listening for events emitted by Verb. + +**Example** + +```js +verb.on('error', function(err) { + // +}); +``` + +## .emit + +The `emit` method is used for emitting events. + +**Example** + +```js +verb.emit('error', new Error('foo')); +``` + +See the [component-emitter][] docs to see all available methods and features. + +### deployBefore + +Emitted before deployment begins. + +### deployAfter + +Emitted after deployment finishes. + +### done + +Emitted before Verb exits. + +### generateBefore + +Emitted before generation begins. + +### generateAfter + +Emitted after generation finishes. + +### new + +Emitted when the `--new` flag is passed. + +``` js +verb.on('new', function(name, args) { + // +}); +``` + +Data | Description +--- | --- +`post.path` | Full path of the post file +`post.content` | Content of the post file + +### processBefore + +Emitted before processing begins. This event returns a path representing the root directory of the box. + +### processAfter + +Emitted after processing finishes. This event returns a path representing the root directory of the box. + +### ready + +Emitted after initialization finishes. diff --git a/docs/recipes/auto-load-middleware.md b/docs/lang/en/recipes/auto-load-middleware.md similarity index 100% rename from docs/recipes/auto-load-middleware.md rename to docs/lang/en/recipes/auto-load-middleware.md diff --git a/docs/recipes/3-ways-to-disable-toc.md b/docs/lang/en/recipes/disable-table-of-contents.md similarity index 100% rename from docs/recipes/3-ways-to-disable-toc.md rename to docs/lang/en/recipes/disable-table-of-contents.md diff --git a/docs/recipes/inspecting-the-context.md b/docs/lang/en/recipes/inspecting-the-context.md similarity index 100% rename from docs/recipes/inspecting-the-context.md rename to docs/lang/en/recipes/inspecting-the-context.md diff --git a/docs/recipes/merging-context.md b/docs/lang/en/recipes/merging-context.md similarity index 100% rename from docs/recipes/merging-context.md rename to docs/lang/en/recipes/merging-context.md diff --git a/docs/recipes/render-a-list.md b/docs/lang/en/recipes/render-a-list.md similarity index 100% rename from docs/recipes/render-a-list.md rename to docs/lang/en/recipes/render-a-list.md diff --git a/docs/lang/en/usage/faq.md b/docs/lang/en/usage/faq.md new file mode 100644 index 00000000..8424ab0f --- /dev/null +++ b/docs/lang/en/usage/faq.md @@ -0,0 +1,16 @@ +--- +layout: usage +meta_title: How to Use Verb - Verb Docs +meta_description: An in depth guide to using the Verb blogging platform. Got Verb but not sure how to get going? Start here! +heading: Using Verb +subheading: Finding your way around, and getting set up the way you want +chapter: usage +section: faq +permalink: /usage/faq/ +prev_section: writing +canonical: http://support.ghost.org/faq/ +redirectToCanonical: true +--- + + +## FAQ diff --git a/docs/recipes/generate.js b/docs/recipes/generate.js deleted file mode 100644 index 185bc9f0..00000000 --- a/docs/recipes/generate.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var glob = require('matched'); - -module.exports = function(verb, base, env) { - verb.task('default', function() {}); - verb.task('readme', function() {}); - - verb.task('templates', function(cb) { - var opts = {cwd: env.cwd, dot: true}; - if (!verb.templates) verb.create('templates'); - - glob('templates/*', opts, function(err, files) { - if (err) return cb(err); - - files.forEach(function(name) { - var fp = path.join(env.cwd, name); - verb.template(name, {path: fp, content: fs.readFileSync(fp)}); - }); - cb(); - }); - }); - - verb.register('docs', function(app) { - app.task('x', function(cb) { - console.log('docs > x'); - cb(); - }); - app.task('y', function() {}); - app.task('z', function() {}); - - app.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); - }); - - verb.task('default', function(cb) { - verb.generate('docs:x', cb); - }); -}; diff --git a/docs/src/conflict-reporter.md b/docs/src/conflict-reporter.md new file mode 100644 index 00000000..0cc2c9cf --- /dev/null +++ b/docs/src/conflict-reporter.md @@ -0,0 +1,38 @@ + +## conflict reporter + +> See conflicts in the command line! + +To output conflicts, just run verb in `verbose` mode: + +```bash +$ verb --verbose +``` + +**Example output** + +![screen shot 2015-04-23 at 5 42 16 am](https://cloud.githubusercontent.com/assets/383994/7294451/9034dbfa-e97b-11e4-930e-372ef4012096.png) + +**How it works** + +In verbose mode, the conflict reporter tells you when there are problems with helpers and properties on the context. This works for "renderable" templates, like `.verb.md` (via a plugin) as well as includes (via a middleware). + +For example, currently verb registers both a `license` data field and a `license()` helper (I kept the conflict so you can see how the manager works. Don't worry, verb handles it just fine now). Normally, both of these cannot exist on the same object at the same time, so only one of them will be merged onto the context. As a result, the template engine will throw an error when it tries to render either `{%= license %}` or `{%= license() %}`. + +**Solution** + +The conflict manager plugin detects these conflicts, then it temporarily renames the helper on the fly, re-registers it on the `__.` object, and deletes it from the data object. That way both the helper and data property will render as expected. (You might be thinking, "why would I have duplicate properties like that?" well, you personally might not, but if you want to use community templates or built-in templates that might have variables you're unaware of, and you want them to "just work", then this is not an uncommon thing...). + +*** + +## visual diffs + +See the difference between pre-render and post-render templates by running the following in the command line: + +```js +$ verb --diff +``` + +**Example output** + +![screen shot 2015-04-23 at 6 24 13 am](https://cloud.githubusercontent.com/assets/383994/7295197/96b5666a-e981-11e4-82f8-3587697fc910.png) diff --git a/docs/src/content/stores.md b/docs/src/content/stores.md index 7e9839c5..6e5a0bfa 100644 --- a/docs/src/content/stores.md +++ b/docs/src/content/stores.md @@ -6,6 +6,8 @@ Verb supports 4 "types" of stores for persisting config values, each is persiste ## Store comparison +The first 3 types are stored in user home: `~/.data-store`, the last type "local configs", is stored locally to a project. + **Type** | **API** | **Description** --- | --- | --- Global defaults | `app.globals` | Generic global defaults, persisted to `~/.data-store/globals/defaults.json`. These defaults are shared by `verb`, `assemble`, `update` and `generate`. From 0f2a766e906fc4143021e8ac397568eb4297fd8b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 2 Apr 2016 20:12:47 -0400 Subject: [PATCH 207/282] minor formatting --- bin/verb.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index d307f976..e135f27b 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,14 +1,13 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; -var generator = require('../lib/generator'); var verb = require('..'); /** * Create verb "runner" */ -var run = verb.runner('verbfile.js', generator); +var cli = verb.runner('verbfile.js', require('../lib/generator')); var app = verb(); app.on('done', function() { @@ -19,7 +18,7 @@ app.on('done', function() { * Run generators and tasks */ -run(app, function(err, argv, app) { +cli(app, function(err, argv, app) { if (err) { console.log(err); process.exit(1); From 30441a541badd8f1722ec3229c9425a9821873f0 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 2 Apr 2016 20:13:33 -0400 Subject: [PATCH 208/282] add ctor events, improve alias regex --- index.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 708d63a1..c7d1ef0c 100644 --- a/index.js +++ b/index.js @@ -8,8 +8,8 @@ 'use strict'; var utils = require('generator-util'); +var define = require('define-property'); var Generate = require('generate'); -var debug = Generate.debug; var pkg = require('./package'); /** @@ -30,14 +30,16 @@ function Verb(options) { this.options = utils.extend({}, this.options, options); Generate.call(this, this.options); + delete this.cache.data.runner; this.is('verb'); - this.define('isApp', true); - debug(this); + Generate.debug(this); + Verb.emit('preInit', this, this.base); this.debug('initializing'); this.verbDefaults(options); this.initVerb(this.options); + Verb.emit('init', this, this.base); } /** @@ -63,9 +65,9 @@ Verb.prototype.verbDefaults = function(options) { Verb.prototype.initVerb = function(opts) { this.debug('initializing verb data'); - this.data({runner: pkg}); - this.data({verb: {related: {}, reflinks: []}}); - var aliasRegex = /(^verb-?|-?generat(e|or)-?)/g; + this.set('appname', 'verb'); + this.data({runner: {name: 'verb', url: 'https://github.com/verbose/verb'}}); + var aliasRegex = /^verb-([^-]+)-generator$/g; this.toFullname = function(name) { return 'verb-' + this.toAlias(name) + '-generator'; @@ -76,7 +78,7 @@ Verb.prototype.initVerb = function(opts) { }; this.toAlias = function(name) { - return name.replace(aliasRegex, ''); + return name.replace(aliasRegex, '$1'); }; // if run via CLI, add defaults From f740bfbe8d4fba0d7db8a2844a5e428c7922cb41 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 2 Apr 2016 20:13:46 -0400 Subject: [PATCH 209/282] minor edits --- lib/generator.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/generator.js b/lib/generator.js index 5f54fc73..558c0beb 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -4,11 +4,11 @@ * Display `--help` when no tasks are defined */ -module.exports = function(verb) { - verb.enable('silent'); +module.exports = function(app) { + app.enable('silent'); - verb.task('default', function(cb) { - verb.cli.process({ help: true }, cb); + app.task('default', function(cb) { + app.cli.process({ help: true }, cb); }); }; From 8d9d2abcb3f250dcb9d4270300cad5f42293dbec Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 3 Apr 2016 14:58:50 -0400 Subject: [PATCH 210/282] remove listener --- bin/verb.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index e135f27b..5d244a18 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -10,10 +10,6 @@ var verb = require('..'); var cli = verb.runner('verbfile.js', require('../lib/generator')); var app = verb(); -app.on('done', function() { - process.exit(0); -}); - /** * Run generators and tasks */ @@ -24,4 +20,5 @@ cli(app, function(err, argv, app) { process.exit(1); } app.emit('done'); + process.exit(); }); From 4b25a6026479edcf8b558c6b8849a1697f60e333 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 03:47:34 -0400 Subject: [PATCH 211/282] run update --- .editorconfig | 4 ++-- .eslintrc.json | 6 ++---- .travis.yml | 12 ++++++------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.editorconfig b/.editorconfig index 991900b7..408d8707 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,10 +13,10 @@ insert_final_newline = true trim_trailing_whitespace = false insert_final_newline = false -[test/**] +[**/{actual,fixtures,expected}/**] trim_trailing_whitespace = false insert_final_newline = false -[templates/**] +[**/templates/**] trim_trailing_whitespace = false insert_final_newline = false diff --git a/.eslintrc.json b/.eslintrc.json index 3ab3bea5..948dbdb6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -34,6 +34,7 @@ "handle-callback-err": [2, "^(err|error)$" ], "indent": [2, 2, { "SwitchCase": 1 }], "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "keyword-spacing": [2, { "before": true, "after": true }], "new-cap": [2, { "newIsCap": true, "capIsNew": false }], "new-parens": 2, "no-array-constructor": 2, @@ -49,7 +50,6 @@ "no-dupe-keys": 2, "no-duplicate-case": 2, "no-empty-character-class": 2, - "no-labels": 2, "no-eval": 2, "no-ex-assign": 2, "no-extend-native": 2, @@ -71,7 +71,7 @@ "no-multi-spaces": 2, "no-multi-str": 2, "no-multiple-empty-lines": [2, { "max": 1 }], - "no-native-reassign": 2, + "no-native-reassign": 0, "no-negated-in-lhs": 2, "no-new": 2, "no-new-func": 2, @@ -108,12 +108,10 @@ "radix": 2, "semi": [2, "always"], "semi-spacing": [2, { "before": false, "after": true }], - "keyword-spacing": [2, { "before": true, "after": true }], "space-before-blocks": [2, "always"], "space-before-function-paren": [2, "never"], "space-in-parens": [2, "never"], "space-infix-ops": 2, - "space-return-throw-case": 2, "space-unary-ops": [2, { "words": true, "nonwords": false }], "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], "use-isnan": 2, diff --git a/.travis.yml b/.travis.yml index 09768f0d..cb1d261d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ sudo: false language: node_js node_js: - - "stable" - - "5" - - "4" - - "0.12" - - "0.10" + - '5' + - '4' + - '0.12' + - '0.10' matrix: fast_finish: true allow_failures: - - node_js: "0.10" + - node_js: '0.10' + - node_js: '0.12' From 97f0e5a80799fbb68a61550ddebdcfa6509e820e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 04:45:44 -0400 Subject: [PATCH 212/282] adds custom commands --- lib/commands.js | 14 +++++++ lib/commands/index.js | 1 + lib/commands/init.js | 92 +++++++++++++++++++++++++++++++++++++++++++ lib/commands/tasks.js | 90 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 lib/commands.js create mode 100644 lib/commands/index.js create mode 100644 lib/commands/init.js create mode 100644 lib/commands/tasks.js diff --git a/lib/commands.js b/lib/commands.js new file mode 100644 index 00000000..a800022a --- /dev/null +++ b/lib/commands.js @@ -0,0 +1,14 @@ +'use strict'; + +var commands = require('./commands/'); + +module.exports = function(app, options) { + app.debug('adding custom verb commands'); + + for (var key in commands) { + if (commands.hasOwnProperty(key)) { + app.debug('adding command > %s', key); + app.cli.map(key, commands[key](app, options)); + } + } +}; diff --git a/lib/commands/index.js b/lib/commands/index.js new file mode 100644 index 00000000..23b2930d --- /dev/null +++ b/lib/commands/index.js @@ -0,0 +1 @@ +module.exports = require('export-files')(__dirname); diff --git a/lib/commands/init.js b/lib/commands/init.js new file mode 100644 index 00000000..b575b24f --- /dev/null +++ b/lib/commands/init.js @@ -0,0 +1,92 @@ +'use strict'; + +var path = require('path'); +var questions = require('../questions'); +var debug = require('debug')('base:cli:init'); +var extend = require('extend-shallow'); +var get = require('get-value'); + +/** + * Ask initialization questions and persist answer data to the global + * config store. + * + * ```sh + * $ --init + * ``` + * @name init + * @api public + * @cli public + */ + +module.exports = function(app, base, options) { + return function(val, key, config, next) { + init(app, {save: false}, function(err, answers) { + if (err) return next(err); + app.cli.process(answers, next); + }); + }; +}; + +function init(app, options, cb) { + if (typeof app.questions === 'undefined') { + cb(new Error('expected base-questions plugin to be defined')); + return; + } + + if (typeof options === 'function') { + cb = options; + options = {}; + } + + options = extend({}, options, app.options); + + questions(app, options); + app.questions.disable('save'); + + app.ask('init.choose', function(err, answers) { + if (err) return cb(err); + + debug('finished with init.choose "%j"', answers); + + var plugins = arrayify(app, get(answers, 'config.plugins')); + if (plugins.length) { + app.ask('after', { save: false }, function(err, res) { + if (err) return cb(err); + + var answer = get(res, 'after.plugins'); + if (answer === true) { + app.pkg.save(); + app.npm.saveDev(plugins, function(err) { + if (err) return cb(err); + app.pkg.queued = true; + cb(null, answers); + }); + } else { + cb(null, answers); + } + }); + } else { + cb(null, answers); + } + }); +}; + +function arrayify(app, val) { + if (!val) return []; + if (typeof val === 'string') { + return val.split(','); + } + + var deps = app.pkg.get('devDependencies') || {}; + var len = val.length; + var idx = -1; + var res = []; + + while (++idx < len) { + var dep = val[idx]; + if (dep && !deps.hasOwnProperty(dep)) { + res.push(dep); + } + } + return res; +} diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js new file mode 100644 index 00000000..13f11afd --- /dev/null +++ b/lib/commands/tasks.js @@ -0,0 +1,90 @@ +'use strict'; + +var exists = require('fs-exists-sync'); +var fs = require('fs'); + +/** + * Run the given generators and tasks. This flag is unnecessary when + * used with [base-runner][]. + * + * ```sh + * # run task 'foo' + * $ app --tasks foo + * # => {task: ['foo']} + * # run generator 'foo', task 'bar' + * $ app --tasks foo:bar + * # => {task: ['foo:bar']} + * ``` + * @name tasks + * @api public + * @cli public + */ + +module.exports = function(app, options) { + var ran = false; + + return function(val, key, config, next) { + if (config.run === false) { + next(); + return; + } + + if (ran) { + next(); + return; + } + + ran = true; + + var argv = app.get('cache.argv'); + var configFile = options.env.configFile; + var tasks = setTasks(app, configFile, val, argv); + app.generateEach(tasks, next); + }; +}; + +/** + * Determine the task to run + */ + +function setTasks(app, configFile, tasks, argv) { + if (argv.processed.hasOwnProperty('new')) { + return ['verb.new:' + argv.processed.new]; + } + + tasks = tasks.map(function(task) { + if (task.indexOf('new:') === 0) { + return 'verb.' + task; + } + return task; + }); + + var configTasks = app.pkg.get('verb.tasks'); + + if (tasks.length === 1 && tasks[0] === 'default') { + // if a `verbfile.js` or custom configFile exists, return tasks + if (exists(configFile)) { + if (configTasks && configTasks.length) { + app.pkg.logWarning('ignoring tasks defined in package.json:', {tasks: configTasks}); + } + return tasks; + } + + if (configTasks && configTasks.length) { + return configTasks; + } + + var verbmd = exists('.verb.md'); + + // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default + if (verbmd && !exists(configFile)) { + return ['readme']; + } + + // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` + if (!verbmd) { + return ['new:verbmd']; + } + } + return tasks; +} From 12bc4dcece3df0d12722bf7c273df8976b68517d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 04:46:28 -0400 Subject: [PATCH 213/282] simply --- index.js | 54 +++++++++--------------------------------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/index.js b/index.js index c7d1ef0c..f13b2c45 100644 --- a/index.js +++ b/index.js @@ -7,10 +7,7 @@ 'use strict'; -var utils = require('generator-util'); -var define = require('define-property'); var Generate = require('generate'); -var pkg = require('./package'); /** * Create a verb application with `options`. @@ -27,19 +24,9 @@ function Verb(options) { if (!(this instanceof Verb)) { return new Verb(options); } - - this.options = utils.extend({}, this.options, options); - Generate.call(this, this.options); - delete this.cache.data.runner; - + Generate.call(this, options); this.is('verb'); - Generate.debug(this); - - Verb.emit('preInit', this, this.base); - this.debug('initializing'); - this.verbDefaults(options); this.initVerb(this.options); - Verb.emit('init', this, this.base); } /** @@ -48,44 +35,21 @@ function Verb(options) { Generate.extend(Verb); -/** - * Initialize verb defaults - */ - -Verb.prototype.verbDefaults = function(options) { - this.debug('initializing verb defaults'); - var defaults = { prefix: 'verb', configfile: 'verbfile.js' }; - this.options = utils.extend(defaults, this.options, options); -}; - /** * Initialize verb data */ Verb.prototype.initVerb = function(opts) { - this.debug('initializing verb data'); - - this.set('appname', 'verb'); - this.data({runner: {name: 'verb', url: 'https://github.com/verbose/verb'}}); - var aliasRegex = /^verb-([^-]+)-generator$/g; - - this.toFullname = function(name) { - return 'verb-' + this.toAlias(name) + '-generator'; - }; - - this.isGeneratorPath = function(fp) { - return aliasRegex.test(fp) || /^generate-/.test(fp); - }; - - this.toAlias = function(name) { - return name.replace(aliasRegex, '$1'); - }; + this.debug('initializing', __filename); - // if run via CLI, add defaults - if (process.env.GENERATE_CLI) { - this.use(utils.create(this.options)); - this.create('files'); + delete this.cache.data.runner; + if (this.options.namespace === true) { + delete this.options.namespace; } + + Verb.emit('preInit', this, this.base); + this.data({runner: require('./package')}); + Verb.emit('init', this, this.base); }; /** From 90129e815082e148236a88e0ac34cd44b23af554 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 04:46:41 -0400 Subject: [PATCH 214/282] use base-runner --- bin/verb.js | 100 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 12 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 5d244a18..a09fcbad 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,24 +1,100 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; -var verb = require('..'); + +var argv = require('minimist')(process.argv.slice(2)); +var runner = require('base-runner'); +var commands = require('../lib/commands'); +var Verb = require('..'); + +// lift-off options +var options = { + name: 'verb', + runner: require('../package'), + lookup: lookup +}; /** - * Create verb "runner" + * Initialize verb CLI */ -var cli = verb.runner('verbfile.js', require('../lib/generator')); -var app = verb(); +runner(Verb, options, argv, function(err, app, runnerContext) { + if (err) app.handleError(err); + + /** + * Register `verb` generator and tasks + */ + + app.register('verb', require('../lib/generator')); + + /** + * Load custom commands + */ + + commands(app, runnerContext); + + // get the config object from package.json + var config = app.pkg.get(runnerContext.env.name); + var args = app.argv(runnerContext.argv); + + // set parsed and unparsed argv on `cache` + app.set('cache.argv', { + orig: argv, + parsed: runnerContext.argv, + processed: args + }); + + if (config && !args.noconfig) { + app.set('cache.config', config); + } + + app.option('runner.new', 'files'); + app.option(argv); + + /** + * Custom `lookup` function, for resolving generators + */ + + app.option('lookup', lookup(app)); + + /** + * Process argv + */ + + if (args.init === true) { + app.cli.process({init: true}, function(err, obj) { + if (err) app.emit('error', err); + delete obj.init; + run(app, obj); + }); + } else { + run(app, args); + } +}); /** - * Run generators and tasks + * cli process */ -cli(app, function(err, argv, app) { - if (err) { - console.log(err); - process.exit(1); +function run(app, args) { + app.cli.process(args, function(err, obj) { + if (err) app.emit('error', err); + + app.emit('done'); + process.exit(); + }); +} + +/** + * Custom lookup function for resolving generators + */ + +function lookup(app) { + return function(key) { + var patterns = [`verb-${key}-generator`, key]; + if (app.enabled('generators')) { + patterns.push(`generate-${key}`); + } + return patterns; } - app.emit('done'); - process.exit(); -}); +} From 65e3a6c9c1fc1cd5009efa8a9737b775d5ef1d93 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 05:25:36 -0400 Subject: [PATCH 215/282] add default tasks --- lib/generator.js | 49 +++++++++++++++++++++++++++++++++------ lib/templates/.verb.md | 5 ++++ lib/templates/verbfile.js | 8 +++++++ 3 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 lib/templates/.verb.md create mode 100644 lib/templates/verbfile.js diff --git a/lib/generator.js b/lib/generator.js index 558c0beb..b72b5e55 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,14 +1,49 @@ 'use strict'; -/** - * Display `--help` when no tasks are defined - */ +var cwd = require('memoize-path')(__dirname); + +module.exports = function(app, base) { + var templates = cwd('templates'); + + /** + * Generate a file from a template + */ + + app.register('new', function(sub) { + sub.task('verbfile', function(cb) { + file(app, 'verbfile.js', cb); + }); + + sub.task('verbmd', function(cb) { + file(app, '.verb.md', cb); + }); + }); -module.exports = function(app) { - app.enable('silent'); + /** + * Display `--help` when no tasks are defined + */ - app.task('default', function(cb) { - app.cli.process({ help: true }, cb); + app.task('help', function(cb) { + base.cli.process({ help: true }, cb); }); + + /** + * Default task + */ + + app.task('default', ['help']); }; +/** + * Generate a file + */ + +function file(app, name, cb) { + app.src(name, {cwd: cwd('templates')()}) + .pipe(app.dest(app.cwd)) + .on('error', cb) + .on('end', function() { + console.log(name, 'written to', app.cwd); + cb(); + }); +} diff --git a/lib/templates/.verb.md b/lib/templates/.verb.md new file mode 100644 index 00000000..383b2629 --- /dev/null +++ b/lib/templates/.verb.md @@ -0,0 +1,5 @@ +## Usage + +```js +var {%= varname %} = require('{%= name %}'); +``` diff --git a/lib/templates/verbfile.js b/lib/templates/verbfile.js new file mode 100644 index 00000000..f8cf15a9 --- /dev/null +++ b/lib/templates/verbfile.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function(verb) { + verb.task('default', function(cb) { + + cb(); + }); +}; \ No newline at end of file From 7201e046668ae6d19b3c0bfe9359326a36edad26 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:01:39 -0400 Subject: [PATCH 216/282] update tests --- test/app.copy.js | 2 +- test/app.dest.js | 16 +- test/app.doc.js | 2 +- test/app.docs.js | 2 +- test/app.env.js | 12 +- test/app.extendWith.js | 49 ++- test/app.generate.js | 4 +- test/app.generateEach.js | 4 +- test/app.generator.js | 10 +- test/app.getGenerator.js | 2 +- test/app.hasGenerator.js | 43 -- test/app.include.js | 2 +- test/app.includes.js | 2 +- test/app.invoke.js | 398 ------------------ test/app.layout.js | 2 +- test/app.layouts.js | 2 +- test/app.list.render.js | 2 +- test/app.page.js | 3 +- test/app.pages.js | 2 +- test/app.partial.js | 2 +- test/app.partials.js | 2 +- test/app.questions.js | 24 -- test/app.register.js | 76 +--- test/app.renderFile.js | 2 +- test/app.resolve.js | 74 ---- test/app.src.js | 2 +- test/app.store.js | 6 +- test/app.symlink.js | 148 +++---- test/app.task.js | 2 +- test/app.tasks.js | 2 +- test/app.toStream.js | 2 +- test/cache.js | 37 -- test/collection.options.js | 2 +- test/collection.render.js | 2 +- test/env.js | 72 ---- .../two/{generate.js => generator.js} | 2 +- test/generate.js | 2 +- test/generators.cache.js | 223 ---------- test/generators.env.js | 87 ++-- test/generators.events.js | 41 +- test/integration/instance.js | 7 + test/list.render.js | 2 +- test/store.js | 6 +- test/view.content.js | 2 +- test/view.events.js | 2 +- test/view.methods.js | 2 +- test/view.option.js | 2 +- test/view.render.js | 2 +- test/view.set.js | 2 +- test/viewTypes.js | 2 +- 50 files changed, 210 insertions(+), 1188 deletions(-) delete mode 100644 test/app.hasGenerator.js delete mode 100644 test/app.invoke.js delete mode 100644 test/app.resolve.js delete mode 100644 test/cache.js delete mode 100644 test/env.js rename test/fixtures/two/{generate.js => generator.js} (91%) delete mode 100644 test/generators.cache.js create mode 100644 test/integration/instance.js diff --git a/test/app.copy.js b/test/app.copy.js index db1ee170..dcc96b0f 100644 --- a/test/app.copy.js +++ b/test/app.copy.js @@ -10,7 +10,7 @@ var app; var fixtures = path.join(__dirname, 'fixtures/copy/*.txt'); var actual = path.join(__dirname, 'actual'); -describe('copy()', function() { +describe('app.copy', function() { beforeEach(function(cb) { rimraf(actual, cb); app = new App(); diff --git a/test/app.dest.js b/test/app.dest.js index d2c58ef0..2fc93d2e 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -47,7 +47,7 @@ function masked(mode) { return mode & MASK_MODE; } -describe('dest stream', function() { +describe('app.dest', function() { beforeEach(wipeOut); afterEach(wipeOut); @@ -55,7 +55,7 @@ describe('dest stream', function() { var stream; try { stream = app.dest(); - }catch (err) { + } catch (err) { assert(err && typeof err === 'object'); should.not.exist(stream); cb(); @@ -66,7 +66,7 @@ describe('dest stream', function() { var stream; try { stream = app.dest(''); - }catch (err) { + } catch (err) { assert(err && typeof err === 'object'); should.not.exist(stream); cb(); @@ -469,9 +469,9 @@ describe('dest stream', function() { var inputPath = path.join(__dirname, 'fixtures/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); + var expectedBase = path.join(__dirname, 'actual'); var startMode = parseInt('0655', 8); var expectedMode = parseInt('0722', 8); @@ -683,8 +683,8 @@ describe('dest stream', function() { path: inputPath, contents: expectedContents, stat: { - mode: expectedMode, - }, + mode: expectedMode + } }); fs.mkdirSync(expectedBase); @@ -899,8 +899,8 @@ describe('dest stream', function() { describe('dest', function() { beforeEach(function(cb) { - rimraf(actual, cb); app = new App(); + rimraf(actual, cb); }); afterEach(function(cb) { diff --git a/test/app.doc.js b/test/app.doc.js index abf08797..08da88fd 100644 --- a/test/app.doc.js +++ b/test/app.doc.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app; -describe('app', function() { +describe('app.doc', function() { beforeEach(function() { app = generate(); app.create('docs'); diff --git a/test/app.docs.js b/test/app.docs.js index 4f7a96cc..b5bb5172 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app; -describe('app', function() { +describe('app.docs', function() { beforeEach(function() { app = generate(); app.create('docs'); diff --git a/test/app.env.js b/test/app.env.js index 23682a1f..5146b73d 100644 --- a/test/app.env.js +++ b/test/app.env.js @@ -8,7 +8,7 @@ var generate; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('env', function() { +describe('app.env', function() { describe('createEnv', function() { beforeEach(function() { generate = new Generate(); @@ -58,15 +58,5 @@ describe('env', function() { assert.equal(generate.env.alias, 'foo'); assert.equal(generate.env.name, 'generate-foo'); }); - - it('should throw an error when the path is not resolved', function(cb) { - try { - generate.createEnv('foo', fixtures('whatever.js')); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); - cb(); - } - }); }); }); diff --git a/test/app.extendWith.js b/test/app.extendWith.js index 6fa02669..e1a21527 100644 --- a/test/app.extendWith.js +++ b/test/app.extendWith.js @@ -1,4 +1,3 @@ - 'use strict'; require('mocha'); @@ -19,7 +18,7 @@ function install(name, cb) { }, cb); } -describe('.extendWith', function() { +describe('app.extendWith', function() { before(function(cb) { if (!utils.exists(path.resolve(gm, 'generate-bar'))) { install('generate-bar', cb); @@ -30,12 +29,30 @@ describe('.extendWith', function() { beforeEach(function() { generate = new Generate(); + generate.option('alias', function(name) { + return name.replace(/^generate-/, ''); + }); + }); + + it('should throw an error when a generator is not found', function(cb) { + generate.register('foo', function(app) { + app.extendWith('fofoofofofofof'); + }); + + try { + generate.getGenerator('foo'); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'cannot find generator fofoofofofofof'); + cb(); + } }); it('should get a named generator', function(cb) { + var count = 0; generate.register('foo', function(app) { app.extendWith('bar'); - cb(); + count++; }); generate.register('bar', function(app) { @@ -45,6 +62,8 @@ describe('.extendWith', function() { }); generate.getGenerator('foo'); + assert.equal(count, 1); + cb(); }); it('should extend a generator with a named generator', function(cb) { @@ -98,7 +117,7 @@ describe('.extendWith', function() { }); describe('invoke generators', function(cb) { - it('should invoke an instance', function(cb) { + it('should extend with a generator instance', function(cb) { generate.register('foo', function(app) { var bar = app.getGenerator('bar'); app.extendWith(bar); @@ -110,6 +129,7 @@ describe('.extendWith', function() { }); generate.register('bar', function(app) { + app.isBar = true; app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); @@ -180,6 +200,7 @@ describe('.extendWith', function() { app.task('c', function() {}); }); + var qux = generate.getGenerator('qux'); generate.getGenerator('foo'); }); @@ -215,22 +236,6 @@ describe('.extendWith', function() { generate.getGenerator('abc'); }); - it('should extend with a generator invoked from node_modules by alias', function(cb) { - generate.register('abc', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.extendWith('foo'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.getGenerator('abc'); - }); - it('should extend with a generator invoked from global modules by name', function(cb) { generate.register('zzz', function(app) { assert(!app.tasks.a); @@ -248,6 +253,8 @@ describe('.extendWith', function() { }); it('should extend with a generator invoked from global modules by alias', function(cb) { + generate.register('generate-bar'); + generate.register('zzz', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); @@ -326,6 +333,8 @@ describe('.extendWith', function() { }); it('should invoke a sub-generator from node_modules by alias', function(cb) { + generate.register('generate-foo'); + generate.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); diff --git a/test/app.generate.js b/test/app.generate.js index 793e06dd..843ad25b 100644 --- a/test/app.generate.js +++ b/test/app.generate.js @@ -5,7 +5,7 @@ var assert = require('assert'); var Generate = require('..'); var generate; -describe('.generate', function() { +describe('app.generate', function() { beforeEach(function() { generate = new Generate(); }); @@ -24,7 +24,7 @@ describe('.generate', function() { generate.option('cwd', 'foo/bar/baz'); generate.generate('sflsjljskksl', function(err) { assert(err); - assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); + assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz"', err.message); cb(); }); }); diff --git a/test/app.generateEach.js b/test/app.generateEach.js index 9a37a362..74542f3c 100644 --- a/test/app.generateEach.js +++ b/test/app.generateEach.js @@ -5,7 +5,7 @@ var assert = require('assert'); var Generate = require('..'); var generate; -describe('.generateEach', function() { +describe('app.generateEach', function() { beforeEach(function() { generate = new Generate(); }); @@ -24,7 +24,7 @@ describe('.generateEach', function() { generate.option('cwd', 'foo/bar/baz'); generate.generateEach('sflsjljskksl', function(err) { assert(err); - assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz/verbfile.js"', err.message); + assert.equal(err.message, 'Cannot find generator: "sflsjljskksl" in "foo/bar/baz"'); cb(); }); }); diff --git a/test/app.generator.js b/test/app.generator.js index 83da725b..5d109fd5 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -8,7 +8,7 @@ var generate; var fixtures = path.resolve.bind(path, __dirname, 'fixtures'); -describe('.generator', function() { +describe('app.generator', function() { beforeEach(function() { generate = new Generate(); }); @@ -189,16 +189,16 @@ describe('.generator', function() { assert.deepEqual(generate.generators.one, one); }); - it('should register a Generate instance from a file path', function() { - var two = generate.generator('two', fixtures('two/generate.js')); + it('should register an instance from a file path', function() { + var two = generate.generator('two', fixtures('two/generator.js')); assert(generate.generators.hasOwnProperty('two')); assert(typeof generate.generators.two === 'object'); assert.deepEqual(generate.generators.two, two); }); it('should get a registered generator by name', function() { - var one = generate.generator('one', fixtures('one/verbfile.js')); - var two = generate.generator('two', fixtures('two/generate.js')); + var one = generate.generator('one', fixtures('one/generator.js')); + var two = generate.generator('two', fixtures('two/generator.js')); assert.deepEqual(generate.generator('one'), one); assert.deepEqual(generate.generator('two'), two); }); diff --git a/test/app.getGenerator.js b/test/app.getGenerator.js index b6c41d3c..4a013c5b 100644 --- a/test/app.getGenerator.js +++ b/test/app.getGenerator.js @@ -7,7 +7,7 @@ var generate var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('.generator', function() { +describe('app.getGenerator', function() { beforeEach(function() { generate = new Generate(); }); diff --git a/test/app.hasGenerator.js b/test/app.hasGenerator.js deleted file mode 100644 index bf8dd745..00000000 --- a/test/app.hasGenerator.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var Generate = require('..'); -var generate; - -describe('.hasGenerator', function() { - beforeEach(function() { - generate = new Generate(); - }); - - it('should return true if a generator is registered', function() { - generate.register('foo', function(app) { - }); - assert(generate.hasGenerator('foo')); - }); - - it('should return false if a generator is not registered', function() { - generate.register('foo', function(app) { - app.register('bar', function() {}); - }); - - assert(!generate.hasGenerator('bar')); - }); - - it('should return false if a sub-generator is registered', function() { - generate.register('foo', function(app) { - app.register('bar', function() {}); - }); - - assert(generate.hasGenerator('foo.bar')); - }); - - it('should return false if a sub-generator is not registered', function() { - generate.register('foo', function(app) { - app.register('bar', function() {}); - }); - - assert(!generate.hasGenerator('foo.baz')); - assert(!generate.hasGenerator('foo.bar.baz')); - }); -}); diff --git a/test/app.include.js b/test/app.include.js index cdf58bbf..8ae616b4 100644 --- a/test/app.include.js +++ b/test/app.include.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app, len; -describe('app', function() { +describe('app.include', function() { beforeEach(function() { app = generate(); if (typeof app.include === 'undefined') { diff --git a/test/app.includes.js b/test/app.includes.js index 390660d6..758d09ff 100644 --- a/test/app.includes.js +++ b/test/app.includes.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app, len; -describe('app', function() { +describe('app.includes', function() { beforeEach(function() { app = generate(); if (typeof app.include === 'undefined') { diff --git a/test/app.invoke.js b/test/app.invoke.js deleted file mode 100644 index 19df0771..00000000 --- a/test/app.invoke.js +++ /dev/null @@ -1,398 +0,0 @@ -'use strict'; - -require('mocha'); -var path = require('path'); -var assert = require('assert'); -var gm = require('global-modules'); -var commands = require('spawn-commands'); -var utils = require('generator-util'); -require('generate-foo/verbfile.js'); -var Generate = require('..'); -var generate; - -var fixture = path.resolve.bind(path, __dirname, 'fixtures/generators'); -function install(name, cb) { - commands({ - args: ['install', '-g', '--silent', name], - cmd: 'npm' - }, cb); -} - -describe('.invoke', function() { - before(function(cb) { - if (!utils.exists(path.resolve(gm, 'generate-bar'))) { - install('generate-bar', cb); - } else { - cb(); - } - }); - - beforeEach(function() { - generate = new Generate(); - }); - - describe('invoke generators', function(cb) { - it('should invoke an instance', function(cb) { - generate.register('foo', function(app) { - var bar = app.getGenerator('bar'); - app.invoke(bar); - - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - assert(app.tasks.hasOwnProperty('c')); - cb(); - }); - - generate.register('bar', function(app) { - app.task('a', function() {}); - app.task('b', function() {}); - app.task('c', function() {}); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke a named generator', function(cb) { - generate.register('foo', function(app) { - app.invoke('bar'); - - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - assert(app.tasks.hasOwnProperty('c')); - cb(); - }); - - generate.register('bar', function(app) { - app.task('a', function() {}); - app.task('b', function() {}); - app.task('c', function() {}); - }); - - generate.getGenerator('foo'); - }); - }); - - describe('extend generators', function(cb) { - it('should extend a generator with a generator invoked by name', function(cb) { - generate.register('foo', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('bar'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.register('bar', function(app) { - app.task('a', function() {}); - app.task('b', function() {}); - app.task('c', function() {}); - }); - - generate.getGenerator('foo'); - }); - - it('should extend a generator with a generator invoked by alias', function(cb) { - generate.register('foo', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('qux'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.register('generate-qux', function(app) { - app.task('a', function() {}); - app.task('b', function() {}); - app.task('c', function() {}); - }); - - generate.getGenerator('foo'); - }); - - it('should extend with a generator invoked by filepath', function(cb) { - generate.register('foo', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke(fixture('qux')); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.getGenerator('foo'); - }); - - it('should extend with a generator invoked from node_modules by name', function(cb) { - generate.register('abc', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('generate-foo'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.getGenerator('abc'); - }); - - it('should extend with a generator invoked from node_modules by alias', function(cb) { - generate.register('abc', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('foo'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.getGenerator('abc'); - }); - - it('should extend with a generator invoked from global modules by name', function(cb) { - generate.register('zzz', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - app.invoke('generate-bar'); - - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.getGenerator('zzz'); - }); - - it('should extend with a generator invoked from global modules by alias', function(cb) { - generate.register('zzz', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('bar'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.getGenerator('zzz'); - }); - }); - - describe('sub-generators', function(cb) { - it('should invoke sub-generators', function(cb) { - generate.register('foo', function(app) { - app.register('one', function(app) { - app.task('a', function() {}); - }); - app.register('two', function(app) { - app.task('b', function() {}); - }); - - app.invoke('one'); - app.invoke('two'); - - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - cb(); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke a sub-generator on the base instance', function(cb) { - generate.register('foo', function(app) { - app.invoke('bar.sub'); - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - assert(app.tasks.hasOwnProperty('c')); - cb(); - }); - - generate.register('bar', function(app) { - app.register('sub', function(sub) { - sub.task('a', function() {}); - sub.task('b', function() {}); - sub.task('c', function() {}); - }); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke a sub-generator from node_modules by name', function(cb) { - generate.register('abc', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('xyz'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.register('xyz', function(app) { - app.invoke('generate-foo'); - }); - - generate.getGenerator('abc'); - }); - - it('should invoke a sub-generator from node_modules by alias', function(cb) { - generate.register('abc', function(app) { - assert(!app.tasks.a); - assert(!app.tasks.b); - assert(!app.tasks.c); - - app.invoke('xyz'); - assert(app.tasks.a); - assert(app.tasks.b); - assert(app.tasks.c); - cb(); - }); - - generate.register('xyz', function(app) { - app.invoke('foo'); - }); - - generate.getGenerator('abc'); - }); - - it('should invoke an array of sub-generators', function(cb) { - generate.register('foo', function(app) { - app.register('one', function(app) { - app.task('a', function() {}); - }); - app.register('two', function(app) { - app.task('b', function() {}); - }); - - app.invoke(['one', 'two']); - - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - cb(); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke sub-generators from sub-generators', function(cb) { - generate.register('foo', function(app) { - app.register('one', function(sub) { - sub.register('a', function(a) { - a.task('a', function() {}); - }); - }); - - app.register('two', function(sub) { - sub.register('a', function(a) { - a.task('b', function() {}); - }); - }); - - app.invoke('one.a'); - app.invoke('two.a'); - - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - cb(); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke an array of sub-generators from sub-generators', function(cb) { - generate.register('foo', function(app) { - app.register('one', function(sub) { - sub.register('a', function(a) { - a.task('a', function() {}); - }); - }); - - app.register('two', function(sub) { - sub.register('a', function(a) { - a.task('b', function() {}); - }); - }); - - app.invoke(['one.a', 'two.a']); - - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - cb(); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke sub-generator that invokes another generator', function(cb) { - generate.register('foo', function(app) { - app.invoke('bar'); - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - assert(app.tasks.hasOwnProperty('c')); - cb(); - }); - - generate.register('bar', function(app) { - app.invoke('baz'); - }); - - generate.register('baz', function(app) { - app.task('a', function() {}); - app.task('b', function() {}); - app.task('c', function() {}); - }); - - generate.getGenerator('foo'); - }); - - it('should invoke sub-generator that invokes another sub-generator', function(cb) { - generate.register('foo', function(app) { - app.invoke('bar.sub'); - assert(app.tasks.hasOwnProperty('a')); - assert(app.tasks.hasOwnProperty('b')); - assert(app.tasks.hasOwnProperty('c')); - cb(); - }); - - generate.register('bar', function(app) { - app.register('sub', function(sub) { - sub.invoke('baz.sub'); - }); - }); - - generate.register('baz', function(app) { - app.register('sub', function(sub) { - sub.task('a', function() {}); - sub.task('b', function() {}); - sub.task('c', function() {}); - }); - }); - - generate.getGenerator('foo'); - }); - }); -}); diff --git a/test/app.layout.js b/test/app.layout.js index 384708e5..b07cabc2 100644 --- a/test/app.layout.js +++ b/test/app.layout.js @@ -6,7 +6,7 @@ var assert = require('assert'); var assemble = require('..'); var app; -describe('.layout()', function() { +describe('app.layout', function() { beforeEach(function() { app = assemble(); if (!app.layout) { diff --git a/test/app.layouts.js b/test/app.layouts.js index ff6ad3d2..37e3c75e 100644 --- a/test/app.layouts.js +++ b/test/app.layouts.js @@ -6,7 +6,7 @@ var assert = require('assert'); var assemble = require('..'); var app; -describe('.layouts()', function() { +describe('app.layouts', function() { beforeEach(function() { app = assemble(); if (!app.layout) { diff --git a/test/app.list.render.js b/test/app.list.render.js index a0f19207..f36d530c 100644 --- a/test/app.list.render.js +++ b/test/app.list.render.js @@ -9,7 +9,7 @@ var App = support.resolve(); var List = App.List; var pages, app; -describe('render', function() { +describe('app.list.render', function() { describe('rendering', function() { beforeEach(function() { app = App(); diff --git a/test/app.page.js b/test/app.page.js index a73b277e..fe1bb715 100644 --- a/test/app.page.js +++ b/test/app.page.js @@ -6,7 +6,7 @@ var assert = require('assert'); var assemble = require('..'); var app; -describe('.page()', function() { +describe('app.page', function() { beforeEach(function() { app = assemble(); if (!app.pages) { @@ -40,7 +40,6 @@ describe('.page()', function() { describe('load', function() { it('should load a page from a non-glob filepath', function() { app.page('test/fixtures/pages/a.hbs'); - console.log(app.views.pages); assert(Object.keys(app.views.pages).length === 1); }); }); diff --git a/test/app.pages.js b/test/app.pages.js index b8800e7f..6fd855f0 100644 --- a/test/app.pages.js +++ b/test/app.pages.js @@ -6,7 +6,7 @@ var assert = require('assert'); var assemble = require('..'); var app; -describe('.pages()', function() { +describe('app.pages', function() { beforeEach(function() { app = assemble(); if (!app.pages) { diff --git a/test/app.partial.js b/test/app.partial.js index d9889e84..27789a5e 100644 --- a/test/app.partial.js +++ b/test/app.partial.js @@ -6,7 +6,7 @@ var assert = require('assert'); var assemble = require('..'); var app; -describe('.partial()', function() { +describe('app.partial', function() { beforeEach(function() { app = assemble(); if (!app.partials) { diff --git a/test/app.partials.js b/test/app.partials.js index 15909369..a92886df 100644 --- a/test/app.partials.js +++ b/test/app.partials.js @@ -6,7 +6,7 @@ var assert = require('assert'); var assemble = require('..'); var app; -describe('.partials()', function() { +describe('app.partials', function() { beforeEach(function() { app = assemble(); if (!app.partials) { diff --git a/test/app.questions.js b/test/app.questions.js index 609ff78f..e5ea10e8 100644 --- a/test/app.questions.js +++ b/test/app.questions.js @@ -53,14 +53,6 @@ describe('app.questions', function() { app.cache.data = {}; }); - it.skip('should force all questions to be asked', function(cb) { - app.questions.option('init', 'author'); - app.ask({force: true}, function(err, answers) { - console.log(answers) - cb(); - }); - }); - it('should store a question:', function() { app.question('a', 'b'); assert(app.questions); @@ -70,22 +62,6 @@ describe('app.questions', function() { assert.equal(app.questions.cache.a.message, 'b'); }); - it.skip('should re-init a specific question:', function(cb) { - this.timeout(20000); - app.question('a', 'b'); - app.question('c', 'd'); - app.question('e', 'f'); - app.data({a: 'b'}); - - app.questions.get('e') - .force() - - app.ask(function(err, answers) { - console.log(answers); - cb(); - }); - }); - it('should ask a question defined on `ask`', function(cb) { app.data('name', 'Brian Woodward'); diff --git a/test/app.register.js b/test/app.register.js index 9c0706c4..c767c2d0 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -8,7 +8,7 @@ var base; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('.register plugin', function() { +describe('app.register', function() { it('should register as a plugin', function() { var base = new Generate(); assert(base.registered.hasOwnProperty('base-generators')); @@ -20,56 +20,8 @@ describe('.register', function() { base = new Generate(); }); - describe('properties', function() { - it('should expose a configfile getter/setter', function() { - assert.equal(typeof base.configfile, 'string'); - }); - - it('should set configfile to verbfile.js by default', function() { - assert.equal(base.configfile, 'verbfile.js'); - }); - - it('should set configfile', function() { - base.configfile = 'foo.js'; - assert.equal(base.configfile, 'foo.js'); - }); - }); - - describe('configname', function() { - it('should expose a configname getter/setter', function() { - assert.equal(typeof base.configname, 'string'); - }); - - it('should set configname to generator by default', function() { - assert.equal(base.configname, 'verbfile'); - }); - - it('should set configname', function() { - base.configname = 'foo'; - assert.equal(base.configname, 'foo'); - }); - }); - - describe('configpath', function() { - it('should expose a configpath getter/setter', function() { - assert.equal(typeof base.configpath, 'string'); - }); - - it('should use configfile as basename of configpath', function() { - base.cwd = __dirname; - base.configfile = 'whatever.js'; - assert.equal(path.basename(base.configpath), 'whatever.js'); - }); - - it('should resolve configpath from base.cwd and base.configfile', function() { - base.cwd = __dirname; - base.configfile = 'whatever.js'; - assert.equal(base.configpath, path.resolve(__dirname, base.configfile)); - }); - }); - describe('function', function() { - it('should get a generator registered as a function', function() { + it('should register a generator function', function() { base.register('foo', function() {}); var foo = base.getGenerator('foo'); assert(foo); @@ -146,7 +98,7 @@ describe('.register', function() { assert(qux.tasks.hasOwnProperty('qux-one')); }); - it('should fail when the wrong generator name is given', function() { + it('should fail when a generator that does not exist is defined', function() { base.register('foo', function(foo) { foo.register('bar', function(bar) { bar.register('baz', function(baz) { @@ -182,14 +134,16 @@ describe('.register', function() { }); }); - describe('alias', function() { + describe('.toAlias', function() { it('should use a custom function to create the alias', function() { - base.option('alias', function(name) { + base.option('toAlias', function(name) { return name.slice(name.lastIndexOf('-') + 1); }); - base.register('generate-abc-xyz', function() {}); - assert(base.generators.hasOwnProperty('generate-abc-xyz')); + base.register('base-abc-xyz', function() {}); + var xyz = base.getGenerator('xyz'); + assert(xyz); + assert.equal(xyz.env.alias, 'xyz'); }); }); @@ -200,8 +154,8 @@ describe('.register', function() { }); it('should register a generator function by alias', function() { - base.register('generate-abc', function() {}); - assert(base.generators.hasOwnProperty('generate-abc')); + base.register('abc', function() {}); + assert(base.generators.hasOwnProperty('abc')); }); it('should register a generator by dirname', function() { @@ -219,7 +173,8 @@ describe('.register', function() { base.register('not-exposed', require(fixtures('not-exposed.js'))); cb(new Error('expected an error')); } catch (err) { - assert.equal(err.message, 'generator instances must be exposed with module.exports'); + var fp = path.resolve(__dirname, '../node_modules/not-exposed'); + assert.equal(err.message, 'Cannot find module \'' + fp + '\''); cb(); } }); @@ -227,8 +182,8 @@ describe('.register', function() { describe('instance', function() { it('should register an instance', function() { - base.register('generate-inst', new Generate()); - assert(base.generators.hasOwnProperty('generate-inst')); + base.register('base-inst', new Generate()); + assert(base.generators.hasOwnProperty('base-inst')); }); it('should get a generator that was registered as an instance', function() { @@ -255,7 +210,6 @@ describe('.register', function() { foo.task('default', function() {}); base.register('foo', foo); var generator = base.getGenerator('foo'); - assert(generator.tasks); assert(generator.tasks.hasOwnProperty('default')); }); diff --git a/test/app.renderFile.js b/test/app.renderFile.js index fff740d8..2fbe52d1 100644 --- a/test/app.renderFile.js +++ b/test/app.renderFile.js @@ -6,7 +6,7 @@ var should = require('should'); var path = require('path'); var app; -describe('app.renderFile()', function() { +describe('app.renderFile', function() { beforeEach(function() { app = assemble(); app.engine('hbs', require('engine-handlebars')); diff --git a/test/app.resolve.js b/test/app.resolve.js deleted file mode 100644 index 4fa54fe3..00000000 --- a/test/app.resolve.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -require('mocha'); -var path = require('path'); -var assert = require('assert'); -var gm = require('global-modules'); -var Generate = require('..'); -var generate; - -var fixtures = path.resolve.bind(path, __dirname, 'fixtures/generators'); - -describe('.resolve', function() { - beforeEach(function() { - generate = new Generate(); - }); - - describe('method', function() { - it('should expose a `resolve` method', function() { - assert.equal(typeof generate.resolve, 'function'); - }); - }); - - describe('local', function() { - it('should resolve a local generator path', function() { - var fp = generate.resolve(fixtures('a')); - assert.equal(typeof fp, 'string'); - }); - - it('should resolve a generator path from a cwd', function() { - assert(generate.resolve('a', {cwd: fixtures()})); - }); - - it('should resolve a generator path from a generator name', function() { - assert(generate.resolve('a', {cwd: fixtures()})); - }); - - it('should resolve the path to a local config file', function() { - var fp = generate.resolve('a', {cwd: fixtures()}); - assert.equal(typeof fp, 'string'); - }); - }); - - describe('global', function() { - it('should resolve a global generator path', function() { - var fp = generate.resolve('bar', gm); - assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); - }); - - it('should resolve a global generator path without a cwd', function() { - var fp = generate.resolve('bar'); - assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); - }); - - it('should resolve a global generator by full name', function() { - var fp = generate.resolve('generate-bar'); - assert.equal(fp, path.resolve(gm, 'generate-bar/index.js')); - }); - - it('should return undefined when a generator is not found', function() { - var fp = generate.resolve('foo-bar'); - assert.equal(typeof fp, 'undefined'); - }); - - it('should throw an error when a generator is not found at the given cwd', function(cb) { - try { - generate.resolve('foofof', {cwd: fixtures()}); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.code, 'ENOENT'); - cb(); - } - }); - }); -}); diff --git a/test/app.src.js b/test/app.src.js index 969a41f0..5f48f1d9 100644 --- a/test/app.src.js +++ b/test/app.src.js @@ -6,7 +6,7 @@ var should = require('should'); var join = require('path').join; var app; -describe('src()', function() { +describe('app.src', function() { beforeEach(function() { app = new App(); }); diff --git a/test/app.store.js b/test/app.store.js index f2382e36..5ec07e9f 100644 --- a/test/app.store.js +++ b/test/app.store.js @@ -10,7 +10,7 @@ var support = require('./support'); var generate = support.resolve(); var app; -describe('store', function() { +describe('app.store', function() { describe('methods', function() { beforeEach(function() { app = generate({cli: true}); @@ -152,7 +152,7 @@ describe('store', function() { }); }); -describe('create', function() { +describe('app.store.create', function() { beforeEach(function() { app = generate({cli: true}); app.use(store()); @@ -203,7 +203,7 @@ describe('create', function() { app.store.create('foo'); var dir = path.dirname(app.store.path); - assert.equal(app.store.foo.path, path.join(dir, 'generate/foo.json')); + assert.equal(app.store.foo.path, path.join(dir, 'verb/foo.json')); app.store.foo.set('a', 'b'); app.store.foo.del({force: true}); }); diff --git a/test/app.symlink.js b/test/app.symlink.js index 4bc3ddd3..9ae029fd 100644 --- a/test/app.symlink.js +++ b/test/app.symlink.js @@ -1,3 +1,5 @@ +'use strict'; + require('mocha'); var should = require('should'); var fs = require('graceful-fs'); @@ -13,7 +15,7 @@ var statSpy = spies.statSpy; var app, bufferStream; var wipeOut = function(cb) { - rimraf(path.join(__dirname, './actual/'), cb); + rimraf(path.join(__dirname, 'actual/'), cb); spies.setError('false'); statSpy.reset(); chmodSpy.reset(); @@ -28,15 +30,15 @@ var dataWrap = function(fn) { }; var realMode = function(n) { - return n & 07777; + return parseInt('777', 8); }; -describe('symlink stream', function() { +describe('app.symlink', function() { beforeEach(wipeOut); afterEach(wipeOut); it('should pass through writes with cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); var expectedFile = new File({ base: __dirname, @@ -45,13 +47,13 @@ describe('symlink stream', function() { contents: null }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); cb(); }; - var stream = app.symlink('./actual/', {cwd: __dirname}); + var stream = app.symlink('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -61,7 +63,7 @@ describe('symlink stream', function() { }); it('should pass through writes with default cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); var expectedFile = new File({ base: __dirname, @@ -70,13 +72,13 @@ describe('symlink stream', function() { contents: null }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); cb(); }; - var stream = app.symlink(path.join(__dirname, './actual/')); + var stream = app.symlink(path.join(__dirname, 'actual/')); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -86,10 +88,10 @@ describe('symlink stream', function() { }); it('should make link to the right folder with relative cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedBase = path.join(__dirname, './actual'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var expectedBase = path.join(__dirname, 'actual'); var expectedContents = fs.readFileSync(inputPath); var expectedFile = new File({ @@ -99,7 +101,7 @@ describe('symlink stream', function() { contents: expectedContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); @@ -111,7 +113,7 @@ describe('symlink stream', function() { cb(); }; - var stream = app.symlink('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); + var stream = app.symlink('actual/', {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -121,10 +123,10 @@ describe('symlink stream', function() { }); it('should write buffer files to the right folder with function and relative cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedBase = path.join(__dirname, './actual'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var expectedBase = path.join(__dirname, 'actual'); var expectedContents = fs.readFileSync(inputPath); var expectedFile = new File({ @@ -134,7 +136,7 @@ describe('symlink stream', function() { contents: expectedContents }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); @@ -146,10 +148,10 @@ describe('symlink stream', function() { cb(); }; - var stream = app.symlink(function(file){ + var stream = app.symlink(function(file) { should.exist(file); file.should.equal(expectedFile); - return './actual'; + return 'actual'; }, {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; @@ -160,12 +162,12 @@ describe('symlink stream', function() { }); it('should write buffer files to the right folder', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = 0655; + var expectedBase = path.join(__dirname, 'actual'); + var expectedMode = parseInt('0655', 8); var expectedFile = new File({ base: inputBase, @@ -177,7 +179,7 @@ describe('symlink stream', function() { } }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); @@ -189,7 +191,7 @@ describe('symlink stream', function() { cb(); }; - var stream = app.symlink('./actual/', {cwd: __dirname}); + var stream = app.symlink('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -199,12 +201,12 @@ describe('symlink stream', function() { }); it('should write streaming files to the right folder', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); + var expectedPath = path.join(__dirname, 'actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = 0655; + var expectedBase = path.join(__dirname, 'actual'); + var expectedMode = parseInt('0655', 8); var contentStream = through.obj(); var expectedFile = new File({ @@ -217,7 +219,7 @@ describe('symlink stream', function() { } }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); @@ -229,13 +231,13 @@ describe('symlink stream', function() { cb(); }; - var stream = app.symlink('./actual/', {cwd: __dirname}); + var stream = app.symlink('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); stream.pipe(bufferStream); stream.write(expectedFile); - setTimeout(function(){ + setTimeout(function() { contentStream.write(expectedContents); contentStream.end(); }, 100); @@ -243,11 +245,11 @@ describe('symlink stream', function() { }); it('should write directories to the right folder', function(cb) { - var inputPath = path.join(__dirname, './fixtures/wow'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/wow'); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = 0655; + var inputPath = path.join(__dirname, 'fixtures/wow'); + var inputBase = path.join(__dirname, 'fixtures/'); + var expectedPath = path.join(__dirname, 'actual/wow'); + var expectedBase = path.join(__dirname, 'actual'); + var expectedMode = parseInt('0655', 8); var expectedFile = new File({ base: inputBase, @@ -255,14 +257,14 @@ describe('symlink stream', function() { path: inputPath, contents: null, stat: { - isDirectory: function(){ + isDirectory: function() { return true; }, mode: expectedMode } }); - var onEnd = function(){ + var onEnd = function() { buffered.length.should.equal(1); buffered[0].should.equal(expectedFile); buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); @@ -274,7 +276,7 @@ describe('symlink stream', function() { cb(); }; - var stream = app.symlink('./actual/', {cwd: __dirname}); + var stream = app.symlink('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -283,43 +285,9 @@ describe('symlink stream', function() { stream.end(); }); - it('should use different modes for files and directories', function(cb) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - var expectedBase = path.join(__dirname, './actual/wow'); - var expectedDirMode = 0755; - var expectedFileMode = 0655; - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function(){ - realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); - realMode(buffered[0].stat.mode).should.equal(expectedFileMode); - cb(); - }; - - var stream = app.symlink('./actual/', { - cwd: __dirname, - mode: expectedFileMode, - dirMode: expectedDirMode - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - it('should change to the specified base', function(cb) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + var inputBase = path.join(__dirname, 'fixtures'); + var inputPath = path.join(__dirname, 'fixtures/wow/suchempty'); var firstFile = new File({ base: inputBase, @@ -328,12 +296,12 @@ describe('symlink stream', function() { stat: fs.statSync(inputPath) }); - var onEnd = function(){ + var onEnd = function() { buffered[0].base.should.equal(inputBase); cb(); }; - var stream = app.symlink('./actual/', { + var stream = app.symlink('actual/', { cwd: __dirname, base: inputBase }); @@ -347,11 +315,11 @@ describe('symlink stream', function() { }); it('should report IO errors', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); + var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputBase = path.join(__dirname, 'fixtures/'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = 0722; + var expectedBase = path.join(__dirname, 'actual'); + var expectedMode = parseInt('722', 8); var expectedFile = new File({ base: inputBase, @@ -366,7 +334,7 @@ describe('symlink stream', function() { fs.mkdirSync(expectedBase); fs.chmodSync(expectedBase, 0); - var stream = app.symlink('./actual/', {cwd: __dirname}); + var stream = app.symlink('actual/', {cwd: __dirname}); stream.on('error', function(err) { err.code.should.equal('EACCES'); cb(); @@ -376,8 +344,8 @@ describe('symlink stream', function() { ['end', 'finish'].forEach(function(eventName) { it('should emit ' + eventName + ' event', function(cb) { - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var stream = app.symlink('./actual/', {cwd: __dirname}); + var srcPath = path.join(__dirname, 'fixtures/test.coffee'); + var stream = app.symlink('actual/', {cwd: __dirname}); stream.on(eventName, function() { cb(); diff --git a/test/app.task.js b/test/app.task.js index a34956b0..0ff1fde4 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -5,7 +5,7 @@ var assert = require('assert'); var Generate = require('..'); var generate; -describe('.generate', function() { +describe('app.task', function() { beforeEach(function() { generate = new Generate(); }); diff --git a/test/app.tasks.js b/test/app.tasks.js index 1da39843..8955d2c4 100644 --- a/test/app.tasks.js +++ b/test/app.tasks.js @@ -5,7 +5,7 @@ var assert = require('assert'); var Generate = require('..'); var generate; -describe('.tasks plugin', function() { +describe('app.tasks', function() { it('should register as a plugin', function() { var generate = new Generate(); assert(generate.registered.hasOwnProperty('base-generators-tasks')); diff --git a/test/app.toStream.js b/test/app.toStream.js index fddfb93c..905814f9 100644 --- a/test/app.toStream.js +++ b/test/app.toStream.js @@ -5,7 +5,7 @@ var assert = require('assert'); var should = require('should'); var app; -describe('toStream()', function() { +describe('app.toStream', function() { beforeEach(function() { app = assemble(); app.create('pages'); diff --git a/test/cache.js b/test/cache.js deleted file mode 100644 index 9df96c5e..00000000 --- a/test/cache.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var Generate = require('..'); -var generate; - -describe('cache', function() { - describe('set', function() { - beforeEach(function() { - generate = new Generate(); - }); - - it('should set an instance', function() { - generate.generators.set('foo', function() {}); - assert(generate.generators.hasOwnProperty('foo')); - }); - - it('should set an instance with a parent instance', function() { - generate.generators.set('foo', function() {}, generate); - assert(generate.generators.hasOwnProperty('foo')); - }); - }); - - describe('get', function() { - beforeEach(function() { - generate = new Generate(); - }); - - it('should get an instance from app.generators', function() { - generate.generators.set('foo', function() {}, generate); - var foo = generate.generators.get('foo'); - assert(foo); - assert(foo.isGenerator); - }); - }); -}); diff --git a/test/collection.options.js b/test/collection.options.js index 17f6b515..b1cd9f3d 100644 --- a/test/collection.options.js +++ b/test/collection.options.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('collection.option()', function() { +describe('collection.options', function() { beforeEach(function() { app = new App(); app.create('page'); diff --git a/test/collection.render.js b/test/collection.render.js index 810326ad..7b3074b5 100644 --- a/test/collection.render.js +++ b/test/collection.render.js @@ -10,7 +10,7 @@ var List = App.List; var Views = App.Views; var pages; -describe('render', function() { +describe('collection.render', function() { describe('rendering', function() { beforeEach(function() { pages = new Views(); diff --git a/test/env.js b/test/env.js deleted file mode 100644 index 7dae91bb..00000000 --- a/test/env.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; - -require('mocha'); -var path = require('path'); -var assert = require('assert'); -var Generate = require('..'); -var generate; - -var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); - -describe('env', function() { - describe('createEnv', function() { - beforeEach(function() { - generate = new Generate(); - }); - - it('should add an env object to the instance', function() { - var fn = function() {}; - generate.createEnv('foo', fn); - assert(generate.env); - }); - - it('should take options as the second arg', function() { - var fn = function() {}; - generate.createEnv('foo', {}, fn); - assert(generate.env); - }); - - it('should prime `env` if it doesn\'t exist', function() { - var fn = function() {}; - delete generate.env; - generate.createEnv('foo', {}, fn); - assert(generate.env); - }); - - it('should add an alias to the env object', function() { - var fn = function() {}; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.alias, 'foo'); - }); - - it('should not use prefix when a function is passed', function() { - var fn = function() {}; - generate.prefix = 'generate'; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.name, 'foo'); - }); - - it('should not use custom prefix when a function is passed', function() { - var fn = function() {}; - generate.prefix = 'whatever'; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.name, 'foo'); - }); - - it('should try to resolve a path passed as the second arg', function() { - generate.createEnv('foo', 'generate-foo/verbfile.js'); - assert.equal(generate.env.alias, 'foo'); - assert.equal(generate.env.name, 'generate-foo'); - }); - - it('should throw an error when the path is not resolved', function(cb) { - try { - generate.createEnv('foo', fixtures('whatever.js')); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); - cb(); - } - }); - }); -}); diff --git a/test/fixtures/two/generate.js b/test/fixtures/two/generator.js similarity index 91% rename from test/fixtures/two/generate.js rename to test/fixtures/two/generator.js index 15cf2281..4e86299b 100644 --- a/test/fixtures/two/generate.js +++ b/test/fixtures/two/generator.js @@ -1,6 +1,6 @@ 'use strict'; -var Generate = require('../..'); +var Generate = require('../../..'); var generate = new Generate(); generate.task('default', function() {}); diff --git a/test/generate.js b/test/generate.js index 84f46689..7010c83e 100644 --- a/test/generate.js +++ b/test/generate.js @@ -29,7 +29,7 @@ describe('generate', function() { it('should register the default generator', function() { generate.register('default', require('./fixtures/def-gen')); - assert(generate.hasGenerator('default')); + assert(generate.getGenerator('default')); }); }); }); diff --git a/test/generators.cache.js b/test/generators.cache.js deleted file mode 100644 index 9f951ef4..00000000 --- a/test/generators.cache.js +++ /dev/null @@ -1,223 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var option = require('base-option'); -var Generate = require('..'); -var generate; - -describe('cache', function() { - describe('set', function() { - beforeEach(function() { - generate = new Generate(); - generate.use(option()); - }); - - it('should set an instance', function() { - generate.generators.set('foo', function() {}); - assert(generate.generators.hasOwnProperty('foo')); - }); - - it('should set an instance with a parent instance', function() { - generate.generators.set('foo', function() {}); - assert(generate.generators.hasOwnProperty('foo')); - }); - - it('should set options from the parent instance on new instances', function() { - generate = new Generate({a: 'b'}); - assert.equal(generate.options.a, 'b'); - - generate.generators.set('foo', function() {}); - assert(generate.generators.hasOwnProperty('foo')); - assert.equal(generate.generators.foo.options.a, 'b'); - }); - - it('should set options from the parent instance on sub-generators', function(cb) { - generate = new Generate({a: 'b'}); - assert.equal(generate.options.a, 'b'); - - generate.generators.set('foo', function(app) { - app.generators.set('bar', function(bar) { - assert.equal(bar.options.a, 'b'); - cb(); - }); - }); - - generate.getGenerator('foo.bar'); - }); - - it('should set parent options on deeply nested sub-generators', function(cb) { - generate = new Generate({a: 'b'}); - generate.use(option()); - - assert.equal(generate.options.a, 'b'); - - generate.option('c', 'd'); - - generate.generators.set('foo', function(foo) { - assert.equal(foo.options.a, 'b'); - - foo.generators.set('bar', function(bar) { - assert.equal(bar.options.a, 'b'); - - bar.generators.set('baz', function(baz) { - assert.equal(baz.options.a, 'b'); - - baz.generators.set('qux', function(qux) { - assert.equal(qux.options.a, 'b'); - - qux.generators.set('fez', function(fez) { - assert.equal(fez.options.a, 'b'); - - cb(); - }); - }); - }); - }); - }); - generate.getGenerator('foo.bar.baz.qux.fez'); - }); - - it('should expose the base instance as the second arg', function(cb) { - generate = new Generate({a: 'b'}); - generate.use(option()); - - assert.equal(generate.options.a, 'b'); - - generate.option('c', 'd'); - - generate.generators.set('foo', function(foo, fooBase) { - foo.generators.set('bar', function(bar, barBase) { - bar.generators.set('baz', function(baz, bazBase) { - baz.generators.set('qux', function(qux, quxBase) { - qux.generators.set('fez', function(fez, fezBase) { - assert(fezBase.hasGenerator('foo')); - assert(!fezBase.hasGenerator('bar')); - assert(fezBase.hasGenerator('foo.bar')); - cb(); - }); - }); - }); - }); - }); - generate.getGenerator('foo.bar.baz.qux.fez'); - }); - - it('should not merge generator options back upstream', function() { - generate = new Generate({a: 'b'}); - generate.use(option()); - - assert.equal(generate.options.a, 'b'); - - generate.generators.set('foo', function() {}); - generate.option('one', 'two'); - - var foo = generate.getGenerator('foo'); - foo.option('x', 'z'); - - assert.equal(foo.options.a, 'b'); - assert.equal(foo.options.x, 'z'); - assert.equal(typeof generate.options.x, 'undefined'); - }); - - it('should break the options reference after instantiation', function() { - generate = new Generate({a: 'b'}); - generate.use(option()); - - assert.equal(generate.options.a, 'b'); - - generate.generators.set('foo', function() {}); - generate.option('one', 'two'); - - var foo = generate.getGenerator('foo'); - assert.equal(foo.options.a, 'b'); - assert.equal(typeof foo.options.one, 'undefined'); - }); - }); - - describe('get', function() { - beforeEach(function() { - generate = new Generate(); - }); - - it('should get an instance from app.generators', function() { - generate.generators.set('foo', function() {}); - var foo = generate.generators.get('foo'); - assert(foo); - assert(foo.isGenerator); - }); - }); - - describe('plugins', function() { - beforeEach(function() { - generate = new Generate(); - }); - - it('should add plugins from a parent generator to child generators', function() { - generate.use(option()); - - generate.generators.set('foo', function() {}); - var foo = generate.generators.get('foo'); - assert(foo); - assert(foo.hasOwnProperty('option')); - assert.equal(typeof foo.option, 'function'); - }); - - it('should add parent plugins to deeply nested generators', function(cb) { - generate.use(option()); - - generate.generators.set('foo', function(foo) { - foo.use(function fn() { - this.aaa = 'aaa'; - return fn; - }); - - assert.equal(this.aaa, 'aaa'); - assert.equal(typeof this.option, 'function'); - - foo.generators.set('bar', function(bar) { - bar.use(function() { - this.bbb = 'bbb'; - }); - - assert.equal(this.aaa, 'aaa'); - assert.equal(typeof this.option, 'function'); - - bar.generators.set('baz', function(baz) { - baz.use(function fn() { - this.ccc = 'ccc'; - return fn; - }); - - assert.equal(this.aaa, 'aaa'); - assert.equal(typeof this.option, 'function'); - - baz.generators.set('qux', function(qux) { - qux.use(function() { - this.ddd = 'ddd'; - }); - - assert.equal(this.ccc, 'ccc'); - assert.equal(this.aaa, 'aaa'); - assert.equal(typeof this.option, 'function'); - - qux.generators.set('fez', function(fez) { - fez.use(function() { - this.eee = 'eee'; - }); - - assert.equal(this.ccc, 'ccc'); - assert.equal(this.aaa, 'aaa'); - assert.equal(typeof this.option, 'function'); - - cb(); - }); - }); - }); - }); - }); - - generate.getGenerator('foo.bar.baz.qux.fez'); - }); - }); -}); diff --git a/test/generators.env.js b/test/generators.env.js index b9079eee..26ee9708 100644 --- a/test/generators.env.js +++ b/test/generators.env.js @@ -3,113 +3,114 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var Verb = require('..'); +var app; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('env', function() { +describe('generators.env', function() { describe('createEnv paths', function() { beforeEach(function() { - generate = new Generate(); + app = new Verb(); }); describe('alias and function', function() { it('should make the alias the exact name when the second arg is a function', function() { var fn = function() {}; - generate.createEnv('foo-bar-baz', fn); - assert(generate.env); - assert(generate.env.alias); - assert.equal(generate.env.alias, 'foo-bar-baz'); + app.createEnv('foo-bar-baz', fn); + assert(app.env); + assert(app.env.alias); + assert.equal(app.env.alias, 'foo-bar-baz'); }); it('should not change the name when the second arg is a function', function() { var fn = function() {}; - generate.createEnv('foo-bar-baz', fn); - assert(generate.env); - assert(generate.env.name); - assert.equal(generate.env.name, 'foo-bar-baz'); + app.createEnv('foo-bar-baz', fn); + assert(app.env); + assert(app.env.name); + assert.equal(app.env.name, 'foo-bar-baz'); }); }); describe('alias and path', function() { - it('should set the name to the basename of a generator\'s dirname', function() { - generate.createEnv('foo', 'generate-foo/verbfile.js'); - assert.equal(generate.env.name, 'generate-foo'); + it('should set a generator by name', function() { + app.createEnv('fofofof', 'generate-foo/verbfile.js'); + assert.equal(app.env.name, 'fofofof'); }); it('should not change the name when the second arg is a function', function() { var fn = function() {}; - generate.createEnv('foo-bar-baz', fn); - assert(generate.env); - assert(generate.env.name); - assert.equal(generate.env.name, 'foo-bar-baz'); + app.createEnv('foo-bar-baz', fn); + assert(app.env); + assert(app.env.name); + assert.equal(app.env.name, 'foo-bar-baz'); }); }); }); describe('createEnv', function() { beforeEach(function() { - generate = new Generate(); + app = new Verb(); }); it('should add an env object to the instance', function() { var fn = function() {}; - generate.createEnv('foo', fn); - assert(generate.env); + app.createEnv('foo', fn); + assert(app.env); }); it('should take options as the second arg', function() { var fn = function() {}; - generate.createEnv('foo', {}, fn); - assert(generate.env); + app.createEnv('foo', {}, fn); + assert(app.env); }); it('should prime `env` if it doesn\'t exist', function() { var fn = function() {}; - delete generate.env; - generate.createEnv('foo', {}, fn); - assert(generate.env); + delete app.env; + app.createEnv('foo', {}, fn); + assert(app.env); }); it('should add an alias to the env object', function() { var fn = function() {}; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.alias, 'foo'); + app.createEnv('foo', {}, fn); + assert.equal(app.env.alias, 'foo'); }); it('should not prefix the alias when a function is passed', function() { var fn = function() {}; - delete generate.prefix; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.name, 'foo'); + delete app.prefix; + app.createEnv('foo', {}, fn); + assert.equal(app.env.name, 'foo'); }); it('should not prefix a custom alias when a function is passed', function() { var fn = function() {}; - generate.prefix = 'whatever'; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.name, 'foo'); + app.prefix = 'whatever'; + app.createEnv('foo', {}, fn); + assert.equal(app.env.name, 'foo'); }); it('should try to resolve a path passed as the second arg', function() { - generate.createEnv('generate-foo', fixtures('verbfile.js')); - assert.equal(generate.env.alias, 'foo'); - assert.equal(generate.env.name, 'generate-foo'); + app.createEnv('generate-foo', fixtures('verbfile.js')); + assert.equal(app.env.alias, 'foo'); + assert.equal(app.env.name, 'generate-foo'); }); it('should try to resolve a path passed as the second arg', function() { - generate.createEnv('generate-foo', 'generate-foo/verbfile.js'); - assert.equal(generate.env.alias, 'foo'); - assert.equal(generate.env.name, 'generate-foo'); + app.createEnv('generate-foo', 'generate-foo/verbfile.js'); + assert.equal(app.env.alias, 'foo'); + assert.equal(app.env.name, 'generate-foo'); }); it('should throw an error when the path is not resolved', function(cb) { try { - generate.createEnv('foo', fixtures('whatever.js')); + var env = app.createEnv('foo', fixtures('whatever.js')); + env.fn(); cb(new Error('expected an error')); } catch (err) { - assert.equal(err.message, 'cannot find generator: ' + fixtures('whatever.js')); + assert.equal(err.message, 'Cannot find module \'' + fixtures('whatever.js') + '\''); cb(); } }); diff --git a/test/generators.events.js b/test/generators.events.js index eed598ec..aa88de69 100644 --- a/test/generators.events.js +++ b/test/generators.events.js @@ -5,55 +5,20 @@ var assert = require('assert'); var Generate = require('..'); var generate; -describe('generators events', function() { +describe('generators.events', function() { describe('generator', function() { beforeEach(function() { generate = new Generate(); }); - it('should emit generator.set when a generator is registered', function(cb) { - generate = new Generate(); - generate.on('generator.set', function(generator) { - assert.equal(generator.env.alias, 'foo'); - cb(); - }); - - generate.register('foo', function() {}); - }); - it('should emit generator when a generator is registered', function(cb) { generate = new Generate(); - generate.on('generator', function(method, generator) { - assert.equal(method, 'set'); - assert.equal(generator.env.alias, 'foo'); - cb(); - }); - - generate.register('foo', function() {}); - }); - - it('should emit generator when generate.generators.get is called', function(cb) { - generate = new Generate(); - generate.register('foo', function() {}); - - generate.on('generator', function(method, generator) { - assert.equal(method, 'get'); - assert.equal(generator.env.alias, 'foo'); - cb(); - }); - - generate.generators.get('foo'); - }); - - it('should emit generator.get when generate.generators.get is called', function(cb) { - generate = new Generate(); - generate.on('generator.get', function(generator) { - assert.equal(generator.env.alias, 'foo'); + generate.on('generator', function(name) { + assert.equal(name, 'foo'); cb(); }); generate.register('foo', function() {}); - generate.generators.get('foo'); }); it('should emit error on base when a base generator emits an error', function(cb) { diff --git a/test/integration/instance.js b/test/integration/instance.js new file mode 100644 index 00000000..2cc22723 --- /dev/null +++ b/test/integration/instance.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = function(app) { + app.extendWith('verb-readme-generator'); + app.helper('example', require('helper-example')); + app.task('default', ['readme']); +}; diff --git a/test/list.render.js b/test/list.render.js index f29b848a..ad127f24 100644 --- a/test/list.render.js +++ b/test/list.render.js @@ -9,7 +9,7 @@ var App = support.resolve(); var List = App.List; var pages; -describe('render', function() { +describe('list.render', function() { describe('rendering', function() { beforeEach(function() { pages = new List(); diff --git a/test/store.js b/test/store.js index 037f338f..9664c5f6 100644 --- a/test/store.js +++ b/test/store.js @@ -21,16 +21,16 @@ describe('store', function() { it('should create a store at the given `cwd`', function() { app = new App({store: {cwd: __dirname + '/actual'}}); app.store.set('foo', 'bar'); - assert(path.basename(app.store.path) === 'generate.json'); + assert(path.basename(app.store.path) === 'verb.json'); assert(app.store.data.hasOwnProperty('foo')); assert(app.store.data.foo === 'bar'); - assert(fs.existsSync(path.join(__dirname, 'actual', 'generate.json'))); + assert(fs.existsSync(path.join(__dirname, 'actual', 'verb.json'))); }); it('should create a store using the given `indent` value', function() { app = new App({store: {cwd: __dirname + '/actual', indent: 0}}); app.store.set('foo', 'bar'); - var contents = fs.readFileSync(path.join(__dirname, 'actual', 'generate.json'), 'utf8'); + var contents = fs.readFileSync(path.join(__dirname, 'actual', 'verb.json'), 'utf8'); assert(contents === '{"foo":"bar"}'); }); diff --git a/test/view.content.js b/test/view.content.js index bf356778..856aa9da 100644 --- a/test/view.content.js +++ b/test/view.content.js @@ -8,7 +8,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('content', function() { +describe('view.content', function() { beforeEach(function() { app = new App(); app.create('page'); diff --git a/test/view.events.js b/test/view.events.js index 837b9553..fa53cb7e 100644 --- a/test/view.events.js +++ b/test/view.events.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('view.option()', function() { +describe('view.events', function() { beforeEach(function() { app = new App(); app.create('page'); diff --git a/test/view.methods.js b/test/view.methods.js index 72ea95c1..27ed99ad 100644 --- a/test/view.methods.js +++ b/test/view.methods.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('view.option()', function() { +describe('view.methods', function() { beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); diff --git a/test/view.option.js b/test/view.option.js index ebde730d..64b28903 100644 --- a/test/view.option.js +++ b/test/view.option.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('view.option()', function() { +describe('view.option', function() { beforeEach(function() { app = new App(); app.create('page'); diff --git a/test/view.render.js b/test/view.render.js index da0d8df0..24c33f7e 100644 --- a/test/view.render.js +++ b/test/view.render.js @@ -7,7 +7,7 @@ var App = support.resolve(); var View = App.View; var view, app; -describe('helpers', function() { +describe('view.render', function() { describe('rendering', function() { beforeEach(function() { app = new App(); diff --git a/test/view.set.js b/test/view.set.js index 321219bc..fe8c4fba 100644 --- a/test/view.set.js +++ b/test/view.set.js @@ -8,7 +8,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('set', function() { +describe('view.set', function() { beforeEach(function() { app = new App(); app.create('page'); diff --git a/test/viewTypes.js b/test/viewTypes.js index 22bf9fe8..8b246958 100644 --- a/test/viewTypes.js +++ b/test/viewTypes.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('viewType', function() { +describe('viewTypes', function() { describe('view types', function() { beforeEach(function() { app = new App(); From b0f6dc30cb10160c6f3e245bb1d5a08c4da6ed5f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:02:27 -0400 Subject: [PATCH 217/282] update patterns, lint gulpfile --- gulpfile.js | 53 +++++++++-------------------------------------------- 1 file changed, 9 insertions(+), 44 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 93b294f6..c60166e4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,60 +4,25 @@ var gulp = require('gulp'); var mocha = require('gulp-mocha'); var istanbul = require('gulp-istanbul'); var eslint = require('gulp-eslint'); -var through = require('through2'); + +var lib = ['index.js', 'lib/**/*.js', 'bin/*.js']; gulp.task('coverage', function() { - return gulp.src(['index.js', 'lib/*.js']) - .pipe(istanbul({includeUntested: true})) + return gulp.src(lib) + .pipe(istanbul()) .pipe(istanbul.hookRequire()); }); -gulp.task('mocha', ['coverage'], function() { +gulp.task('test', ['coverage'], function() { return gulp.src('test/*.js') - .pipe(mocha()) + .pipe(mocha({reporter: 'spec'})) .pipe(istanbul.writeReports()); }); -gulp.task('eslint', function() { - return gulp.src(['*.js', 'lib/*.js', 'test/*.js']) +gulp.task('lint', function() { + return gulp.src(['*.js', 'test/*.js'].concat(lib)) .pipe(eslint()) .pipe(eslint.format()); }); -gulp.task('vars', function() { - var utils = require('./lib/utils'); - var keys = Object.keys(utils); - var report = {}; - var cache = {}; - - return gulp.src(['index.js', 'lib/**/*.js', 'bin/*.']) - .pipe(through.obj(function(file, enc, next) { - var str = file.contents.toString(); - keys.forEach(function(key) { - report[key] = report[key] || 0; - var re = cache[key] || (cache[key] = new RegExp('\\.' + key, 'g')); - var m = str.match(re); - if (!m) return; - report[key]++; - }); - - next(null, file); - }, function(next) { - var keys = Object.keys(report); - var res = {}; - - keys.sort(function(a, b) { - return report[a] > report[b] ? -1 : 1; - }); - - keys.forEach(function(key) { - res[key] = report[key]; - }); - - console.log(res); - console.log(keys.length + 1, 'modules'); - next(); - })) -}); - -gulp.task('default', ['mocha', 'eslint']); +gulp.task('default', ['test', 'lint']); From 60f813b5a87c681deb0b1de4599f30f876f44f4c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:03:37 -0400 Subject: [PATCH 218/282] add `questions` for init --- lib/questions.js | 171 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 lib/questions.js diff --git a/lib/questions.js b/lib/questions.js new file mode 100644 index 00000000..cb832841 --- /dev/null +++ b/lib/questions.js @@ -0,0 +1,171 @@ +'use strict'; + +var isObject = require('isobject'); +var color = require('ansi-colors'); + +module.exports = function(app, options) { + + /** + * Allow the user to choose the stores to use for saving a value (after prompts) + */ + + app.question('set', 'To save your answers, pick one or more of the following (press to skip):', { + type: 'checkbox', + save: false, + choices: [{ + key: 'a', + name: color.yellow(' app store') + ' (for ' + app._name + '-specific values)', + value: 'app' + }, { + key: 'g', + name: color.yellow(' global store') + ' (for values to be shared across all base applications (generate, assemble, verb, update, etc))', + value: 'global' + }, { + key: 'p', + name: color.yellow(' project config') + ' (for project-specific values, saved to the "' + app._name + '" object in package.json)', + value: 'project' + }], + next: function(answer, question, answers, cb) { + // try to erase some of the junk produced by inquirer + process.stdout.write('\r\x1b[K'); + + answers.stores = answers.stores || {}; + // rename store choices to actual properties on `app` + answers.stores.set = answer.reduce(function(acc, name) { + if (name === 'app') name = 'store'; + if (name === 'global') name = 'globals'; + if (name === 'project') name = 'pkg'; + + if (!isObject(app[name]) || typeof app[name].set !== 'function') { + return acc; + } + acc.push(name); + return acc; + }, []); + + delete answers.set; + cb(null, answers); + } + }); + + /** + * Delete + */ + + app.question('del', 'Which stores would you like to delete the value from?:', { + type: 'checkbox', + save: false, + choices: [{ + key: 'a', + name: color.yellow(' app store') + ' (for ' + app._name + '-specific values)', + value: 'app' + }, { + key: 'g', + name: color.yellow(' global store') + ' (for values to be shared across all base applications (generate, assemble, verb, update, etc))', + value: 'global' + }, { + key: 'p', + name: color.yellow(' project config') + ' (for project-specific values, saved to the "' + app._name + '" object in package.json)', + value: 'project' + }], + next: function(answer, question, answers, cb) { + // try to erase some of the junk produced by inquirer + process.stdout.write('\r\x1b[K'); + + answers.stores = answers.stores || {}; + // rename store choices to actual properties on `app` + answers.stores.del = answer.reduce(function(acc, name) { + if (name === 'app') name = 'store'; + if (name === 'global') name = 'globals'; + if (name === 'project') name = 'pkg'; + + if (!isObject(app[name]) || typeof app[name].del !== 'function') { + return acc; + } + acc.push(name); + return acc; + }, []); + + delete answers.del; + cb(null, answers); + } + }); + + /** + * Config questions + */ + + app.questions + .set('config.layout', 'What layout would you like to use?', { + default: 'default' + }) + .set('config.toc', 'Add Table of Contents to README.md?', { + type: 'confirm', + default: false + }) + .set('config.plugins', 'Plugins to use (comma-separated):', { + default: ['gulp-format-md'] + }) + .set('config.tasks', 'Tasks or generators to run (comma-separated)', { + default: ['readme'] + }) + .set('config.lint.reflinks', 'Do you want to lint for missing reflinks and add them to verb config?', { + type: 'confirm' + }); + + /** + * Ask the user if they want to install plugins, if any were specified + */ + + app.confirm('after.plugins', 'Plugins need to be installed, want to do that now?'); + + /** + * Init questions + */ + + app.question('init.preferences', 'Would you like to set defaults for this project?', { + type: 'confirm', + next: function(answer, question, answers, cb) { + // ensure `init` questions aren't asked again + delete answers.init; + if (answer === true) { + app.ask('init.choose', cb); + } else { + cb(null, answers); + } + } + }); + + app.choices('init.choose', 'Which options would you like to set?', { + choices: buildChoices(app), + save: false, + next: function(answer, question, answers, cb) { + // ensure `init` questions aren't asked again + delete answers.init; + if (typeof answer === 'undefined' || answer.length === 0) { + cb(null, answers); + return; + } + + var choices = answer.map(function(val) { + return 'config.' + val; + }); + app.ask(choices, cb); + } + }); +}; + +/** + * Build the list of `config.*` options to prompt the user about + */ + +function buildChoices(app) { + return app.questions.queue.reduce(function(acc, key) { + if (key.indexOf('config.') !== 0) { + return acc; + } + key = key.slice('config.'.length); + acc.push(key); + return acc; + }, []); +} From bd80cb304a043ff5c266e3fa492c9a9ae27aa0fc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:04:11 -0400 Subject: [PATCH 219/282] update deps, scaffold out `reflinks`, `format` (WIP) --- .verb.md | 9 +++++-- index.js | 6 ----- lib/commands/format.js | 23 +++++++++++++++++ lib/commands/tasks.js | 4 +++ lib/help.md | 6 +++++ lib/reflinks.js | 31 +++++++++++++++++++++++ package.json | 57 +++++++++++++++++++++++++----------------- 7 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 lib/commands/format.js create mode 100644 lib/help.md create mode 100644 lib/reflinks.js diff --git a/.verb.md b/.verb.md index 526f541d..fed5d7bb 100644 --- a/.verb.md +++ b/.verb.md @@ -1,7 +1,12 @@ +A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. + +**I just want to generate a readme, can verb do this?** + +Yes! See [verb-readme-generator][]. ## What is verb? -Verb is a documentation system that is simple and fast enough to use for generating the readmes for GitHub projects, but powerful and smart enough to build the most complex documentation projects around. +Verb is a documentation system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. @@ -147,4 +152,4 @@ $ verb --config=tasks:foo,bar,baz {%= apidocs("index.js") %} ## Upgrading -{%= include("upgrading") %} \ No newline at end of file +{%= include("upgrading") %} diff --git a/index.js b/index.js index f13b2c45..28258a51 100644 --- a/index.js +++ b/index.js @@ -41,12 +41,6 @@ Generate.extend(Verb); Verb.prototype.initVerb = function(opts) { this.debug('initializing', __filename); - - delete this.cache.data.runner; - if (this.options.namespace === true) { - delete this.options.namespace; - } - Verb.emit('preInit', this, this.base); this.data({runner: require('./package')}); Verb.emit('init', this, this.base); diff --git a/lib/commands/format.js b/lib/commands/format.js new file mode 100644 index 00000000..5f929869 --- /dev/null +++ b/lib/commands/format.js @@ -0,0 +1,23 @@ +'use strict'; + +var format = require('gulp-format-md'); +var path = require('path'); + +/** + * Format the given file. + * + * ```sh + * $ verb --format foo/bar.md + * ``` + * @cli public + */ + +module.exports = function(app, options) { + return function(file, key, config, next) { + app.src(path.resolve(file)) + .pipe(format()) + .pipe(app.dest(path.resolve(path.dirname(file)))) + .on('error', next) + .on('end', next); + }; +}; diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js index 13f11afd..deca8650 100644 --- a/lib/commands/tasks.js +++ b/lib/commands/tasks.js @@ -52,6 +52,10 @@ function setTasks(app, configFile, tasks, argv) { return ['verb.new:' + argv.processed.new]; } + if (argv.processed.hasOwnProperty('format')) { + return []; + } + tasks = tasks.map(function(task) { if (task.indexOf('new:') === 0) { return 'verb.' + task; diff --git a/lib/help.md b/lib/help.md new file mode 100644 index 00000000..027bbff0 --- /dev/null +++ b/lib/help.md @@ -0,0 +1,6 @@ + + +--disable=help disable this message +Try running `verb readme` if verb-readme-generator is installed + +Thanks for using verb! diff --git a/lib/reflinks.js b/lib/reflinks.js new file mode 100644 index 00000000..d722839a --- /dev/null +++ b/lib/reflinks.js @@ -0,0 +1,31 @@ +'use strict'; + +// var reflinks = require('helper-') + +/** + * Lint readme.md for missing reflinks, and add them to + * package.json if enabled by the user. + */ + +module.exports = function(app) { + var arr = app.pkg.get('verb.reflinks') || []; + var re = /(\[[\w._-]+?\]\[\])/g; + + return through.obj(function(file, enc, next) { + var matches = file.content.match(re); + if (matches && matches.length) { + matches.forEach(function(match) { + var idx = match.indexOf(']'); + var name = match.slice(1, idx).trim(); + if (arr.indexOf(name) === -1) { + arr.push(name); + count++; + } + }); + } + + reflinks(arr, function() { + + }); + }); +}; diff --git a/package.json b/package.json index c93c29f2..4f12d2f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "verb", - "description": "Documentation system for GitHub projects.", + "description": "Documentation build system for GitHub projects.", "version": "0.9.0", "homepage": "https://github.com/verbose/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", @@ -10,63 +10,74 @@ }, "license": "MIT", "files": [ + "bin", "index.js", - "bin/", - "lib/" + "lib" ], "main": "index.js", + "preferGlobal": true, + "bin": { + "verb": "bin/verb.js" + }, "engines": { "node": ">=0.10.0" }, "scripts": { "test": "mocha" }, - "preferGlobal": true, - "bin": { - "verb": "bin/verb.js" - }, "dependencies": { - "generate": "^0.4.12", - "generator-util": "^0.2.9" + "ansi-colors": "^0.1.0", + "base-runner": "^0.6.3", + "debug": "^2.2.0", + "export-files": "^2.1.1", + "extend-shallow": "^2.0.1", + "fs-exists-sync": "^0.1.0", + "generate": "^0.5.1", + "isobject": "^2.0.0", + "memoize-path": "^0.1.2", + "minimist": "^1.2.0" }, "devDependencies": { "async": "^1.5.2", - "base-option": "^0.6.2", - "base-store": "^0.4.1", + "base-store": "^0.4.2", "buffer-equal": "^1.0.0", "consolidate": "^0.14.0", "define-property": "^0.2.5", "engine-base": "^0.1.2", "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", - "expect": "^1.14.0", + "expect": "^1.18.0", "generate-foo": "^0.1.5", - "get-value": "^2.0.3", - "global-modules": "^0.2.0", + "generator-util": "^0.2.9", + "get-value": "^2.0.5", + "global-modules": "^0.2.1", "graceful-fs": "^4.1.3", "gulp": "^3.9.1", "gulp-eslint": "^2.0.0", - "gulp-format-md": "^0.1.7", - "gulp-istanbul": "^0.10.3", + "gulp-format-md": "^0.1.8", + "gulp-istanbul": "^0.10.4", "gulp-mocha": "^2.2.0", - "is-buffer": "^1.1.2", + "helper-example": "^0.1.0", + "is-buffer": "^1.1.3", "kind-of": "^3.0.2", "load-pkg": "^3.0.1", - "matched": "^0.4.1", "mocha": "^2.4.5", "parser-front-matter": "^1.3.0", "resolve-glob": "^0.1.8", "rimraf": "^2.5.2", - "should": "^8.2.2", + "should": "^8.3.1", "sinon": "^1.17.3", "spawn-commands": "^0.3.1", "swig": "^1.4.2", "through2": "^2.0.1", "vinyl": "^1.1.1" }, + "keywords": [ + "verb" + ], "verb": { "run": true, - "toc": true, + "toc": false, "layout": "default", "tasks": [ "readme" @@ -82,10 +93,10 @@ ] }, "reflinks": [ + "verb-readme-generator", + "verb", "assemble", - "base", "generate", - "verb", "update" ], "lint": { @@ -95,4 +106,4 @@ "lintDeps": { "ignore": [] } -} +} \ No newline at end of file From 589f5e2779690b09292e261b152b7f8058175f3e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:04:52 -0400 Subject: [PATCH 220/282] draft recipe for extending a generator --- docs/lang/en/recipes/extend-a-generator.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/lang/en/recipes/extend-a-generator.md diff --git a/docs/lang/en/recipes/extend-a-generator.md b/docs/lang/en/recipes/extend-a-generator.md new file mode 100644 index 00000000..3ad7a795 --- /dev/null +++ b/docs/lang/en/recipes/extend-a-generator.md @@ -0,0 +1,20 @@ +# Extend a generator + + +```js +module.exports = function(verb) { + verb.extendWith('verb-readme-generator'); + verb.docs('docs/*.md', {cwd: __dirname}); +}; +``` + +## Extend with the default generator + +Extend a specified generator with the `default` generator (`verbfile.js`): + +```js +// --verbfile.js-- +module.exports = function(app) { + app.data({name: 'foo-bar'}); +}; +``` From 1b103b69cdb19ff39030e9fdfc265e73d5bb4f21 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:04:58 -0400 Subject: [PATCH 221/282] generate readme --- readme.md | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/readme.md b/readme.md index 7a41d160..7e0f5bb1 100644 --- a/readme.md +++ b/readme.md @@ -1,29 +1,6 @@ -# verb [![NPM version](https://img.shields.io/npm/v/verb.svg)](https://www.npmjs.com/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg)](https://travis-ci.org/verbose/verb) - -> Documentation system for GitHub projects. - -## TOC - -- [Install](#install) -- [What is verb?](#what-is-verb-) -- [Quickstart](#quickstart) -- [verbfile](#verbfile) -- [CLI Commands](#cli-commands) - * [config](#config) - * [save](#save) - * [file](#file) - * [cwd](#cwd) - + [tasks](#tasks) -- [API](#api) -- [Upgrading](#upgrading) -- [Related projects](#related-projects) -- [Contributing](#contributing) -- [Building docs](#building-docs) -- [Running tests](#running-tests) -- [Author](#author) -- [License](#license) - -_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ +# verb [![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) + +> Documentation build system for GitHub projects. ## Install @@ -33,9 +10,15 @@ Install with [npm](https://www.npmjs.com/): $ npm install verb --save ``` +A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. + +**I just want to generate a readme, can verb do this?** + +Yes! See [verb-readme-generator](https://github.com/verbose/verb-readme-generator). + ## What is verb? -Verb is a documentation system that is simple and fast enough to use for generating the readmes for GitHub projects, but powerful and smart enough to build the most complex documentation projects around. +Verb is a documentation system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. @@ -178,7 +161,7 @@ $ verb --config=tasks:foo,bar,baz ## API -### [Verb](index.js#L26) +### [Verb](index.js#L23) Create a verb application with `options`. @@ -205,6 +188,8 @@ $ npm cache clean && npm i -g verb ## Related projects +You might also be interested in these projects: + * [assemble](https://www.npmjs.com/package/assemble): Assemble is a powerful, extendable and easy to use static site generator for node.js. Used… [more](https://www.npmjs.com/package/assemble) | [homepage](https://github.com/assemble/assemble) * [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base) | [homepage](https://github.com/node-base/base) * [generate](https://www.npmjs.com/package/generate): Fast, composable, highly extendable project generator with a user-friendly and expressive API. | [homepage](https://github.com/generate/generate) @@ -244,9 +229,9 @@ $ npm install -d && npm test ## License -Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert) +Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert). Released under the [MIT license](https://github.com/verbose/verb/blob/master/LICENSE). *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on March 06, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on April 21, 2016._ \ No newline at end of file From 8bd7ba0411673dd8103991afbc782868db878187 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 21 Apr 2016 12:36:58 -0400 Subject: [PATCH 222/282] remove wip files --- lib/commands/format.js | 23 ----------------------- lib/reflinks.js | 31 ------------------------------- 2 files changed, 54 deletions(-) delete mode 100644 lib/commands/format.js delete mode 100644 lib/reflinks.js diff --git a/lib/commands/format.js b/lib/commands/format.js deleted file mode 100644 index 5f929869..00000000 --- a/lib/commands/format.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var format = require('gulp-format-md'); -var path = require('path'); - -/** - * Format the given file. - * - * ```sh - * $ verb --format foo/bar.md - * ``` - * @cli public - */ - -module.exports = function(app, options) { - return function(file, key, config, next) { - app.src(path.resolve(file)) - .pipe(format()) - .pipe(app.dest(path.resolve(path.dirname(file)))) - .on('error', next) - .on('end', next); - }; -}; diff --git a/lib/reflinks.js b/lib/reflinks.js deleted file mode 100644 index d722839a..00000000 --- a/lib/reflinks.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -// var reflinks = require('helper-') - -/** - * Lint readme.md for missing reflinks, and add them to - * package.json if enabled by the user. - */ - -module.exports = function(app) { - var arr = app.pkg.get('verb.reflinks') || []; - var re = /(\[[\w._-]+?\]\[\])/g; - - return through.obj(function(file, enc, next) { - var matches = file.content.match(re); - if (matches && matches.length) { - matches.forEach(function(match) { - var idx = match.indexOf(']'); - var name = match.slice(1, idx).trim(); - if (arr.indexOf(name) === -1) { - arr.push(name); - count++; - } - }); - } - - reflinks(arr, function() { - - }); - }); -}; From 20083a327d5b4da96905625833450d9e62bb99dd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 28 Apr 2016 17:24:00 -0400 Subject: [PATCH 223/282] adds `toAlias` function --- index.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 28258a51..45000594 100644 --- a/index.js +++ b/index.js @@ -40,12 +40,35 @@ Generate.extend(Verb); */ Verb.prototype.initVerb = function(opts) { - this.debug('initializing', __filename); Verb.emit('preInit', this, this.base); + this.data({before: {}, after: {}}); + + this.debug('initializing', __filename); + + this.option('toAlias', function(name) { + return name.replace( /^verb-([^-]+)-generator$/, '$1'); + }); + this.data({runner: require('./package')}); Verb.emit('init', this, this.base); }; +/** + * Add a generator and its tasks to the tree object. + * Mostly used for debugging, but also useful for + * creating custom-formatted visual trees. + * + * @param {String} `name` + * @param {Object} `app` + */ + +Verb.prototype.addLeaf = function(name, app) { + this.tree[name] = {}; + this.tree[name].tasks = Object.keys(app.tasks || {}); + this.tree[name].generators = app.tree; + return this; +}; + /** * Expose static `is*` methods from Templates */ From a34cefbf21f2f5fd1720bc3ff2bac387f9e9d07a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 28 Apr 2016 17:27:29 -0400 Subject: [PATCH 224/282] generate docs --- .verb.md | 30 ++++++++++++++++++++++++++++++ readme.md | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/.verb.md b/.verb.md index fed5d7bb..7f88311b 100644 --- a/.verb.md +++ b/.verb.md @@ -4,6 +4,36 @@ A project without documentation is like a project that doesn't exist. Verb solve Yes! See [verb-readme-generator][]. +## Features + +* Convert markdown templates to markdown files _or_ static HTML +* Use any template engine, such as [handlebars][], [lodash][] or [swig][] +* Use templates, partials, or layouts +* Helper support (sync and async!) +* Collections +* Plugins + +**Plugins** + +Verb has first-class plugin support, along with native support for [gulp][] plugins, so you can do things like: + +* Ignore files marked as "drafts" +* CSS minification or reduction +* Spin up a dev server +* SASS or LESS compiling and minification +* JavaScript minification, reduction or concatenation +* Asset copying or renaming +* Image compression +* HTML minification and linting +* RSS feeds + +**Developers** + +* Use custom code to your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`) +* Use globally or locally installed verb "generators" +* Support for sub-generators +* Generators can be composed of multiple single-responsibility generators + ## What is verb? Verb is a documentation system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. diff --git a/readme.md b/readme.md index 7e0f5bb1..8b604e6e 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # verb [![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) -> Documentation build system for GitHub projects. +Documentation build system for GitHub projects. ## Install @@ -16,6 +16,36 @@ A project without documentation is like a project that doesn't exist. Verb solve Yes! See [verb-readme-generator](https://github.com/verbose/verb-readme-generator). +## Features + +* Convert markdown templates to markdown files _or_ static HTML +* Use any template engine, such as [handlebars](http://www.handlebarsjs.com/), [lodash](https://lodash.com/) or [swig](https://github.com/paularmstrong/swig) +* Use templates, partials, or layouts +* Helper support (sync and async!) +* Collections +* Plugins + +**Plugins** + +Verb has first-class plugin support, along with native support for [gulp](http://gulpjs.com) plugins, so you can do things like: + +* Ignore files marked as "drafts" +* CSS minification or reduction +* Spin up a dev server +* SASS or LESS compiling and minification +* JavaScript minification, reduction or concatenation +* Asset copying or renaming +* Image compression +* HTML minification and linting +* RSS feeds + +**Developers** + +* Use custom code to your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`) +* Use globally or locally installed verb "generators" +* Support for sub-generators +* Generators can be composed of multiple single-responsibility generators + ## What is verb? Verb is a documentation system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. @@ -196,7 +226,7 @@ You might also be interested in these projects: ## Contributing -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/verb/issues/new). +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/verbose/verb/issues/new). ## Building docs @@ -234,4 +264,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on April 21, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on April 24, 2016._ \ No newline at end of file From f11c6fd9d3b84389cb14555afeca228138f38d46 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 28 Apr 2016 17:27:36 -0400 Subject: [PATCH 225/282] update deps --- package.json | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4f12d2f5..4540c4be 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,11 @@ "export-files": "^2.1.1", "extend-shallow": "^2.0.1", "fs-exists-sync": "^0.1.0", - "generate": "^0.5.1", + "generate": "^0.5.4", "isobject": "^2.0.0", "memoize-path": "^0.1.2", - "minimist": "^1.2.0" + "minimist": "^1.2.0", + "reflinks": "^0.2.1" }, "devDependencies": { "async": "^1.5.2", @@ -93,11 +94,15 @@ ] }, "reflinks": [ - "verb-readme-generator", - "verb", "assemble", "generate", - "update" + "update", + "verb", + "verb-readme-generator", + "handlebars", + "lodash", + "swig", + "gulp" ], "lint": { "reflinks": true @@ -106,4 +111,4 @@ "lintDeps": { "ignore": [] } -} \ No newline at end of file +} From 6bb36fdc67cccda73d6f5d068bfbd381754272f9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sun, 1 May 2016 21:22:22 -0400 Subject: [PATCH 226/282] adds support for `--format` --- docs/lang/en/cli/examples/format-after.md | 19 ++++++++++++ docs/lang/en/cli/examples/format-before.md | 24 +++++++++++++++ lib/commands/format.js | 34 ++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 docs/lang/en/cli/examples/format-after.md create mode 100644 docs/lang/en/cli/examples/format-before.md create mode 100644 lib/commands/format.js diff --git a/docs/lang/en/cli/examples/format-after.md b/docs/lang/en/cli/examples/format-after.md new file mode 100644 index 00000000..5462cfc9 --- /dev/null +++ b/docs/lang/en/cli/examples/format-after.md @@ -0,0 +1,19 @@ +# Typography + +### Indented code + +Or indent several lines of code by at least four spaces, as in: + +```js + // Some comments + line 1 of code + line 2 of code + line 3 of code +``` + +``` +// Some comments +line 1 of code +line 2 of code +line 3 of code +``` \ No newline at end of file diff --git a/docs/lang/en/cli/examples/format-before.md b/docs/lang/en/cli/examples/format-before.md new file mode 100644 index 00000000..84c07aa3 --- /dev/null +++ b/docs/lang/en/cli/examples/format-before.md @@ -0,0 +1,24 @@ +# Typography + + + + +### Indented code + + + +Or indent several lines of code by at least four spaces, as in: + + + +``` js + // Some comments + line 1 of code + line 2 of code + line 3 of code +``` + + // Some comments + line 1 of code + line 2 of code + line 3 of code diff --git a/lib/commands/format.js b/lib/commands/format.js new file mode 100644 index 00000000..ba5d6205 --- /dev/null +++ b/lib/commands/format.js @@ -0,0 +1,34 @@ +'use strict'; + +var reflinks = require('../reflinks'); +var format = require('gulp-format-md'); +var path = require('path'); + +/** + * Format a markdown file using [pretty-remarkable][]. + * + * ```sh + * $ verb --format foo/bar.md + * ``` + * @name --file + * @api public + */ + +module.exports = function(app, options) { + return function(filepath, key, config, next) { + if (!filepath || typeof filepath !== 'string') { + next(); + return; + } + + app.src(path.resolve(filepath)) + .pipe(reflinks(app)) + .pipe(format()) + .pipe(app.dest(function(file) { + file.basename = (config.name || path.basename(filepath)); + return (config.dest || path.resolve(path.dirname(filepath))); + })) + .on('error', next) + .on('end', next); + }; +}; From 7acdeb40da775cefd20f9f7d782915be74c0849a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 3 May 2016 22:23:57 -0400 Subject: [PATCH 227/282] reflect changes from base-generators --- test/app.cli.js | 2 +- test/app.collection.js | 16 +++--- test/app.create.js | 6 +- test/app.env.js | 3 + test/app.extendWith.js | 6 +- test/app.generator.js | 126 +++++++++++++++++++++-------------------- test/app.page.js | 20 +------ test/app.pages.js | 16 +++--- test/app.questions.js | 10 ++-- test/generators.env.js | 10 +--- 10 files changed, 99 insertions(+), 116 deletions(-) diff --git a/test/app.cli.js b/test/app.cli.js index 0627517b..2d7c6952 100644 --- a/test/app.cli.js +++ b/test/app.cli.js @@ -9,7 +9,7 @@ var app; describe('app.cli', function() { beforeEach(function() { - app = new App(); + app = new App({cli: true}); }); describe('app.cli.map', function() { diff --git a/test/app.collection.js b/test/app.collection.js index cbcccf5f..35687023 100644 --- a/test/app.collection.js +++ b/test/app.collection.js @@ -54,7 +54,7 @@ describe('app.collection', function() { it('should load a view onto the respective collection:', function() { app.pages('test/fixtures/pages/a.hbs'); - app.views.pages.should.have.property(path.resolve('test/fixtures/pages/a.hbs')); + app.views.pages.should.have.property('test/fixtures/pages/a.hbs'); }); it('should allow collection methods to be chained:', function() { @@ -64,9 +64,9 @@ describe('app.collection', function() { .pages('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - path.resolve('test/fixtures/pages/a.hbs'), - path.resolve('test/fixtures/pages/b.hbs'), - path.resolve('test/fixtures/pages/c.hbs') + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs', ]); }); @@ -79,9 +79,9 @@ describe('app.collection', function() { app.pages.options.should.have.property('foo', 'bar'); app.views.pages.should.have.properties([ - path.resolve('test/fixtures/pages/a.hbs'), - path.resolve('test/fixtures/pages/b.hbs'), - path.resolve('test/fixtures/pages/c.hbs') + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs' ]); }); @@ -168,7 +168,7 @@ describe('collection singular method', function() { it('should add a view to the created collection:', function() { app.page('test/fixtures/pages/a.hbs'); - assert(typeof app.views.pages[path.resolve('test/fixtures/pages/a.hbs')] === 'object'); + assert(typeof app.views.pages['test/fixtures/pages/a.hbs'] === 'object'); }); it('should expose the `option` method:', function() { diff --git a/test/app.create.js b/test/app.create.js index 3f9d091d..f2501c14 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -156,9 +156,9 @@ describe('app.create', function() { app.page('test/fixtures/pages/c.hbs'); app.views.pages.should.have.properties([ - path.resolve('test/fixtures/pages/a.hbs'), - path.resolve('test/fixtures/pages/b.hbs'), - path.resolve('test/fixtures/pages/c.hbs'), + 'test/fixtures/pages/a.hbs', + 'test/fixtures/pages/b.hbs', + 'test/fixtures/pages/c.hbs', ]); }); }); diff --git a/test/app.env.js b/test/app.env.js index 5146b73d..af87a498 100644 --- a/test/app.env.js +++ b/test/app.env.js @@ -12,6 +12,9 @@ describe('app.env', function() { describe('createEnv', function() { beforeEach(function() { generate = new Generate(); + generate.option('toAlias', function(name) { + return name.replace(/^generate-/, ''); + }); }); it('should add an env object to the instance', function() { diff --git a/test/app.extendWith.js b/test/app.extendWith.js index e1a21527..14c7e780 100644 --- a/test/app.extendWith.js +++ b/test/app.extendWith.js @@ -4,11 +4,11 @@ require('mocha'); var path = require('path'); var assert = require('assert'); var gm = require('global-modules'); -var commands = require('spawn-commands'); var utils = require('generator-util'); -require('generate-foo/verbfile.js'); +var commands = require('spawn-commands'); var Generate = require('..'); var generate; +require('generate-foo'); var fixture = path.resolve.bind(path, __dirname, 'fixtures/generators'); function install(name, cb) { @@ -29,7 +29,7 @@ describe('app.extendWith', function() { beforeEach(function() { generate = new Generate(); - generate.option('alias', function(name) { + generate.option('toAlias', function(name) { return name.replace(/^generate-/, ''); }); }); diff --git a/test/app.generator.js b/test/app.generator.js index 5d109fd5..6ef3fcb4 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -1,57 +1,59 @@ 'use strict'; -require('mocha'); var path = require('path'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var Verb = require('..'); +var verb; var fixtures = path.resolve.bind(path, __dirname, 'fixtures'); describe('app.generator', function() { beforeEach(function() { - generate = new Generate(); + verb = new Verb(); + verb.option('toAlias', function(name) { + return name.replace(/^generate-/, ''); + }); }); describe('get generator', function() { it('should get a generator by full name name', function() { - var gen = generate.getGenerator('generate-mocha'); + var gen = verb.getGenerator('generate-foo'); assert(gen); - assert.equal(gen.env.alias, 'mocha'); - assert.equal(gen.env.name, 'generate-mocha'); + assert.equal(gen.env.alias, 'foo'); + assert.equal(gen.env.name, 'generate-foo'); }); it('should get a generator by aliased name', function() { - var gen = generate.getGenerator('generate-mocha'); + var gen = verb.getGenerator('generate-foo'); assert(gen); - assert.equal(gen.env.alias, 'mocha'); - assert.equal(gen.env.name, 'generate-mocha'); + assert.equal(gen.env.alias, 'foo'); + assert.equal(gen.env.name, 'generate-foo'); }); it('should get a generator by alias', function() { - var gen = generate.getGenerator('generate-mocha'); + var gen = verb.getGenerator('generate-foo'); assert(gen); - assert.equal(gen.env.alias, 'mocha'); - assert.equal(gen.env.name, 'generate-mocha'); + assert.equal(gen.env.alias, 'foo'); + assert.equal(gen.env.name, 'generate-foo'); }); }); describe('register > function', function() { it('should register a generator function by name', function() { - generate.generator('foo', function() {}); - assert(generate.generators.hasOwnProperty('foo')); + verb.generator('foo', function() {}); + assert(verb.generators.hasOwnProperty('foo')); }); it('should register a generator function by alias', function() { - generate.generator('generate-abc', function() {}); - assert(generate.generators.hasOwnProperty('generate-abc')); + verb.generator('generate-abc', function() {}); + assert(verb.generators.hasOwnProperty('generate-abc')); }); }); describe('get > alias', function() { it('should get a generator by alias', function() { - generate.generator('generate-abc', function() {}); - var abc = generate.generator('abc'); + verb.generator('generate-abc', function() {}); + var abc = verb.generator('abc'); assert(abc); assert.equal(typeof abc, 'object'); }); @@ -59,8 +61,8 @@ describe('app.generator', function() { describe('get > name', function() { it('should get a generator by name', function() { - generate.generator('generate-abc', function() {}); - var abc = generate.generator('generate-abc'); + verb.generator('generate-abc', function() {}); + var abc = verb.generator('generate-abc'); assert(abc); assert.equal(typeof abc, 'object'); }); @@ -68,22 +70,22 @@ describe('app.generator', function() { describe('generators', function() { it('should invoke a registered generator when `getGenerator` is called', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function() {}); cb(); }); - generate.getGenerator('foo'); + verb.getGenerator('foo'); }); it('should expose the generator instance on `app`', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('default', function(next) { assert.equal(app.get('a'), 'b'); next(); }) }); - var foo = generate.getGenerator('foo'); + var foo = verb.getGenerator('foo'); foo.set('a', 'b'); foo.build('default', function(err) { if (err) return cb(err); @@ -92,15 +94,15 @@ describe('app.generator', function() { }); it('should expose the "base" instance on `base`', function(cb) { - generate.set('x', 'z'); - generate.register('foo', function(app, base) { + verb.set('x', 'z'); + verb.register('foo', function(app, base) { app.task('default', function(next) { - assert.equal(generate.get('x'), 'z'); + assert.equal(verb.get('x'), 'z'); next(); }) }); - var foo = generate.getGenerator('foo'); + var foo = verb.getGenerator('foo'); foo.set('a', 'b'); foo.build('default', function(err) { if (err) return cb(err); @@ -109,21 +111,21 @@ describe('app.generator', function() { }); it('should expose the "env" object on `env`', function(cb) { - generate.register('foo', function(app, base, env) { + verb.register('foo', function(app, base, env) { app.task('default', function(next) { assert.equal(env.alias, 'foo'); next(); }) }); - generate.getGenerator('foo').build('default', function(err) { + verb.getGenerator('foo').build('default', function(err) { if (err) return cb(err); cb() }); }); it('should expose an app\'s generators on app.generators', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.register('a', function() {}); app.register('b', function() {}); @@ -132,45 +134,45 @@ describe('app.generator', function() { cb(); }); - generate.getGenerator('foo'); + verb.getGenerator('foo'); }); - it('should expose all root generators on generate.generators', function(cb) { - generate.register('foo', function(app, b) { + it('should expose all root generators on verb.generators', function(cb) { + verb.register('foo', function(app, b) { b.generators.hasOwnProperty('foo'); b.generators.hasOwnProperty('bar'); b.generators.hasOwnProperty('baz'); cb(); }); - generate.register('bar', function(app, base) {}); - generate.register('baz', function(app, base) {}); - generate.getGenerator('foo'); + verb.register('bar', function(app, base) {}); + verb.register('baz', function(app, base) {}); + verb.getGenerator('foo'); }); }); describe('cross-generators', function() { it('should get a generator from another generator', function(cb) { - generate.register('foo', function(app, b) { + verb.register('foo', function(app, b) { var bar = b.getGenerator('bar'); assert(bar); cb(); }); - generate.register('bar', function(app, base) {}); - generate.register('baz', function(app, base) {}); - generate.getGenerator('foo'); + verb.register('bar', function(app, base) {}); + verb.register('baz', function(app, base) {}); + verb.getGenerator('foo'); }); it('should set options on another generator instance', function(cb) { - generate.generator('foo', function(app) { + verb.generator('foo', function(app) { app.task('default', function(next) { assert.equal(app.option('abc'), 'xyz'); next(); }); }); - generate.generator('bar', function(app, b) { + verb.generator('bar', function(app, b) { var foo = b.getGenerator('foo'); foo.option('abc', 'xyz'); foo.build(function(err) { @@ -183,30 +185,30 @@ describe('app.generator', function() { describe('generators > filepath', function() { it('should register a generator function from a file path', function() { - var one = generate.generator('one', fixtures('one/verbfile.js')); - assert(generate.generators.hasOwnProperty('one')); - assert(typeof generate.generators.one === 'object'); - assert.deepEqual(generate.generators.one, one); + var one = verb.generator('one', fixtures('one/verbfile.js')); + assert(verb.generators.hasOwnProperty('one')); + assert(typeof verb.generators.one === 'object'); + assert.deepEqual(verb.generators.one, one); }); it('should register an instance from a file path', function() { - var two = generate.generator('two', fixtures('two/generator.js')); - assert(generate.generators.hasOwnProperty('two')); - assert(typeof generate.generators.two === 'object'); - assert.deepEqual(generate.generators.two, two); + var two = verb.generator('two', fixtures('two/generator.js')); + assert(verb.generators.hasOwnProperty('two')); + assert(typeof verb.generators.two === 'object'); + assert.deepEqual(verb.generators.two, two); }); it('should get a registered generator by name', function() { - var one = generate.generator('one', fixtures('one/generator.js')); - var two = generate.generator('two', fixtures('two/generator.js')); - assert.deepEqual(generate.generator('one'), one); - assert.deepEqual(generate.generator('two'), two); + var one = verb.generator('one', fixtures('one/generator.js')); + var two = verb.generator('two', fixtures('two/generator.js')); + assert.deepEqual(verb.generator('one'), one); + assert.deepEqual(verb.generator('two'), two); }); }); describe('tasks', function() { it('should expose a generator\'s tasks on app.tasks', function(cb) { - generate.register('foo', function(app) { + verb.register('foo', function(app) { app.task('a', function() {}); app.task('b', function() {}); assert(app.tasks.a); @@ -214,22 +216,22 @@ describe('app.generator', function() { cb(); }); - generate.getGenerator('foo'); + verb.getGenerator('foo'); }); it('should get tasks from another generator', function(cb) { - generate.register('foo', function(app, b) { + verb.register('foo', function(app, b) { var baz = b.getGenerator('baz'); var task = baz.tasks.aaa; assert(task); cb(); }); - generate.register('bar', function(app, base) {}); - generate.register('baz', function(app, base) { + verb.register('bar', function(app, base) {}); + verb.register('baz', function(app, base) { app.task('aaa', function() {}); }); - generate.getGenerator('foo'); + verb.getGenerator('foo'); }); }); }); diff --git a/test/app.page.js b/test/app.page.js index fe1bb715..0b279bd1 100644 --- a/test/app.page.js +++ b/test/app.page.js @@ -1,7 +1,7 @@ 'use strict'; require('mocha'); -require('should'); +var path = require('path'); var assert = require('assert'); var assemble = require('..'); var app; @@ -19,28 +19,14 @@ describe('app.page', function() { app.page('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); app.page('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); app.page('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); - assert(Object.keys(app.views.pages).length === 3); - }); - }); - - describe('load', function() { - it('should throw an error if a glob is passed', function(cb) { - try { - app.page('test/fixtures/pages/*.hbs'); - cb(new Error('expected an error')); - } catch (err) { - assert(err); - assert(err.message); - assert(err.message === 'loadView does not support globs, only filepaths.'); - cb(); - } + assert.equal(Object.keys(app.views.pages).length, 3); }); }); describe('load', function() { it('should load a page from a non-glob filepath', function() { app.page('test/fixtures/pages/a.hbs'); - assert(Object.keys(app.views.pages).length === 1); + assert.equal(Object.keys(app.views.pages).length, 1); }); }); }); diff --git a/test/app.pages.js b/test/app.pages.js index 6fd855f0..dbc6ec6e 100644 --- a/test/app.pages.js +++ b/test/app.pages.js @@ -3,32 +3,30 @@ require('mocha'); require('should'); var assert = require('assert'); -var assemble = require('..'); +var Verb = require('..'); var app; describe('app.pages', function() { beforeEach(function() { - app = assemble(); - if (!app.pages) { - app.create('pages'); - } + app = new Verb({cli: true, cwd: process.cwd()}); + app.create('pages'); }); describe('add pages', function() { - it('should add pages to `app.views.pages`:', function() { + it('should add pages to `app.views.pages`', function() { app.pages({ 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, }); - assert(Object.keys(app.views.pages).length === 3); + assert.equal(Object.keys(app.views.pages).length, 3); }); }); describe('load pages', function() { - it('should load pages onto `app.views.pages`:', function() { + it('should load pages onto `app.views.pages`', function() { app.pages('test/fixtures/pages/*.hbs'); - assert(Object.keys(app.views.pages).length === 3); + assert.equal(Object.keys(app.views.pages).length, 3); }); }); }); diff --git a/test/app.questions.js b/test/app.questions.js index e5ea10e8..8f93588c 100644 --- a/test/app.questions.js +++ b/test/app.questions.js @@ -12,10 +12,10 @@ var app, base, site; describe('app.questions', function() { describe('plugin', function() { beforeEach(function() { - base = new App(); + base = new App({cli: true}); base.use(store('base-questions-tests/base')); - app = new App(); + app = new App({cli: true}); app.use(store('base-questions-tests/app')); }); @@ -43,7 +43,7 @@ describe('app.questions', function() { describe('app.ask', function() { beforeEach(function() { - app = new App(); + app = new App({cli: true}); app.use(store('base-questions-tests/ask')); }); @@ -149,10 +149,10 @@ describe('app.questions', function() { describe('session data', function() { before(function() { - site = new App(); + site = new App({cli: true}); site.use(store('base-questions-tests/site')); - app = new App(); + app = new App({cli: true}); app.use(store('base-questions-tests/ask')); }); diff --git a/test/generators.env.js b/test/generators.env.js index 26ee9708..e1699dbd 100644 --- a/test/generators.env.js +++ b/test/generators.env.js @@ -93,15 +93,9 @@ describe('generators.env', function() { }); it('should try to resolve a path passed as the second arg', function() { - app.createEnv('generate-foo', fixtures('verbfile.js')); + app.createEnv('verb-foo-generator', fixtures('verbfile.js')); assert.equal(app.env.alias, 'foo'); - assert.equal(app.env.name, 'generate-foo'); - }); - - it('should try to resolve a path passed as the second arg', function() { - app.createEnv('generate-foo', 'generate-foo/verbfile.js'); - assert.equal(app.env.alias, 'foo'); - assert.equal(app.env.name, 'generate-foo'); + assert.equal(app.env.name, 'verb-foo-generator'); }); it('should throw an error when the path is not resolved', function(cb) { From ccc923f17ca8067694f9ab1f4e588cd5771e2180 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 3 May 2016 22:27:37 -0400 Subject: [PATCH 228/282] remove unused code from lib --- lib/commands.js | 1 - lib/commands/format.js | 11 ++--- lib/commands/init.js | 20 ++++----- lib/commands/tasks.js | 25 +++++------ lib/generator.js | 40 ++++++++++++----- lib/help.md | 6 --- lib/questions.js | 93 ++------------------------------------- lib/reflinks.js | 58 ++++++++++++++++++++++++ lib/templates/verbfile.js | 2 +- lib/utils.js | 30 +++++++++++++ 10 files changed, 147 insertions(+), 139 deletions(-) delete mode 100644 lib/help.md create mode 100644 lib/reflinks.js create mode 100644 lib/utils.js diff --git a/lib/commands.js b/lib/commands.js index a800022a..addc7972 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -4,7 +4,6 @@ var commands = require('./commands/'); module.exports = function(app, options) { app.debug('adding custom verb commands'); - for (var key in commands) { if (commands.hasOwnProperty(key)) { app.debug('adding command > %s', key); diff --git a/lib/commands/format.js b/lib/commands/format.js index ba5d6205..747a11a4 100644 --- a/lib/commands/format.js +++ b/lib/commands/format.js @@ -1,16 +1,17 @@ 'use strict'; -var reflinks = require('../reflinks'); -var format = require('gulp-format-md'); var path = require('path'); +var reflinks = require('../reflinks'); +var utils = require('../utils'); /** - * Format a markdown file using [pretty-remarkable][]. + * Format a markdown file using [pretty-remarkable][]. Optionally + * specify a `--dest` directory and/or file `--name`. * * ```sh * $ verb --format foo/bar.md * ``` - * @name --file + * @name --format * @api public */ @@ -23,7 +24,7 @@ module.exports = function(app, options) { app.src(path.resolve(filepath)) .pipe(reflinks(app)) - .pipe(format()) + .pipe(utils.format()) .pipe(app.dest(function(file) { file.basename = (config.name || path.basename(filepath)); return (config.dest || path.resolve(path.dirname(filepath))); diff --git a/lib/commands/init.js b/lib/commands/init.js index b575b24f..d4582d64 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -1,27 +1,25 @@ 'use strict'; -var path = require('path'); -var questions = require('../questions'); var debug = require('debug')('base:cli:init'); -var extend = require('extend-shallow'); -var get = require('get-value'); +var questions = require('../questions'); +var utils = require('../utils'); /** - * Ask initialization questions and persist answer data to the global - * config store. + * Initialize a prompt session and persist the answers to the `verb` object + * in the package.json in the current working directory. * * ```sh * $ --init * ``` - * @name init + * @name --init * @api public - * @cli public */ module.exports = function(app, base, options) { return function(val, key, config, next) { init(app, {save: false}, function(err, answers) { if (err) return next(err); + app.set('cache.initKeys', Object.keys(answers.config)); app.cli.process(answers, next); }); }; @@ -38,7 +36,7 @@ function init(app, options, cb) { options = {}; } - options = extend({}, options, app.options); + options = utils.extend({}, options, app.options); questions(app, options); app.questions.disable('save'); @@ -48,12 +46,12 @@ function init(app, options, cb) { debug('finished with init.choose "%j"', answers); - var plugins = arrayify(app, get(answers, 'config.plugins')); + var plugins = arrayify(app, utils.get(answers, 'config.plugins')); if (plugins.length) { app.ask('after', { save: false }, function(err, res) { if (err) return cb(err); - var answer = get(res, 'after.plugins'); + var answer = utils.get(res, 'after.plugins'); if (answer === true) { app.pkg.save(); app.npm.saveDev(plugins, function(err) { diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js index deca8650..72c33e3d 100644 --- a/lib/commands/tasks.js +++ b/lib/commands/tasks.js @@ -1,7 +1,6 @@ 'use strict'; -var exists = require('fs-exists-sync'); -var fs = require('fs'); +var utils = require('../utils'); /** * Run the given generators and tasks. This flag is unnecessary when @@ -35,10 +34,8 @@ module.exports = function(app, options) { } ran = true; - var argv = app.get('cache.argv'); - var configFile = options.env.configFile; - var tasks = setTasks(app, configFile, val, argv); + var tasks = setTasks(app, options.env.configFile, val, argv); app.generateEach(tasks, next); }; }; @@ -48,11 +45,11 @@ module.exports = function(app, options) { */ function setTasks(app, configFile, tasks, argv) { - if (argv.processed.hasOwnProperty('new')) { - return ['verb.new:' + argv.processed.new]; + if (argv.hasOwnProperty('new')) { + return ['verb.new:' + argv.new]; } - if (argv.processed.hasOwnProperty('format')) { + if (argv.init === true) { return []; } @@ -66,10 +63,12 @@ function setTasks(app, configFile, tasks, argv) { var configTasks = app.pkg.get('verb.tasks'); if (tasks.length === 1 && tasks[0] === 'default') { + var configExists = utils.exists(configFile); + // if a `verbfile.js` or custom configFile exists, return tasks - if (exists(configFile)) { + if (configExists) { if (configTasks && configTasks.length) { - app.pkg.logWarning('ignoring tasks defined in package.json:', {tasks: configTasks}); + app.pkg.logWarning('ignoring tasks defined in package.json:', configTasks); } return tasks; } @@ -78,16 +77,16 @@ function setTasks(app, configFile, tasks, argv) { return configTasks; } - var verbmd = exists('.verb.md'); + var verbmd = utils.exists('.verb.md'); // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default - if (verbmd && !exists(configFile)) { + if (verbmd && !configExists) { return ['readme']; } // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` if (!verbmd) { - return ['new:verbmd']; + return ['verb.new:prompt-verbmd']; } } return tasks; diff --git a/lib/generator.js b/lib/generator.js index b72b5e55..05aa013d 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,29 +1,45 @@ 'use strict'; -var cwd = require('memoize-path')(__dirname); +var path = require('path'); +var cwd = path.resolve.bind(path, __dirname, 'templates'); -module.exports = function(app, base) { - var templates = cwd('templates'); +module.exports = function(verb, base) { + var argv = verb.get('cache.argv'); /** * Generate a file from a template */ - app.register('new', function(sub) { - sub.task('verbfile', function(cb) { + verb.register('new', function(app) { + app.task('verbfile', function(cb) { file(app, 'verbfile.js', cb); }); - sub.task('verbmd', function(cb) { + app.task('verbmd', function(cb) { file(app, '.verb.md', cb); }); + + app.task('prompt-verbmd', function(cb) { + app.confirm('verbmd', 'Looks like .verb.md is missing, want to add one?'); + app.ask('verbmd', function(err, answers) { + if (err) { + cb(err); + return; + } + if (answers.verbmd) { + file(app, '.verb.md', cb); + return; + } + cb(); + }); + }); }); /** * Display `--help` when no tasks are defined */ - app.task('help', function(cb) { + verb.task('help', { silent: true }, function(cb) { base.cli.process({ help: true }, cb); }); @@ -31,19 +47,19 @@ module.exports = function(app, base) { * Default task */ - app.task('default', ['help']); + verb.task('default', argv.tasks); }; /** * Generate a file */ -function file(app, name, cb) { - app.src(name, {cwd: cwd('templates')()}) - .pipe(app.dest(app.cwd)) +function file(verb, name, cb) { + verb.src(name, {cwd: cwd('templates')}) + .pipe(verb.dest(verb.cwd)) .on('error', cb) .on('end', function() { - console.log(name, 'written to', app.cwd); + console.log(name, 'written to', verb.cwd); cb(); }); } diff --git a/lib/help.md b/lib/help.md deleted file mode 100644 index 027bbff0..00000000 --- a/lib/help.md +++ /dev/null @@ -1,6 +0,0 @@ - - ---disable=help disable this message -Try running `verb readme` if verb-readme-generator is installed - -Thanks for using verb! diff --git a/lib/questions.js b/lib/questions.js index cb832841..34513f4c 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -1,96 +1,7 @@ 'use strict'; -var isObject = require('isobject'); -var color = require('ansi-colors'); - module.exports = function(app, options) { - /** - * Allow the user to choose the stores to use for saving a value (after prompts) - */ - - app.question('set', 'To save your answers, pick one or more of the following (press to skip):', { - type: 'checkbox', - save: false, - choices: [{ - key: 'a', - name: color.yellow(' app store') + ' (for ' + app._name + '-specific values)', - value: 'app' - }, { - key: 'g', - name: color.yellow(' global store') + ' (for values to be shared across all base applications (generate, assemble, verb, update, etc))', - value: 'global' - }, { - key: 'p', - name: color.yellow(' project config') + ' (for project-specific values, saved to the "' + app._name + '" object in package.json)', - value: 'project' - }], - next: function(answer, question, answers, cb) { - // try to erase some of the junk produced by inquirer - process.stdout.write('\r\x1b[K'); - - answers.stores = answers.stores || {}; - // rename store choices to actual properties on `app` - answers.stores.set = answer.reduce(function(acc, name) { - if (name === 'app') name = 'store'; - if (name === 'global') name = 'globals'; - if (name === 'project') name = 'pkg'; - - if (!isObject(app[name]) || typeof app[name].set !== 'function') { - return acc; - } - acc.push(name); - return acc; - }, []); - - delete answers.set; - cb(null, answers); - } - }); - - /** - * Delete - */ - - app.question('del', 'Which stores would you like to delete the value from?:', { - type: 'checkbox', - save: false, - choices: [{ - key: 'a', - name: color.yellow(' app store') + ' (for ' + app._name + '-specific values)', - value: 'app' - }, { - key: 'g', - name: color.yellow(' global store') + ' (for values to be shared across all base applications (generate, assemble, verb, update, etc))', - value: 'global' - }, { - key: 'p', - name: color.yellow(' project config') + ' (for project-specific values, saved to the "' + app._name + '" object in package.json)', - value: 'project' - }], - next: function(answer, question, answers, cb) { - // try to erase some of the junk produced by inquirer - process.stdout.write('\r\x1b[K'); - - answers.stores = answers.stores || {}; - // rename store choices to actual properties on `app` - answers.stores.del = answer.reduce(function(acc, name) { - if (name === 'app') name = 'store'; - if (name === 'global') name = 'globals'; - if (name === 'project') name = 'pkg'; - - if (!isObject(app[name]) || typeof app[name].del !== 'function') { - return acc; - } - acc.push(name); - return acc; - }, []); - - delete answers.del; - cb(null, answers); - } - }); - /** * Config questions */ @@ -117,7 +28,9 @@ module.exports = function(app, options) { * Ask the user if they want to install plugins, if any were specified */ - app.confirm('after.plugins', 'Plugins need to be installed, want to do that now?'); + app.question('after.plugins', 'Plugins need to be installed, want to do that now?', { + type: 'confirm' + }); /** * Init questions diff --git a/lib/reflinks.js b/lib/reflinks.js new file mode 100644 index 00000000..37f293e3 --- /dev/null +++ b/lib/reflinks.js @@ -0,0 +1,58 @@ +'use strict'; + +var utils = require('./utils'); + +/** + * Lint readme.md for missing reflinks, and add them to + * package.json if enabled by the user. + */ + +module.exports = function(app) { + var arr = app.pkg.get('verb.reflinks') || []; + var re = /(\[[\w._-]+?\]\[\])/g; + var count = 0; + + return utils.through.obj(function(file, enc, next) { + var options = app.option('reflinks') || {}; + var matches = file.content.match(re); + + if (matches && matches.length) { + matches.forEach(function(match) { + var idx = match.indexOf(']'); + var name = match.slice(1, idx).trim(); + if (arr.indexOf(name) === -1) { + arr.push(name); + count++; + } + }); + } + + if (count === 0) { + next(null, file); + return; + } + + if (file.basename === '.verb.md') { + save(app, arr); + } + + utils.reflinks(arr, options, function(err, links) { + if (err) return next(err); + file.content += '\n\n'; + file.content += links.join('\n'); + next(null, file); + }); + }); +}; + +/** + * Save reflinks to package.json config + */ + +function save(app, arr) { + if (app && app.pkg && typeof app.pkg.set !== 'function') { + app.pkg.set('verb.reflinks', arr); + app.pkg.logInfo('updated package.json with:', { reflinks: arr }); + app.pkg.save(); + } +} diff --git a/lib/templates/verbfile.js b/lib/templates/verbfile.js index f8cf15a9..51504378 100644 --- a/lib/templates/verbfile.js +++ b/lib/templates/verbfile.js @@ -5,4 +5,4 @@ module.exports = function(verb) { cb(); }); -}; \ No newline at end of file +}; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 00000000..4d0a2fa7 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,30 @@ +'use strict'; + +var utils = require('lazy-cache')(require); +var fn = require; +require = utils; + +/** + * Lazily required module dependencies + */ + +require('ansi-colors', 'colors'); +require('base-runner', 'runner'); +require('extend-shallow', 'extend'); +require('fs-exists-sync', 'exists'); +require('generate'); +require('gulp-format-md', 'format'); +require('isobject', 'isObject'); +require('memoize-path', 'memo'); +require('reflinks'); +require('set-value', 'set'); +require('through2', 'through'); +require('unset-value', 'del'); +require('yargs-parser', 'yargs'); +require = fn; + +/** + * Expose `utils` modules + */ + +module.exports = utils; From 08c5df54010491996082297b87344d6ab6db0cd4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 3 May 2016 22:27:49 -0400 Subject: [PATCH 229/282] reflect changes from base-runner --- bin/verb.js | 81 ++++++++++------------------------------------------- 1 file changed, 15 insertions(+), 66 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index a09fcbad..6c4345ce 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -2,88 +2,37 @@ process.env.GENERATE_CLI = true; -var argv = require('minimist')(process.argv.slice(2)); -var runner = require('base-runner'); +var generator = require('../lib/generator'); var commands = require('../lib/commands'); +var utils = require('../lib/utils'); +var argv = utils.yargs(process.argv.slice(2)); var Verb = require('..'); -// lift-off options -var options = { - name: 'verb', - runner: require('../package'), - lookup: lookup -}; - /** * Initialize verb CLI */ -runner(Verb, options, argv, function(err, app, runnerContext) { - if (err) app.handleError(err); - - /** - * Register `verb` generator and tasks - */ - - app.register('verb', require('../lib/generator')); - - /** - * Load custom commands - */ - - commands(app, runnerContext); - - // get the config object from package.json - var config = app.pkg.get(runnerContext.env.name); - var args = app.argv(runnerContext.argv); - - // set parsed and unparsed argv on `cache` - app.set('cache.argv', { - orig: argv, - parsed: runnerContext.argv, - processed: args - }); - - if (config && !args.noconfig) { - app.set('cache.config', config); +utils.runner(Verb, {name: 'verb'}, argv, function(err, app, ctx) { + if (err) { + console.log(err.stack); + process.exit(1); } - app.option('runner.new', 'files'); - app.option(argv); - - /** - * Custom `lookup` function, for resolving generators - */ + app.on('error', function(err) { + console.log(err.stack); + process.exit(1); + }); + commands(app, ctx); + app.register('verb', require('../lib/generator')); app.option('lookup', lookup(app)); - /** - * Process argv - */ - - if (args.init === true) { - app.cli.process({init: true}, function(err, obj) { - if (err) app.emit('error', err); - delete obj.init; - run(app, obj); - }); - } else { - run(app, args); - } -}); - -/** - * cli process - */ - -function run(app, args) { - app.cli.process(args, function(err, obj) { + app.cli.process(ctx.argv, function(err) { if (err) app.emit('error', err); - app.emit('done'); process.exit(); }); -} +}); /** * Custom lookup function for resolving generators From 8ec427cb4cd532f4b9898b87af19942378b2f90b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 3 May 2016 22:29:38 -0400 Subject: [PATCH 230/282] lint --- index.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/index.js b/index.js index 45000594..d5c95dee 100644 --- a/index.js +++ b/index.js @@ -44,31 +44,14 @@ Verb.prototype.initVerb = function(opts) { this.data({before: {}, after: {}}); this.debug('initializing', __filename); - this.option('toAlias', function(name) { - return name.replace( /^verb-([^-]+)-generator$/, '$1'); + return name.replace(/^verb-([^-]+)-generator$/, '$1'); }); this.data({runner: require('./package')}); Verb.emit('init', this, this.base); }; -/** - * Add a generator and its tasks to the tree object. - * Mostly used for debugging, but also useful for - * creating custom-formatted visual trees. - * - * @param {String} `name` - * @param {Object} `app` - */ - -Verb.prototype.addLeaf = function(name, app) { - this.tree[name] = {}; - this.tree[name].tasks = Object.keys(app.tasks || {}); - this.tree[name].generators = app.tree; - return this; -}; - /** * Expose static `is*` methods from Templates */ From 1b19801a5845a681896fedf573d0a498673da140 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 3 May 2016 22:30:56 -0400 Subject: [PATCH 231/282] update verb config, description --- .../lang/en/recipes/inspecting-the-context.md | 63 ------------------- docs/lang/en/usage/faq.md | 16 ----- package.json | 56 ++++++++++------- 3 files changed, 32 insertions(+), 103 deletions(-) delete mode 100644 docs/lang/en/recipes/inspecting-the-context.md delete mode 100644 docs/lang/en/usage/faq.md diff --git a/docs/lang/en/recipes/inspecting-the-context.md b/docs/lang/en/recipes/inspecting-the-context.md deleted file mode 100644 index d0de15ea..00000000 --- a/docs/lang/en/recipes/inspecting-the-context.md +++ /dev/null @@ -1,63 +0,0 @@ -# Inspecting the context - -Generally, "inspecting the context" means that we're looking at the object that will be used for rendering a template or templates. - -**Where can we see the context?** - -The context object is created in-memory at render time, and to inspect it we need to see the object _as a template is being rendered_ (not before or after), which means there is really only one place to do it: inside a helper function. - -**Create a helper** - -A simple `log` helper can be used to show any object we pass to it in the console. Add the following to your `verbfile.js`: - -```js -app.helper('log', function(context) { - console.log(context); -}); -``` -Next, add the following to the template you want to inspect: - -```handlebars -{{log .}} -``` -Handlebars uses `.` as an alias for `this`. You can replace the `.` with any variable you want to inspect. - -**Other objects** - -Inside the helper, there are a number of different objects we can inspect, depending on how we're building up the context. Here is a quick overview: - -```js -app.helper('log', function(context) { - console.log(arguments); - console.log(context); // the object passed to the helper - console.log(context.hash); // hash arguments, like `foo="bar"` - - console.log(this); // handlebars context - console.log(this.options); // assemble `options` - console.log(this.context); // context of the current "view" - console.log(this.app); // assemble instance -}); -``` - -## Debugging - -**Detective work** - -Sometimes it takes a little more work to figure out what's happening. In addition to inspecting the context at render time, we can get a better perspective on what's happening if we also: - -- **inspect pre-render data**: inspect the objects that will be used to create the context **before** passing the object to the `render` method -- **diff content**: diff the generated content **after** the view is rendered (assuming the view makes it this far and is actually being rendered) -- **inspect post-render data**: inspect the `view.data` object post-render to see what, if anything, has been changed - -```js -// before -// - inspect `locals` -// - inspect `view.data` -// - inspect `app.cache.data` -app.render(view, locals, function(err, res) { - - // after - // - inspect `res.data` - console.log(res.data); -}); -``` \ No newline at end of file diff --git a/docs/lang/en/usage/faq.md b/docs/lang/en/usage/faq.md deleted file mode 100644 index 8424ab0f..00000000 --- a/docs/lang/en/usage/faq.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -layout: usage -meta_title: How to Use Verb - Verb Docs -meta_description: An in depth guide to using the Verb blogging platform. Got Verb but not sure how to get going? Start here! -heading: Using Verb -subheading: Finding your way around, and getting set up the way you want -chapter: usage -section: faq -permalink: /usage/faq/ -prev_section: writing -canonical: http://support.ghost.org/faq/ -redirectToCanonical: true ---- - - -## FAQ diff --git a/package.json b/package.json index 4540c4be..39914d94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "verb", - "description": "Documentation build system for GitHub projects.", + "description": "Documentation build system for GitHub projects, with full support for gulp and assemble plugins. Built verb can be used to create documentation generators, themes, documentation websites and much more!", "version": "0.9.0", "homepage": "https://github.com/verbose/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", @@ -27,27 +27,32 @@ }, "dependencies": { "ansi-colors": "^0.1.0", - "base-runner": "^0.6.3", + "base-runner": "^0.7.0", "debug": "^2.2.0", "export-files": "^2.1.1", "extend-shallow": "^2.0.1", "fs-exists-sync": "^0.1.0", "generate": "^0.5.4", - "isobject": "^2.0.0", + "gulp-format-md": "^0.1.9", + "isobject": "^2.1.0", + "lazy-cache": "^2.0.1", "memoize-path": "^0.1.2", - "minimist": "^1.2.0", - "reflinks": "^0.2.1" + "reflinks": "^0.2.6", + "set-value": "^0.3.3", + "through2": "^2.0.1", + "unset-value": "^0.1.1", + "yargs-parser": "^2.4.0" }, "devDependencies": { "async": "^1.5.2", "base-store": "^0.4.2", "buffer-equal": "^1.0.0", - "consolidate": "^0.14.0", + "consolidate": "^0.14.1", "define-property": "^0.2.5", "engine-base": "^0.1.2", "engine-handlebars": "^0.8.0", "event-stream": "^3.3.2", - "expect": "^1.18.0", + "expect": "^1.19.0", "generate-foo": "^0.1.5", "generator-util": "^0.2.9", "get-value": "^2.0.5", @@ -55,34 +60,34 @@ "graceful-fs": "^4.1.3", "gulp": "^3.9.1", "gulp-eslint": "^2.0.0", - "gulp-format-md": "^0.1.8", "gulp-istanbul": "^0.10.4", "gulp-mocha": "^2.2.0", "helper-example": "^0.1.0", "is-buffer": "^1.1.3", - "kind-of": "^3.0.2", + "kind-of": "^3.0.3", "load-pkg": "^3.0.1", "mocha": "^2.4.5", "parser-front-matter": "^1.3.0", "resolve-glob": "^0.1.8", "rimraf": "^2.5.2", "should": "^8.3.1", - "sinon": "^1.17.3", + "sinon": "^1.17.4", "spawn-commands": "^0.3.1", "swig": "^1.4.2", - "through2": "^2.0.1", "vinyl": "^1.1.1" }, "keywords": [ "verb" ], + "lintDeps": { + "ignore": [ + "coverage", + "docs" + ] + }, "verb": { - "run": true, "toc": false, - "layout": "default", - "tasks": [ - "readme" - ], + "layout": "default-dev", "plugins": [ "gulp-format-md" ], @@ -95,20 +100,23 @@ }, "reflinks": [ "assemble", + "base-runner", "generate", - "update", - "verb", - "verb-readme-generator", + "gulp", "handlebars", "lodash", + "pretty-remarkable", "swig", - "gulp" + "update", + "verb", + "verb-readme-generator" ], + "task": [ + "readme" + ], + "sections": false, "lint": { "reflinks": true } - }, - "lintDeps": { - "ignore": [] } -} +} \ No newline at end of file From 5f3d68b824d2741913216e1eb3a2ffa0e9067111 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Thu, 5 May 2016 06:08:52 -0400 Subject: [PATCH 232/282] add/update vinyl-fs tests --- test/app.dest.js | 4 +- test/support/spy.js | 2 +- test/vinyl.js | 13 + test/vinyl/LICENSE | 20 + test/vinyl/dest-modes.js | 483 +++++++++++ test/vinyl/dest-times.js | 223 +++++ test/vinyl/dest.js | 1229 +++++++++++++++++++++++++++ test/vinyl/file-operations.js | 917 ++++++++++++++++++++ test/vinyl/fixtures/bom-utf16be.txt | Bin 0 -> 244 bytes test/vinyl/fixtures/bom-utf16le.txt | Bin 0 -> 244 bytes test/vinyl/fixtures/bom-utf8.txt | 1 + test/vinyl/fixtures/foo/bar/baz.txt | 1 + test/vinyl/fixtures/test.coffee | 1 + test/vinyl/fixtures/wow/suchempty | 1 + test/vinyl/not-owned.js | 75 ++ test/vinyl/not-owned/not-owned.txt | 1 + test/vinyl/spy.js | 32 + test/vinyl/src.js | 514 +++++++++++ test/vinyl/symlink.js | 455 ++++++++++ 19 files changed, 3969 insertions(+), 3 deletions(-) create mode 100644 test/vinyl.js create mode 100644 test/vinyl/LICENSE create mode 100644 test/vinyl/dest-modes.js create mode 100644 test/vinyl/dest-times.js create mode 100644 test/vinyl/dest.js create mode 100644 test/vinyl/file-operations.js create mode 100644 test/vinyl/fixtures/bom-utf16be.txt create mode 100644 test/vinyl/fixtures/bom-utf16le.txt create mode 100644 test/vinyl/fixtures/bom-utf8.txt create mode 100644 test/vinyl/fixtures/foo/bar/baz.txt create mode 100644 test/vinyl/fixtures/test.coffee create mode 100644 test/vinyl/fixtures/wow/suchempty create mode 100644 test/vinyl/not-owned.js create mode 100644 test/vinyl/not-owned/not-owned.txt create mode 100644 test/vinyl/spy.js create mode 100644 test/vinyl/src.js create mode 100644 test/vinyl/symlink.js diff --git a/test/app.dest.js b/test/app.dest.js index 2fc93d2e..9c0dc1ed 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -624,7 +624,7 @@ describe('app.dest', function() { stream.write(expectedFile); }); - it('should report stat errors', function(cb) { + it.skip('should report stat errors', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -659,7 +659,7 @@ describe('app.dest', function() { stream.write(expectedFile); }); - it('should report fchmod errors', function(cb) { + it.skip('should report fchmod errors', function(cb) { if (isWindows) { this.skip(); return; diff --git a/test/support/spy.js b/test/support/spy.js index f64b6cec..cb9ad861 100644 --- a/test/support/spy.js +++ b/test/support/spy.js @@ -28,5 +28,5 @@ module.exports = { fchmodSpy: maybeCallAsync(fs, 'fchmod'), futimesSpy: maybeCallAsync(fs, 'futimes'), statSpy: maybeCallAsync(fs, 'stat'), - fstatSpy: maybeCallAsync(fs, 'fstat'), + fstatSpy: maybeCallAsync(fs, 'fstat') }; diff --git a/test/vinyl.js b/test/vinyl.js new file mode 100644 index 00000000..f4d64df0 --- /dev/null +++ b/test/vinyl.js @@ -0,0 +1,13 @@ +var fs = require('fs'); +var path = require('path'); + +/** + * Placeholder for when assemble-core is updated with the latest vinyl-fs + */ + +// var cwd = path.resolve(__dirname, 'vinyl'); +// fs.readdirSync(cwd).forEach(function(file) { +// if (/\.js$/.test(file)) { +// require(path.resolve(cwd, file)); +// } +// }); diff --git a/test/vinyl/LICENSE b/test/vinyl/LICENSE new file mode 100644 index 00000000..3ebc3089 --- /dev/null +++ b/test/vinyl/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013-2016 Fractal + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/vinyl/dest-modes.js b/test/vinyl/dest-modes.js new file mode 100644 index 00000000..cdcc3cf5 --- /dev/null +++ b/test/vinyl/dest-modes.js @@ -0,0 +1,483 @@ +'use strict'; + +var os = require('os'); +var path = require('path'); + +var fs = require('graceful-fs'); +var del = require('delete'); +var File = require('vinyl'); +var expect = require('expect'); +var through = require('through2'); + +var vfs = require('vinyl-fs'); + +function wipeOut(cb) { + this.timeout(20000); + + expect.restoreSpies(); + + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + del(path.join(__dirname, './fixtures/highwatermark'), function(err) { + if (err) return cb(err); + del(path.join(__dirname, './out-fixtures/'), cb); + }); +} + +var MASK_MODE = parseInt('7777', 8); + +function masked(mode) { + return mode & MASK_MODE; +} + +var isWindows = (os.platform() === 'win32'); +var isDarwin = (os.platform() === 'darwin'); + +describe('.dest() with custom modes', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should set the mode of a written buffer file if set on the vinyl object', function(done) { + if (isWindows) { + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + console.log('Windows is treated as though it does not have permission to make this operation.'); + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('655', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + + + it('should set the sticky bit on the mode of a written stream file if set on the vinyl object', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('1655', 8); + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + setTimeout(function() { + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should set the mode of a written stream file if set on the vinyl object', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('655', 8); + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + setTimeout(function() { + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should set the mode of a written directory if set on the vinyl object', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test'); + var expectedMode = parseInt('655', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should set sticky bit on the mode of a written directory if set on the vinyl object', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test'); + var expectedMode = parseInt('1655', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should write new files with the mode specified in options', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('744', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, mode: expectedMode }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should update file mode to match the vinyl mode', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var startMode = parseInt('0655', 8); + var expectedMode = parseInt('0722', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, startMode); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should update directory mode to match the vinyl mode', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputBase = path.join(__dirname, './fixtures/'); + var inputPath = path.join(__dirname, './fixtures/wow'); + var expectedPath = path.join(__dirname, './out-fixtures/wow'); + var expectedBase = path.join(__dirname, './out-fixtures'); + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: expectedPath, + stat: fs.statSync(inputPath), + }); + var startMode = firstFile.stat.mode; + var expectedMode = parseInt('727', 8); + + var expectedFile = new File(firstFile); + expectedFile.stat.mode = (startMode & ~parseInt('7777', 8)) | expectedMode; + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(firstFile); + stream.write(expectedFile); + stream.end(); + }); + + it('should use different modes for files and directories', function(done) { + if (isWindows) { + this.skip(); + return; + } + + + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + var expectedBase = path.join(__dirname, './out-fixtures/wow'); + var expectedPath = path.join(__dirname, './out-fixtures/wow/suchempty'); + // NOTE: Darwin does not set setgid + var expectedDirMode = isDarwin ? parseInt('755', 8) : parseInt('2755', 8); + var expectedFileMode = parseInt('655', 8); + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: fs.readFileSync(inputPath), + stat: fs.statSync(inputPath), + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedBase).mode)).toEqual(expectedDirMode); + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedFileMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { + cwd: __dirname, + mode: expectedFileMode, + dirMode: expectedDirMode, + }); + stream.on('end', onEnd); + stream.write(firstFile); + stream.end(); + }); + + it('should not fchmod a matching file', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('711', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(fchmodSpy.calls.length).toEqual(0); + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should see a file with special chmod (setuid/setgid/sticky) as distinct', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = parseInt('3722', 8); + var normalMode = parseInt('722', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: normalMode, + }, + }); + + var onEnd = function() { + expect(fchmodSpy.calls.length).toEqual(1); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, expectedMode); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should report fchmod errors', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = parseInt('722', 8); + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function() { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + }); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('error', function(err) { + expect(err).toExist(); + expect(fchmodSpy.calls.length).toEqual(1); + done(); + }); + stream.write(expectedFile); + }); +}); diff --git a/test/vinyl/dest-times.js b/test/vinyl/dest-times.js new file mode 100644 index 00000000..0af64735 --- /dev/null +++ b/test/vinyl/dest-times.js @@ -0,0 +1,223 @@ +'use strict'; + +var os = require('os'); +var path = require('path'); + +var fs = require('graceful-fs'); +var del = require('delete'); +var File = require('vinyl'); +var expect = require('expect'); + +var vfs = require('vinyl-fs'); + +function wipeOut() { + this.timeout(20000); + + expect.restoreSpies(); + + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + return del(path.join(__dirname, './out-fixtures/')); +} + +var isWindows = (os.platform() === 'win32'); + +describe('.dest() with custom times', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should not call futimes when no mtime is provided on the vinyl stat', function(done) { + if (isWindows) { + console.log('Changing the time of a directory errors in Windows.'); + console.log('Windows is treated as though it does not have permission to make this operation.'); + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var earlier = Date.now() - 1000; + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: {}, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(0); + expect(stats.atime.getTime()).toBeGreaterThan(earlier); + expect(stats.mtime.getTime()).toBeGreaterThan(earlier); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should call futimes when an mtime is provided on the vinyl stat', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMtime = fs.lstatSync(inputPath).mtime; + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mtime: expectedMtime, + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(1); + expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); + expect(expectedFile.stat.mtime).toEqual(expectedMtime); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should not call futimes when provided mtime on the vinyl stat is invalid', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var earlier = Date.now() - 1000; + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mtime: new Date(undefined), + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(0); + expect(stats.mtime.getTime()).toBeGreaterThan(earlier); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should call futimes when provided mtime on the vinyl stat is valid but provided atime is invalid', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMtime = fs.lstatSync(inputPath).mtime; + var invalidAtime = new Date(undefined); + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + atime: invalidAtime, + mtime: expectedMtime, + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(1); + expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should write file atime and mtime using the vinyl stat', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedAtime = fs.lstatSync(inputPath).atime; + var expectedMtime = fs.lstatSync(inputPath).mtime; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + atime: expectedAtime, + mtime: expectedMtime, + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(stats.atime.getTime()).toEqual(expectedAtime.getTime()); + expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); + expect(expectedFile.stat.mtime).toEqual(expectedMtime); + expect(expectedFile.stat.atime).toEqual(expectedAtime); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); +}); diff --git a/test/vinyl/dest.js b/test/vinyl/dest.js new file mode 100644 index 00000000..551f8ea7 --- /dev/null +++ b/test/vinyl/dest.js @@ -0,0 +1,1229 @@ +'use strict'; + +var spies = require('./spy'); +var chmodSpy = spies.chmodSpy; +var fchmodSpy = spies.fchmodSpy; +var futimesSpy = spies.futimesSpy; +var fstatSpy = spies.fstatSpy; + +var vfs = require('vinyl-fs'); + +var os = require('os'); +var path = require('path'); +var fs = require('graceful-fs'); +var del = require('delete'); +var Writeable = require('readable-stream/writable'); +var expect = require('expect'); + +var bufEqual = require('buffer-equal'); +var through = require('through2'); +var File = require('vinyl'); + +var should = require('should'); +require('mocha'); + +function wipeOut(cb) { + this.timeout(20000); + spies.setError('false'); + fstatSpy.reset(); + chmodSpy.reset(); + fchmodSpy.reset(); + futimesSpy.reset(); + expect.restoreSpies(); + + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + del(path.join(__dirname, './fixtures/highwatermark'), function(err) { + if (err) return cb(err); + del(path.join(__dirname, './out-fixtures/'), cb); + }); +}; + +var dataWrap = function(fn) { + return function(data, enc, cb) { + fn(data); + cb(); + }; +}; + +var realMode = function(n) { + return n & parseInt('777', 8); +}; + +function noop() {} + +describe('dest stream', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it.skip('should explode on invalid folder (empty)', function(done) { + var stream; + try { + stream = vfs.dest(); + } catch (err) { + should.exist(err); + should.not.exist(stream); + done(); + } + }); + + it.skip('should explode on invalid folder (empty string)', function(done) { + var stream; + try { + stream = vfs.dest(''); + } catch (err) { + should.exist(err); + should.not.exist(stream); + done(); + } + }); + + it('should not explode if the sourcemap option is true', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + var stream = vfs.dest(path.join(__dirname, './out-fixtures/'), { sourcemaps: true }); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not explode if the sourcemap option is string', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + var stream = vfs.dest(path.join(__dirname, './out-fixtures/'), { sourcemaps: '.' }); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not explode if sourcemap option is an object', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var options = { + sourcemaps: { + addComment: false, + }, + }; + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + var stream = vfs.dest(path.join(__dirname, './out-fixtures/'), options); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should pass through writes with cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should pass through writes with default cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + done(); + }; + + var stream = vfs.dest(path.join(__dirname, './out-fixtures/')); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should not write null files', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(false); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with relative cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: path.relative(process.cwd(), __dirname) }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder with function and relative cwd', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedContents = fs.readFileSync(inputPath); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = vfs.dest(function(file) { + should.exist(file); + file.should.equal(expectedFile); + return './out-fixtures'; + }, { cwd: path.relative(process.cwd(), __dirname) }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write buffer files to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should write streaming files to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered))); + bufferStream.on('finish', onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + setTimeout(function() { + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should write directories to the right folder', function(done) { + var inputPath = path.join(__dirname, './fixtures/test'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test'); + var expectedCwd = __dirname; + var expectedBase = path.join(__dirname, './out-fixtures'); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + }, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); + buffered[0].base.should.equal(expectedBase, 'base should have changed'); + buffered[0].path.should.equal(expectedPath, 'path should have changed'); + fs.existsSync(expectedPath).should.equal(true); + fs.lstatSync(expectedPath).isDirectory().should.equal(true); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should allow piping multiple dests in streaming mode', function(done) { + var inputPath1 = path.join(__dirname, './out-fixtures/multiple-first'); + var inputPath2 = path.join(__dirname, './out-fixtures/multiple-second'); + var inputBase = path.join(__dirname, './out-fixtures/'); + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var stream1 = vfs.dest('./out-fixtures/', { cwd: __dirname }); + var stream2 = vfs.dest('./out-fixtures/', { cwd: __dirname }); + var content = fs.readFileSync(srcPath); + var rename = through.obj(function(file, _, next) { + file.path = inputPath2; + this.push(file); + next(); + }); + + stream1.on('data', function(file) { + file.path.should.equal(inputPath1); + }); + + stream1.pipe(rename).pipe(stream2); + stream2.on('data', function(file) { + file.path.should.equal(inputPath2); + }).once('end', function() { + fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); + fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); + done(); + }); + + var file = new File({ + base: inputBase, + path: inputPath1, + cwd: __dirname, + contents: content, + }); + + stream1.write(file); + stream1.end(); + }); + + it('should write new files with the default user mode', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('666', 8) & (~process.umask()); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + buffered[0].should.equal(expectedFile); + fs.existsSync(expectedPath).should.equal(true); + realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); + done(); + }; + + chmodSpy.reset(); + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(expectedFile); + stream.end(); + }); + + it('should change to the specified base as string', function(done) { + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + + var firstFile = new File({ + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath), + }); + + var buffered = []; + + var onEnd = function() { + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { + cwd: __dirname, + base: inputBase, + }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should change to the specified base as function', function(done) { + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + + var firstFile = new File({ + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath), + }); + + var buffered = []; + + var onEnd = function() { + buffered[0].base.should.equal(inputBase); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { + cwd: __dirname, + base: function(file) { + should.exist(file); + file.path.should.equal(inputPath); + return inputBase; + }, + }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + + it('should report IO errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, 0); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('error', function(err) { + expect(err).toExist(); + done(); + }); + stream.write(expectedFile); + }); + + it('should report stat errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = parseInt('722', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + spies.setError(function(mod, fn) { + if (fn === 'fstat' && typeof arguments[2] === 'number') { + return new Error('stat error'); + } + }); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('error', function(err) { + err.message.should.equal('stat error'); + done(); + }); + stream.write(expectedFile); + }); + + it('should not overwrite files with overwrite option set to false', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, './out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); + done(); + }; + + // Write expected file which should not be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: false }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should overwrite files with overwrite option set to true', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, './out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); + done(); + }; + + // This should be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: true }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should not overwrite files with overwrite option set to a function that returns false', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, './out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); + done(); + }; + + // Write expected file which should not be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: function() { + return false; + }, }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should overwrite files with overwrite option set to a function that returns true', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var inputContents = fs.readFileSync(inputPath); + + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedBase = path.join(__dirname, './out-fixtures'); + var existingContents = 'Lorem Ipsum'; + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: inputContents, + }); + + var buffered = []; + + var onEnd = function() { + buffered.length.should.equal(1); + bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); + done(); + }; + + // This should be overwritten + fs.mkdirSync(expectedBase); + fs.writeFileSync(expectedPath, existingContents); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: function() { + return true; + }, }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should create symlinks when the `symlink` attribute is set on the file', function(done) { + var inputPath = path.join(__dirname, './fixtures/test-create-dir-symlink'); + var inputBase = path.join(__dirname, './fixtures/'); + var inputRelativeSymlinkPath = 'wow'; + + var expectedPath = path.join(__dirname, './out-fixtures/test-create-dir-symlink'); + + var inputFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, // '' + }); + + // `src()` adds this side-effect with `keepSymlinks` option set to false + inputFile.symlink = inputRelativeSymlinkPath; + + var buffered = []; + + var onEnd = function() { + fs.readlink(buffered[0].path, function() { + buffered[0].symlink.should.equal(inputFile.symlink); + buffered[0].path.should.equal(expectedPath); + done(); + }); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.on('error', done); + stream.pipe(bufferStream); + stream.write(inputFile); + stream.end(); + }); + + it('should emit finish event', function(done) { + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + stream.once('finish', function() { + done(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: new Buffer('1234567890'), + }); + + stream.write(file); + stream.end(); + }); + + it('does not get clogged by highWaterMark', function(done) { + fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); + for (var idx = 0; idx < 17; idx++) { + fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); + } + + var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); + var srcStream = vfs.src(srcPath); + var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var fileCount = 0; + var countFiles = through.obj(function(file, enc, cb) { + fileCount++; + + cb(null, file); + }); + + destStream.once('finish', function() { + fileCount.should.equal(17); + done(); + }); + + srcStream.pipe(countFiles).pipe(destStream); + }); + + it('allows backpressure when piped to another, slower stream', function(done) { + this.timeout(20000); + + fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); + for (var idx = 0; idx < 24; idx++) { + fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); + } + + var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); + var srcStream = vfs.src(srcPath); + var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var fileCount = 0; + var countFiles = through.obj(function(file, enc, cb) { + fileCount++; + + cb(null, file); + }); + + var slowFileCount = 0; + var slowCountFiles = new Writeable({ + objectMode: true, + write: function(file, enc, cb) { + slowFileCount++; + + setTimeout(function() { + cb(null, file); + }, 250); + }, + }); + + slowCountFiles.once('finish', function() { + fileCount.should.equal(24); + slowFileCount.should.equal(24); + done(); + }); + + srcStream + .pipe(countFiles) + .pipe(destStream) + .pipe(slowCountFiles); + }); + + it('should respect readable listeners on destination stream', function(done) { + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var srcStream = vfs.src(srcPath); + var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + srcStream + .pipe(destStream); + + var readables = 0; + destStream.on('readable', function() { + var data = destStream.read(); + + if (data != null) { + readables++; + } + }); + + destStream.on('error', done); + + destStream.on('finish', function() { + readables.should.equal(1); + done(); + }); + }); + + it('should respect data listeners on destination stream', function(done) { + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var srcStream = vfs.src(srcPath); + var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + srcStream + .pipe(destStream); + + var datas = 0; + destStream.on('data', function() { + datas++; + }); + + destStream.on('error', done); + + destStream.on('finish', function() { + datas.should.equal(1); + done(); + }); + }); + + it('sinks the stream if all the readable event handlers are removed', function(done) { + fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); + for (var idx = 0; idx < 17; idx++) { + fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); + } + + var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); + var srcStream = vfs.src(srcPath); + var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var fileCount = 0; + var countFiles = through.obj(function(file, enc, cb) { + fileCount++; + + cb(null, file); + }); + + destStream.on('readable', noop); + + destStream.once('finish', function() { + fileCount.should.equal(17); + done(); + }); + + srcStream.pipe(countFiles).pipe(destStream); + + process.nextTick(function() { + destStream.removeListener('readable', noop); + }); + }); + + it('sinks the stream if all the data event handlers are removed', function(done) { + + this.timeout(10000); + + fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); + for (var idx = 0; idx < 17; idx++) { + fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); + } + + var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); + var srcStream = vfs.src(srcPath); + var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + + var fileCount = 0; + function onData() { + fileCount++; + } + + var countFiles = through.obj(function(file, enc, cb) { + onData(); + + cb(null, file); + }); + + destStream.on('data', onData); + + destStream.once('finish', function() { + fileCount.should.equal(17); + done(); + }); + + srcStream.pipe(countFiles).pipe(destStream); + + process.nextTick(function() { + destStream.removeListener('data', onData); + }); + }); + + it('should pass options to through2', function(done) { + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var content = fs.readFileSync(srcPath); + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, objectMode: false }); + + stream.on('error', function(err) { + err.should.match(/Invalid non-string\/buffer chunk/); + done(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: content, + }); + + stream.write(file); + stream.end(); + }); + + it('should successfully process unbuffered items', function(done) { + var srcPath = path.join(__dirname, './fixtures/*'); + var srcStream = vfs.src(srcPath, { buffer: false }); + var destStream = vfs.dest('./out-fixtures', { cwd: __dirname }); + + srcStream + .pipe(destStream) + .once('finish', done); + }); + + it('should not exhaust available file descriptors when streaming thousands of files', function(done) { + // This can be a very slow test on boxes with slow disk i/o + this.timeout(0); + + // Make a ton of files. Changed from hard links due to Windows failures + var numFiles = 6000; + fs.mkdirSync(path.join(__dirname, './out-fixtures')); + fs.mkdirSync(path.join(__dirname, './out-fixtures/in/')); + + for (var idx = 0; idx < numFiles; idx++) { + fs.writeFileSync(path.join(__dirname, './out-fixtures/in/test' + idx + '.coffee'), ''); + } + + var srcStream = vfs.src(path.join(__dirname, './out-fixtures/in/*.coffee'), { buffer: false }); + var destStream = vfs.dest('./out-fixtures/out/', { cwd: __dirname }); + + var fileCount = 0; + + srcStream + .pipe(through.obj(function(file, enc, cb) { + fileCount++; + + cb(null, file); + })) + .pipe(destStream) + .once('finish', function() { + fileCount.should.equal(numFiles); + done(); + }); + }); + + it('errors if we cannot mkdirp', function(done) { + var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + }); + + var outputDir = path.join(__dirname, './out-fixtures/'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + }); + + var stream = vfs.dest(outputDir); + stream.write(expectedFile); + stream.on('error', function(err) { + expect(err).toExist(); + expect(mkdirSpy.calls.length).toEqual(1); + done(); + }); + }); + + it('errors if vinyl object is a directory and we cannot mkdirp', function(done) { + var ogMkdir = fs.mkdir; + + var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { + if (mkdirSpy.calls.length > 1) { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + } else { + ogMkdir.apply(null, arguments); + } + }); + + var outputDir = path.join(__dirname, './out-fixtures/'); + var inputPath = path.join(__dirname, './other-dir/'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + }, + }); + + var stream = vfs.dest(outputDir); + stream.write(expectedFile); + stream.on('error', function(err) { + expect(err).toExist(); + expect(mkdirSpy.calls.length).toEqual(2); + done(); + }); + }); + + // TODO: is this correct behavior? had to adjust it + it('does not error if vinyl object is a directory and we cannot open it', function(done) { + var outputDir = path.join(__dirname, './out-fixtures/'); + var inputPath = path.join(__dirname, './other-dir/'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + mode: parseInt('000', 8), + }, + }); + + var stream = vfs.dest(outputDir); + stream.write(expectedFile); + stream.on('error', function(err) { + expect(err).toNotExist(); + done(err); + }); + stream.end(function() { + var exists = fs.existsSync(path.join(outputDir, './other-dir/')); + expect(exists).toEqual(true); + done(); + }); + }); + + it('errors if vinyl object is a directory and open errors', function(done) { + var openSpy = expect.spyOn(fs, 'open').andCall(function(writePath, flag, cb) { + cb(new Error('mocked error')); + }); + + var outputDir = path.join(__dirname, './out-fixtures/'); + var inputPath = path.join(__dirname, './other-dir/'); + + var expectedFile = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + }, + }); + + var stream = vfs.dest(outputDir); + stream.write(expectedFile); + stream.on('error', function(err) { + expect(err).toExist(); + expect(openSpy.calls.length).toEqual(1); + done(); + }); + }); + + it('error if content stream errors', function(done) { + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + }); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.write(expectedFile); + setTimeout(function() { + contentStream.emit('error', new Error('mocked error')); + }, 100); + stream.on('error', function(err) { + expect(err).toExist(); + done(); + }); + }); +}); diff --git a/test/vinyl/file-operations.js b/test/vinyl/file-operations.js new file mode 100644 index 00000000..748e1a0c --- /dev/null +++ b/test/vinyl/file-operations.js @@ -0,0 +1,917 @@ +'use strict'; + +var expect = require('expect'); + +var os = require('os'); +var fs = require('graceful-fs'); +var del = require('delete'); +var path = require('path'); +var File = require('vinyl'); +var buffer = require('buffer'); +var defaultResolution = require('default-resolution'); + +var fo = require('vinyl-fs/lib/file-operations'); + +var closeFd = fo.closeFd; +var isOwner = fo.isOwner; +var writeFile = fo.writeFile; +var getModeDiff = fo.getModeDiff; +var getTimesDiff = fo.getTimesDiff; +var updateMetadata = fo.updateMetadata; + +var resolution = defaultResolution(); + +var MASK_MODE = parseInt('7777', 8); + +function masked(mode) { + return mode & MASK_MODE; +} + +function noop() {} + +var isWindows = (os.platform() === 'win32'); + +describe('isOwner', function() { + + var ownerStat = { + uid: 9001, + }; + + var nonOwnerStat = { + uid: 9002, + }; + + var getuidSpy; + var geteuidSpy; + + beforeEach(function(done) { + if (typeof process.geteuid !== 'function') { + process.geteuid = noop; + } + + // Windows :( + if (typeof process.getuid !== 'function') { + process.getuid = noop; + } + + getuidSpy = expect.spyOn(process, 'getuid').andReturn(ownerStat.uid); + geteuidSpy = expect.spyOn(process, 'geteuid').andReturn(ownerStat.uid); + + done(); + }); + + afterEach(function(done) { + expect.restoreSpies(); + + if (process.geteuid === noop) { + delete process.geteuid; + } + + // Windows :( + if (process.getuid === noop) { + delete process.getuid; + } + + done(); + }); + + // TODO: test for having neither + + it('uses process.geteuid() when available', function(done) { + + isOwner(ownerStat); + + expect(getuidSpy.calls.length).toEqual(0); + expect(geteuidSpy.calls.length).toEqual(1); + + done(); + }); + + it('uses process.getuid() when geteuid() is not available', function(done) { + delete process.geteuid; + + isOwner(ownerStat); + + expect(getuidSpy.calls.length).toEqual(1); + + done(); + }); + + it('returns false when non-root and non-owner', function(done) { + var result = isOwner(nonOwnerStat); + + expect(result).toEqual(false); + + done(); + }); + + it('returns true when owner and non-root', function(done) { + var result = isOwner(ownerStat); + + expect(result).toEqual(true); + + done(); + }); + + it('returns true when non-owner but root', function(done) { + expect.spyOn(process, 'geteuid').andReturn(0); // 0 is root uid + + var result = isOwner(nonOwnerStat); + + expect(result).toEqual(true); + + done(); + }); +}); + +describe('getModeDiff', function() { + + it('returns 0 if both modes are the same', function(done) { + var fsMode = parseInt('777', 8); + var vfsMode = parseInt('777', 8); + + var result = getModeDiff(fsMode, vfsMode); + + expect(result).toEqual(0); + + done(); + }); + + it('returns 0 if vinyl mode is not a number', function(done) { + var fsMode = parseInt('777', 8); + var vfsMode = undefined; + + var result = getModeDiff(fsMode, vfsMode); + + expect(result).toEqual(0); + + done(); + }); + + it('returns a value greater than 0 if modes are different', function(done) { + var fsMode = parseInt('777', 8); + var vfsMode = parseInt('744', 8); + + var result = getModeDiff(fsMode, vfsMode); + + expect(result).toEqual(27); + + done(); + }); + + it('does not matter the order of diffing', function(done) { + var fsMode = parseInt('655', 8); + var vfsMode = parseInt('777', 8); + + var result = getModeDiff(fsMode, vfsMode); + + expect(result).toEqual(82); + + done(); + }); + + it('includes the sticky/setuid/setgid bits', function(done) { + var fsMode = parseInt('1777', 8); + var vfsMode = parseInt('4777', 8); + + var result = getModeDiff(fsMode, vfsMode); + + expect(result).toEqual(fsMode ^ vfsMode); + + done(); + }); +}); + +describe('getTimesDiff', function() { + + it('returns undefined if vinyl mtime is not a valid date', function(done) { + var fsStat = { + mtime: new Date(), + }; + var vfsStat = { + mtime: new Date(undefined), + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(undefined); + + done(); + }); + + it('returns undefined if vinyl mtime & atime are both equal to counterparts', function(done) { + var now = Date.now(); + var fsStat = { + mtime: new Date(now), + atime: new Date(now), + }; + var vfsStat = { + mtime: new Date(now), + atime: new Date(now), + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(undefined); + + done(); + }); + + // TODO: is this proper/expected? + it('returns undefined if vinyl mtimes equals the counterpart and atimes are null', function(done) { + var now = Date.now(); + var fsStat = { + mtime: new Date(now), + atime: null, + }; + var vfsStat = { + mtime: new Date(now), + atime: null, + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(undefined); + + done(); + }); + + it('returns a diff object if mtimes do not match', function(done) { + var now = Date.now(); + var then = now - 1000; + var fsStat = { + mtime: new Date(now), + }; + var vfsStat = { + mtime: new Date(then), + }; + var expected = { + mtime: new Date(then), + atime: undefined, + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(expected); + + done(); + }); + + it('returns a diff object if atimes do not match', function(done) { + var now = Date.now(); + var then = now - 1000; + var fsStat = { + mtime: new Date(now), + atime: new Date(now), + }; + var vfsStat = { + mtime: new Date(now), + atime: new Date(then), + }; + var expected = { + mtime: new Date(now), + atime: new Date(then), + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(expected); + + done(); + }); + + it('returns the fs atime if the vinyl atime is invalid', function(done) { + var now = Date.now(); + var fsStat = { + mtime: new Date(now), + atime: new Date(now), + }; + var vfsStat = { + mtime: new Date(now), + atime: new Date(undefined), + }; + var expected = { + mtime: new Date(now), + atime: new Date(now), + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(expected); + + done(); + }); + + // TODO: is this proper/expected? + it('makes atime diff undefined if fs and vinyl atime are invalid', function(done) { + var now = Date.now(); + var fsStat = { + mtime: new Date(now), + atime: new Date(undefined), + }; + var vfsStat = { + mtime: new Date(now), + atime: new Date(undefined), + }; + var expected = { + mtime: new Date(now), + atime: undefined, + }; + + var result = getTimesDiff(fsStat, vfsStat); + + expect(result).toEqual(expected); + + done(); + }); +}); + +describe('closeFd', function() { + + it('calls the callback with propagated error if fd is not a number', function(done) { + var propagatedError = new Error(); + + closeFd(propagatedError, null, function(err) { + expect(err).toEqual(propagatedError); + + done(); + }); + }); + + it('calls the callback with close error if no error to propagate', function(done) { + closeFd(null, -1, function(err) { + expect(err).toExist(); + + done(); + }); + }); + + it('calls the callback with propagated error if close errors', function(done) { + var propagatedError = new Error(); + + closeFd(propagatedError, -1, function(err) { + expect(err).toEqual(propagatedError); + + done(); + }); + }); + + it('calls the callback with propagated error if close succeeds', function(done) { + var propagatedError = new Error(); + + var fd = fs.openSync(path.join(__dirname, './fixtures/test.coffee'), 'r'); + + var spy = expect.spyOn(fs, 'close').andCallThrough(); + + closeFd(propagatedError, fd, function(err) { + spy.restore(); + + expect(spy.calls.length).toEqual(1); + expect(err).toEqual(propagatedError); + + done(); + }); + }); + + it('calls the callback with no error if close succeeds & no propagated error', function(done) { + var fd = fs.openSync(path.join(__dirname, './fixtures/test.coffee'), 'r'); + + var spy = expect.spyOn(fs, 'close').andCallThrough(); + + closeFd(null, fd, function(err) { + spy.restore(); + + expect(spy.calls.length).toEqual(1); + expect(err).toEqual(undefined); + + done(); + }); + }); +}); + +describe('writeFile', function() { + + var filepath; + + beforeEach(function(done) { + filepath = path.join(__dirname, './fixtures/writeFile.txt'); + + done(); + }); + + afterEach(function() { + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + return del(filepath); + }); + + it('writes a file to the filesystem, does not close and returns the fd', function(done) { + var expected = 'test'; + var content = new Buffer(expected); + + writeFile(filepath, content, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, function() { + var written = fs.readFileSync(filepath, 'utf-8'); + + expect(written).toEqual(expected); + + done(); + }); + }); + }); + + it('defaults to writing files with 0666 mode', function(done) { + var expected = parseInt('0666', 8) & (~process.umask()); + var content = new Buffer('test'); + + writeFile(filepath, content, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, function() { + var stats = fs.lstatSync(filepath); + + expect(masked(stats.mode)).toEqual(expected); + + done(); + }); + }); + }); + + it('accepts a different mode in options', function(done) { + if (isWindows) { + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + this.skip(); + return; + } + + var expected = parseInt('0777', 8) & (~process.umask()); + var content = new Buffer('test'); + var options = { + mode: parseInt('0777', 8), + }; + + writeFile(filepath, content, options, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, function() { + var stats = fs.lstatSync(filepath); + + expect(masked(stats.mode)).toEqual(expected); + + done(); + }); + }); + }); + + it('defaults to opening files with write flag', function(done) { + var content = new Buffer('test'); + + writeFile(filepath, content, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.read(fd, new Buffer(4), 0, 4, 0, function(readErr) { + expect(readErr).toExist(); + + fs.close(fd, done); + }); + }); + }); + + it('accepts a different flag in options', function(done) { + var expected = 'test'; + var content = new Buffer(expected); + var options = { + flag: 'w+', + }; + + writeFile(filepath, content, options, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.read(fd, new Buffer(4), 0, 4, 0, function(readErr, _, written) { + expect(readErr).toNotExist(); + + expect(written.toString()).toEqual(expected); + + fs.close(fd, done); + }); + }); + }); + + it('appends to a file if append flag is given', function(done) { + var initial = 'test'; + var toWrite = '-a-thing'; + + fs.writeFileSync(filepath, initial, 'utf-8'); + + var expected = initial + toWrite; + + var content = new Buffer(toWrite); + var options = { + flag: 'a', + }; + + writeFile(filepath, content, options, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, function() { + var written = fs.readFileSync(filepath, 'utf-8'); + + expect(written).toEqual(expected); + + done(); + }); + }); + }); + + it('does not pass a file descriptor if open call errors', function(done) { + filepath = path.join(__dirname, './not-exist-dir/writeFile.txt'); + var content = new Buffer('test'); + + writeFile(filepath, content, function(err, fd) { + expect(err).toExist(); + expect(typeof fd === 'number').toEqual(false); + + done(); + }); + }); + + it('passes a file descriptor if write call errors', function(done) { + var existsFilepath = path.join(__dirname, './fixtures/test.coffee'); // File must exist + var content = new Buffer('test'); + var options = { + flag: 'r', + }; + + writeFile(existsFilepath, content, options, function(err, fd) { + expect(err).toExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, done); + }); + }); + + it('passes an error if called with string as data', function(done) { + writeFile(filepath, 'test', function(err) { + expect(err).toExist(); + + done(); + }); + }); + + it('does not error on SlowBuffer', function(done) { + if (!buffer.SlowBuffer) { + this.skip(); + return; + } + + var expected = 'test'; + var buf = new Buffer(expected); + var content = new buffer.SlowBuffer(4); + buf.copy(content, 0, 0, 4); + + writeFile(filepath, content, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, function() { + var written = fs.readFileSync(filepath, 'utf-8'); + + expect(written).toEqual(expected); + + done(); + }); + }); + }); + + it('does not error if options is falsey', function(done) { + var content = new Buffer('test'); + writeFile(filepath, content, null, function(err, fd) { + expect(err).toNotExist(); + expect(typeof fd === 'number').toEqual(true); + + fs.close(fd, done); + }); + }); +}); + +describe('updateMetadata', function() { + + var inputPath = path.join(__dirname, './fixtures/stats.txt'); + var file; + + beforeEach(function(done) { + file = new File({ + base: __dirname, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + + }, + }); + + done(); + }); + + afterEach(function(done) { + expect.restoreSpies(); + + del.sync(inputPath); + + if (process.geteuid === noop) { + delete process.geteuid; + } + + done(); + }); + + it('passes the error and file descriptor if fstat fails', function(done) { + if (isWindows) { + console.log('Changing the time of a directory errors in Windows.'); + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + console.log('Windows is treated as though it does not have permission to make these operations.'); + this.skip(); + return; + } + + var fd = 9001; + + updateMetadata(fd, file, function(err, fd2) { + expect(err).toExist(); + expect(typeof fd === 'number').toEqual(true); + expect(fd2).toEqual(fd); + + done(); + }); + }); + + it('updates the vinyl object with fs stats', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fd = fs.openSync(inputPath, 'w+'); + var stats = fs.fstatSync(fd); + + updateMetadata(fd, file, function(err, fd2) { + // Not sure why .toEqual doesn't match these + Object.keys(file.stat).forEach(function(key) { + expect(file.stat[key]).toEqual(stats[key]); + }); + + fs.close(fd2, done); + }); + }); + + it('does not touch the fs if nothing to update', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(fchmodSpy.calls.length).toEqual(0); + expect(futimesSpy.calls.length).toEqual(0); + + fs.close(fd2, done); + }); + }); + + it('does not touch the fs if process is not owner of the file', function(done) { + if (isWindows) { + this.skip(); + return; + } + + if (typeof process.geteuid !== 'function') { + process.geteuid = noop; + } + + expect.spyOn(process, 'geteuid').andReturn(9002); + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + file.stat.mtime = new Date(Date.now() - 1000); + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(fchmodSpy.calls.length).toEqual(0); + expect(futimesSpy.calls.length).toEqual(0); + + fs.close(fd2, done); + }); + }); + + it('updates times on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var now = Date.now(); + var then = now - 1000; + file.stat.mtime = new Date(then); + file.stat.atime = new Date(then); + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(futimesSpy.calls.length).toEqual(1); + var stats = fs.fstatSync(fd); + var mtimeMs = Date.parse(file.stat.mtime); + var mtime = resolution ? mtimeMs - (mtimeMs % resolution) : mtimeMs; + var atimeMs = Date.parse(file.stat.atime); + var atime = resolution ? atimeMs - (atimeMs % resolution) : atimeMs; + expect(file.stat.mtime).toEqual(new Date(then)); + expect(mtime).toEqual(Date.parse(stats.mtime)); + expect(file.stat.atime).toEqual(new Date(then)); + expect(atime).toEqual(Date.parse(stats.atime)); + + fs.close(fd2, done); + }); + }); + + it('forwards futimes error and descriptor upon error', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var futimesSpy = expect.spyOn(fs, 'futimes').andCall(function(fd, atime, mtime, cb) { + cb(new Error('mocked error')); + }); + + var now = Date.now(); + var then = now - 1000; + file.stat.mtime = new Date(then); + file.stat.atime = new Date(then); + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(err).toExist(); + expect(futimesSpy.calls.length).toEqual(1); + expect(typeof fd2 === 'number').toEqual(true); + + fs.close(fd2, done); + }); + }); + + it('updates the mode on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var mode = parseInt('777', 8); + file.stat.mode = mode; + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(fchmodSpy.calls.length).toEqual(1); + var stats = fs.fstatSync(fd); + expect(file.stat.mode).toEqual(stats.mode); + + fs.close(fd2, done); + }); + }); + + + it('updates the sticky bit on mode on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var mode = parseInt('1777', 8); + file.stat.mode = mode; + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(fchmodSpy.calls.length).toEqual(1); + var stats = fs.fstatSync(fd); + expect(file.stat.mode).toEqual(stats.mode); + + fs.close(fd2, done); + }); + }); + + it('forwards fchmod error and descriptor upon error', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var mode = parseInt('777', 8); + file.stat.mode = mode; + + var fd = fs.openSync(inputPath, 'w+'); + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { + cb(new Error('mocked error')); + }); + + updateMetadata(fd, file, function(err, fd2) { + expect(err).toExist(); + expect(fchmodSpy.calls.length).toEqual(1); + expect(typeof fd2 === 'number').toEqual(true); + + fs.close(fd2, done); + }); + }); + + it('updates the mode & times on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var mode = parseInt('777', 8); + file.stat.mode = mode; + + var now = Date.now(); + var then = now - 1000; + file.stat.mtime = new Date(then); + file.stat.atime = new Date(then); + + var fd = fs.openSync(inputPath, 'w+'); + + updateMetadata(fd, file, function(err, fd2) { + expect(fchmodSpy.calls.length).toEqual(1); + expect(futimesSpy.calls.length).toEqual(1); + + var stats = fs.fstatSync(fd); + var mtimeMs = Date.parse(file.stat.mtime); + var mtime = resolution ? mtimeMs - (mtimeMs % resolution) : mtimeMs; + var atimeMs = Date.parse(file.stat.atime); + var atime = resolution ? atimeMs - (atimeMs % resolution) : atimeMs; + + expect(file.stat.mtime).toEqual(new Date(then)); + expect(mtime).toEqual(Date.parse(stats.mtime)); + expect(file.stat.atime).toEqual(new Date(then)); + expect(atime).toEqual(Date.parse(stats.atime)); + expect(file.stat.mode).toEqual(stats.mode); + + fs.close(fd2, done); + }); + }); + + it('forwards fchmod error and descriptor through futimes if there is a time diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var expectedErr = new Error('mocked error'); + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { + cb(expectedErr); + }); + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var mode = parseInt('777', 8); + file.stat.mode = mode; + + var now = Date.now(); + var then = now - 1000; + file.stat.mtime = new Date(then); + file.stat.atime = new Date(then); + + var fd = fs.openSync(inputPath, 'w'); + + updateMetadata(fd, file, function(err, fd2) { + expect(err).toExist(); + expect(err).toEqual(expectedErr); + expect(fchmodSpy.calls.length).toEqual(1); + expect(futimesSpy.calls.length).toEqual(1); + expect(typeof fd2 === 'number').toEqual(true); + + fs.close(fd2, done); + }); + }); +}); diff --git a/test/vinyl/fixtures/bom-utf16be.txt b/test/vinyl/fixtures/bom-utf16be.txt new file mode 100644 index 0000000000000000000000000000000000000000..b9dce78a5d31af4803acd1a0f0dfc14f064a5de1 GIT binary patch literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 literal 0 HcmV?d00001 diff --git a/test/vinyl/fixtures/bom-utf16le.txt b/test/vinyl/fixtures/bom-utf16le.txt new file mode 100644 index 0000000000000000000000000000000000000000..07cc600c98675d221bb56d10af38e650538734c9 GIT binary patch literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15l Date: Thu, 5 May 2016 06:17:14 -0400 Subject: [PATCH 233/282] use `verb-readme-generate` full name as default so that in the case there are errors it's less confusing --- lib/commands/tasks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js index 72c33e3d..22329f5a 100644 --- a/lib/commands/tasks.js +++ b/lib/commands/tasks.js @@ -81,7 +81,7 @@ function setTasks(app, configFile, tasks, argv) { // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default if (verbmd && !configExists) { - return ['readme']; + return ['verb-readme-generator']; } // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` From ce1ca570c698c221ebb65c112f9ad2bcf0e3a64f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 10 May 2016 08:59:03 -0400 Subject: [PATCH 234/282] update error messages in tests --- test/app.dest.js | 2 +- test/app.register.js | 2 +- test/generators.env.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/app.dest.js b/test/app.dest.js index 9c0dc1ed..129f2130 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -733,7 +733,7 @@ describe('app.dest', function() { stream.end(); }); - it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { + it.skip('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { if (isWindows) { this.skip(); return; diff --git a/test/app.register.js b/test/app.register.js index c767c2d0..ca99e85d 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -173,7 +173,7 @@ describe('.register', function() { base.register('not-exposed', require(fixtures('not-exposed.js'))); cb(new Error('expected an error')); } catch (err) { - var fp = path.resolve(__dirname, '../node_modules/not-exposed'); + var fp = path.resolve(__dirname, '../node_modules/not-exposed/generator.js'); assert.equal(err.message, 'Cannot find module \'' + fp + '\''); cb(); } diff --git a/test/generators.env.js b/test/generators.env.js index e1699dbd..8957de39 100644 --- a/test/generators.env.js +++ b/test/generators.env.js @@ -104,7 +104,7 @@ describe('generators.env', function() { env.fn(); cb(new Error('expected an error')); } catch (err) { - assert.equal(err.message, 'Cannot find module \'' + fixtures('whatever.js') + '\''); + assert.equal(err.message, 'Cannot find module \'' + fixtures('whatever.js/generator.js') + '\''); cb(); } }); From a709ddb8f3deed8ce8cbf7888789a9110986c85a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 10 May 2016 08:59:27 -0400 Subject: [PATCH 235/282] add sub-generators and tasks to `defaults` --- lib/commands/dest.js | 14 +++++ lib/commands/tasks.js | 17 ++++-- lib/{commands => }/format.js | 0 lib/generator.js | 107 ++++++++++++++++++++++++++++++++--- lib/templates/doc.md | 8 +++ lib/templates/post.md | 9 +++ lib/templates/readme.md | 4 ++ lib/utils.js | 5 +- 8 files changed, 146 insertions(+), 18 deletions(-) create mode 100644 lib/commands/dest.js rename lib/{commands => }/format.js (100%) create mode 100644 lib/templates/doc.md create mode 100644 lib/templates/post.md create mode 100644 lib/templates/readme.md diff --git a/lib/commands/dest.js b/lib/commands/dest.js new file mode 100644 index 00000000..0a2f4c02 --- /dev/null +++ b/lib/commands/dest.js @@ -0,0 +1,14 @@ +'use strict'; + +var path = require('path'); + +module.exports = function(app, base) { + return function(val, key, config, next) { + if (typeof val === 'undefined') { + config[key] = app.cwd; + } else { + config[key] = path.resolve(val); + } + next(); + } +}; diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js index 22329f5a..51507ca0 100644 --- a/lib/commands/tasks.js +++ b/lib/commands/tasks.js @@ -34,19 +34,21 @@ module.exports = function(app, options) { } ran = true; - var argv = app.get('cache.argv'); + var argv = app.base.get('cache.argv'); var tasks = setTasks(app, options.env.configFile, val, argv); + app.base.set('cache.argv.tasks', tasks); app.generateEach(tasks, next); }; }; /** - * Determine the task to run + * Determine the task to run. This is only necessary because we're doing + * some re-routing with verb's small handful of built-in tasks. */ function setTasks(app, configFile, tasks, argv) { if (argv.hasOwnProperty('new')) { - return ['verb.new:' + argv.new]; + return ['defaults.new:' + argv.new]; } if (argv.init === true) { @@ -54,8 +56,11 @@ function setTasks(app, configFile, tasks, argv) { } tasks = tasks.map(function(task) { - if (task.indexOf('new:') === 0) { - return 'verb.' + task; + if (task.indexOf('new') === 0) { + return 'defaults.' + task; + } + if (task === 'format') { + return 'defaults:format'; } return task; }); @@ -86,7 +91,7 @@ function setTasks(app, configFile, tasks, argv) { // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` if (!verbmd) { - return ['verb.new:prompt-verbmd']; + return ['defaults.new:prompt-verbmd']; } } return tasks; diff --git a/lib/commands/format.js b/lib/format.js similarity index 100% rename from lib/commands/format.js rename to lib/format.js diff --git a/lib/generator.js b/lib/generator.js index 05aa013d..a87da027 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,16 +1,82 @@ -'use strict'; + +var stack = require('callsite'); var path = require('path'); var cwd = path.resolve.bind(path, __dirname, 'templates'); +var reflinks = require('./reflinks'); +var utils = require('./utils'); module.exports = function(verb, base) { - var argv = verb.get('cache.argv'); + var argv = base.get('cache.argv'); + + verb.on('view', function(view) { + if (view.basename === 'doc.md') { + view.options.frontMatter = false; + view.layout = false; + } + }); + + /** + * Prompt the user for the `dest` directory to use for generated files. + * This is called by the [new]() task. + * + * ```sh + * $ verb defaults:dest + * ``` + * @name dest + * @api public + */ + + verb.task('dest', function(cb) { + verb.question('dest', 'Destination directory?', {default: verb.cwd}); + if (verb.option('dest')) return cb(); + verb.ask('dest', {save: false}, function(err, answers) { + if (err) return cb(err); + verb.option('dest', path.resolve(verb.cwd, answers.dest)); + cb(); + }); + }); + + /** + * Format a markdown file using [pretty-remarkable][]. Optionally + * specify a `--dest` directory and/or file `--name`. + * + * ```sh + * $ verb format --src=foo/bar.md --dest=baz + * ``` + * @name format + * @api public + */ + + verb.task('format', function(cb) { + var src = verb.option('src'); + var dest = verb.option('dest'); + + verb.src(src) + .pipe(reflinks(verb)) + .pipe(utils.format()) + .pipe(verb.dest(function(file) { + file.basename = (argv.name || path.basename(src)); + return (dest || path.resolve(path.dirname(src))); + })) + .on('error', cb) + .on('end', cb); + }); /** - * Generate a file from a template + * Sub-generator with a handful of tasks for generating a file from + * a template. + * + * ```sh + * $ verb new + * ``` + * @name new + * @api public */ verb.register('new', function(app) { + app.option(verb.options); + app.task('verbfile', function(cb) { file(app, 'verbfile.js', cb); }); @@ -19,6 +85,10 @@ module.exports = function(verb, base) { file(app, '.verb.md', cb); }); + app.task('readme', function(cb) { + file(app, 'README.md', cb); + }); + app.task('prompt-verbmd', function(cb) { app.confirm('verbmd', 'Looks like .verb.md is missing, want to add one?'); app.ask('verbmd', function(err, answers) { @@ -33,10 +103,20 @@ module.exports = function(verb, base) { cb(); }); }); + + app.task('doc', function(cb) { + file(verb, 'doc.md', cb); + }); }); /** - * Display `--help` when no tasks are defined + * Display a help menu of available commands and flags. + * + * ```sh + * $ verb help + * ``` + * @name help + * @api public */ verb.task('help', { silent: true }, function(cb) { @@ -44,7 +124,13 @@ module.exports = function(verb, base) { }); /** - * Default task + * Default task for the built-in `defaults` generator. + * + * ```sh + * $ verb defaults + * ``` + * @name defaults + * @api public */ verb.task('default', argv.tasks); @@ -55,11 +141,16 @@ module.exports = function(verb, base) { */ function file(verb, name, cb) { - verb.src(name, {cwd: cwd('templates')}) - .pipe(verb.dest(verb.cwd)) + var dest = verb.option('dest') || verb.cwd; + + verb.engine('*', require('engine-base')); + verb.src(name, {cwd: cwd()}) + .pipe(verb.renderFile('*')) + .pipe(verb.conflicts(dest)) + .pipe(verb.dest(dest)) .on('error', cb) .on('end', function() { - console.log(name, 'written to', verb.cwd); + console.log(name, 'written to', dest); cb(); }); } diff --git a/lib/templates/doc.md b/lib/templates/doc.md new file mode 100644 index 00000000..03d04c45 --- /dev/null +++ b/lib/templates/doc.md @@ -0,0 +1,8 @@ +--- +title: <%= ask("doc.title") %> +layout: nil +dest: ":docs/:filename" +tags: [] +--- + +This is {%= title %} \ No newline at end of file diff --git a/lib/templates/post.md b/lib/templates/post.md new file mode 100644 index 00000000..bbb6e94a --- /dev/null +++ b/lib/templates/post.md @@ -0,0 +1,9 @@ +--- +title: <%= ask("title") %> +date: <%= date() %> +layout: default +dest: ":posts/:filename" +tags: [] +--- + +This is {%= title %} \ No newline at end of file diff --git a/lib/templates/readme.md b/lib/templates/readme.md new file mode 100644 index 00000000..a8ea3630 --- /dev/null +++ b/lib/templates/readme.md @@ -0,0 +1,4 @@ +# <%= ask("name", "Project name?") %> + +<%= ask("description", "Project description") %> + diff --git a/lib/utils.js b/lib/utils.js index 4d0a2fa7..be8d74c2 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -8,18 +8,15 @@ require = utils; * Lazily required module dependencies */ -require('ansi-colors', 'colors'); require('base-runner', 'runner'); require('extend-shallow', 'extend'); require('fs-exists-sync', 'exists'); require('generate'); +require('get-value', 'get'); require('gulp-format-md', 'format'); -require('isobject', 'isObject'); -require('memoize-path', 'memo'); require('reflinks'); require('set-value', 'set'); require('through2', 'through'); -require('unset-value', 'del'); require('yargs-parser', 'yargs'); require = fn; From e7297f0df0e9d51831b9bd32013b85f0b04043c1 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 10 May 2016 09:01:11 -0400 Subject: [PATCH 236/282] fix pattern used for lookups --- bin/verb.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 6c4345ce..1e6cac2d 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -24,7 +24,7 @@ utils.runner(Verb, {name: 'verb'}, argv, function(err, app, ctx) { }); commands(app, ctx); - app.register('verb', require('../lib/generator')); + app.register('defaults', require('../lib/generator')); app.option('lookup', lookup(app)); app.cli.process(ctx.argv, function(err) { @@ -40,7 +40,11 @@ utils.runner(Verb, {name: 'verb'}, argv, function(err, app, ctx) { function lookup(app) { return function(key) { - var patterns = [`verb-${key}-generator`, key]; + var patterns = [key]; + if (!/^verb-([^-]+)-generator/.test(key)) { + patterns.unshift(`verb-${key}-generator`); + } + if (app.enabled('generators')) { patterns.push(`generate-${key}`); } From a2d6edb0763ecfcb2294cb55a4e0a223b25a8e66 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 10 May 2016 09:01:24 -0400 Subject: [PATCH 237/282] adds `gulp-unused` --- gulpfile.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gulpfile.js b/gulpfile.js index c60166e4..0ea89019 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,6 +4,7 @@ var gulp = require('gulp'); var mocha = require('gulp-mocha'); var istanbul = require('gulp-istanbul'); var eslint = require('gulp-eslint'); +var unused = require('gulp-unused'); var lib = ['index.js', 'lib/**/*.js', 'bin/*.js']; @@ -25,4 +26,9 @@ gulp.task('lint', function() { .pipe(eslint.format()); }); +gulp.task('unused', function() { + return gulp.src(['index.js', 'lib/**/*.js', 'bin/*.js']) + .pipe(unused({keys: Object.keys(require('./lib/utils.js'))})) +}); + gulp.task('default', ['test', 'lint']); From 22dd58092922edf4952d707b48a41555d024083d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 10 May 2016 09:02:06 -0400 Subject: [PATCH 238/282] add verbfile.js for docs --- verbfile.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 verbfile.js diff --git a/verbfile.js b/verbfile.js new file mode 100644 index 00000000..f15380b4 --- /dev/null +++ b/verbfile.js @@ -0,0 +1,16 @@ +'use strict'; + +var through = require('through2'); + +module.exports = function(verb) { + verb.extendWith('verb-readme-generator'); + + verb.task('default', ['readme'], function() { + return verb.src('README.md') + .pipe(through.obj(function(file, enc, next) { + file.content = file.content.replace(/^(#{2,}\s*\[)\./gm, '$1'); + next(null, file); + })) + .pipe(verb.dest('.')); + }); +}; From d7527f2579c86f50bf936cd7f0b782ccd0da68a8 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 02:35:57 -0400 Subject: [PATCH 239/282] update unit tests --- test/app.applyLayout.js | 2 +- test/app.collection.js | 18 +- test/app.collection.render.js | 14 +- test/app.create.js | 32 +- test/app.dest.js | 58 ++-- test/app.events.js | 228 +++++++++---- test/{app.tasks.js => app.generator.tasks.js} | 0 test/app.get-set.js | 26 +- test/app.handle.js | 6 +- test/app.handleOnce.js | 39 +++ test/app.handlers.js | 62 +++- test/app.js | 10 + test/app.list.render.js | 8 +- test/app.lookups.js | 5 + test/app.mergePartials.js | 3 +- test/app.middleware.js | 2 +- test/app.option.js | 52 +-- test/app.options.initTemplates.js | 24 ++ test/app.render.js | 102 +++++- test/app.route.js | 14 +- test/app.src.js | 230 ++++++++----- test/app.symlink.js | 126 ++++--- test/app.task.js | 86 ++--- test/app.toStream.js | 2 +- test/collection.engines.js | 5 +- test/collection.events.js | 2 +- test/collection.isType.js | 26 ++ test/collection.js | 36 +- test/collection.render.js | 56 ++- test/collection.routes.js | 130 +++++++ test/collection.src.js | 322 ++++++++++++++++++ test/collection.use.js | 87 +++++ test/group.js | 71 ++-- test/handlers.js | 87 ++++- test/helpers.js | 253 +++++++++++++- test/item.js | 299 +++++++++++----- test/layouts.js | 113 +++++- test/list.deleteItem.js | 1 - test/list.js | 54 ++- test/list.render.js | 73 ++-- test/out-fixtures/test.coffee | 0 test/renameKey.js | 108 ++++++ test/render.js | 10 +- test/view.js | 128 +++---- test/view.render.js | 14 +- test/view.set.js | 2 - test/view.use.js | 46 ++- test/views.js | 64 +++- 48 files changed, 2475 insertions(+), 661 deletions(-) rename test/{app.tasks.js => app.generator.tasks.js} (100%) create mode 100644 test/app.handleOnce.js create mode 100644 test/app.options.initTemplates.js create mode 100644 test/collection.isType.js create mode 100644 test/collection.routes.js create mode 100644 test/collection.src.js delete mode 100644 test/out-fixtures/test.coffee diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js index 2f1faea3..c62d83f6 100644 --- a/test/app.applyLayout.js +++ b/test/app.applyLayout.js @@ -19,7 +19,7 @@ describe('app.applyLayout', function() { beforeEach(function() { app = new App(); app.engine('tmpl', require('engine-base')); - app.create('layout', {viewType: 'layout'}); + app.create('layout', { viewType: 'layout' }); app.create('page'); }); diff --git a/test/app.collection.js b/test/app.collection.js index 35687023..9116953c 100644 --- a/test/app.collection.js +++ b/test/app.collection.js @@ -3,7 +3,6 @@ require('mocha'); require('should'); var fs = require('fs'); -var path = require('path'); var assert = require('assert'); var define = require('define-property'); var support = require('./support'); @@ -18,17 +17,17 @@ describe('app.collection', function() { }); it('should expose the collection method', function() { - assert(typeof app.collection === 'function'); + assert.equal(typeof app.collection, 'function'); }); it('should return a new collection', function() { var collection = app.collection(); - assert(typeof collection === 'object'); + assert.equal(typeof collection, 'object'); }); it('should have isCollection property', function() { var collection = app.collection(); - assert(collection.isCollection === true); + assert.equal(collection.isCollection, true); }); }); @@ -66,7 +65,7 @@ describe('app.collection', function() { app.views.pages.should.have.properties([ 'test/fixtures/pages/a.hbs', 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs', + 'test/fixtures/pages/c.hbs' ]); }); @@ -77,7 +76,6 @@ describe('app.collection', function() { .pages('test/fixtures/pages/c.hbs'); app.pages.options.should.have.property('foo', 'bar'); - app.views.pages.should.have.properties([ 'test/fixtures/pages/a.hbs', 'test/fixtures/pages/b.hbs', @@ -91,7 +89,7 @@ describe('app.collection', function() { .pages('test/fixtures/pages/b.hbs') .pages('test/fixtures/pages/c.hbs'); - assert(app.pages.count === 3); + assert.equal(app.pages.count, 3); }); }); @@ -140,7 +138,7 @@ describe('app.collection', function() { .set('data.name', 'Brian') .render(function(err, res) { if (err) return cb(err); - assert(res.content === 'Brian'); + assert.equal(res.content, 'Brian'); cb(); }); }); @@ -155,7 +153,7 @@ describe('collection singular method', function() { it('should add a pluralized collection from singular name', function() { app.create('page'); - assert(typeof app.views.pages === 'object'); + assert.equal(typeof app.views.pages, 'object'); }); }); @@ -168,7 +166,7 @@ describe('collection singular method', function() { it('should add a view to the created collection:', function() { app.page('test/fixtures/pages/a.hbs'); - assert(typeof app.views.pages['test/fixtures/pages/a.hbs'] === 'object'); + assert.equal(typeof app.views.pages['test/fixtures/pages/a.hbs'], 'object'); }); it('should expose the `option` method:', function() { diff --git a/test/app.collection.render.js b/test/app.collection.render.js index f2cc639f..58dd3b36 100644 --- a/test/app.collection.render.js +++ b/test/app.collection.render.js @@ -2,7 +2,7 @@ require('mocha'); require('should'); -var async = require('async'); +var each = require('async-each'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); @@ -18,10 +18,14 @@ describe('app.collection.render', function() { pages.engine('tmpl', require('engine-base')); }); - it('should throw an error when no callback is given:', function() { - (function() { + it('should throw an error when no callback is given:', function(cb) { + try { app.pages.render({}); - }).should.throw('Views#render is async and expects a callback function'); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'Views#render is async and expects a callback function'); + cb(); + } }); it('should throw an error when an engine is not defined:', function(cb) { @@ -139,7 +143,7 @@ describe('app.collection.render', function() { collection.renderEach = function(cb) { var list = new List(collection); - async.map(list.items, function(item, next) { + each(list.items, function(item, next) { collection.render(item, next); }, cb); }; diff --git a/test/app.create.js b/test/app.create.js index f2501c14..9f88049e 100644 --- a/test/app.create.js +++ b/test/app.create.js @@ -16,19 +16,19 @@ describe('app.create', function() { }); it('should expose the create method', function() { - assert(typeof app.create === 'function'); + assert.equal(typeof app.create, 'function'); }); it('should add a collection to `views`', function() { app.create('pages'); - assert(typeof app.views.pages === 'object'); - assert(typeof app.pages === 'function'); + assert.equal(typeof app.views.pages, 'object'); + assert.equal(typeof app.pages, 'function'); }); it('should add a pluralized collection to `views`', function() { app.create('page'); - assert(typeof app.views.pages === 'object'); - assert(typeof app.page === 'function'); + assert.equal(typeof app.views.pages, 'object'); + assert.equal(typeof app.page, 'function'); }); }); @@ -45,9 +45,9 @@ describe('app.create', function() { assert(app.views.pages.hasOwnProperty('foo')); }); - it('should add view Ctor names to views', function() { + it('should add view collection name to view._name', function() { app.pages.addView('foo', {content: 'bar'}); - assert(app.views.pages.foo._name === 'Page'); + assert.equal(app.views.pages.foo._name, 'page'); }); it('should add partial views when partial type is defined', function() { @@ -126,7 +126,7 @@ describe('app.create', function() { var a = app.pages.getView('a.hbs'); assert(a instanceof Vinyl); assert(Vinyl.isVinyl(a)); - assert(typeof a.read === 'function'); + assert.equal(typeof a.read, 'function'); views.addView('d.hbs', {path: 'd.hbs', content: 'd'}); var d = app.pages.getView('d.hbs'); @@ -147,7 +147,7 @@ describe('app.create', function() { app.page('b.hbs', {content: 'b'}); app.page('c.hbs', {content: 'c'}); app.views.pages.should.have.properties(['a.hbs', 'b.hbs', 'c.hbs']); - assert(app.views.pages['a.hbs'].contents.toString() === 'a'); + assert.equal(app.views.pages['a.hbs'].contents.toString(), 'a'); }); it('should create views from file paths:', function() { @@ -158,7 +158,7 @@ describe('app.create', function() { app.views.pages.should.have.properties([ 'test/fixtures/pages/a.hbs', 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs', + 'test/fixtures/pages/c.hbs' ]); }); }); @@ -180,13 +180,15 @@ describe('app.create', function() { .use(function(views) { views.read = function(name) { var view = this.getView(name); - view.contents = fs.readFileSync(view.path); + if (!view.contents) { + view.contents = fs.readFileSync(view.path); + } }; }); collection.addView('test/fixtures/templates/a.tmpl'); collection.read('a.tmpl'); - assert(collection.getView('a.tmpl').contents.toString() === '<%= name %>'); + assert.equal(collection.getView('a.tmpl').contents.toString(), '<%= name %>'); }); }); @@ -198,7 +200,7 @@ describe('app.create', function() { it('should add collection to the given viewType', function() { app.create('layout', {viewType: 'layout'}); - assert(app.layouts.options.viewType[0] === 'layout'); + assert.equal(app.layouts.options.viewType[0], 'layout'); }); it('should add a collection to multiple viewTypes', function() { @@ -222,7 +224,7 @@ describe('app.create', function() { app.create('layout'); app.layout('one', {path: 'two', contents: '...'}); - assert(app.layouts.options.foo === 'bar'); + assert.equal(app.layouts.options.foo, 'bar'); }); }); @@ -236,7 +238,7 @@ describe('app.create', function() { }); assert(app.pages.foo); - assert(typeof app.pages.foo === 'function'); + assert.equal(typeof app.pages.foo, 'function'); }); }); }); diff --git a/test/app.dest.js b/test/app.dest.js index 129f2130..0086bf7e 100644 --- a/test/app.dest.js +++ b/test/app.dest.js @@ -89,7 +89,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -147,7 +147,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -182,7 +182,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); + var stream = app.dest('actual/', {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -261,7 +261,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -302,7 +302,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -348,7 +348,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -362,8 +362,8 @@ describe('app.dest', function() { var inputPath2 = path.join(__dirname, 'actual/multiple-second'); var inputBase = path.join(__dirname, 'actual/'); var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var stream1 = app.dest('./actual/', {cwd: __dirname}); - var stream2 = app.dest('./actual/', {cwd: __dirname}); + var stream1 = app.dest('actual/', {cwd: __dirname}); + var stream2 = app.dest('actual/', {cwd: __dirname}); var content = fs.readFileSync(srcPath); var rename = through.obj(function(file, _, next) { file.path = inputPath2; @@ -418,7 +418,7 @@ describe('app.dest', function() { }; chmodSpy.reset(); - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -451,7 +451,7 @@ describe('app.dest', function() { }; chmodSpy.reset(); - var stream = app.dest('./actual/', { cwd: __dirname, mode: expectedMode }); + var stream = app.dest('actual/', { cwd: __dirname, mode: expectedMode }); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -469,9 +469,9 @@ describe('app.dest', function() { var inputPath = path.join(__dirname, 'fixtures/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); + var expectedBase = path.join(__dirname, './actual'); var startMode = parseInt('0655', 8); var expectedMode = parseInt('0722', 8); @@ -520,7 +520,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', { + var stream = app.dest('actual/', { cwd: __dirname, mode: expectedFileMode, dirMode: expectedDirMode @@ -549,7 +549,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', { + var stream = app.dest('actual/', { cwd: __dirname, base: inputBase }); @@ -577,7 +577,7 @@ describe('app.dest', function() { cb(); }; - var stream = app.dest('./actual/', { + var stream = app.dest('actual/', { cwd: __dirname, base: function(file) { should.exist(file); @@ -616,7 +616,7 @@ describe('app.dest', function() { fs.closeSync(fs.openSync(expectedPath, 'w')); fs.chmodSync(expectedPath, 0); - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); stream.on('error', function(err) { err.code.should.equal('EACCES'); cb(); @@ -624,7 +624,7 @@ describe('app.dest', function() { stream.write(expectedFile); }); - it.skip('should report stat errors', function(cb) { + it('should report stat errors', function(cb) { var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); var inputBase = path.join(__dirname, 'fixtures/vinyl/'); var expectedPath = path.join(__dirname, 'actual/test.coffee'); @@ -651,7 +651,7 @@ describe('app.dest', function() { } }); - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); stream.on('error', function(err) { err.message.should.equal('stat error'); cb(); @@ -659,7 +659,7 @@ describe('app.dest', function() { stream.write(expectedFile); }); - it.skip('should report fchmod errors', function(cb) { + it('should report fchmod errors', function(cb) { if (isWindows) { this.skip(); return; @@ -692,8 +692,8 @@ describe('app.dest', function() { var stream = app.dest('actual/', { cwd: __dirname }); stream.on('error', function(err) { - expect(err).toExist(); - expect(fchmodSpy.calls.length).toEqual(1); + assert(err); + assert.equal(fchmodSpy.calls.length, 1); cb(); }); stream.write(expectedFile); @@ -733,7 +733,7 @@ describe('app.dest', function() { stream.end(); }); - it.skip('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { + it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { if (isWindows) { this.skip(); return; @@ -755,12 +755,12 @@ describe('app.dest', function() { path: inputPath, contents: expectedContents, stat: { - mode: normalMode, - }, + mode: normalMode + } }); var onEnd = function() { - expect(fchmodSpy.calls.length).toEqual(0); + assert.equal(fchmodSpy.calls.length, 0); cb(); }; @@ -800,7 +800,7 @@ describe('app.dest', function() { fs.mkdirSync(expectedBase); fs.writeFileSync(expectedPath, existingContents); - var stream = app.dest('./actual/', {cwd: __dirname, overwrite: false}); + var stream = app.dest('actual/', {cwd: __dirname, overwrite: false}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -835,7 +835,7 @@ describe('app.dest', function() { fs.mkdirSync(expectedBase); fs.writeFileSync(expectedPath, existingContents); - var stream = app.dest('./actual/', {cwd: __dirname, overwrite: true}); + var stream = app.dest('actual/', {cwd: __dirname, overwrite: true}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -869,7 +869,7 @@ describe('app.dest', function() { }); }; - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -880,7 +880,7 @@ describe('app.dest', function() { it('should emit finish event', function(cb) { var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var stream = app.dest('./actual/', {cwd: __dirname}); + var stream = app.dest('actual/', {cwd: __dirname}); stream.once('finish', function() { cb(); diff --git a/test/app.events.js b/test/app.events.js index 11b92748..fb95c980 100644 --- a/test/app.events.js +++ b/test/app.events.js @@ -8,108 +8,192 @@ var App = support.resolve(); var app; describe('app.events', function() { - beforeEach(function() { - app = new App(); - }); + describe('events', function() { + beforeEach(function() { + app = new App(); + }); - it('should listen for an event:', function() { - var app = new App(); - app.on('foo', function() {}); - assert(Array.isArray(app._callbacks['$foo'])); - }); + it('should listen for an event:', function() { + app = new App(); + app.on('foo', function() {}); + assert(Array.isArray(app._callbacks['$foo'])); + }); + + it('should emit an event:', function(cb) { + app = new App(); + app.on('foo', function(val) { + assert.equal(val, 'bar'); + cb(); + }); + assert(Array.isArray(app._callbacks['$foo'])); + app.emit('foo', 'bar'); + }); + + it('should listen for `view` events:', function(cb) { + app = new App(); + var count = 0; - it('should emit an event:', function(cb) { - var app = new App(); - app.on('foo', function(val) { - assert(val === 'bar'); + app.on('view', function(view) { + view.foo = 'bar'; + count++; + }); + + var view = app.view({path: 'a', content: 'b'}); + assert.equal(view.foo, 'bar'); + assert.equal(count, 1); cb(); }); - assert(Array.isArray(app._callbacks['$foo'])); - app.emit('foo', 'bar'); }); - it('should listen for `view` events:', function() { - app = new App(); - - app.on('view', function(view) { - view.foo = 'bar'; + describe('onLoad', function() { + beforeEach(function() { + app = new App(); }); - var view = app.view({path: 'a', content: 'b'}); - assert(view.foo === 'bar'); - }); -}); + describe('app.collection', function() { + it('should emit a `view` event when view is created', function(cb) { + var collection = app.collection(); -describe('onLoad', function() { - beforeEach(function() { - app = new App(); - }); + app.on('view', function(view) { + assert.equal(view.path, 'blog/foo.js'); + cb(); + }); - describe('app.collection', function() { - it('should emit a `view` event when view is created', function(cb) { - var collection = app.collection(); + app.onLoad('blog/:title', function(view, next) { + assert.equal(view.path, 'blog/foo.js'); + next(); + }); - app.on('view', function(view) { - assert(view.path === 'blog/foo.js'); - cb(); + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); - app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); - next(); - }); + it('should emit an onLoad event when view is created', function(cb) { + var collection = app.collection(); - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); + app.on('onLoad', function(view) { + assert.equal(view.path, 'blog/foo.js'); + cb(); + }); - it('should emit an onLoad event when view is created', function(cb) { - var collection = app.collection(); + app.onLoad('blog/:title', function(view, next) { + assert.equal(view.path, 'blog/foo.js'); + next(); + }); - app.on('onLoad', function(view) { - assert(view.path === 'blog/foo.js'); - cb(); + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); - app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); - next(); + it('should not emit an onLoad event when view is created and `app.options.onLoad` is `false', function(cb) { + var emitted = false; + var handled = false; + var collection = app.collection(); + app.options.onLoad = false; + + app.on('onLoad', function(view) { + emitted = true; + }); + + app.onLoad('blog/:title', function(view, next) { + handled = true; + next(); + }); + + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + setImmediate(function() { + assert.equal(typeof collection.views.whatever, 'object'); + assert.equal(emitted, false); + assert.equal(handled, false); + cb(); + }); }); - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + it('should not emit an onLoad event when view is created and `collection.options.onLoad` is `false', function(cb) { + var emitted = false; + var handled = false; + var collection = app.collection(); + collection.options.onLoad = false; + + app.on('onLoad', function(view) { + emitted = true; + }); + + app.onLoad('blog/:title', function(view, next) { + handled = true; + next(); + }); + + collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); + setImmediate(function() { + assert.equal(typeof collection.views.whatever, 'object'); + assert.equal(emitted, false); + assert.equal(handled, false); + cb(); + }); + }); + + it('should not emit an onLoad event when view is created and `view.options.onLoad` is `false', function(cb) { + var emitted = false; + var handled = false; + var collection = app.collection(); + + app.on('onLoad', function(view) { + emitted = true; + }); + + app.onLoad('blog/:title', function(view, next) { + handled = true; + next(); + }); + + collection.addView('whatever', { + options: { + onLoad: false + }, + path: 'blog/foo.js', + content: 'bar baz' + }); + + setImmediate(function() { + assert.equal(typeof collection.views.whatever, 'object'); + assert.equal(emitted, false); + assert.equal(handled, false); + cb(); + }); + }); }); - }); - describe('view collections', function() { - it('should emit a view event when view is created', function(cb) { - app.create('posts'); + describe('view collections', function() { + it('should emit a view event when view is created', function(cb) { + app.create('posts'); - app.on('view', function(view) { - assert(view.path === 'blog/foo.js'); - cb(); - }); + app.on('view', function(view) { + assert.equal(view.path, 'blog/foo.js'); + cb(); + }); + + app.onLoad('blog/:title', function(view, next) { + assert.equal(view.path, 'blog/foo.js'); + next(); + }); - app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); - next(); + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); + it('should emit an onLoad event when view is created', function(cb) { + app.create('posts'); - it('should emit an onLoad event when view is created', function(cb) { - app.create('posts'); + app.on('onLoad', function(view) { + assert.equal(view.path, 'blog/foo.js'); + cb(); + }); - app.on('onLoad', function(view) { - assert(view.path === 'blog/foo.js'); - cb(); - }); + app.onLoad('blog/:title', function(view, next) { + assert.equal(view.path, 'blog/foo.js'); + next(); + }); - app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); - next(); + app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); - - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); }); }); }); diff --git a/test/app.tasks.js b/test/app.generator.tasks.js similarity index 100% rename from test/app.tasks.js rename to test/app.generator.tasks.js diff --git a/test/app.get-set.js b/test/app.get-set.js index e60fe6e2..c4ebda33 100644 --- a/test/app.get-set.js +++ b/test/app.get-set.js @@ -13,48 +13,48 @@ describe('app.get-set', function() { it('should set a value', function() { app.set('a', 'b'); - app.get('a').should.equal('b'); + assert.equal(app.get('a'), 'b'); }); it('should set properties on the instance.', function() { app.set('a', 'b'); - app.a.should.equal('b'); + assert.equal(app.a, 'b'); }); it('should allow an object to be set directly.', function() { app.set({x: 'y'}); - app.x.should.equal('y'); - app.get('x').should.equal('y'); + assert.equal(app.x, 'y'); + assert.equal(app.get('x'), 'y'); }); it('should set nested properties on the instance.', function() { app.set('c', {d: 'e'}); - app.get('c').d.should.equal('e'); + assert.equal(app.get('c').d, 'e'); }); it('should use dot notation to `set` values.', function() { app.set('h.i', 'j'); - app.get('h').should.eql({i: 'j'}); + assert.deepEqual(app.get('h'), {i: 'j'}); }); it('should use dot notation to `get` values.', function() { app.set('h', {i: 'j'}); - app.get('h.i').should.equal('j'); + assert.equal(app.get('h.i'), 'j'); }); it('should return `this` for chaining', function() { - app.set('a', 'b').should.equal(app); + assert.equal(app.set('a', 'b'), app); app .set('aa', 'bb') .set('bb', 'cc') .set('cc', 'dd'); - app.get('aa').should.equal('bb'); - app.get('bb').should.equal('cc'); - app.get('cc').should.equal('dd'); + assert.equal(app.get('aa'), 'bb'); + assert.equal(app.get('bb'), 'cc'); + assert.equal(app.get('cc'), 'dd'); }); it('should return undefined when not set', function() { - app.set('a', undefined).should.equal(app); + assert.equal(app.set('a', undefined), app); }); }); @@ -69,6 +69,6 @@ describe('app.get()', function() { it('should otherwise return the value', function() { app.set('a', 'b'); - app.get('a').should.equal('b'); + assert.equal(app.get('a'), 'b'); }); }); diff --git a/test/app.handle.js b/test/app.handle.js index 4e996da4..442a836a 100644 --- a/test/app.handle.js +++ b/test/app.handle.js @@ -19,8 +19,7 @@ describe('app.handle', function() { app.handle('foo', page, function(err, view) { if (err) return cb(err); - - assert(typeof view.path === 'string'); + assert.equal(typeof view.path, 'string'); cb(); }); }); @@ -31,8 +30,7 @@ describe('app.handle', function() { app.handle('foo', page, function(err, view) { if (err) return cb(err); - - assert(typeof view.path === 'string'); + assert.equal(typeof view.path, 'string'); cb(); }); }); diff --git a/test/app.handleOnce.js b/test/app.handleOnce.js new file mode 100644 index 00000000..a17d9a40 --- /dev/null +++ b/test/app.handleOnce.js @@ -0,0 +1,39 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.handle', function() { + beforeEach(function() { + app = new App(); + app.create('pages'); + app.handlers(['foo']); + }); + + it('should support custom handle methods:', function(cb) { + var page = app.page('foo', {contents: null}); + + app.handleOnce('foo', page, function(err, view) { + if (err) return cb(err); + + assert(typeof view.path === 'string'); + cb(); + }); + }); + + it('should not blow up if `options.handled` does not exist:', function(cb) { + var page = app.page('foo', {contents: null}); + delete page.options.handled; + + app.handleOnce('foo', page, function(err, view) { + if (err) return cb(err); + + assert(typeof view.path === 'string'); + cb(); + }); + }); +}); diff --git a/test/app.handlers.js b/test/app.handlers.js index 2b1c9c20..21cee5c7 100644 --- a/test/app.handlers.js +++ b/test/app.handlers.js @@ -1,46 +1,74 @@ 'use strict'; require('should'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var resolve = require('resolve-glob'); var support = require('./support'); var App = support.resolve(); var app; +function decorateViews(views) { + var fn = views.decorateView; + views.decorateView = function() { + var view = fn.apply(fn, arguments); + view.read = function() { + if (!this.contents) { + this.contents = fs.readFileSync(this.path); + } + }; + return view; + }; + views.loader = function(pattern) { + var files = resolve.sync(pattern); + return files.reduce(function(acc, fp) { + acc[fp] = {path: fp}; + return acc; + }, {}); + }; + return views; +} + describe('app.handlers', function() { describe('custom handlers', function() { beforeEach(function() { app = new App(); - app.create('page'); + app.create('pages') + .use(decorateViews) + .option('renameKey', function(key) { + return path.basename(key); + }); }); it('should add custom middleware handlers:', function() { app.handler('foo'); - app.handler('bar'); + app.router.should.have.property('foo'); + assert.equal(typeof app.router.foo, 'function'); + }); - app.pages.use(function() { - return function(view) { - app.handle('foo', view); - app.handle('bar', view); - }; - }); + it('should add custom middleware handlers:', function() { + app.handler('foo'); + app.handler('bar'); - app.foo(/a/, function(view, next) { + app.foo(/./, function(view, next) { view.one = 'aaa'; next(); }); - app.bar(/z/, function(view, next) { + app.bar(/./, function(view, next) { view.two = 'zzz'; next(); }); - app.pages('a.txt', {content: 'aaa'}); - app.pages('z.txt', {content: 'zzz'}); - - app.pages.getView('a.txt').should.have.property('one'); - app.pages.getView('a.txt').should.not.have.property('two'); + app.page('abc', {content: '...'}) + .use(function(view) { + app.handleView('foo', view); + app.handleView('bar', view); + }); - app.pages.getView('z.txt').should.not.have.property('one'); - app.pages.getView('z.txt').should.have.property('two'); + app.views.pages.abc.should.have.property('one', 'aaa'); + app.views.pages.abc.should.have.property('two', 'zzz'); }); }); }); diff --git a/test/app.js b/test/app.js index c64f8e76..2ba1759b 100644 --- a/test/app.js +++ b/test/app.js @@ -96,6 +96,16 @@ describe('app', function() { assert.equal(app.get('Group'), MyGroup); }); + it('should mixin prototype methods defined on options:', function() { + app = new App({ + mixins: { + foo: function() {} + } + }); + assert(typeof app.foo === 'function'); + delete App.prototype.foo; + }); + it('should expose `_` on app:', function() { app = new App(); assert(typeof app._ === 'object'); diff --git a/test/app.list.render.js b/test/app.list.render.js index f36d530c..f0c97eb1 100644 --- a/test/app.list.render.js +++ b/test/app.list.render.js @@ -2,7 +2,7 @@ require('mocha'); require('should'); -var async = require('async'); +var each = require('async-each'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); @@ -131,15 +131,15 @@ describe('app.list.render', function() { 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}} }); pages.use(function(collection) { collection.option('pager', false); - + collection.renderEach = function(cb) { var list = new List(collection); - async.map(list.items, function(item, next) { + each(list.items, function(item, next) { collection.render(item, next); }, cb); }; diff --git a/test/app.lookups.js b/test/app.lookups.js index 1a7dfd55..0a28bf00 100644 --- a/test/app.lookups.js +++ b/test/app.lookups.js @@ -49,6 +49,11 @@ describe('app.lookups', function() { it('should return undefined when nothing is found', function() { var view = app.getView('pages', 'test/fixtures/templates/foo.tmpl'); + assert.equal(typeof view, 'undefined'); + }); + + it('should return undefined when name is a directory', function() { + var view = app.getView('pages', 'test/fixtures/templates'); assert(typeof view === 'undefined'); }); diff --git a/test/app.mergePartials.js b/test/app.mergePartials.js index edd65c44..01c9a17b 100644 --- a/test/app.mergePartials.js +++ b/test/app.mergePartials.js @@ -25,7 +25,7 @@ describe('app.mergePartials', function() { actual.partials.should.have.properties(['a', 'b', 'c']); }); - it('should keep partials collections on separaet collections:', function() { + it('should keep partials collections on separate collections:', function() { var opts = { viewType: 'partial' }; app.create('foo', opts); app.create('bar', opts); @@ -104,3 +104,4 @@ describe('app.mergePartials', function() { actual.should.eql({ bazs: { c: 'ccc' } }); }); }); + diff --git a/test/app.middleware.js b/test/app.middleware.js index b5c07aa0..437d17ec 100644 --- a/test/app.middleware.js +++ b/test/app.middleware.js @@ -55,7 +55,7 @@ describe('app.middleware', function() { app.page('foo.tmpl', {content: 'foo'}) .render(function(err) { if (err) return cb(err); - assert(i === 3); + assert.equal(i, 3); cb(); }); }); diff --git a/test/app.option.js b/test/app.option.js index ec417827..bc9d855c 100644 --- a/test/app.option.js +++ b/test/app.option.js @@ -14,79 +14,79 @@ describe('app.option', function() { it('should set a key-value pair on options:', function() { app.option('a', 'b'); - assert(app.options.a === 'b'); + assert.equal(app.options.a, 'b'); }); it('should set an object on options:', function() { app.option({c: 'd'}); - assert(app.options.c === 'd'); + assert.equal(app.options.c, 'd'); }); it('should set an option.', function() { app.option('a', 'b'); - app.options.should.have.property('a'); + assert(app.options.hasOwnProperty('a')); }); it('should get an option.', function() { app.option('a', 'b'); - app.option('a').should.equal('b'); + assert.equal(app.option('a'), 'b'); }); it('should extend the `options` object.', function() { app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - app.option('x').should.equal('xxx'); - app.option('y').should.equal('yyy'); - app.option('z').should.equal('zzz'); + assert.equal(app.option('x'), 'xxx'); + assert.equal(app.option('y'), 'yyy'); + assert.equal(app.option('z'), 'zzz'); }); it('options should be on the `options` object.', function() { app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - app.options.x.should.equal('xxx'); - app.options.y.should.equal('yyy'); - app.options.z.should.equal('zzz'); + assert.equal(app.options.x, 'xxx'); + assert.equal(app.options.y, 'yyy'); + assert.equal(app.options.z, 'zzz'); }); it('should be chainable.', function() { app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); app.option({a: 'aaa', b: 'bbb', c: 'ccc'}); - app.option('x').should.equal('xxx'); - app.option('a').should.equal('aaa'); + assert.equal(app.option('x'), 'xxx'); + assert.equal(app.option('a'), 'aaa'); }); it('should extend the `options` object when the first param is a string.', function() { app.option('foo', {x: 'xxx', y: 'yyy', z: 'zzz'}); app.option('bar', {a: 'aaa', b: 'bbb', c: 'ccc'}); - app.option('foo').should.have.property('x'); - app.option('bar').should.have.property('a'); + assert(app.option('foo').hasOwnProperty('x')); + assert(app.option('bar').hasOwnProperty('a')); - app.options.foo.should.have.property('x'); - app.options.bar.should.have.property('a'); + assert(app.options.foo.hasOwnProperty('x')); + assert(app.options.bar.hasOwnProperty('a')); }); it('should set an option.', function() { app.option('a', 'b'); - app.options.should.have.property('a'); + assert(app.options.hasOwnProperty('a')); }); it('should get an option.', function() { app.option('a', 'b'); - app.option('a').should.equal('b'); + assert.equal(app.option('a'), 'b'); }); it('should extend the `options` object.', function() { app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - app.option('x').should.equal('xxx'); - app.option('y').should.equal('yyy'); - app.option('z').should.equal('zzz'); + assert.equal(app.option('x'), 'xxx'); + assert.equal(app.option('y'), 'yyy'); + assert.equal(app.option('z'), 'zzz'); }); it('options should be on the `options` object.', function() { app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - app.options.x.should.equal('xxx'); - app.options.y.should.equal('yyy'); - app.options.z.should.equal('zzz'); + assert.equal(app.options.x, 'xxx'); + assert.equal(app.options.y, 'yyy'); + assert.equal(app.options.z, 'zzz'); }); it('should be chainable.', function() { @@ -94,7 +94,7 @@ describe('app.option', function() { .option({x: 'xxx', y: 'yyy', z: 'zzz'}) .option({a: 'aaa', b: 'bbb', c: 'ccc'}); - app.option('x').should.equal('xxx'); - app.option('a').should.equal('aaa'); + assert.equal(app.option('x'), 'xxx'); + assert.equal(app.option('a'), 'aaa'); }); }); diff --git a/test/app.options.initTemplates.js b/test/app.options.initTemplates.js new file mode 100644 index 00000000..20ac2f09 --- /dev/null +++ b/test/app.options.initTemplates.js @@ -0,0 +1,24 @@ +'use strict'; + +require('mocha'); +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app; + +describe('app.options.initTemplates', function() { + it('should call `options.initTemplates` before any instance methods are called', function() { + app = new App({ + initTemplates: function(app) { + this.on('create', function(name, options) { + options.viewType = 'partial'; + }); + } + }); + + app.create('includes'); + app.include('one', {path: 'two', contents: '...'}); + assert(app.includes.isType('partial')); + }); +}); diff --git a/test/app.render.js b/test/app.render.js index 6f6b119b..d3632429 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -10,14 +10,20 @@ var app; describe('app.render', function() { beforeEach(function() { app = new App(); + app.engine('hbs', require('engine-handlebars')); app.engine('tmpl', require('engine-base')); + app.create('partials', {viewType: 'partial'}); app.create('page'); }); - it('should throw an error when no callback is given:', function() { - (function() { - app.render({}); - }).should.throw('Templates#render is async and expects a callback function'); + it('should throw an error when no callback is given:', function(cb) { + try { + app.render(); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'Templates#render is async and expects a callback function'); + cb(); + } }); it('should throw an error when an engine is not defined:', function(cb) { @@ -48,6 +54,92 @@ describe('app.render', function() { }); }); + it('should use global app data in template.', function(cb) { + app.data({name: 'CCC'}); + app.page('a.tmpl', {content: 'a <%= name %> b'}); + app.render('a.tmpl', function(err, res) { + if (err) return cb(err); + res.content.should.equal('a CCC b'); + cb(); + }); + }); + + it('should use page data in template.', function(cb) { + app.data({name: 'CCC'}); + app.page('a.tmpl', {content: 'a <%= name %> b', data: {name: 'DDD'}}); + app.render('a.tmpl', function(err, res) { + if (err) return cb(err); + res.content.should.equal('a DDD b'); + cb(); + }); + }); + + it('should use passed in locals in template.', function(cb) { + app.data({name: 'CCC'}); + app.page('a.tmpl', {content: 'a <%= name %> b', data: {name: 'DDD'}}); + app.render('a.tmpl', {name: 'EEE'}, function(err, res) { + if (err) return cb(err); + res.content.should.equal('a EEE b'); + cb(); + }); + }); + + it('should render the same template twice with a helper', function(cb) { + app.partial('button.tmpl', {content: '<%= name %>'}); + app.page('a.tmpl', {content: 'a <%= partial("button.tmpl", {name: "foo"}) %> <%= partial("button.tmpl", {name: "bar"}) %> b'}); + + app.pages.getView('a.tmpl') + .render(function(err, res) { + if (err) return cb(err); + assert.equal(res.content, 'a foo bar b'); + cb(); + }); + }); + + it('should render the same template multiple times with different engines', function(cb) { + app.partial('button.tmpl', {content: '<%= name %>'}); + app.partial('foo.hbs', {content: '{{name}}'}); + + app.page('a.hbs', {content: 'a <%= partial("button.tmpl", {name: "foo"}) %> <%= partial("button.tmpl", {name: "bar"}) %> {{partial "foo.hbs" name="one"}} {{partial "foo.hbs" name="two"}} b'}); + + var view = app.pages.getView('a.hbs'); + + app.render(view, function(err, res) { + if (err) return cb(err); + + res.engine = 'tmpl'; + + app.render(res, function(err, res) { + if (err) return cb(err); + assert.equal(res.content, 'a foo bar one two b'); + cb(); + }); + }); + }); + + it('should render the same template multiple times with different engines', function(cb) { + app.partial('button.tmpl', {content: '{{title}}', engine: 'hbs'}); + app.partial('foo.hbs', {content: '{{title}}'}); + + app.page('a.hbs', { + content: 'a <%= partial("button.tmpl", {title: "foo"}) %> <%= partial("button.tmpl", {title: "bar"}) %> {{partial "foo.hbs" title="one"}} {{partial "foo.hbs" title="two"}} b' + }); + + var view = app.pages.getView('a.hbs'); + + app.render(view, function(err, res) { + if (err) return cb(err); + + res.engine = 'tmpl'; + + app.render(res, function(err, res) { + if (err) return cb(err); + assert.equal(res.content, 'a foo bar one two b'); + cb(); + }); + }); + }); + it('should use helpers when rendering a view:', function(cb) { var locals = {name: 'Halle'}; app.helper('upper', function(str) { @@ -85,4 +177,4 @@ describe('app.render', function() { cb(); }); }); -}); \ No newline at end of file +}); diff --git a/test/app.route.js b/test/app.route.js index 9b7689f2..bc4ebaf4 100644 --- a/test/app.route.js +++ b/test/app.route.js @@ -18,7 +18,7 @@ describe('app.route', function() { app.create('posts'); app.on('all', function(msg) { - assert(msg === 'cb'); + assert.equal(msg, 'cb'); cb(); }); @@ -36,17 +36,17 @@ describe('app.route', function() { app.create('posts'); app.on('onLoad', function(view) { - assert(view.path === 'blog/foo.js'); + assert.equal(view.path, 'blog/foo.js'); cb(); }); app.param('title', function(view, next, title) { - assert(title === 'foo.js'); + assert.equal(title, 'foo.js'); next(); }); app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); + assert.equal(view.path, 'blog/foo.js'); next(); }); @@ -58,18 +58,18 @@ describe('app.route', function() { app.create('posts'); app.on('error', function(err) { - assert(err.message === 'false == true'); + assert.equal(err.message, "'foo.js' == 'fo.js'"); cb(); }); // wrong... app.param('title', function(view, next, title) { - assert(title === 'fo.js'); + assert.equal(title, 'fo.js'); next(); }); app.onLoad('/blog/:title', function(view, next) { - assert(view.path === '/blog/foo.js'); + assert.equal(view.path, '/blog/foo.js'); next(); }); diff --git a/test/app.src.js b/test/app.src.js index 5f48f1d9..88e7e39f 100644 --- a/test/app.src.js +++ b/test/app.src.js @@ -1,18 +1,18 @@ 'use strict'; var App = require('..'); +var path = require('path'); var assert = require('assert'); var should = require('should'); -var join = require('path').join; var app; -describe('app.src', function() { +describe('src()', function() { beforeEach(function() { app = new App(); }); it('should return a stream', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); + var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); assert(stream); assert.equal(typeof stream.on, 'function'); assert.equal(typeof stream.pipe, 'function'); @@ -20,13 +20,78 @@ describe('app.src', function() { }); it('should return an input stream from a flat glob', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); + var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); + stream.on('error', cb); + stream.on('data', function(file) { + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', cb); + }); + + it('should add files to an existing collection', function(cb) { + app.create('files'); + app.files('foo', {content: 'this is content'}); + var stream = app.src(path.join(__dirname, 'fixtures/test.coffee'), {collection: 'files'}); + stream.on('error', cb); + stream.on('data', function(file) { + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + assert.equal(Object.keys(app.views.files).length, 2); + cb(); + }); + }); + + it('should extend file.options with src options', function(cb) { + app.create('files'); + app.file('foo', {content: 'this is content'}); + var stream = app.src(path.join(__dirname, 'fixtures/test.coffee'), {layout: 'default'}); + stream.on('error', cb); + stream.on('data', function(file) { + assert(file); + assert(file.path); + assert(file.contents); + assert.equal(file.options.layout, 'default'); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + cb(); + }); + }); + + it('should add files to a new specified collection', function(cb) { + var stream = app.src(path.join(__dirname, 'fixtures/test.coffee'), {collection: 'docs'}); + stream.on('error', cb); + stream.on('data', function(file) { + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + assert.equal(Object.keys(app.views.docs).length, 1); + cb(); + }); + }); + + it('should return an input stream from a flat glob', function(cb) { + var stream = app.src(path.join(__dirname, './fixtures/*.coffee')); stream.on('error', cb); stream.on('data', function(file) { should.exist(file); should.exist(file.path); should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + path.join(file.path, '').should.equal(path.join(__dirname, './fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); stream.on('end', function() { @@ -36,16 +101,16 @@ describe('app.src', function() { it('should return an input stream for multiple globs', function(cb) { var globArray = [ - join(__dirname, './fixtures/generic/run.dmc'), - join(__dirname, './fixtures/generic/test.dmc') + path.join(__dirname, './fixtures/generic/run.dmc'), + path.join(__dirname, './fixtures/generic/test.dmc') ]; var stream = app.src(globArray); var files = []; stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); + assert(file); + assert(file.path); files.push(file); }); stream.on('end', function() { @@ -57,18 +122,18 @@ describe('app.src', function() { }); it('should return an input stream for multiple globs with negation', function(cb) { - var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); + var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); var globArray = [ - join(__dirname, './fixtures/generic/*.dmc'), - '!' + join(__dirname, './fixtures/generic/test.dmc'), + path.join(__dirname, 'fixtures/generic/*.dmc'), + '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), ]; var stream = app.src(globArray); var files = []; stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); + assert(file); + assert(file.path); files.push(file); }); stream.on('end', function() { @@ -79,26 +144,31 @@ describe('app.src', function() { }); it('should return an input stream with no contents when read is false', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - }); - stream.on('end', function() { - cb(); - }); + app.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}) + .on('error', cb) + .on('data', function(file) { + assert(file); + assert(file.path); + assert(!file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + }) + .on('end', cb) + }); + + it('should not blow up when no files are matched', function(cb) { + app.src(['test.js', 'foo/*.js']) + .on('error', cb) + .on('data', function() {}) + .on('end', cb) }); it('should return an input stream with contents as stream when buffer is false', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {buffer: false}); + var stream = app.src(path.join(__dirname, 'fixtures/*.coffee'), {buffer: false}); stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); + assert(file); + assert(file.path); + assert(file.contents); var buf = ''; file.contents.on('data', function(d) { buf += d; @@ -107,27 +177,25 @@ describe('app.src', function() { buf.should.equal('Hello world!'); cb(); }); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); }); }); it('should return an input stream from a deep glob', function(cb) { - var stream = app.src(join(__dirname, './fixtures/**/*.jade')); + var stream = app.src(path.join(__dirname, 'fixtures/**/*.jade')); stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); String(file.contents).should.equal('test template'); }); - stream.on('end', function() { - cb(); - }); + stream.on('end', cb); }); it('should return an input stream from a deeper glob', function(cb) { - var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); + var stream = app.src(path.join(__dirname, 'fixtures/**/*.dmc')); var a = 0; stream.on('error', cb); stream.on('data', function() { @@ -141,14 +209,14 @@ describe('app.src', function() { it('should return a file stream from a flat path', function(cb) { var a = 0; - var stream = app.src(join(__dirname, './fixtures/test.coffee')); + var stream = app.src(path.join(__dirname, 'fixtures/test.coffee')); stream.on('error', cb); stream.on('data', function(file) { ++a; - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); stream.on('end', function() { @@ -158,39 +226,37 @@ describe('app.src', function() { }); it('should return a stream', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); - should.exist(stream); - should.exist(stream.on); + var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); + assert(stream); + assert(stream.on); cb(); }); it('should return an input stream from a flat glob', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee')); + var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); - stream.on('end', function() { - cb(); - }); + stream.on('end', cb); }); it('should return an input stream for multiple globs', function(cb) { var globArray = [ - join(__dirname, './fixtures/generic/run.dmc'), - join(__dirname, './fixtures/generic/test.dmc') + path.join(__dirname, 'fixtures/generic/run.dmc'), + path.join(__dirname, 'fixtures/generic/test.dmc') ]; var stream = app.src(globArray); var files = []; stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); + assert(file); + assert(file.path); files.push(file); }); stream.on('end', function() { @@ -202,18 +268,18 @@ describe('app.src', function() { }); it('should return an input stream for multiple globs, with negation', function(cb) { - var expectedPath = join(__dirname, './fixtures/generic/run.dmc'); + var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); var globArray = [ - join(__dirname, './fixtures/generic/*.dmc'), - '!' + join(__dirname, './fixtures/generic/test.dmc'), + path.join(__dirname, 'fixtures/generic/*.dmc'), + '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), ]; var stream = app.src(globArray); var files = []; stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); + assert(file); + assert(file.path); files.push(file); }); stream.on('end', function() { @@ -224,27 +290,25 @@ describe('app.src', function() { }); it('should return an input stream with no contents when read is false', function(cb) { - var stream = app.src(join(__dirname, './fixtures/*.coffee'), {read: false}); + var stream = app.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}); stream.on('error', cb); stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); - }); - stream.on('end', function() { - cb(); + assert(file); + assert(file.path); + assert(!file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); }); + stream.on('end', cb); }); it('should return an input stream from a deep glob', function(cb) { - app.src(join(__dirname, './fixtures/**/*.jade')) + app.src(path.join(__dirname, 'fixtures/**/*.jade')) .on('error', cb) .on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test/run.jade')); + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); String(file.contents).should.equal('test template'); }) .on('end', function() { @@ -253,7 +317,7 @@ describe('app.src', function() { }); it('should return an input stream from a deeper glob', function(cb) { - var stream = app.src(join(__dirname, './fixtures/**/*.dmc')); + var stream = app.src(path.join(__dirname, 'fixtures/**/*.dmc')); var a = 0; stream.on('error', cb); stream.on('data', function() { @@ -267,14 +331,14 @@ describe('app.src', function() { it('should return a file stream from a flat path', function(cb) { var a = 0; - var stream = app.src(join(__dirname, './fixtures/test.coffee')); + var stream = app.src(path.join(__dirname, 'fixtures/test.coffee')); stream.on('error', cb); stream.on('data', function(file) { ++a; - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - join(file.path, '').should.equal(join(__dirname, './fixtures/test.coffee')); + assert(file); + assert(file.path); + assert(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); String(file.contents).should.equal('Hello world!'); }); stream.on('end', function() { diff --git a/test/app.symlink.js b/test/app.symlink.js index 9ae029fd..1bf164f1 100644 --- a/test/app.symlink.js +++ b/test/app.symlink.js @@ -15,7 +15,7 @@ var statSpy = spies.statSpy; var app, bufferStream; var wipeOut = function(cb) { - rimraf(path.join(__dirname, 'actual/'), cb); + rimraf(path.join(__dirname, './actual/'), cb); spies.setError('false'); statSpy.reset(); chmodSpy.reset(); @@ -30,7 +30,7 @@ var dataWrap = function(fn) { }; var realMode = function(n) { - return parseInt('777', 8); + return n & parseInt('777', 8); }; describe('app.symlink', function() { @@ -38,7 +38,7 @@ describe('app.symlink', function() { afterEach(wipeOut); it('should pass through writes with cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); var expectedFile = new File({ base: __dirname, @@ -53,7 +53,7 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink('actual/', {cwd: __dirname}); + var stream = app.symlink('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -63,7 +63,7 @@ describe('app.symlink', function() { }); it('should pass through writes with default cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); var expectedFile = new File({ base: __dirname, @@ -78,7 +78,7 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink(path.join(__dirname, 'actual/')); + var stream = app.symlink(path.join(__dirname, './actual/')); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -88,10 +88,10 @@ describe('app.symlink', function() { }); it('should make link to the right folder with relative cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedBase = path.join(__dirname, 'actual'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedBase = path.join(__dirname, './actual'); var expectedContents = fs.readFileSync(inputPath); var expectedFile = new File({ @@ -113,7 +113,7 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink('actual/', {cwd: path.relative(process.cwd(), __dirname)}); + var stream = app.symlink('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -123,10 +123,10 @@ describe('app.symlink', function() { }); it('should write buffer files to the right folder with function and relative cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedBase = path.join(__dirname, 'actual'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); + var expectedBase = path.join(__dirname, './actual'); var expectedContents = fs.readFileSync(inputPath); var expectedFile = new File({ @@ -151,7 +151,7 @@ describe('app.symlink', function() { var stream = app.symlink(function(file) { should.exist(file); file.should.equal(expectedFile); - return 'actual'; + return './actual'; }, {cwd: path.relative(process.cwd(), __dirname)}); var buffered = []; @@ -162,12 +162,12 @@ describe('app.symlink', function() { }); it('should write buffer files to the right folder', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('0655', 8); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = parseInt('655', 8); var expectedFile = new File({ base: inputBase, @@ -191,7 +191,7 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink('actual/', {cwd: __dirname}); + var stream = app.symlink('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -201,12 +201,12 @@ describe('app.symlink', function() { }); it('should write streaming files to the right folder', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/test.coffee'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('0655', 8); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = parseInt('655', 8); var contentStream = through.obj(); var expectedFile = new File({ @@ -231,25 +231,25 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink('actual/', {cwd: __dirname}); + var stream = app.symlink('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); stream.pipe(bufferStream); stream.write(expectedFile); - setTimeout(function() { + setImmediate(function() { contentStream.write(expectedContents); contentStream.end(); - }, 100); + }); stream.end(); }); it('should write directories to the right folder', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/wow'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/wow'); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('0655', 8); + var inputPath = path.join(__dirname, './fixtures/wow'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './actual/wow'); + var expectedBase = path.join(__dirname, './actual'); + var expectedMode = parseInt('655', 8); var expectedFile = new File({ base: inputBase, @@ -276,7 +276,7 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink('actual/', {cwd: __dirname}); + var stream = app.symlink('./actual/', {cwd: __dirname}); var buffered = []; bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); @@ -285,9 +285,43 @@ describe('app.symlink', function() { stream.end(); }); + it('should use different modes for files and directories', function(cb) { + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + var expectedBase = path.join(__dirname, './actual/wow'); + var expectedDirMode = parseInt('755', 8); + var expectedFileMode = parseInt('655', 8); + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + stat: fs.statSync(inputPath) + }); + + var onEnd = function() { + realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); + realMode(buffered[0].stat.mode).should.equal(expectedFileMode); + cb(); + }; + + var stream = app.symlink('./actual/', { + cwd: __dirname, + mode: expectedFileMode, + dirMode: expectedDirMode + }); + + var buffered = []; + bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + + stream.pipe(bufferStream); + stream.write(firstFile); + stream.end(); + }); + it('should change to the specified base', function(cb) { - var inputBase = path.join(__dirname, 'fixtures'); - var inputPath = path.join(__dirname, 'fixtures/wow/suchempty'); + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); var firstFile = new File({ base: inputBase, @@ -301,7 +335,7 @@ describe('app.symlink', function() { cb(); }; - var stream = app.symlink('actual/', { + var stream = app.symlink('./actual/', { cwd: __dirname, base: inputBase }); @@ -315,10 +349,10 @@ describe('app.symlink', function() { }); it('should report IO errors', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); + var expectedBase = path.join(__dirname, './actual'); var expectedMode = parseInt('722', 8); var expectedFile = new File({ @@ -334,7 +368,7 @@ describe('app.symlink', function() { fs.mkdirSync(expectedBase); fs.chmodSync(expectedBase, 0); - var stream = app.symlink('actual/', {cwd: __dirname}); + var stream = app.symlink('./actual/', {cwd: __dirname}); stream.on('error', function(err) { err.code.should.equal('EACCES'); cb(); @@ -344,8 +378,8 @@ describe('app.symlink', function() { ['end', 'finish'].forEach(function(eventName) { it('should emit ' + eventName + ' event', function(cb) { - var srcPath = path.join(__dirname, 'fixtures/test.coffee'); - var stream = app.symlink('actual/', {cwd: __dirname}); + var srcPath = path.join(__dirname, './fixtures/test.coffee'); + var stream = app.symlink('./actual/', {cwd: __dirname}); stream.on(eventName, function() { cb(); @@ -354,7 +388,7 @@ describe('app.symlink', function() { var file = new File({ path: srcPath, cwd: __dirname, - contents: new Buffer("1234567890") + contents: new Buffer('1234567890') }); stream.write(file); diff --git a/test/app.task.js b/test/app.task.js index 0ff1fde4..8a3a334d 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -1,48 +1,47 @@ 'use strict'; -require('mocha'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var App = require('..'); +var app; describe('app.task', function() { beforeEach(function() { - generate = new Generate(); + app = new App(); }); it('should register a task', function() { var fn = function(cb) { cb(); }; - generate.task('default', fn); - assert.equal(typeof generate.tasks.default, 'object'); - assert.equal(generate.tasks.default.fn, fn); + app.task('default', fn); + assert.equal(typeof app.tasks.default, 'object'); + assert.equal(app.tasks.default.fn, fn); }); it('should register a task with an array of dependencies', function() { - generate.task('default', ['foo', 'bar'], function(cb) { + app.task('default', ['foo', 'bar'], function(cb) { cb(); }); - assert.equal(typeof generate.tasks.default, 'object'); - assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof app.tasks.default, 'object'); + assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); }); it('should register a task with a list of strings as dependencies', function() { - generate.task('default', 'foo', 'bar', function(cb) { + app.task('default', 'foo', 'bar', function(cb) { cb(); }); - assert.equal(typeof generate.tasks.default, 'object'); - assert.deepEqual(generate.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof app.tasks.default, 'object'); + assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); }); it('should run a task', function(cb) { var count = 0; - generate.task('default', function(cb) { + app.task('default', function(cb) { count++; cb(); }); - generate.build('default', function(err) { + app.build('default', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -50,16 +49,22 @@ describe('app.task', function() { }); it('should throw an error when a task with unregistered dependencies is run', function(cb) { - generate.task('default', ['foo', 'bar']); - generate.build('default', function(err) { - assert(err); + var count = 0; + app.task('default', ['foo', 'bar'], function(cb) { + count++; + cb(); + }); + + app.build('default', function(err) { + if (!err) return cb(new Error('Expected an error to be thrown.')); + assert.equal(count, 0); cb(); }); }); it('should throw an error when `.build` is called without a callback function.', function() { try { - generate.build('default'); + app.build('default'); throw new Error('Expected an error to be thrown.'); } catch (err) { } @@ -67,23 +72,25 @@ describe('app.task', function() { it('should emit task events', function(cb) { var events = []; - generate.on('task:starting', function(task) { + app.on('task:starting', function(task) { events.push('starting.' + task.name); }); - generate.on('task:finished', function(task) { + app.on('task:finished', function(task) { events.push('finished.' + task.name); }); - generate.on('task:error', function(e, task) { + app.on('task:error', function(err, task) { + assert(err); events.push('error.' + task.name); }); - generate.task('foo', function(cb) { + + app.task('foo', function(cb) { cb(); }); - generate.task('bar', ['foo'], function(cb) { + app.task('bar', ['foo'], function(cb) { cb(); }); - generate.task('default', ['bar']); - generate.build('default', function(err) { + app.task('default', ['bar']); + app.build('default', function(err) { if (err) return cb(err); assert.deepEqual(events, [ 'starting.default', @@ -98,49 +105,52 @@ describe('app.task', function() { }); it('should emit an error event when an error is passed back in a task', function(cb) { - generate.on('error', function(err) { + app.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - generate.task('default', function(cb) { + app.task('default', function(cb) { return cb(new Error('This is an error')); }); - generate.build('default', function(err) { + app.build('default', function(err) { if (err) return cb(); cb(new Error('Expected an error')); }); }); it('should emit an error event when an error is thrown in a task', function(cb) { - generate.on('error', function(err) { + var errors = 0; + app.on('error', function(err) { + errors++; assert(err); assert.equal(err.message, 'This is an error'); }); - generate.task('default', function(cb) { + app.task('default', function(cb) { cb(new Error('This is an error')); }); - generate.build('default', function(err) { - assert(err); - cb(); + app.build('default', function(err) { + assert.equal(errors, 1); + if (err) return cb(); + cb(new Error('Expected an error')); }); }); it('should run dependencies before running the dependent task.', function(cb) { var seq = []; - generate.task('foo', function(cb) { + app.task('foo', function(cb) { seq.push('foo'); cb(); }); - generate.task('bar', function(cb) { + app.task('bar', function(cb) { seq.push('bar'); cb(); }); - generate.task('default', ['foo', 'bar'], function(cb) { + app.task('default', ['foo', 'bar'], function(cb) { seq.push('default'); cb(); }); - generate.build('default', function(err) { + app.build('default', function(err) { if (err) return cb(err); assert.deepEqual(seq, ['foo', 'bar', 'default']); cb(); diff --git a/test/app.toStream.js b/test/app.toStream.js index 905814f9..b87dc122 100644 --- a/test/app.toStream.js +++ b/test/app.toStream.js @@ -61,4 +61,4 @@ describe('app.toStream', function() { cb(); }); }); -}); \ No newline at end of file +}); diff --git a/test/collection.engines.js b/test/collection.engines.js index 71b2a815..2dbd9c05 100644 --- a/test/collection.engines.js +++ b/test/collection.engines.js @@ -8,7 +8,7 @@ var App = support.resolve(); var Views = App.Views; var collection, pages; -describe('collection engines', function() { +describe('collection.engines', function() { beforeEach(function() { pages = new Views(); }); @@ -49,7 +49,7 @@ describe('collection engines', function() { }); }); -describe('engines', function() { +describe('collection.engines', function() { beforeEach(function() { pages = new Views(); pages.addView('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); @@ -79,6 +79,7 @@ describe('engines', function() { evaluate: /\{{([^}]+)}}/g, escape: /\{{-([^}]+)}}/g }); + pages.render('bar.tmpl', {letter: 'B'}, function(err, res) { if (err) return cb(err); res.content.should.equal('A <%= letter %> B C'); diff --git a/test/collection.events.js b/test/collection.events.js index 5abab928..5b2608a4 100644 --- a/test/collection.events.js +++ b/test/collection.events.js @@ -5,7 +5,7 @@ var support = require('./support'); var App = support.resolve(); var app; -describe('collection events', function() { +describe('collection.events', function() { beforeEach(function() { app = new App(); app.create('page'); diff --git a/test/collection.isType.js b/test/collection.isType.js new file mode 100644 index 00000000..423dcdce --- /dev/null +++ b/test/collection.isType.js @@ -0,0 +1,26 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var View = App.View; +var collection; + +describe('collection.isType', function() { + beforeEach(function() { + collection = new View(); + }); + + it('should expose thie "isType" method', function() { + assert.equal(typeof collection.isType, 'function'); + }); + + it('should return true if a collection is the given "type"', function() { + assert(collection.isType('renderable')); + }); + + it('should return false if a collection is not the given "type"', function() { + assert(!collection.isType('partial')); + }); +}); diff --git a/test/collection.js b/test/collection.js index 8665e857..1c4d8411 100644 --- a/test/collection.js +++ b/test/collection.js @@ -6,7 +6,6 @@ var path = require('path'); var assert = require('assert'); var typeOf = require('kind-of'); var isBuffer = require('is-buffer'); - var support = require('./support'); var App = support.resolve(); var List = App.List; @@ -230,7 +229,42 @@ describe('methods', function() { }); }); + describe('deleteItem', function() { + beforeEach(function() { + collection = new Collection(); + }); + + it('should delete an item from `items` by item instance', function() { + collection.addItem('foo'); + assert(collection.items.hasOwnProperty('foo')); + + collection.addItem('one', {content: '...'}); + assert(collection.items.hasOwnProperty('one')); + assert.equal(Object.keys(collection.items).length, 2); + + var foo = collection.getItem('foo'); + collection.deleteItem(foo); + assert.equal(Object.keys(collection.items).length, 1); + }); + + it('should delete an item from `items` by item `key`', function() { + collection.addItem('foo'); + assert(collection.items.hasOwnProperty('foo')); + + collection.addItem('one', {content: '...'}); + assert(collection.items.hasOwnProperty('one')); + assert.equal(Object.keys(collection.items).length, 2); + + collection.deleteItem('foo'); + assert.equal(Object.keys(collection.items).length, 1); + }); + }); + describe('addItems', function() { + beforeEach(function() { + collection = new Collection(); + }); + it('should add multiple items', function() { collection.addItems({ one: {content: 'foo'}, diff --git a/test/collection.render.js b/test/collection.render.js index 7b3074b5..12ebdcd4 100644 --- a/test/collection.render.js +++ b/test/collection.render.js @@ -2,7 +2,7 @@ require('mocha'); require('should'); -var async = require('async'); +var each = require('async-each'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); @@ -17,10 +17,14 @@ describe('collection.render', function() { pages.engine('tmpl', require('engine-base')); }); - it('should throw an error when no callback is given:', function() { - (function() { - pages.render({}); - }).should.throw('Views#render is async and expects a callback function'); + it('should throw an error when no callback is given:', function(cb) { + try { + pages.render(); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'Views#render is async and expects a callback function'); + cb(); + } }); it('should throw an error when an engine is not defined:', function(cb) { @@ -28,7 +32,7 @@ describe('collection.render', function() { var page = pages.getView('foo.bar'); pages.render(page, function(err) { - assert(err.message === 'Views#render cannot find an engine for: .bar'); + assert.equal(err.message, 'Views#render cannot find an engine for: .bar'); cb(); }); }); @@ -43,6 +47,24 @@ describe('collection.render', function() { pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); var page = pages.getView('a.tmpl'); + pages.render(page, function(err, res) { + if (err) return cb(err); + + assert.equal(res.content, 'a HALLE b'); + cb(); + }); + }); + + it('should use globally defined data to render a view', function(cb) { + pages.data({name: 'Halle'}); + + pages.helper('upper', function(str) { + return str.toUpperCase(str); + }); + + pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b'}); + var page = pages.getView('a.tmpl'); + pages.render(page, function(err, res) { if (err) return cb(err); @@ -62,7 +84,7 @@ describe('collection.render', function() { pages.render(page, function(err, res) { if (err) return cb(err); - assert(res.content === 'a HALLE b'); + assert.equal(res.content, 'a HALLE b'); cb(); }); }); @@ -73,7 +95,7 @@ describe('collection.render', function() { pages.render(view, function(err, view) { if (err) return cb(err); - assert(view.contents.toString() === 'b'); + assert.equal(view.contents.toString(), 'b'); cb(); }); }); @@ -84,7 +106,7 @@ describe('collection.render', function() { pages.render(view, function(err, view) { if (err) return cb(err); - assert(view.contents.toString() === 'b'); + assert.equal(view.contents.toString(), 'b'); cb(); }); }); @@ -94,7 +116,7 @@ describe('collection.render', function() { pages.render('a.tmpl', function(err, view) { if (err) return cb(err); - assert(view.content === 'b'); + assert.equal(view.content, 'b'); cb(); }); }); @@ -113,26 +135,26 @@ describe('collection.render', function() { 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}} }); pages.use(function(collection) { collection.option('pager', false); - collection.renderEach = function(cb) { + collection.renderEach = function(done) { var list = new List(collection); - async.map(list.items, function(item, next) { + each(list.items, function(item, next) { collection.render(item, next); - }, cb); + }, done); }; }); pages.renderEach(function(err, items) { if (err) return cb(err); - assert(items[0].content === 'aaa'); - assert(items[9].content === 'jjj'); - assert(items.length === 10); + assert.equal(items[0].content, 'aaa'); + assert.equal(items[9].content, 'jjj'); + assert.equal(items.length, 10); cb(); }); }); diff --git a/test/collection.routes.js b/test/collection.routes.js new file mode 100644 index 00000000..4bfc9b09 --- /dev/null +++ b/test/collection.routes.js @@ -0,0 +1,130 @@ +'use strict'; + +require('should'); +var assert = require('assert'); +var support = require('./support'); +var App = support.resolve(); +var app, pages; + +function append(str) { + return function(view, next) { + var content = view.contents.toString(); + view.contents = new Buffer(content + ' ' + str); + next(); + }; +} +function prepend(str) { + return function(view, next) { + var content = view.contents.toString(); + view.contents = new Buffer(str + ' ' + content); + next(); + }; +} + +describe('collection.routes', function() { + beforeEach(function() { + app = new App(); + app.engine('tmpl', require('engine-base')); + pages = app.create('page'); + }); + + describe('params', function() { + it('should call param function when routing', function(cb) { + pages.param('id', function(view, next, id) { + assert.equal(id, '123'); + next(); + }); + + pages.all('/foo/:id/bar', function(view, next) { + assert.equal(view.options.params.id, '123'); + next(); + }); + + pages.router.handle({ path: '/foo/123/bar' }, cb); + }); + }); + + describe('onLoad middleware', function() { + it('should run when templates are loaded:', function() { + pages.onLoad(/\.tmpl/, prepend('onLoad')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>'}); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('onLoad <%= name %>'); + }); + }); + + describe('preCompile middleware', function() { + it('should run before templates are compiled:', function() { + + }); + }); + + describe('postCompile middleware', function() { + it('should run after templates are compiled:', function() { + + }); + }); + + describe('preRender middleware', function() { + it('should run before templates are rendered:', function(cb) { + pages.preRender(/\.tmpl/, prepend('preRender')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('<%= name %>'); + + page.render({}, function(err, res) { + if (err) return cb(err); + res.contents.toString().should.equal('preRender aaa'); + cb(); + }); + }); + + it('should run before templates are rendered on both collection and app middleware:', function(cb) { + app.preRender(/\.tmpl/, prepend('app::preRender')); + pages.preRender(/\.tmpl/, prepend('collection::preRender')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('<%= name %>'); + + page.render({}, function(err, res) { + if (err) return cb(err); + res.contents.toString().should.equal('collection::preRender app::preRender aaa'); + cb(); + }); + }); + }); + + describe('postRender middleware', function() { + it('should run after templates are rendered:', function(cb) { + pages.postRender(/\.tmpl/, append('postRender')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('<%= name %>'); + + page.render({}, function(err, res) { + if (err) return cb(err); + res.contents.toString().should.equal('aaa postRender'); + cb(); + }); + }); + + it('should run after templates are rendered on both collection and app middleware:', function(cb) { + app.postRender(/\.tmpl/, append('app::postRender')); + pages.postRender(/\.tmpl/, append('collection::postRender')); + app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); + + var page = app.pages.getView('a.tmpl'); + page.contents.toString().should.equal('<%= name %>'); + + page.render({}, function(err, res) { + if (err) return cb(err); + res.contents.toString().should.equal('aaa app::postRender collection::postRender'); + cb(); + }); + }); + }); +}); diff --git a/test/collection.src.js b/test/collection.src.js new file mode 100644 index 00000000..b600131d --- /dev/null +++ b/test/collection.src.js @@ -0,0 +1,322 @@ +'use strict'; + +var path = require('path'); +var assert = require('assert'); +var should = require('should'); +var App = require('..'); +var app, pages, posts; + +describe('collection.src()', function() { + beforeEach(function() { + app = new App(); + + pages = app.create('pages'); + posts = app.create('posts'); + }); + + it('should return a stream', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); + assert(stream); + assert.equal(typeof stream.on, 'function'); + assert.equal(typeof stream.pipe, 'function'); + cb(); + }); + + it('should convert vinyl files to views', function(cb) { + var patterns = path.join(__dirname, 'fixtures/*.coffee'); + pages.src(patterns) + .on('error', cb) + .on('data', function(file) { + assert(file.isView); + }) + .on('end', cb); + }); + + it('should add src files to the collection', function(cb) { + var patterns = path.join(__dirname, 'fixtures/*.coffee'); + pages.src(patterns) + .on('error', cb) + .on('data', function(file) { + assert(pages.views); + assert(Object.keys(pages.views).length === 1); + }) + .on('end', cb); + }); + + it('should work with views added with other methods', function(cb) { + pages.addView('a', {content: '...'}); + pages.addView('b', {content: '...'}); + pages.addView('c', {content: '...'}); + + var patterns = path.join(__dirname, 'fixtures/*.coffee'); + var stream = pages.src(patterns); + stream.on('error', cb); + stream.on('data', function(file) { + assert(pages.views); + assert(Object.keys(pages.views).length === 4); + }); + stream.on('end', cb); + }); + + it('should return an input stream from a flat glob', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + cb(); + }); + }); + + it('should return an input stream for multiple globs', function(cb) { + var globArray = [ + path.join(__dirname, 'fixtures/generic/run.dmc'), + path.join(__dirname, 'fixtures/generic/test.dmc') + ]; + var stream = pages.src(globArray); + + var files = []; + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function() { + files.length.should.equal(2); + files[0].path.should.equal(globArray[0]); + files[1].path.should.equal(globArray[1]); + cb(); + }); + }); + + it('should return an input stream for multiple globs with negation', function(cb) { + var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); + var globArray = [ + path.join(__dirname, 'fixtures/generic/*.dmc'), + '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), + ]; + var stream = pages.src(globArray); + + var files = []; + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function() { + files.length.should.equal(1); + files[0].path.should.equal(expectedPath); + cb(); + }); + }); + + it('should return an input stream with no contents when read is false', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}); + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + }); + stream.on('end', function() { + cb(); + }); + }); + + it('should return an input stream with contents as stream when buffer is false', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee'), {buffer: false}); + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + var buf = ''; + file.contents.on('data', function(d) { + buf += d; + }); + file.contents.on('end', function() { + buf.should.equal('Hello world!'); + cb(); + }); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + }); + }); + + it('should return an input stream from a deep glob', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/**/*.jade')); + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); + String(file.contents).should.equal('test template'); + }); + stream.on('end', function() { + cb(); + }); + }); + + it('should return an input stream from a deeper glob', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/**/*.dmc')); + var a = 0; + stream.on('error', cb); + stream.on('data', function() { + ++a; + }); + stream.on('end', function() { + a.should.equal(2); + cb(); + }); + }); + + it('should return a file stream from a flat path', function(cb) { + var a = 0; + var stream = pages.src(path.join(__dirname, 'fixtures/test.coffee')); + stream.on('error', cb); + stream.on('data', function(file) { + ++a; + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + a.should.equal(1); + cb(); + }); + }); + + it('should return a stream', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); + should.exist(stream); + should.exist(stream.on); + cb(); + }); + + it('should return an input stream from a flat glob', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + cb(); + }); + }); + + it('should return an input stream for multiple globs', function(cb) { + var globArray = [ + path.join(__dirname, 'fixtures/generic/run.dmc'), + path.join(__dirname, 'fixtures/generic/test.dmc') + ]; + var stream = pages.src(globArray); + + var files = []; + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function() { + files.length.should.equal(2); + files[0].path.should.equal(globArray[0]); + files[1].path.should.equal(globArray[1]); + cb(); + }); + }); + + it('should return an input stream for multiple globs, with negation', function(cb) { + var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); + var globArray = [ + path.join(__dirname, 'fixtures/generic/*.dmc'), + '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), + ]; + var stream = pages.src(globArray); + + var files = []; + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + files.push(file); + }); + stream.on('end', function() { + files.length.should.equal(1); + files[0].path.should.equal(expectedPath); + cb(); + }); + }); + + it('should return an input stream with no contents when read is false', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}); + stream.on('error', cb); + stream.on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.not.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + }); + stream.on('end', function() { + cb(); + }); + }); + + it('should return an input stream from a deep glob', function(cb) { + pages.src(path.join(__dirname, 'fixtures/**/*.jade')) + .on('error', cb) + .on('data', function(file) { + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); + String(file.contents).should.equal('test template'); + }) + .on('end', cb); + }); + + it('should return an input stream from a deeper glob', function(cb) { + var stream = pages.src(path.join(__dirname, 'fixtures/**/*.dmc')); + var a = 0; + stream.on('error', cb); + stream.on('data', function() { + ++a; + }); + stream.on('end', function() { + a.should.equal(2); + cb(); + }); + }); + + it('should return a file stream from a flat path', function(cb) { + var a = 0; + var stream = pages.src(path.join(__dirname, 'fixtures/test.coffee')); + stream.on('error', cb); + stream.on('data', function(file) { + ++a; + should.exist(file); + should.exist(file.path); + should.exist(file.contents); + path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); + String(file.contents).should.equal('Hello world!'); + }); + stream.on('end', function() { + a.should.equal(1); + cb(); + }); + }); +}); diff --git a/test/collection.use.js b/test/collection.use.js index a8eb32ac..55645834 100644 --- a/test/collection.use.js +++ b/test/collection.use.js @@ -156,3 +156,90 @@ describe('collection.use', function() { assert(collection.items.hasOwnProperty('d')); }); }); + +describe('app > collection .use', function() { + var app; + beforeEach(function() { + app = new App(); + }); + + it('should pass plugins down to collections', function(cb) { + var count = 0; + app.use(function(inst) { + return function(collection) { + count++; + }; + }); + + app.create('pages'); + assert.equal(count, 1); + cb(); + }); + + it('should pass plugins down to collections after a collection is created', function(cb) { + var count = 0; + app.create('pages'); + + app.use(function(inst) { + return function(collection) { + count++; + }; + }); + + assert.equal(count, 1); + cb(); + }); + + it('should pass plugins down to every collections', function(cb) { + var count = 0; + app.create('pages'); + app.create('posts'); + app.create('docs'); + + app.use(function(inst) { + return function(collection) { + count++; + }; + }); + + assert.equal(count, 3); + cb(); + }); + + it('should pass plugins all the way down to views', function(cb) { + var count = {pages: 0, posts: 0}; + + app.create('pages'); + app.create('posts'); + + app.pages.addView('foo', {content: 'this is content'}); + app.pages.addView('bar', {content: 'this is content'}); + app.pages.addView('baz', {content: 'this is content'}); + + app.posts.addView('foo', {content: 'this is content'}); + app.posts.addView('bar', {content: 'this is content'}); + app.posts.addView('baz', {content: 'this is content'}); + + // add plugin after adding views and collections + app.use(function(inst) { + return function(collection) { + var name = collection.options.plural; + return function(view) { + count[name]++; + view.count = count[name]; + }; + }; + }); + + assert.equal(app.pages.getView('foo').count, 1); + assert.equal(app.pages.getView('bar').count, 2); + assert.equal(app.pages.getView('baz').count, 3); + assert.equal(count.pages, 3); + + assert.equal(app.posts.getView('foo').count, 1); + assert.equal(app.posts.getView('bar').count, 2); + assert.equal(app.posts.getView('baz').count, 3); + assert.equal(count.posts, 3); + cb(); + }); +}); diff --git a/test/group.js b/test/group.js index 38cdeced..b5eaa8d2 100644 --- a/test/group.js +++ b/test/group.js @@ -6,8 +6,6 @@ require('should'); var assert = require('assert'); var support = require('./support/'); assert.containEql = support.containEql; - -var support = require('./support'); var App = support.resolve(); var List = App.List; var Group = App.Group; @@ -20,6 +18,11 @@ describe('group', function() { assert(group instanceof Group); }); + it('should instantiate without new', function() { + var group = Group(); + assert(group instanceof Group); + }); + it('should create an instance of Group with default List:', function() { var group = new Group(); assert.deepEqual(group.List, List); @@ -30,9 +33,7 @@ describe('group', function() { List.apply(this, arguments); } List.extend(CustomList); - var group = new Group({ - List: CustomList - }); + var group = new Group({List: CustomList}); assert.deepEqual(group.List, CustomList); }); }); @@ -85,30 +86,22 @@ describe('group', function() { }); }); + describe('option', function() { + it('should set options on group.options', function() { + var group = new Group(); + group.option('a', {b: {c: 'd'}}); + assert.equal(group.option('a.b.c'), 'd'); + }); + }); + describe('get', function() { it('should get a normal value when not an array', function() { - var group = new Group({ - 'foo': { - items: [1, 2, 3] - } - }); - assert.deepEqual(group.get('foo'), { - items: [1, 2, 3] - }); + var group = new Group({'foo': {items: [1, 2, 3]}}); + assert.deepEqual(group.get('foo'), {items: [1, 2, 3]}); }); it('should get an instance of List when value is an array', function() { - var group = new Group({ - 'foo': { - items: [{ - path: 'one.hbs' - }, { - path: 'two.hbs' - }, { - path: 'three.hbs' - }] - } - }); + var group = new Group({'foo': {items: [{path: 'one.hbs'}, {path: 'two.hbs'}, {path: 'three.hbs'}]}}); var list = group.get('foo.items'); assert(list instanceof List); assert.deepEqual(list.items.length, 3); @@ -116,26 +109,17 @@ describe('group', function() { it('should throw an error when trying to use a List method on a non List value', function() { (function() { - var group = new Group({ - 'foo': { - items: [1, 2, 3] - } - }); + var group = new Group({'foo': {items: [1, 2, 3]}}); var foo = group.get('foo'); foo.paginate(); }).should.throw('paginate can only be used with an array of `List` items.'); }); it('should not override properties already existing on non List values', function(cb) { - var group = new Group({ - 'foo': { - items: [1, 2, 3], - paginate: function() { - assert(true); - cb(); - } - } - }); + var group = new Group({'foo': {items: [1, 2, 3], paginate: function() { + assert(true); + cb(); + }}}); var foo = group.get('foo'); foo.paginate(); }); @@ -147,12 +131,8 @@ describe('group', function() { }); it('should use plugins on a group:', function() { - group.set('one', { - contents: new Buffer('aaa') - }); - group.set('two', { - contents: new Buffer('zzz') - }); + group.set('one', {contents: new Buffer('aaa')}); + group.set('two', {contents: new Buffer('zzz')}); group .use(function(group) { @@ -169,4 +149,5 @@ describe('group', function() { assert(group.options.foo === 'bar'); }); }); -}); \ No newline at end of file +}); + diff --git a/test/handlers.js b/test/handlers.js index 96c5c532..253cd4d4 100644 --- a/test/handlers.js +++ b/test/handlers.js @@ -1,6 +1,10 @@ 'use strict'; -require('should'); +var path = require('path'); +var assert = require('assert'); +var should = require('should'); +var rimraf = require('rimraf'); +var File = require('vinyl'); var support = require('./support'); var App = support.resolve(); var app; @@ -43,4 +47,85 @@ describe('handlers', function() { app.pages.getView('z.txt').should.have.property('two'); }); }); + + describe('stream', function() { + beforeEach(function() { + app = new App(); + }); + + afterEach(function(cb) { + rimraf(path.join(__dirname, './out-fixtures/'), cb); + }); + + it('should handle onLoad', function(cb) { + var count = 0; + app.onLoad(/./, function(file, next) { + count++; + next(); + }); + + app.src(path.join(__dirname, './fixtures/vinyl/test.coffee')) + .pipe(app.dest('./out-fixtures/', {cwd: __dirname})) + .on('end', function() { + assert.equal(count, 1); + cb(); + }); + }); + + it('should handle preWrite', function(cb) { + var count = 0; + app.preWrite(/./, function(file, next) { + count++; + next(); + }); + + var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var stream = app.dest('./out-fixtures/', { + cwd: __dirname + }); + + stream.once('finish', function() { + assert.equal(count, 1); + cb(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: new Buffer("1234567890") + }); + file.options = {}; + + stream.write(file); + stream.end(); + }); + + it('should handle postWrite', function(cb) { + var count = 0; + app.postWrite(/./, function(file, next) { + count++; + next(); + }); + + var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); + var stream = app.dest('./out-fixtures/', { + cwd: __dirname + }); + + stream.once('finish', function() { + assert.equal(count, 1); + cb(); + }); + + var file = new File({ + path: srcPath, + cwd: __dirname, + contents: new Buffer("1234567890") + }); + file.options = {}; + + stream.write(file); + stream.end(); + }); + }); }); diff --git a/test/helpers.js b/test/helpers.js index 6a383c2e..3943bdbe 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -7,13 +7,13 @@ var assert = require('assert'); var consolidate = require('consolidate'); var handlebars = require('engine-handlebars'); var matter = require('parser-front-matter'); +var helpers = require('templates/lib/plugins/helpers'); +var init = require('templates/lib/plugins/init'); var swig = consolidate.swig; require('swig'); var support = require('./support'); var App = support.resolve(); -var helpers = App._.plugin.helpers; -var init = App._.plugin.init; var app; describe('helpers', function() { @@ -314,14 +314,18 @@ describe('built-in helpers:', function() { it('should emit `helper` when a built-in helper is called', function(cb) { app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); + var count = 0; app.once('helper', function(msg) { assert(msg); assert.equal(msg, 'partial helper > rendering "a.md"'); - cb(); + count++; }); app.render('b.md', function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); }); }); @@ -432,6 +436,89 @@ describe('built-in helpers:', function() { }); }); + it('should expose a "this" object', function(cb) { + app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); + app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); + var count = 0; + app.option('helper.foo', {some: 'opt'}); + + app.helper('foo', function() { + assert(this); + count++; + return 'foo'; + }); + + app.render('xyz.md', function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + res.content.should.equal('a foo b'); + cb(); + }); + }); + + it('should expose a "this.helper" object', function(cb) { + app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); + app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); + var count = 0; + app.option('helper.foo', {some: 'opt'}); + + app.helper('foo', function() { + assert(this.helper); + assert.equal(typeof this.helper, 'object'); + count++; + return 'foo'; + }); + + app.render('xyz.md', function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + res.content.should.equal('a foo b'); + cb(); + }); + }); + + it('should expose a "this.options" object', function(cb) { + app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); + app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); + var count = 0; + app.option('helper.foo', {some: 'opt'}); + + app.helper('foo', function() { + assert(this.options); + assert.equal(typeof this.options, 'object'); + count++; + return 'foo'; + }); + + app.render('xyz.md', function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + res.content.should.equal('a foo b'); + cb(); + }); + }); + + it('should expose a "this.context" object', function(cb) { + app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); + app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); + var count = 0; + app.option('helper.foo', {some: 'opt'}); + + app.helper('foo', function() { + assert(this.context); + assert.equal(typeof this.context, 'object'); + count++; + return 'foo'; + }); + + app.render('xyz.md', function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + res.content.should.equal('a foo b'); + cb(); + }); + }); + it('should prefer helper locals over view locals.', function(cb) { app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); @@ -595,10 +682,38 @@ describe('built-in helpers:', function() { }); }); +describe('helper debug', function() { + beforeEach(function() { + app = new App(); + app.create('pages'); + app.engine('hbs', require('engine-handlebars')); + app.engine('md', require('engine-base')); + }); + + it('should expose a `debug` method on the context', function(cb) { + var count = 0; + app.helper('foo', function(str) { + assert.equal(typeof this.debug, 'function'); + this.debug('rendering "%s"', str); + count++; + return str; + }); + + app.page('doc.md', {content: 'a <%= foo("some string") %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a some string b'); + cb(); + }); + }); +}); + describe('helpers integration', function() { beforeEach(function() { app = new App(); app.create('pages'); + app.engine('hbs', require('engine-handlebars')); app.engine('md', require('engine-base')); }); @@ -620,13 +735,13 @@ describe('helpers integration', function() { }); describe('helper options:', function() { - it('should expose `this.options` to helpers:', function(cb) { + it('should expose global options to helpers:', function(cb) { app.helper('cwd', function(fp) { return path.join(this.options.cwd, fp); }); - app.option('one', 'two'); app.option('cwd', 'foo/bar'); + app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) .render(function(err, res) { if (err) return cb(err); @@ -635,13 +750,12 @@ describe('helpers integration', function() { }); }); - it('should pass helper options to helpers:', function(cb) { + it('should expose helper-specific options to helpers:', function(cb) { app.helper('cwd', function(fp) { return path.join(this.options.cwd, fp); }); app.option('helper.cwd', 'foo/bar'); - app.option('helper.whatever', '...'); app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) .render(function(err, res) { @@ -650,6 +764,125 @@ describe('helpers integration', function() { cb(); }); }); + + it('should prefer helper options over global options:', function(cb) { + app.helper('cwd', function(fp) { + return path.join(this.options.cwd, fp); + }); + + app.option('cwd', 'one/two'); + app.option('helper.cwd', 'foo/bar'); + + app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(res.content, 'a foo/bar/baz b'); + cb(); + }); + }); + + it('should expose a `merge` method on context options', function(cb) { + var count = 0; + app.helper('foo', function(str) { + assert.equal(typeof this.options.merge, 'function'); + count++; + return str; + }); + + app.page('doc.md', {content: 'a <%= foo("foo") %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a foo b'); + cb(); + }); + }); + + it('should merge the given object onto context options', function(cb) { + var count = 0; + app.helper('foo', function(options) { + this.options.merge(options); + count++; + return this.options.one; + }); + + app.page('doc.md', {content: 'a <%= foo({one: "two"}) %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a two b'); + cb(); + }); + }); + + it('should merge a list of objects onto context options', function(cb) { + var count = 0; + app.helper('foo', function(locals, options) { + this.options.merge(locals, options); + + count++; + return this.options.abc + this.options.one; + }); + + app.page('doc.md', {content: 'a <%= foo({abc: "def"}, {one: "two"}) %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a deftwo b'); + cb(); + }); + }); + + it('should merge the handlebars "hash" object onto context options', function(cb) { + var count = 0; + app.helper('foo', function(options) { + this.options.merge(options); + count++; + return this.options.abc; + }); + + app.page('doc.hbs', {content: 'a {{foo abc="xyz"}} b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a xyz b'); + cb(); + }); + }); + + it('should expose a `get` method on context options', function(cb) { + var count = 0; + app.helper('foo', function(str) { + assert.equal(typeof this.options.get, 'function'); + count++; + return str; + }); + + app.page('doc.md', {content: 'a <%= foo("foo") %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a foo b'); + cb(); + }); + }); + + it('should expose a `set` method on context options', function(cb) { + var count = 0; + app.helper('foo', function(str) { + assert.equal(typeof this.options.set, 'function'); + count++; + return str; + }); + + app.page('doc.md', {content: 'a <%= foo("foo") %> b'}) + .render(function(err, res) { + if (err) return cb(err); + assert.equal(count, 1); + assert.equal(res.content, 'a foo b'); + cb(); + }); + }); }); describe('options.helpers', function() { @@ -704,7 +937,7 @@ describe('collection helpers', function() { app.create('snippet', {viewType: 'partial'}); app.engine('hbs', require('engine-handlebars')); app.helper('log', function(ctx) { - console.log(ctx); + // console.log(ctx); }); }); @@ -798,7 +1031,7 @@ describe('collection helpers', function() { app.create('bar', {engine: 'tmpl'}); app.foo('a.tmpl', {path: 'a.tmpl', content: '<%= blah.bar %>'}); - app.bar('one.tmpl', {content: '<%= foo("a.tmpl") %>'}) + app.bar('b.tmpl', {content: '<%= foo("a.tmpl") %>'}) .render(function(err) { assert(err); assert.equal(typeof err, 'object'); @@ -847,7 +1080,7 @@ describe('collection helpers', function() { app.page('two', {content: '{{view "b.hbs" "pages" render=true}}'}) .render(function(err, page) { if (err) return cb(err); - assert.equal(page.content, 'page-b') + assert.equal(page.content, 'page-b'); cb(); }); }); diff --git a/test/item.js b/test/item.js index 7c2990a1..82f24f0c 100644 --- a/test/item.js +++ b/test/item.js @@ -26,15 +26,24 @@ describe('Item', function() { }); it('inspect should not double name `Stream` when ctor is `Stream`', function(cb) { + var fn = console.log; + var count = 0; + console.log = function(val) { + console.log = fn; + assert.deepEqual(val.inspect(), '>'); + count++; + }; var val = new Stream(); - var item = new Item({contents: val}); + item = new Item({contents: val}); + console.log(item); + assert.equal(count, 1); cb(); }); }); describe('static methods', function() { it('should expose `extend`:', function() { - assert(typeof Item.extend === 'function'); + assert.equal(typeof Item.extend, 'function'); }); }); @@ -44,23 +53,39 @@ describe('Item', function() { }); it('should expose `set`:', function() { - assert(typeof item.set === 'function'); + assert.equal(typeof item.set, 'function'); }); it('should expose `get`:', function() { - assert(typeof item.get === 'function'); + assert.equal(typeof item.get, 'function'); }); it('should expose `del`:', function() { - assert(typeof item.del === 'function'); + assert.equal(typeof item.del, 'function'); }); it('should expose `define`:', function() { - assert(typeof item.define === 'function'); + assert.equal(typeof item.define, 'function'); }); it('should expose `visit`:', function() { - assert(typeof item.visit === 'function'); + assert.equal(typeof item.visit, 'function'); }); }); describe('properties', function() { + it('should expose a `_name` property', function() { + item = new Item({}); + assert(item._name); + }); + + it('should use `Item` as the default `_name`', function() { + item = new Item({}); + assert.equal(item._name, 'item'); + }); + + it('should allow `_name` to be set after instantiation', function() { + item = new Item({}); + item._name = 'foo'; + assert.equal(item._name, 'foo'); + }); + it('should expose an `options` property', function() { item = new Item({}); assert.deepEqual(item.options, {}); @@ -69,7 +94,7 @@ describe('Item', function() { it('should add `options` when passed on the constructor', function() { item = new Item({options: {foo: 'bar'}}); - assert(item.options.foo === 'bar'); + assert.equal(item.options.foo, 'bar'); }); it('should expose a `data` property', function() { @@ -80,12 +105,12 @@ describe('Item', function() { it('should add `data` when passed on the constructor', function() { item = new Item({data: {foo: 'bar'}}); - assert(item.data.foo === 'bar'); + assert.equal(item.data.foo, 'bar'); }); it('should add `locals` when passed on the constructor', function() { item = new Item({locals: {foo: 'bar'}}); - assert(item.locals.foo === 'bar'); + assert.equal(item.locals.foo, 'bar'); }); }); @@ -108,7 +133,7 @@ describe('Item', function() { describe('cwd', function() { it('should get properties from the object', function() { item = new Item({cwd: 'test/fixtures'}); - assert(item.cwd === 'test/fixtures'); + assert.equal(item.cwd, 'test/fixtures'); }); }); @@ -122,11 +147,11 @@ describe('Item', function() { clone.set('baz', 'quux'); clone.set('options.three', 'four'); assert.equal(clone.get('foo'), item.get('foo')); - assert(clone.get('baz') === 'quux'); + assert.equal(clone.get('baz'), 'quux'); assert(!item.get('baz')); // not deep cloned - assert(clone.get('options.three') === 'four'); - assert(item.get('options.three') === 'four'); + assert.equal(clone.get('options.three'), 'four'); + assert.equal(item.get('options.three'), 'four'); }); it('should deep clone the entire object', function() { @@ -135,10 +160,11 @@ describe('Item', function() { item.set('options.one', 'two'); var clone = item.clone({deep: true}); clone.set('options.three', 'four'); - assert(item.get('options.one') === 'two'); - assert(clone.get('options.one') === 'two'); - assert(clone.get('options.three') === 'four'); - assert(!item.get('options.three')); + assert.equal(item.get('options.one'), 'two'); + assert.equal(clone.get('options.one'), 'two'); + assert.equal(clone.get('options.three'), 'four'); + + assert.equal(typeof item.get('options.three'), 'undefined'); }); }); @@ -176,81 +202,81 @@ describe('Item', function() { describe('Item', function() { describe('isVinyl()', function() { it('should return true on a vinyl object', function(cb) { - var item = new Item(); - assert(Item.isVinyl(item) === true); + item = new Item(); + assert.equal(Item.isVinyl(item), true); cb(); }); it('should return false on a normal object', function(cb) { - assert(Item.isVinyl({}) === false); + assert.equal(Item.isVinyl({}), false); cb(); }); it('should return false on a null object', function(cb) { - assert(Item.isVinyl({}) === false); + assert.equal(Item.isVinyl({}), false); cb(); }); }); describe('constructor()', function() { it('should default cwd to process.cwd', function(cb) { - var item = new Item(); + item = new Item(); item.cwd.should.equal(process.cwd()); cb(); }); it('should default base to cwd', function(cb) { var cwd = '/'; - var item = new Item({cwd: cwd}); + item = new Item({cwd: cwd}); item.base.should.equal(cwd); cb(); }); it('should default base to cwd even when none is given', function(cb) { - var item = new Item(); + item = new Item(); item.base.should.equal(process.cwd()); cb(); }); it('should default path to null', function(cb) { - var item = new Item(); + item = new Item(); should.not.exist(item.path); cb(); }); it('should default history to []', function(cb) { - var item = new Item(); + item = new Item(); item.history.should.eql([]); cb(); }); it('should default stat to null', function(cb) { - var item = new Item(); + item = new Item(); should.not.exist(item.stat); cb(); }); it('should default contents to null', function(cb) { - var item = new Item(); + item = new Item(); should.not.exist(item.contents); cb(); }); it('should set base to given value', function(cb) { var val = '/'; - var item = new Item({base: val}); + item = new Item({base: val}); item.base.should.equal(val); cb(); }); it('should set cwd to given value', function(cb) { var val = '/'; - var item = new Item({cwd: val}); + item = new Item({cwd: val}); item.cwd.should.equal(val); cb(); }); it('should set path to given value', function(cb) { var val = '/test.coffee'; - var item = new Item({path: val}); + item = new Item({path: val}); item.path.should.equal(val); item.history.should.eql([val]); cb(); @@ -258,7 +284,7 @@ describe('Item', function() { it('should set history to given value', function(cb) { var val = '/test.coffee'; - var item = new Item({history: [val]}); + item = new Item({history: [val]}); item.path.should.equal(val); item.history.should.eql([val]); cb(); @@ -266,14 +292,14 @@ describe('Item', function() { it('should set stat to given value', function(cb) { var val = {}; - var item = new Item({stat: val}); + item = new Item({stat: val}); item.stat.should.equal(val); cb(); }); it('should set contents to given value', function(cb) { var val = new Buffer('test'); - var item = new Item({contents: val}); + item = new Item({contents: val}); item.contents.should.equal(val); cb(); }); @@ -282,7 +308,7 @@ describe('Item', function() { describe('isBuffer()', function() { it('should return true when the contents are a Buffer', function(cb) { var val = new Buffer('test'); - var item = new Item({contents: val}); + item = new Item({contents: val}); item.isBuffer().should.equal(true); cb(); }); @@ -417,15 +443,15 @@ describe('Item', function() { it('should copy all attributes over with Stream', function(cb) { var contents = new Stream.PassThrough(); - var options = { + + var item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee', contents: contents - }; - var item = new Item(options); - var item2 = item.clone(); + }); + var item2 = item.clone(); contents.write(new Buffer('wa')); process.nextTick(function() { @@ -439,7 +465,9 @@ describe('Item', function() { item2.path.should.equal(item.path); item2.contents.should.not.equal(item.contents, 'stream ref should not be the same'); item.contents.pipe(es.wait(function(err, data) { + if (err) return cb(err); item2.contents.pipe(es.wait(function(err, data2) { + if (err) return cb(err); data2.should.not.equal(data, 'stream contents ref should not be the same'); data2.should.eql(data, 'stream contents should be the same'); })); @@ -479,10 +507,6 @@ describe('Item', function() { assert(copy.stat.isFile()); assert(!copy.stat.isDirectory()); - - assert(item.stat.hasOwnProperty('birthtime')); - assert(copy.stat.hasOwnProperty('birthtime')); - assert.deepEqual(item.stat, copy.stat); cb(); }); @@ -723,7 +747,33 @@ describe('Item', function() { describe('inspect()', function() { it('should return correct format when no contents and no path', function(cb) { var item = new Item(); - item.inspect().should.equal(''); + assert.equal(item.inspect(), ''); + cb(); + }); + + it('should update the name when `_name` is defined', function(cb) { + var item = new Item(); + item._name = 'Foo'; + assert.equal(item.inspect(), ''); + cb(); + }); + + it('should not add duplicate `Stream` name to inspect name', function(cb) { + function PassThroughStream() { + Stream.PassThrough.apply(this, arguments); + } + util.inherits(PassThroughStream, Stream.PassThrough); + var contents = new PassThroughStream(); + + var item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee', + contents: contents + }); + + contents.write(new Buffer('foo')); + assert.equal(item.inspect(), '>'); cb(); }); @@ -732,7 +782,7 @@ describe('Item', function() { var item = new Item({ contents: val }); - item.inspect().should.equal('>'); + assert.equal(item.inspect(), '>'); cb(); }); @@ -744,7 +794,7 @@ describe('Item', function() { path: '/test/test.coffee', contents: val }); - item.inspect().should.equal('>'); + assert.equal(item.inspect(), '>'); cb(); }); @@ -756,7 +806,7 @@ describe('Item', function() { contents: val }); delete item.base; - item.inspect().should.equal('>'); + assert.equal(item.inspect(), '>'); cb(); }); @@ -767,7 +817,7 @@ describe('Item', function() { path: '/test/test.coffee', contents: new Stream.PassThrough() }); - item.inspect().should.equal('>'); + assert.equal(item.inspect(), '>'); cb(); }); @@ -778,7 +828,7 @@ describe('Item', function() { path: '/test/test.coffee', contents: null }); - item.inspect().should.equal(''); + assert.equal(item.inspect(), ''); cb(); }); }); @@ -804,7 +854,7 @@ describe('Item', function() { var val = null; var item = new Item(); item.contents = val; - (item.contents === null).should.equal(true); + assert.equal(item.contents, null); cb(); }); @@ -812,7 +862,7 @@ describe('Item', function() { var val = 'test'; var item = new Item(); item.contents = val; - item.contents.should.deepEqual(new Buffer(val)); + assert.deepEqual(item.contents, new Buffer(val)); cb(); }); }); @@ -829,11 +879,10 @@ describe('Item', function() { }); it('should error on get when no base', function(cb) { - var a; var item = new Item(); delete item.base; try { - a = item.relative; + item.relative; } catch (err) { should.exist(err); cb(); @@ -841,10 +890,9 @@ describe('Item', function() { }); it('should error on get when no path', function(cb) { - var a; var item = new Item(); try { - a = item.relative; + item.relative; } catch (err) { should.exist(err); cb(); @@ -873,10 +921,9 @@ describe('Item', function() { describe('dirname get/set', function() { it('should error on get when no path', function(cb) { - var a; var item = new Item(); try { - a = item.dirname; + item.dirname; } catch (err) { should.exist(err); cb(); @@ -915,32 +962,111 @@ describe('Item', function() { }); }); - describe('basename get/set', function() { + describe('stem', function() { it('should error on get when no path', function(cb) { - var a; - var item = new Item(); + item = new Item(); try { - a = item.basename; + item.stem; } catch (err) { should.exist(err); cb(); } }); - it('should return the basename of the path', function(cb) { - var item = new Item({ + it('should set the stem of the path', function(cb) { + item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' }); - item.basename.should.equal('test.coffee'); + item.stem = 'foo'; + item.path.should.equal('/test/foo.coffee'); + cb(); + }); + + it('should get the stem of the path', function(cb) { + item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.stem.should.equal('test'); cb(); }); it('should error on set when no path', function(cb) { - var item = new Item(); + item = new Item(); try { - item.basename = 'test.coffee'; + item.stem = 'test.coffee'; + } catch (err) { + should.exist(err); + cb(); + } + }); + }); + + describe('filename', function() { + it('should error on get when no path', function(cb) { + item = new Item(); + try { + item.filename; + } catch (err) { + should.exist(err); + cb(); + } + }); + + it('should set the filename of the path', function(cb) { + item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.filename = 'foo'; + item.path.should.equal('/test/foo.coffee'); + cb(); + }); + + it('should get the filename of the path', function(cb) { + item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.filename.should.equal('test'); + cb(); + }); + + it('should stay synchronized with stem', function(cb) { + item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + assert.equal(item.filename, item.stem); + item.stem = 'foo'; + assert.equal(item.filename, 'foo'); + item.filename = 'bar'; + assert.equal(item.stem, 'bar'); + cb(); + }); + + it('should error on set when no path', function(cb) { + item = new Item(); + try { + item.filename = 'test.coffee'; + } catch (err) { + should.exist(err); + cb(); + } + }); + }); + + describe('basename', function() { + it('should error on get when no path', function(cb) { + item = new Item(); + try { + item.basename; } catch (err) { should.exist(err); cb(); @@ -948,7 +1074,7 @@ describe('Item', function() { }); it('should set the basename of the path', function(cb) { - var item = new Item({ + item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' @@ -957,14 +1083,33 @@ describe('Item', function() { item.path.should.equal('/test/foo.png'); cb(); }); + + it('should get the basename of the path', function(cb) { + item = new Item({ + cwd: '/', + base: '/test/', + path: '/test/test.coffee' + }); + item.basename.should.equal('test.coffee'); + cb(); + }); + + it('should error on set when no path', function(cb) { + item = new Item(); + try { + item.basename = 'test.coffee'; + } catch (err) { + should.exist(err); + cb(); + } + }); }); - describe('extname get/set', function() { + describe('extname', function() { it('should error on get when no path', function(cb) { - var a; - var item = new Item(); + item = new Item(); try { - a = item.extname; + item.extname; } catch (err) { should.exist(err); cb(); @@ -972,7 +1117,7 @@ describe('Item', function() { }); it('should return the extname of the path', function(cb) { - var item = new Item({ + item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' @@ -982,7 +1127,7 @@ describe('Item', function() { }); it('should error on set when no path', function(cb) { - var item = new Item(); + item = new Item(); try { item.extname = '.coffee'; } catch (err) { @@ -992,7 +1137,7 @@ describe('Item', function() { }); it('should set the extname of the path', function(cb) { - var item = new Item({ + item = new Item({ cwd: '/', base: '/test/', path: '/test/test.coffee' @@ -1049,7 +1194,7 @@ describe('Item', function() { it('should throw when set path null in constructor', function() { (function() { - new Item({ + Item({ cwd: '/', path: null }); @@ -1057,7 +1202,7 @@ describe('Item', function() { }); it('should throw when set path null', function() { - var item = new Item({ + item = new Item({ cwd: '/', path: 'foo' }); diff --git a/test/layouts.js b/test/layouts.js index f563cc5a..b14cdea9 100644 --- a/test/layouts.js +++ b/test/layouts.js @@ -2,6 +2,7 @@ require('mocha'); require('should'); +var each = require('async-each'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); @@ -12,10 +13,11 @@ describe('layouts', function() { app = new App(); app.engine('tmpl', require('engine-base')); app.create('layout', { viewType: 'layout' }); + app.create('partial', { viewType: 'partial' }); app.create('page'); }); - it('should apply a layout to a view:', function(cb) { + it('should add a layout to a view:', function(cb) { app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); var page = app.pages.getView('a.tmpl'); @@ -28,7 +30,93 @@ describe('layouts', function() { }); }); - it('should not apply a layout when `layoutApplied` is set:', function(cb) { + it('should use a "default" layout defined on global options', function(cb) { + app.option('layout', 'base'); + + app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); + app.page('a.tmpl', {path: 'a.tmpl', content: 'b'}) + .render(function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c'); + cb(); + }); + }); + + it('should use a "default" layout defined on collection options', function(cb) { + app.pages.option('layout', 'base'); + + app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); + app.page('a.tmpl', {path: 'a.tmpl', content: 'b'}) + .render(function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c'); + cb(); + }); + }); + + it('should use the "default" layout on layouts', function(cb) { + app.option('layout', 'base'); + + app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); + var foo = app.layout('foo.tmpl', {content: 'b'}); + + app.render(foo, function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c'); + cb(); + }); + }); + + it('should not use the "default" layout on partials', function(cb) { + app.option('layout', 'base'); + + app.partial('foo.tmpl', {content: 'c'}); + app.layout('base.tmpl', {content: 'a {% body %} d'}); + + app.page('a.tmpl', {path: 'a.tmpl', content: 'b <%= partial("foo") %>'}) + .render(function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b c d'); + cb(); + }); + }); + + it('should add a layout to a partial when defined on a partial', function(cb) { + app.option('layout', 'base'); + + app.partial('foo.tmpl', {content: 'c', layout: 'base'}); + app.layout('base.tmpl', {content: 'a {% body %} d'}); + + app.page('a.tmpl', {path: 'a.tmpl', content: 'b <%= partial("foo") %>'}) + .render(function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'a b a c d d'); + cb(); + }); + }); + + it('should add a layout to a layout when defined on a layout', function(cb) { + app.option('layout', 'base'); + + app.partial('foo.tmpl', {content: 'c'}); + app.layout('default.tmpl', {content: 'x {% body %} z'}); + app.layout('base.tmpl', {content: 'a {% body %} d', layout: 'default'}); + + app.page('a.tmpl', {path: 'a.tmpl', content: 'b <%= partial("foo") %>'}) + .render(function(err, view) { + if (err) return cb(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'x a b c d z'); + cb(); + }); + }); + + it('should not add a layout when `layoutApplied` is set:', function(cb) { app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); var page = app.pages.getView('a.tmpl'); @@ -72,6 +160,27 @@ describe('layouts', function() { }); }); + it('should apply nested layouts to multiple views when layout is defined on data property:', function(cb) { + app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', data: { layout: 'b' }}); + app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', data: { layout: 'c' }}); + app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', data: { layout: 'base' }}); + app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); + + app.pages('x.tmpl', {path: 'x.tmpl', content: 'x inner x', data: { layout: 'a' }}); + app.pages('y.tmpl', {path: 'y.tmpl', content: 'y inner y', data: { layout: 'a' }}); + app.pages('z.tmpl', {path: 'z.tmpl', content: 'z inner z', data: { layout: 'a' }}); + + each(['x', 'y', 'z'], function(key, next) { + var page = app.pages.getView(key + '.tmpl'); + app.render(page, function(err, view) { + if (err) return next(err); + assert.equal(typeof view.content, 'string'); + assert.equal(view.content, 'outter c b a ' + key + ' inner ' + key + ' a b c outter'); + next(); + }); + }, cb); + }); + it('should track layout stack history on `layoutStack`:', function(cb) { app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); diff --git a/test/list.deleteItem.js b/test/list.deleteItem.js index 7ec1e012..8a83310e 100644 --- a/test/list.deleteItem.js +++ b/test/list.deleteItem.js @@ -2,7 +2,6 @@ require('mocha'); require('should'); -var path = require('path'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); diff --git a/test/list.js b/test/list.js index dace90c6..eaab1cbf 100644 --- a/test/list.js +++ b/test/list.js @@ -4,13 +4,12 @@ require('mocha'); require('should'); var path = require('path'); var get = require('get-value'); -var isBuffer = require('is-buffer'); var assert = require('assert'); +var each = require('async-each'); var typeOf = require('kind-of'); var support = require('./support/'); +var isBuffer = require('is-buffer'); assert.containEql = support.containEql; - -var support = require('./support'); var App = support.resolve(); var List = App.List; var Views = App.Views; @@ -149,7 +148,7 @@ describe('list', function() { }); }); - describe('removeItem', function() { + describe('deleteItem', function() { beforeEach(function() { list = new List(); }); @@ -238,7 +237,7 @@ describe('list', function() { list.addList([ { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } } ], addContents); assert(isBuffer(list.items[0].contents)); @@ -255,7 +254,7 @@ describe('list', function() { list.addList({ 'a.md': {locals: { date: '2014-01-01', foo: 'zzz', bar: 1 }}, 'f.md': {locals: { date: '2014-01-01', foo: 'mmm', bar: 2 }}, - 'd.md': {locals: { date: '2014-01-01', foo: 'xxx', bar: 3 }}, + 'd.md': {locals: { date: '2014-01-01', foo: 'xxx', bar: 3 }} }, addContents); assert(isBuffer(list.items[0].contents)); @@ -266,7 +265,9 @@ describe('list', function() { it('should signal `loaded` when finished (addList)', function() { list.on('addList', function(items) { - var len = items.length, i = -1; + var len = items.length; + var i = -1; + while (++i < len) { if (items[i].path === 'd.md') { list.loaded = true; @@ -279,7 +280,7 @@ describe('list', function() { list.addList([ { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, + { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } } ]); assert.equal(list.items.length, 2); @@ -349,7 +350,7 @@ describe('list', function() { { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } ]; it('should sort a list', function() { @@ -504,10 +505,10 @@ describe('list', function() { { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, + { path: 'm.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, + { path: 'b.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } ]; it('should group a list by a property', function() { @@ -518,6 +519,16 @@ describe('list', function() { var keys = ['zzz', 'mmm', 'xxx', 'aaa', 'ccc', 'rrr', 'ttt', 'yyy']; assert.deepEqual(Object.keys(res), keys); }); + + it('should group a collection by a property', function() { + list = new List(); + list.addList(items); + + var views = new Views(list); + var res = views.groupBy('locals.foo'); + var keys = ['zzz', 'mmm', 'xxx', 'aaa', 'ccc', 'rrr', 'ttt', 'yyy']; + assert.deepEqual(Object.keys(res), keys); + }); }); describe('sort and group', function() { @@ -538,7 +549,7 @@ describe('list', function() { { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } ]; it('should group a list by a property', function() { @@ -576,7 +587,7 @@ describe('list', function() { { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, + { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } ]; it('should paginate a list', function() { @@ -596,6 +607,15 @@ describe('list', function() { }); }); + it('should render items when pager is `true`', function(cb) { + list = new List({pager: true}); + list.engine('.md', require('engine-base')); + list.addList(items); + each(list.items, function(item, next) { + item.render(next); + }, cb); + }); + it('should paginate a list with given options', function() { list = new List(items); var res = list.paginate({limit: 5}); @@ -655,7 +675,7 @@ describe('list', function() { list = new List(); }); - it('should get an view from `views`', function() { + it('should get an item from `items`', function() { list.addItem('one', {content: 'aaa'}); list.addItem('two', {content: 'zzz'}); assert(list.items.length === 2); @@ -664,6 +684,10 @@ describe('list', function() { assert(list.getItem('one').contents.toString() === 'aaa'); assert(list.getItem('two').contents.toString() === 'zzz'); }); + + it('should return `undefined` when the item is not found', function() { + assert.equal(typeof list.getItem('flflflfl'), 'undefined'); + }); }); describe('use', function() { diff --git a/test/list.render.js b/test/list.render.js index ad127f24..6915ac6f 100644 --- a/test/list.render.js +++ b/test/list.render.js @@ -2,31 +2,36 @@ require('mocha'); require('should'); -var async = require('async'); +var each = require('async-each'); var assert = require('assert'); var support = require('./support'); var App = support.resolve(); var List = App.List; -var pages; +var posts; describe('list.render', function() { describe('rendering', function() { beforeEach(function() { - pages = new List(); - pages.engine('tmpl', require('engine-base')); + posts = new List(); + posts.engine('tmpl', require('engine-base')); }); - it('should throw an error when no callback is given:', function() { - (function() { - pages.render({}); - }).should.throw('List#render is async and expects a callback function'); + it('should throw an error when no callback is given:', function(cb) { + try { + posts.render({}); + cb(new Error('expected an error')); + } catch (err) { + console.log(err); + assert.equal(err.message, 'List#render is async and expects a callback function'); + cb(); + } }); it('should throw an error when an engine is not defined:', function(cb) { - pages.addItem('foo.bar', {content: '<%= name %>'}); - var page = pages.getItem('foo.bar'); + posts.addItem('foo.bar', {content: '<%= name %>'}); + var page = posts.getItem('foo.bar'); - pages.render(page, function(err) { + posts.render(page, function(err) { assert(err.message === 'List#render cannot find an engine for: .bar'); cb(); }); @@ -35,14 +40,14 @@ describe('list.render', function() { it('should use helpers to render a item:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function(str) { + posts.helper('upper', function(str) { return str.toUpperCase(str); }); - pages.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getItem('a.tmpl'); + posts.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = posts.getItem('a.tmpl'); - pages.render(page, function(err, res) { + posts.render(page, function(err, res) { if (err) return cb(err); assert(res.content === 'a HALLE b'); @@ -52,14 +57,14 @@ describe('list.render', function() { it('should use helpers when rendering a item:', function(cb) { var locals = {name: 'Halle'}; - pages.helper('upper', function(str) { + posts.helper('upper', function(str) { return str.toUpperCase(str); }); - pages.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getItem('a.tmpl'); + posts.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); + var page = posts.getItem('a.tmpl'); - pages.render(page, function(err, res) { + posts.render(page, function(err, res) { if (err) return cb(err); assert(res.content === 'a HALLE b'); cb(); @@ -67,10 +72,10 @@ describe('list.render', function() { }); it('should render a template when contents is a buffer:', function(cb) { - pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var item = pages.getItem('a.tmpl'); + posts.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var item = posts.getItem('a.tmpl'); - pages.render(item, function(err, item) { + posts.render(item, function(err, item) { if (err) return cb(err); assert(item.contents.toString() === 'b'); cb(); @@ -78,10 +83,10 @@ describe('list.render', function() { }); it('should render a template when content is a string:', function(cb) { - pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var item = pages.getItem('a.tmpl'); + posts.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + var item = posts.getItem('a.tmpl'); - pages.render(item, function(err, item) { + posts.render(item, function(err, item) { if (err) return cb(err); assert(item.contents.toString() === 'b'); cb(); @@ -89,9 +94,9 @@ describe('list.render', function() { }); it('should render a item from its path:', function(cb) { - pages.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); + posts.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - pages.render('a.tmpl', function(err, item) { + posts.render('a.tmpl', function(err, item) { if (err) return cb(err); assert(item.content === 'b'); cb(); @@ -99,10 +104,10 @@ describe('list.render', function() { }); it('should use a plugin for rendering:', function(cb) { - pages.engine('tmpl', require('engine-base')); - pages.option('engine', 'tmpl'); + posts.engine('tmpl', require('engine-base')); + posts.option('engine', 'tmpl'); - pages.addItems({ + posts.addItems({ 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, @@ -112,22 +117,22 @@ describe('list.render', function() { 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}}, + 'j': {content: '<%= title %>', locals: {title: 'jjj'}} }); - pages.use(function(collection) { + posts.use(function(collection) { collection.option('pager', false); collection.renderEach = function(cb) { var list = new List(collection); - async.map(list.items, function(item, next) { + each(list.items, function(item, next) { collection.render(item, next); }, cb); }; }); - pages.renderEach(function(err, items) { + posts.renderEach(function(err, items) { if (err) return cb(err); assert(items[0].content === 'aaa'); assert(items[9].content === 'jjj'); diff --git a/test/out-fixtures/test.coffee b/test/out-fixtures/test.coffee deleted file mode 100644 index e69de29b..00000000 diff --git a/test/renameKey.js b/test/renameKey.js index 801c1c29..9d4d788c 100644 --- a/test/renameKey.js +++ b/test/renameKey.js @@ -34,6 +34,24 @@ describe('renameKey', function() { app.views.posts.should.have.property('e'); }); + it('should expose `view` when defined on global opts:', function() { + app.option('renameKey', function(key, view) { + return view.filename; + }); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.posts.should.have.property('a'); + app.views.posts.should.have.property('b'); + app.views.posts.should.have.property('c'); + app.views.posts.should.have.property('d'); + app.views.posts.should.have.property('e'); + }); + it('should not have conflicts when view name is the collection name:', function() { app.option('renameKey', renameKey); @@ -86,6 +104,26 @@ describe('renameKey', function() { app.views.posts.should.have.property('posts/d.txt'); app.views.posts.should.have.property('posts/e.txt'); }); + + it('should expose `view` as the second argument', function() { + app.create('post', { + renameKey: function(key, view) { + return 'posts/' + view.basename; + } + }); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.posts.should.have.property('posts/a.txt'); + app.views.posts.should.have.property('posts/b.txt'); + app.views.posts.should.have.property('posts/c.txt'); + app.views.posts.should.have.property('posts/d.txt'); + app.views.posts.should.have.property('posts/e.txt'); + }); }); describe('collections:', function() { @@ -138,6 +176,40 @@ describe('renameKey', function() { app.views.posts.should.have.property('post/e.txt'); }); + it('should expose `view` when defined on collection.options:', function() { + app.pages.option('renameKey', function(key, view) { + return 'page/' + view.basename; + }); + + app.posts.option('renameKey', function(key, view) { + return 'post/' + view.basename; + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.posts('a/b/c/a.txt', {content: '...'}); + app.posts('a/b/c/b.txt', {content: '...'}); + app.posts('a/b/c/c.txt', {content: '...'}); + app.post('a/b/c/d.txt', {content: '...'}); + app.post('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('page/a.txt'); + app.views.pages.should.have.property('page/b.txt'); + app.views.pages.should.have.property('page/c.txt'); + app.views.pages.should.have.property('page/d.txt'); + app.views.pages.should.have.property('page/e.txt'); + + app.views.posts.should.have.property('post/a.txt'); + app.views.posts.should.have.property('post/b.txt'); + app.views.posts.should.have.property('post/c.txt'); + app.views.posts.should.have.property('post/d.txt'); + app.views.posts.should.have.property('post/e.txt'); + }); + it('should use the `collection.renameKey()` method:', function() { app.pages.renameKey(function(key) { return 'baz/' + path.basename(key); @@ -156,6 +228,24 @@ describe('renameKey', function() { app.views.pages.should.have.property('baz/e.txt'); }); + it('should expose `view` with the `collection.renameKey()` method:', function() { + app.pages.renameKey(function(key, view) { + return 'baz/' + view.basename; + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('baz/a.txt'); + app.views.pages.should.have.property('baz/b.txt'); + app.views.pages.should.have.property('baz/c.txt'); + app.views.pages.should.have.property('baz/d.txt'); + app.views.pages.should.have.property('baz/e.txt'); + }); + it('should use the `app.renameKey()` method:', function() { app.renameKey(function(key) { return 'app/' + path.basename(key); @@ -174,6 +264,24 @@ describe('renameKey', function() { app.views.pages.should.have.property('app/e.txt'); }); + it('should expose `view` with the `app.renameKey()` method:', function() { + app.renameKey(function(key, view) { + return 'app/' + view.basename; + }); + + app.pages('a/b/c/a.txt', {content: '...'}); + app.pages('a/b/c/b.txt', {content: '...'}); + app.pages('a/b/c/c.txt', {content: '...'}); + app.page('a/b/c/d.txt', {content: '...'}); + app.page('a/b/c/e.txt', {content: '...'}); + + app.views.pages.should.have.property('app/a.txt'); + app.views.pages.should.have.property('app/b.txt'); + app.views.pages.should.have.property('app/c.txt'); + app.views.pages.should.have.property('app/d.txt'); + app.views.pages.should.have.property('app/e.txt'); + }); + it('should prefer collection method over app.options:', function() { // this works when you switch the order around... app.pages.renameKey(function pagesRenameKey(key) { diff --git a/test/render.js b/test/render.js index a4dd2da4..2854b4a8 100644 --- a/test/render.js +++ b/test/render.js @@ -22,7 +22,7 @@ describe('render', function() { app.page('a.tmpl', view) .render(function(err, res) { if (err) return cb(err); - assert(res.contents.toString() === 'a Halle b'); + assert.equal(res.contents.toString(), 'a Halle b'); cb(); }); }); @@ -32,7 +32,7 @@ describe('render', function() { app.page('a.tmpl', view) .render(function(err) { - assert(err.message === 'foo is not defined'); + assert.equal(err.message, 'foo is not defined'); cb(); }); }); @@ -46,7 +46,7 @@ describe('render', function() { app.page('a.tmpl', view) .render(function(err) { - assert(err.message === 'foo is not defined'); + assert.equal(err.message, 'foo is not defined'); cb(); }); }); @@ -59,13 +59,13 @@ describe('render', function() { app.create('page'); app.on('error', function(err) { - assert(err.message === 'foo is not defined'); + assert.equal(err.message, 'foo is not defined'); cb(); }); app.page('a.tmpl', view) .render(function(err) { - assert(err.message === 'foo is not defined'); + assert.equal(err.message, 'foo is not defined'); }); }); }); diff --git a/test/view.js b/test/view.js index 1052d9b9..668ec8b3 100644 --- a/test/view.js +++ b/test/view.js @@ -22,7 +22,7 @@ describe('View', function() { describe('static methods', function() { it('should expose `extend`:', function() { - assert(typeof View.extend === 'function'); + assert.equal(typeof View.extend, 'function'); }); }); @@ -32,25 +32,28 @@ describe('View', function() { }); it('should expose `set`:', function() { - assert(typeof view.set === 'function'); + assert.equal(typeof view.set, 'function'); }); it('should expose `get`:', function() { - assert(typeof view.get === 'function'); + assert.equal(typeof view.get, 'function'); }); it('should expose `del`:', function() { - assert(typeof view.del === 'function'); + assert.equal(typeof view.del, 'function'); }); it('should expose `define`:', function() { - assert(typeof view.define === 'function'); + assert.equal(typeof view.define, 'function'); }); it('should expose `visit`:', function() { - assert(typeof view.visit === 'function'); + assert.equal(typeof view.visit, 'function'); }); it('should expose `compile`:', function() { - assert(typeof view.compile === 'function'); + assert.equal(typeof view.compile, 'function'); }); it('should expose `render`:', function() { - assert(typeof view.render === 'function'); + assert.equal(typeof view.render, 'function'); + }); + it('should expose `isType`:', function() { + assert.equal(typeof view.isType, 'function'); }); }); @@ -63,7 +66,7 @@ describe('View', function() { it('should add `options` when passed on the constructor', function() { view = new View({options: {foo: 'bar'}}); - assert(view.options.foo === 'bar'); + assert.equal(view.options.foo, 'bar'); }); it('should expose a `data` property', function() { @@ -74,12 +77,12 @@ describe('View', function() { it('should add `data` when passed on the constructor', function() { view = new View({data: {foo: 'bar'}}); - assert(view.data.foo === 'bar'); + assert.equal(view.data.foo, 'bar'); }); it('should add `locals` when passed on the constructor', function() { view = new View({locals: {foo: 'bar'}}); - assert(view.locals.foo === 'bar'); + assert.equal(view.locals.foo, 'bar'); }); }); @@ -102,7 +105,7 @@ describe('View', function() { describe('cwd', function() { it('should get properties from the object', function() { view = new View({cwd: 'test/fixtures'}); - assert(view.cwd === 'test/fixtures'); + assert.equal(view.cwd, 'test/fixtures'); }); }); @@ -116,11 +119,11 @@ describe('View', function() { clone.set('baz', 'quux'); clone.set('options.three', 'four'); assert.equal(clone.get('foo'), view.get('foo')); - assert(clone.get('baz') === 'quux'); - assert(!view.get('baz')); + assert.equal(clone.get('baz'), 'quux'); + assert.equal(typeof view.get('baz'), 'undefined'); // not deep cloned - assert(clone.get('options.three') === 'four'); - assert(view.get('options.three') === 'four'); + assert.equal(clone.get('options.three'), 'four'); + assert.equal(view.get('options.three'), 'four'); }); it('should deep clone the entire object', function() { @@ -129,10 +132,10 @@ describe('View', function() { view.set('options.one', 'two'); var clone = view.clone({deep: true}); clone.set('options.three', 'four'); - assert(view.get('options.one') === 'two'); - assert(clone.get('options.one') === 'two'); - assert(clone.get('options.three') === 'four'); - assert(!view.get('options.three')); + assert.equal(view.get('options.one'), 'two'); + assert.equal(clone.get('options.one'), 'two'); + assert.equal(clone.get('options.three'), 'four'); + assert.equal(typeof view.get('options.three'), 'undefined'); }); }); @@ -163,25 +166,25 @@ describe('View', function() { describe('compile', function() { it('should get view.layout from view.data.layout', function() { view = new View({path: 'foo', contents: 'a b c', data: {layout: 'default'}}); - assert(view.layout === 'default'); + assert.equal(view.layout, 'default'); }); it('should get view.layout from view.options.layout', function() { view = new View({path: 'foo', contents: 'a b c', options: {layout: 'default'}}); - assert(view.layout === 'default'); + assert.equal(view.layout, 'default'); }); it('should get view.layout from view.locals.layout', function() { view = new View({path: 'foo', contents: 'a b c', locals: {layout: 'default'}}); - assert(view.layout === 'default'); + assert.equal(view.layout, 'default'); }); it('should get view.layout from the view', function() { view = new View({path: 'foo', contents: 'a b c', layout: 'default'}); - assert(view.layout === 'default'); + assert.equal(view.layout, 'default'); }); it('should add a compiled function to `view.fn`', function() { view = new View({path: 'foo', contents: 'a <%= name %> z'}); view.compile(); - assert(typeof view.fn === 'function'); + assert.equal(typeof view.fn, 'function'); }); it('should render a compiled template', function(cb) { @@ -189,7 +192,7 @@ describe('View', function() { view.compile(); view.render({name: 'Halle'}, function(err, res) { if (err) return cb(err); - assert(res.contents.toString() === 'a Halle z'); + assert.equal(res.contents.toString(), 'a Halle z'); cb(); }); }); @@ -206,7 +209,7 @@ describe('View', function() { view.compile(); view.render(function(err, res) { if (err) return cb(err); - assert(res.contents.toString() === 'a Brooke z'); + assert.equal(res.contents.toString(), 'a Brooke z'); cb(); }); }); @@ -217,7 +220,7 @@ describe('View', function() { view = new View({path: 'foo', contents: 'a <%= name %> z'}); view.render({name: 'Halle'}, function(err, res) { if (err) return cb(err); - assert(res.contents.toString() === 'a Halle z'); + assert.equal(res.contents.toString(), 'a Halle z'); cb(); }); }); @@ -233,7 +236,7 @@ describe('View', function() { view.render(function(err, res) { if (err) return cb(err); - assert(res.contents.toString() === 'a Brooke z'); + assert.equal(res.contents.toString(), 'a Brooke z'); cb(); }); }); @@ -245,7 +248,7 @@ describe('View', function() { }); view.render(function(err) { - assert(err.message === 'name is not defined'); + assert.equal(err.message, 'name is not defined'); cb(); }); }); @@ -262,15 +265,15 @@ describe('View', function() { describe('isVinyl()', function() { it('should return true on a vinyl object', function(cb) { var view = new View(); - assert(View.isVinyl(view) === true); + assert.equal(View.isVinyl(view), true); cb(); }); it('should return false on a normal object', function(cb) { - assert(View.isVinyl({}) === false); + assert.equal(View.isVinyl({}), false); cb(); }); it('should return false on a null object', function(cb) { - assert(View.isVinyl({}) === false); + assert.equal(View.isVinyl({}), false); cb(); }); }); @@ -375,13 +378,13 @@ describe('View', function() { it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val}); - view.isBuffer().should.equal(false); + assert(!view.isBuffer()); cb(); }); it('should return false when the contents are a null', function(cb) { var view = new View({contents: null}); - view.isBuffer().should.equal(false); + assert(!view.isBuffer()); cb(); }); }); @@ -390,7 +393,7 @@ describe('View', function() { it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val}); - view.isStream().should.equal(false); + assert(!view.isStream()); cb(); }); @@ -403,7 +406,7 @@ describe('View', function() { it('should return false when the contents are a null', function(cb) { var view = new View({contents: null}); - view.isStream().should.equal(false); + assert(!view.isStream()); cb(); }); }); @@ -412,14 +415,14 @@ describe('View', function() { it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val}); - view.isNull().should.equal(false); + assert(!view.isNull()); cb(); }); it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val}); - view.isNull().should.equal(false); + assert(!view.isNull()); cb(); }); @@ -440,14 +443,14 @@ describe('View', function() { it('should return false when the contents are a Buffer', function(cb) { var val = new Buffer('test'); var view = new View({contents: val, stat: fakeStat}); - view.isDirectory().should.equal(false); + assert(!view.isDirectory()); cb(); }); it('should return false when the contents are a Stream', function(cb) { var val = new Stream(); var view = new View({contents: val, stat: fakeStat}); - view.isDirectory().should.equal(false); + assert(!view.isDirectory()); cb(); }); @@ -501,21 +504,22 @@ describe('View', function() { }); it('should copy all attributes over with Stream', function(cb) { - var contents = new Stream.PassThrough(); + var stream = new Stream.PassThrough(); var options = { cwd: '/', base: '/test/', path: '/test/test.coffee', - contents: contents + contents: stream }; + var view = new View(options); var view2 = view.clone(); - contents.write(new Buffer('wa')); + stream.write(new Buffer('wa')); process.nextTick(function() { - contents.write(new Buffer('dup')); - contents.end(); + stream.write(new Buffer('dup')); + stream.end(); }); view2.should.not.equal(view, 'refs should be different'); @@ -524,12 +528,17 @@ describe('View', function() { view2.path.should.equal(view.path); view2.contents.should.not.equal(view.contents, 'stream ref should not be the same'); view.contents.pipe(es.wait(function(err, data) { + if (err) return cb(err); + view2.contents.pipe(es.wait(function(err, data2) { + if (err) return cb(err); + data2.should.not.equal(data, 'stream contents ref should not be the same'); data2.should.eql(data, 'stream contents should be the same'); })); })); - cb(); + + stream.on('end', cb); }); it('should copy all attributes over with null', function(cb) { @@ -564,6 +573,7 @@ describe('View', function() { assert(copy.stat.isFile()); assert(!copy.stat.isDirectory()); + assert(copy.stat instanceof fs.Stats); assert(view.stat.hasOwnProperty('birthtime')); assert(copy.stat.hasOwnProperty('birthtime')); @@ -889,7 +899,7 @@ describe('View', function() { var val = null; var view = new View(); view.contents = val; - (view.contents === null).should.equal(true); + assert.equal(view.contents, null); cb(); }); @@ -897,7 +907,7 @@ describe('View', function() { var val = 'test'; var view = new View(); view.contents = val; - view.contents.should.deepEqual(new Buffer(val)); + assert.deepEqual(view.contents, new Buffer(val)); cb(); }); }); @@ -914,11 +924,10 @@ describe('View', function() { }); it('should error on get when no base', function(cb) { - var a; var view = new View(); delete view.base; try { - a = view.relative; + view.relative; } catch (err) { should.exist(err); cb(); @@ -926,10 +935,9 @@ describe('View', function() { }); it('should error on get when no path', function(cb) { - var a; var view = new View(); try { - a = view.relative; + view.relative; } catch (err) { should.exist(err); cb(); @@ -958,10 +966,9 @@ describe('View', function() { describe('dirname get/set', function() { it('should error on get when no path', function(cb) { - var a; var view = new View(); try { - a = view.dirname; + view.dirname; } catch (err) { should.exist(err); cb(); @@ -1002,10 +1009,9 @@ describe('View', function() { describe('basename get/set', function() { it('should error on get when no path', function(cb) { - var a; var view = new View(); try { - a = view.basename; + view.basename; } catch (err) { should.exist(err); cb(); @@ -1046,10 +1052,9 @@ describe('View', function() { describe('extname get/set', function() { it('should error on get when no path', function(cb) { - var a; var view = new View(); try { - a = view.extname; + view.extname; } catch (err) { should.exist(err); cb(); @@ -1089,7 +1094,6 @@ describe('View', function() { }); describe('path get/set', function() { - it('should record history when instantiation', function() { var view = new View({ cwd: '/', @@ -1134,7 +1138,7 @@ describe('View', function() { it('should throw when set path null in constructor', function() { (function() { - new View({ + view = new View({ cwd: '/', path: null }); @@ -1142,7 +1146,7 @@ describe('View', function() { }); it('should throw when set path null', function() { - var view = new View({ + view = new View({ cwd: '/', path: 'foo' }); diff --git a/test/view.render.js b/test/view.render.js index 24c33f7e..95b0efb6 100644 --- a/test/view.render.js +++ b/test/view.render.js @@ -8,7 +8,19 @@ var View = App.View; var view, app; describe('view.render', function() { - describe('rendering', function() { + describe('views', function() { + it('should expose `.render` for rendering a view:', function(cb) { + view = new View({path: 'a.tmpl', content: '<%= a %>'}); + + view.render({a: 'bbb'}, function(err, res) { + if (err) return cb(err); + res.content.should.equal('bbb'); + cb(); + }); + }); + }); + + describe('views created by collection and app', function() { beforeEach(function() { app = new App(); view = new View(); diff --git a/test/view.set.js b/test/view.set.js index fe8c4fba..9654bca4 100644 --- a/test/view.set.js +++ b/test/view.set.js @@ -13,8 +13,6 @@ describe('view.set', function() { app = new App(); app.create('page'); app.engine('tmpl', require('engine-base')); - - app.cache.data = {}; }); it('should set a property on a view:', function(cb) { diff --git a/test/view.use.js b/test/view.use.js index d7e714ad..f20593bd 100644 --- a/test/view.use.js +++ b/test/view.use.js @@ -6,7 +6,7 @@ var assert = require('assert'); var support = require('./support'); var App = support.resolve(); var View = App.View; -var view; +var view, app; describe('view.use', function() { beforeEach(function() { @@ -60,3 +60,47 @@ describe('view.use', function() { assert(view.c === 'ccc'); }); }); + +describe('collection > view .use', function() { + beforeEach(function() { + app = new App(); + }); + + it('should pass plugins down to views', function(cb) { + var count = 0; + app.create('pages'); + app.pages.use(function(inst) { + return function(view) { + count++; + view.count = count; + }; + }); + app.pages.addView('foo', {content: 'this is content'}); + var view = app.pages.getView('foo'); + assert.equal(view.count, 1); + cb(); + }); + + it('should pass plugins down to views after a view is created', function(cb) { + var count = 0; + + app.create('pages'); + app.pages.addView('foo', {content: 'this is content'}); + app.pages.addView('bar', {content: 'this is content'}); + app.pages.addView('baz', {content: 'this is content'}); + + // add plugin after adding views... + app.pages.use(function(inst) { + return function(view) { + count++; + view.count = count; + }; + }); + + assert.equal(app.pages.getView('foo').count, 1); + assert.equal(app.pages.getView('bar').count, 2); + assert.equal(app.pages.getView('baz').count, 3); + assert.equal(count, 3); + cb(); + }); +}); diff --git a/test/views.js b/test/views.js index aa5d7d49..4737e03e 100644 --- a/test/views.js +++ b/test/views.js @@ -163,6 +163,58 @@ describe('views', function() { assert(isBuffer(collection.views.one.contents)); assert(collection.views.one.abc === 'xyz'); }); + + it('should expose the `isType` method on items', function() { + var collection = new Views({View: View}); + var view = new View({content: '...'}); + collection.setView('one', view); + + var one = collection.getView('one'); + assert(one.isType('renderable')); + }); + + it('should set viewTypes on a collection', function() { + var collection = new Views({View: View}); + collection.viewType(['partial']); + + var view = new View({content: '...'}); + collection.setView('one', view); + + var one = collection.getView('one'); + assert(!one.isType('renderable')); + assert(one.isType('partial')); + }); + }); + + describe('deleteView', function() { + beforeEach(function() { + collection = new Views(); + }); + + it('should delete an view from `views` by view instance', function() { + collection.addView('foo'); + assert(collection.views.hasOwnProperty('foo')); + + collection.addView('one', {content: '...'}); + assert(collection.views.hasOwnProperty('one')); + assert.equal(Object.keys(collection.views).length, 2); + + var foo = collection.getView('foo'); + collection.deleteView(foo); + assert.equal(Object.keys(collection.views).length, 1); + }); + + it('should delete an view from `views` by view `key`', function() { + collection.addView('foo'); + assert(collection.views.hasOwnProperty('foo')); + + collection.addView('one', {content: '...'}); + assert(collection.views.hasOwnProperty('one')); + assert.equal(Object.keys(collection.views).length, 2); + + collection.deleteView('foo'); + assert.equal(Object.keys(collection.views).length, 1); + }); }); describe('addViews', function() { @@ -337,18 +389,18 @@ describe('views', function() { }); it('should load an array of items from an event:', function() { - var collection = new Views(); + var pages = new Views(); - collection.on('addList', function(list) { + pages.on('addList', function(list) { while (list.length) { - collection.addView({path: list.pop()}); + pages.addView({path: list.pop()}); } this.loaded = true; }); - collection.addList(['a.txt', 'b.txt', 'c.txt']); - assert(collection.views.hasOwnProperty('a.txt')); - assert(collection.views['a.txt'].path === 'a.txt'); + pages.addList(['a.txt', 'b.txt', 'c.txt']); + assert(pages.views.hasOwnProperty('a.txt')); + assert(pages.views['a.txt'].path === 'a.txt'); }); it('should load an array of items from the addList callback:', function() { From 5d24979471783d044e3ae4aa3669cc18f381b152 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 02:36:10 -0400 Subject: [PATCH 240/282] temporarily remove docs --- docs/lang/en/api/events.md | 79 ------ docs/lang/en/cli/examples/format-after.md | 19 -- docs/lang/en/cli/examples/format-before.md | 24 -- docs/lang/en/recipes/auto-load-middleware.md | 47 ---- .../en/recipes/disable-table-of-contents.md | 48 ---- docs/lang/en/recipes/extend-a-generator.md | 20 -- docs/lang/en/recipes/merging-context.md | 60 ---- docs/lang/en/recipes/render-a-list.md | 152 ---------- docs/src/conflict-reporter.md | 38 --- docs/src/content/actions.md | 6 - docs/src/content/argv.md | 6 - docs/src/content/built-in-templates.md | 18 -- docs/src/content/cli-commands.md | 230 --------------- docs/src/content/cli.md | 82 ------ docs/src/content/collections.md | 92 ------ docs/src/content/config.md | 265 ------------------ docs/src/content/configuration-and-data.md | 29 -- docs/src/content/context.md | 15 - docs/src/content/create.md | 28 -- docs/src/content/data.md | 40 --- docs/src/content/features.md | 26 -- docs/src/content/fs.md | 10 - docs/src/content/middleware.md | 259 ----------------- docs/src/content/naming-conventions.md | 13 - docs/src/content/plugins.md | 169 ----------- docs/src/content/settings.md | 80 ------ docs/src/content/stores.md | 153 ---------- docs/src/content/tasks.md | 1 - docs/src/content/templates.md | 2 - docs/src/content/terminology.md | 14 - docs/src/content/verb.config.md | 22 -- docs/src/content/verb.options.md | 65 ----- docs/src/flags.md | 30 -- docs/src/templates/contributing.md | 44 --- 34 files changed, 2186 deletions(-) delete mode 100644 docs/lang/en/api/events.md delete mode 100644 docs/lang/en/cli/examples/format-after.md delete mode 100644 docs/lang/en/cli/examples/format-before.md delete mode 100644 docs/lang/en/recipes/auto-load-middleware.md delete mode 100644 docs/lang/en/recipes/disable-table-of-contents.md delete mode 100644 docs/lang/en/recipes/extend-a-generator.md delete mode 100644 docs/lang/en/recipes/merging-context.md delete mode 100644 docs/lang/en/recipes/render-a-list.md delete mode 100644 docs/src/conflict-reporter.md delete mode 100644 docs/src/content/actions.md delete mode 100644 docs/src/content/argv.md delete mode 100644 docs/src/content/built-in-templates.md delete mode 100644 docs/src/content/cli-commands.md delete mode 100644 docs/src/content/cli.md delete mode 100644 docs/src/content/collections.md delete mode 100644 docs/src/content/config.md delete mode 100644 docs/src/content/configuration-and-data.md delete mode 100644 docs/src/content/context.md delete mode 100644 docs/src/content/create.md delete mode 100644 docs/src/content/data.md delete mode 100644 docs/src/content/features.md delete mode 100644 docs/src/content/fs.md delete mode 100644 docs/src/content/middleware.md delete mode 100644 docs/src/content/naming-conventions.md delete mode 100644 docs/src/content/plugins.md delete mode 100644 docs/src/content/settings.md delete mode 100644 docs/src/content/stores.md delete mode 100644 docs/src/content/tasks.md delete mode 100644 docs/src/content/templates.md delete mode 100644 docs/src/content/terminology.md delete mode 100644 docs/src/content/verb.config.md delete mode 100644 docs/src/content/verb.options.md delete mode 100644 docs/src/flags.md delete mode 100644 docs/src/templates/contributing.md diff --git a/docs/lang/en/api/events.md b/docs/lang/en/api/events.md deleted file mode 100644 index 3e511347..00000000 --- a/docs/lang/en/api/events.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: Events ---- - -Verb inherits from [component-emitter][]. - - -_(See the [component-emitter][] docs to see all available methods and features.)_ - -## .on - -The `on` method is used for listening for events emitted by Verb. - -**Example** - -```js -verb.on('error', function(err) { - // -}); -``` - -## .emit - -The `emit` method is used for emitting events. - -**Example** - -```js -verb.emit('error', new Error('foo')); -``` - -See the [component-emitter][] docs to see all available methods and features. - -### deployBefore - -Emitted before deployment begins. - -### deployAfter - -Emitted after deployment finishes. - -### done - -Emitted before Verb exits. - -### generateBefore - -Emitted before generation begins. - -### generateAfter - -Emitted after generation finishes. - -### new - -Emitted when the `--new` flag is passed. - -``` js -verb.on('new', function(name, args) { - // -}); -``` - -Data | Description ---- | --- -`post.path` | Full path of the post file -`post.content` | Content of the post file - -### processBefore - -Emitted before processing begins. This event returns a path representing the root directory of the box. - -### processAfter - -Emitted after processing finishes. This event returns a path representing the root directory of the box. - -### ready - -Emitted after initialization finishes. diff --git a/docs/lang/en/cli/examples/format-after.md b/docs/lang/en/cli/examples/format-after.md deleted file mode 100644 index 5462cfc9..00000000 --- a/docs/lang/en/cli/examples/format-after.md +++ /dev/null @@ -1,19 +0,0 @@ -# Typography - -### Indented code - -Or indent several lines of code by at least four spaces, as in: - -```js - // Some comments - line 1 of code - line 2 of code - line 3 of code -``` - -``` -// Some comments -line 1 of code -line 2 of code -line 3 of code -``` \ No newline at end of file diff --git a/docs/lang/en/cli/examples/format-before.md b/docs/lang/en/cli/examples/format-before.md deleted file mode 100644 index 84c07aa3..00000000 --- a/docs/lang/en/cli/examples/format-before.md +++ /dev/null @@ -1,24 +0,0 @@ -# Typography - - - - -### Indented code - - - -Or indent several lines of code by at least four spaces, as in: - - - -``` js - // Some comments - line 1 of code - line 2 of code - line 3 of code -``` - - // Some comments - line 1 of code - line 2 of code - line 3 of code diff --git a/docs/lang/en/recipes/auto-load-middleware.md b/docs/lang/en/recipes/auto-load-middleware.md deleted file mode 100644 index 305048d6..00000000 --- a/docs/lang/en/recipes/auto-load-middleware.md +++ /dev/null @@ -1,47 +0,0 @@ -# Load middleware from Verb config - -The `verb` property in package.json can be used to define configuration options for verb. This recipe shows how to define a middleware and related options. - -## Middleware method - -On the `verb` object in `package.json`, add a property where: - -- the key is the name of the middleware method to run -- the value is either an options object, or an array of "options" objects (if multiple middleware are to be run) - -**Example** - -This would tell verb to add an `onLoad` middleware (but would fail since we haven't defined options yet): - -```json -{ - "name": "foo", - - "verb": { - "onLoad": {} - } -} -``` - -## Middleware options - -Now we need to tell verb a few things about the middleware: - -- `path`: Where is the middleware? Whether it's a module dependency in `node_modules` or a local file, define this value the way you would define a require statement. -- `pattern`: the regex pattern to use for matching `view.path` -- `alias`: optionally specify an alias to use if, for example, you want to pass options to the middleware by the alias etc. - -```json -{ - "name": "foo", - - "verb": { - "onLoad": [ - { - "path": "parser-front-matter", - "pattern": "readme\\.md$" - } - ] - } -} -``` \ No newline at end of file diff --git a/docs/lang/en/recipes/disable-table-of-contents.md b/docs/lang/en/recipes/disable-table-of-contents.md deleted file mode 100644 index 0c33beb0..00000000 --- a/docs/lang/en/recipes/disable-table-of-contents.md +++ /dev/null @@ -1,48 +0,0 @@ -# Disable Verb's built-in TOC - -A couple of Verb's built-in layouts include a `` tag, which is used as a placeholder to drop the automatically generated Table of Contents into your document. - -Here are three ways to disable the TOC: - -**1. Front-matter** - -Add a section of YAML front-matter to your document with a `toc` property. - -```markdown ---- -toc: false ---- - -## Usage - -Some really useful information, that you can use - because it's so useful. - -``` - -Verb uses [falsey][] to check values for just a few different properties, including `toc`. So any value that the falsey lib evaluates as `falsey` will disable the `toc`. - -**2: verb config** - -Disable the TOC by defining it on an `options` property in your [local verb config](./settings.md): - -```json -{ - "name": "my-project", - "verb": { - "layout": "default", - "options": { - "toc": false - } - } -} -``` - -**3: verb.option()** - -Use the `option` API to disable the TOC: - -```js -verb.option('toc', false); -``` - -[falsey]: https://github.com/jonschlinkert/falsey \ No newline at end of file diff --git a/docs/lang/en/recipes/extend-a-generator.md b/docs/lang/en/recipes/extend-a-generator.md deleted file mode 100644 index 3ad7a795..00000000 --- a/docs/lang/en/recipes/extend-a-generator.md +++ /dev/null @@ -1,20 +0,0 @@ -# Extend a generator - - -```js -module.exports = function(verb) { - verb.extendWith('verb-readme-generator'); - verb.docs('docs/*.md', {cwd: __dirname}); -}; -``` - -## Extend with the default generator - -Extend a specified generator with the `default` generator (`verbfile.js`): - -```js -// --verbfile.js-- -module.exports = function(app) { - app.data({name: 'foo-bar'}); -}; -``` diff --git a/docs/lang/en/recipes/merging-context.md b/docs/lang/en/recipes/merging-context.md deleted file mode 100644 index 87c43f55..00000000 --- a/docs/lang/en/recipes/merging-context.md +++ /dev/null @@ -1,60 +0,0 @@ -# Merging context - -> This recipe shows different strategies for merging the context object before rendering, based on which data should be given preferential treatment. - -Learn more about [context](./terminology#context) - -## Middleware - -If you need control over how context is merged on a file-by-file basis, one solution is to create a custom `preRender` middleware. - -**Example** - -Let's say you have a view (any template. can be a partial, page, layout, etc.) with the following contents: - -```hbs ---- -title: Foo ---- - -This is {{title}} -``` - -**Pre-render middleware** - -Try the following - -```js -var merge = require('mixin-deep'); -var app = assemble(); - -// merge data onto `app.cache.data` -app.data({title: 'Site'}); - -// pre-render middleware, called right before the engine renders -app.preRender(/\.hbs$/, function(view, next) { - console.log(view.data); - //=> { title: 'Foo' } - console.log(app.cache.data); - //=> { title: 'Site' } - - file.data = merge({}, app.cache.data, file.data); - next(); -}); -``` - -To create a re-usable middleware: - -```js -function mergeContext(app, locals) { - return function(view, next) { - file.data = merge({}, app.cache.data, file.data, locals); - next(); - }; -} - -// usage -app.preRender(/\.hbs$/, mergeContext(app, {title: 'Foo'})); -app.preRender(/\.txt$/, mergeContext(app, {title: 'Bar'})); -app.preRender(/\.ejs$/, mergeContext(app, {title: 'Baz'})); -``` \ No newline at end of file diff --git a/docs/lang/en/recipes/render-a-list.md b/docs/lang/en/recipes/render-a-list.md deleted file mode 100644 index b2e9e0ad..00000000 --- a/docs/lang/en/recipes/render-a-list.md +++ /dev/null @@ -1,152 +0,0 @@ -# Rendering a list - -> This recipe shows how to render a list from a view collection - -## Preparation - -First, create an `verbfile.js`. The rest of this recipe assumes that you have the following defined: - -```js -var assemble = require('assemble'); -var app = assemble(); -``` - -**Add a "log" helper** - -Next, let's add a `log` helper that we can use for inspecting the context and debugging: - -```js -app.helper('log', function(context) { - console.log(context); -}); -``` - -_(Learn more about [inspecting the context](./inspecting-the-context.md))_ - -## Create a collection - -We need a collection to use for our list, so let's create a `pages` collection: - -```js -app.create('pages'); -``` - -### Add pages to the collection - -```js -app.page('foo', 'this is foo'); -app.page('bar', 'this is bar'); -app.page('baz', 'this is baz'); -``` - -## Build the context - -Before we can render a list, we need to expose the - -```js -/** - * Middleware - * - * Add the `pages` collection to `view.data`, - * which exposes it to the context for rendering - */ - -app.preRender(/./, function(view, next) { - view.data.pages = app.views.pages; - next(); -}); - -/** - * Task for rendering "site" - */ - -app.task('site', function() { - app.pages('src/pages/**/*.hbs'); - app.partials('src/partials/*.hbs'); - app.layouts('src/layouts/*.hbs'); - - // use the `toStream` method instead of `src` - // so that all pages are available at render time - return app.toStream('pages') - .pipe(app.renderFile()) - .pipe(extname()) - .pipe(app.dest('_build')); -}); -``` - -Layout: `default.hbs` - -```handlebars - - - - - {{ title }} - - - {% body %} - - {{> list }} - - -``` - -Partial: `list.hbs` (or you could add this inline in the layout) - - -```handlebars -{{#each pages}} -{{log .}} -{{@key}} -{{/each}} -``` - -## Inspecting the context - - -Since views are vinyl files, you'll need to inspect them to see what's available to use. To make this easier, you might also try adding a helper to see what's on the context: - -**Example** - -Create a helper, arbitrarily named `ctx` (for context) or whatever you want, and add it to your `verbfile.js`: - -```js -app.helper('ctx', function(context) { - console.log(arguments); - console.log(context); // the object passed to the helper - - console.log(this); // handlebars context - console.log(this.options); // assemble `options` - console.log(this.context); // context of the current "view" - console.log(this.app); // assemble instance -}); -``` - -Then use the `ctx` helper inside the `{{#each}}` loop: - -```handlebars -{{#each pages}} - {{ctx .}} -{{/each}} -``` - -And try it outside the loop: - -```handlebars -{{ctx .}} -``` - - -## FAQ - -**What does "render a list" mean?** - -When a template is rendered, placeholder variables are replaced with actual values from the "context" object. Sometimes it's necessary to render a list of something, like pages, posts, related links, and so on. To do so, the _context_ must contain an object (or array) representation of the list we wish to render. - -**Why isn't my list rendering?** - -A common mistake is trying to render a list of _something_ before all items in that list have actually been loaded onto the context. - -For example, if you're using `app.src()` (or `gulp.src()`) to read in a glob of "pages" (or posts, etc.), and you try to generate a list of pages, you might be wondering why the list isn't rendering. This is because, when using `.src()`, files are rendered one-by-one, so the entire context (of all pages) is not yet available until the last page has already been rendered. - -The good news is that this is easily solved by building up the list in the flush function of a plugin, or by _not using `app.src()` to load pages_. Instead, we can use the `.toStream()` method. The latter is what this recipe shows. diff --git a/docs/src/conflict-reporter.md b/docs/src/conflict-reporter.md deleted file mode 100644 index 0cc2c9cf..00000000 --- a/docs/src/conflict-reporter.md +++ /dev/null @@ -1,38 +0,0 @@ - -## conflict reporter - -> See conflicts in the command line! - -To output conflicts, just run verb in `verbose` mode: - -```bash -$ verb --verbose -``` - -**Example output** - -![screen shot 2015-04-23 at 5 42 16 am](https://cloud.githubusercontent.com/assets/383994/7294451/9034dbfa-e97b-11e4-930e-372ef4012096.png) - -**How it works** - -In verbose mode, the conflict reporter tells you when there are problems with helpers and properties on the context. This works for "renderable" templates, like `.verb.md` (via a plugin) as well as includes (via a middleware). - -For example, currently verb registers both a `license` data field and a `license()` helper (I kept the conflict so you can see how the manager works. Don't worry, verb handles it just fine now). Normally, both of these cannot exist on the same object at the same time, so only one of them will be merged onto the context. As a result, the template engine will throw an error when it tries to render either `{%= license %}` or `{%= license() %}`. - -**Solution** - -The conflict manager plugin detects these conflicts, then it temporarily renames the helper on the fly, re-registers it on the `__.` object, and deletes it from the data object. That way both the helper and data property will render as expected. (You might be thinking, "why would I have duplicate properties like that?" well, you personally might not, but if you want to use community templates or built-in templates that might have variables you're unaware of, and you want them to "just work", then this is not an uncommon thing...). - -*** - -## visual diffs - -See the difference between pre-render and post-render templates by running the following in the command line: - -```js -$ verb --diff -``` - -**Example output** - -![screen shot 2015-04-23 at 6 24 13 am](https://cloud.githubusercontent.com/assets/383994/7295197/96b5666a-e981-11e4-82f8-3587697fc910.png) diff --git a/docs/src/content/actions.md b/docs/src/content/actions.md deleted file mode 100644 index 2995e257..00000000 --- a/docs/src/content/actions.md +++ /dev/null @@ -1,6 +0,0 @@ -# Actions - -## Table of Contents - -- `toc.isInserted` -- `toc.isRendered` diff --git a/docs/src/content/argv.md b/docs/src/content/argv.md deleted file mode 100644 index c5b0272d..00000000 --- a/docs/src/content/argv.md +++ /dev/null @@ -1,6 +0,0 @@ -# argv - - -```js -var argv = base.get('env.argv'); -``` \ No newline at end of file diff --git a/docs/src/content/built-in-templates.md b/docs/src/content/built-in-templates.md deleted file mode 100644 index 7d97258d..00000000 --- a/docs/src/content/built-in-templates.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: Built-in templates ---- - - - -Verb ships with a small collection of built-in templates. Each was chosen based on usage patterns and experience with hundreds of projects that use verb. - -## .verb.md - - -## layouts - - -## includes - - -Want something different? We love to [hear your feedback]({%= bugs.url %})! \ No newline at end of file diff --git a/docs/src/content/cli-commands.md b/docs/src/content/cli-commands.md deleted file mode 100644 index a16a1ee4..00000000 --- a/docs/src/content/cli-commands.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -title: CLI Commands ---- - -The following CLI commands are currently supported in verb. - -### --ask - -Force questions that match the given pattern to be asked. The resulting answer data is merged onto `app.cache.data`. - -After questions are answered: - -* Use `app.data('answers')` to get answer data. -* To open the directory where data is persisted, enter `--open answers` in the command line - -**Example** - -```sh -# ask all questions -$ --ask -# ask all `author.*` questions -$ --ask 'author.*' -# ask all `*.name` questions (like `project.name` and `author.name`) -$ --ask '*.name*' -``` - -### --config - -Persist a value to a namespaced config object in package.json. For example, if you're using `verb`, the value would be saved to the `verb` object. - -**Params** - -* **{Object}**: app - -**Example** - -```sh -# display the config -$ --config -# set a boolean for the current project -$ --config=toc -# save the cwd to use for the current project -$ --config=cwd:foo -# save the tasks to run for the current project -$ --config=tasks:readme -``` - -### --cwd - -Set the current working directory. - -**Example** - -```sh -# set working directory to 'foo' -$ --cwd=foo -# display cwd -$ --cwd -``` - -### --data - -Set data on the `app.cache.data` object. This is the API-equivalent of calling `app.data()`. - -**Example** - -```sh -$ --data -# display data object -$ --data=foo -# sets {foo: true} -$ --data=foo:bar -# sets {foo: 'bar'} -$ --data=foo.bar:baz -# sets {foo:{bar: 'baz'}} -``` - -### --disable - -Disable a configuration setting. This is the API-equivalent of calling `app.disable('foo')`, or `app.option('foo', false)`. - -**Example** - -```sh -$ --disable=foo -# sets {foo: false} -``` - -### --emit - -Bind `console.error` to the given event listener, so that when event `name` is emitted, the event arguments will be output in the console. - -**Example** - -```sh -# emit errors -$ --emit error -# emit all views as they're created -$ --emit view -# emit only 'pages' as they're created -$ --emit page -``` - -### --enable - -Enable a configuration setting. This is the API-equivalent of calling `app.enable('foo')`, or `app.option('foo', true)`. - -**Example** - -```sh -$ --enable=foo -# sets {foo: true} -``` - -### --global - -Persist a value to the global config store by prefixing a command line option with `-g` or `--global`. - -**Params** - -* **{Object}**: app - -**Example** - -```sh -# save a boolean -$ -g=toc # saves `{ toc: true }` to global defaults -# save the cwd to use as a global default -$ -g=cwd:foo -# save the tasks to run by default -$ -g=tasks:readme -``` - -### --init - -Ask initialization questions and persist answer data to the global config store. - -**Example** - -```sh -$ --init -``` - -### --open - -Open a directory, or open a file in the default application associated with the file type. - -**Example** - -```sh -# Open the directory where answer data is persisted -$ --open answers -# Open the directory where store data is persisted -$ --open store -``` - -### --option - -Set options on the `app.options` object. This is the API-equivalent of calling `app.option()`. You may also use the plural `--options` flag for identical behavior. - -**Example** - -```sh -$ --option=foo -# sets {foo: true} -$ --option=foo:bar -# sets {foo: 'bar'} -$ --option=foo.bar:baz -# sets {foo:{bar: 'baz'}} -``` - -### --options - -Set in-memory options on the `app.options` object. This is the API-equivalent of calling `app.option()`. You may also use the singular `--option` flag for identical behavior. - -To display currently defined options, pass the `--options` flag with no value. - -**Example** - -```sh -$ --options=foo -# sets {foo: true} -$ --options=foo:bar -# sets {foo: 'bar'} -$ --options=foo.bar:baz -# sets {foo:{bar: 'baz'}} -``` - -### --save - -Persist a value to the global config store by prefixing a command line option with `--save` or `-s`. - -**Params** - -* **{Object}**: app - -**Example** - -```sh -# save a boolean -$ --save=toc # saves `{ toc: true }` to global config -# save the cwd to use as a global default -$ --save=cwd:foo -# save the tasks to run by default -$ --save=tasks:readme -``` - -### --tasks - -Run the given generators and tasks. This flag is unnecessary when used with [base-runner](https://github.com/jonschlinkert/base-runner). - -**Example** - -```sh -# run task 'foo' -$ app --tasks foo -# => {task: ['foo']} -# run generator 'foo', task 'bar' -$ app --tasks foo:bar -# => {task: ['foo:bar']} -``` - -### --show - -Returns true if `val` is true or is an object with `show: true` - -**Params** - -* `val` **{String}** -* `returns` **{Boolean}** diff --git a/docs/src/content/cli.md b/docs/src/content/cli.md deleted file mode 100644 index 834eb028..00000000 --- a/docs/src/content/cli.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: <%= proper(name) %> CLI -description: "Working with <%= name %> from the command line." -related: ["terminology", "tasks", "features"] -tags: ['config', 'cli', 'command line', 'options', 'flags'] ---- - -**CLI FAQ** - -- Dot-notation may be used for most command line arguments -- In cases where dots should not be expanded to an object, you may escape the dot with a single backslash - - -## Summary - -``` -todo: generate/display CLI help here -``` - -## option - - -Set options to be used in plugins and middleware. - -```sh -$ verb --option=: -# or -$ verb --option= -``` - -- `key` The property to set -- `value`: the value of `key`. If no value is given, the value will be set to `true` - - -**Example** - -Omit the Table of Contents when generating a document from a built-in verb template: - -```sh -$ verb --option=toc:false -``` - -## config - -Persist project-specific `config` values to the `verb` object in `package.json`. These values will override config settings defined anywhere else (accept for those passed via command line): - -```sh -$ verb --config= -``` - -### config examples - -**Set the layout to use** - -Use a verb [built-in layout](./built-in-templates.md#layouts) with `.verb.md` every time it's rendered: - -```sh -$ verb --config=layout:default -``` - -**Disable Table of Contents** - -Disable the Table of Contents for all templates: - -```sh -$ verb --config=option.toc:false -``` - -Disable the Table of Contents for a specific template: - -```sh -$ verb --config=disable.toc:"foo\.md" -# remember to escape dots in filepaths! -``` - -Disable the Table of Contents for multiple templates: - -_(TODO: implement me)_ - -```sh -$ verb --config=disable.toc:"foo\.md,bar\.md,baz\.md" -``` diff --git a/docs/src/content/collections.md b/docs/src/content/collections.md deleted file mode 100644 index 4ea528be..00000000 --- a/docs/src/content/collections.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Collections ---- - -> Collections are first-class citizens in {%= name %}, with three different collection types to choose from. This document describes each collection type and provides the information you need to start using them. - -- `create` -- `collection` -- `list` - -## View collections - -**What is a "view collection"?** - -A view collection has all of the features of a "generic" verb collection, along with special features and methods that are specific to managing views (templates). - -**Special features** - -View collections have methods getting, setting and finding views, as well as assigning "view types" to individual views (Learn more about [view types](./view-types.md)). - -**Methods** - -- `setView` -- `getView` -- `viewType` -- `isType` - -## Create - -The `create` method is used for adding custom "view collections" to verb. - -**Example** - -To create a "pages" collection: - -```js -app.create('pages'); -``` - -A few things happened when the `.create` method was used: - -1. The `page` and `pages` methods were decorated onto verb (verb automatically detects inflections - plural and singular forms) -2. A `pages` object was added to `verb.views` for caching views, so when pages are created they can be found on `verb.views.pages` -3. ~~A `page` helper was created~~ (helpers are only created for `partial` [view types](view-types.md)) - -## Collections types - -Assemble 0.6.0 supports **3 different collection types**: - -| **Collection type** | **unit** | **storage object** | **description** | -| --- | --- | --- | ---| -| **collections** | `item` | `items` | generic collections, with methods for setting and getting `items` | -| **view collections** | `view` | `views` | Methods for working with template collections, like pages, posts, layouts, partials, etc. | -| **lists** | `item` | `items` | Stored as an array, has methods for getting, setting, [sorting][], [grouping][]. etc | - -_(sidenote: views and items are also [vinyl](https://github.com/gulpjs/vinyl) files)_ - -Since "generic" collections are mostly used internally, from here forward "collection" will usually refer to "view collection" unless noted otherwise. - -## View collections - -View collections have methods for adding, finding and getting views, such as: - -- `.getView` -- `.setView` - -Views (templates) are stored on the `views` object of a collection, allowing views to be looked up by key. - -Keys can be customized and renamed using a `renameKey` function passed on the options of the collection, or to rename all keys in all collections, you may pass a `renameKey` function on the verb options. - -Ultimately this gives you full control over how views are named and how lookups are done. - -## Lists - -Lists are similar to collections but instead of storing an object of views, `items` are stored as an array. - -**Nice to know** - -- You can create [lists from collections][lists-from-collections] -- You can create [collections from lists][collections-from-lists] -- Lists are useful for doing things like: - * [pagination][], - * [sorting](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L359), and - * [grouping](https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L333) - -_(the above links go to [templates](https://github.com/jonschlinkert/templates), which is the underlying library that provides verb with methods for managing views, collections, rendering, engines, and so on. This lib could be used to create your own static site generator if you need something different)_ - -Last, keep in mind that verb is highly pluggable, so you can extend it to do whatever you need (plugins can be used on verb itself, a collection, or even a specific view). Let us know if you want to do something that you think ought to be in verb itself and we can discuss ways to implement it. - -[lists-from-collections]: https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L77-L83 -[collections-from-lists]: https://github.com/jonschlinkert/templates/blob/master/lib/views.js#L75-L81 -[pagination]: https://github.com/jonschlinkert/templates/blob/master/lib/list.js#L393 diff --git a/docs/src/content/config.md b/docs/src/content/config.md deleted file mode 100644 index ba10322e..00000000 --- a/docs/src/content/config.md +++ /dev/null @@ -1,265 +0,0 @@ ---- -title: Config ---- - -Define verb configuration settings on the `verb` object in package.json. - -Also see the [CLI commands](cli-commands.md#config) documentation to learn how to set config values from the command line. - -**Example** - -```js -{ - "name": "my-project", - "verb": { - // verb config settings here - } -} -``` - -### .cwd - -Set the current working directory. - -**Example** - -```json -{ - "name": "my-project", - "verb": { - "cwd": "foo/bar" - } -} -``` - -### .data - -Merge data onto the `app.cache.data` object. - -**Example** - -```json -{ - "name": "my-project", - "verb": { - "data": { - "username": "jonschlinkert", - "twitter": "jonschlinkert" - } - } -} -``` - -If the [base-data][] plugin is registered, this is the API-equivalent of calling `app.data()`. - -### .engines - -Register engines to use for rendering templates. Object-keys are used for the engine name, and the value can either be the module name, or an options object with the module name defined on the `name` property. - -**Prerequisites** - -- ~~Requires [templates][], otherwise ignored~~ (included in verb by default) -- Modules must be locally installed and listed in `dependencies` or `devDependencies` - -**Examples** - -Key-value pairs, where the `key` is the name (or file extension) to associate with the engine, and the `value` is the module name to require: - -```json -{ - "name": "my-project", - "verb": { - "engines": { - "*": "engine-base" - } - } -} -``` - -Objects, where the `key` is the name (or file extension) to associate with the engine, and the `value` is an options object to pass to the engine. - -_(When this format is used, the object must include a `name` property with the name of the module to require)_. - -```json -{ - "name": "my-project", - "verb": { - "engines": { - "*": { - "name": "engine-base" - } - } - } -} -``` - -### .engine - -Alias for [engines](#engines) - -### .helpers - -Register helpers to be used in templates. Value can be a string or array of module names, glob patterns, file paths, or an object where each key is a filepath, glob or module name, and the value is an options object to pass to resolved helpers. - -**Prerequisites** - -- ~~Requires [templates][], otherwise ignored~~ (included in verb by default) -- Modules must be locally installed and listed in `dependencies` or `devDependencies` - -**Examples** - -Module names: - -```json -{ - "name": "my-project", - "verb": { - "helpers": { - "helper-foo": {}, - "helper-bar": {} - } - } -} -``` - -Glob of helpers: - -```json -{ - "name": "my-project", - "verb": { - "helpers": ["foo/*.js"] - } -} -``` - -### .options - -Merge options onto the `app.options` object. - -**Example** - -```json -{ - "name": "my-project", - "verb": { - "options": { - "foo": "bar" - } - } -} -``` - -If the [base-option][] plugin is registered, this is the API-equivalent of calling `app.option()`. - -### .plugins - -Load [pipeline plugins][plugins] to be used for transforming files in the `.src` string. - -**Prerequisites** - -- ~~Requires the [base-pipeline][] plugin to be registered~~ (included in verb by default) -- Modules must be locally installed and listed in `dependencies` or `devDependencies` - -**Example** - -Defined as an array of plugin names: - -```json -{ - "name": "my-project", - "verb": { - "plugins": ["gulp-eslint"] - } -} -``` - -Defined as objects: - -```json -{ - "name": "my-project", - "verb": { - "plugins": { - "gulp-format-md": {}, - "gulp-eslint": {} - } - } -} -``` - -### .toc - -Enable or disable Table of Contents rendering, or pass options to the [verb-toc][] library. - -**Prerequisites** - -- Requires the [verb-toc][] plugin to be registered. _This **Not** included in verb by default_. -- Modules must be locally installed and listed in `dependencies` or `devDependencies` - -**Example** - -```json -{ - "name": "my-project", - "verb": { - "toc": true - } -} -``` - -### .use - -Define plugins to load. Value can be a string or array of module names. - -**Prerequisites** - -- Modules must be locally installed and listed in `dependencies` or `devDependencies` - -**Example** - -```json -{ - "verb": { - "use": ["base-option", "base-data"] - } -} -``` - -### .run - -Always run tasks, regardless of additional command line flags that may have been passed. - -**Example** - -```json -{ - "verb": { - "run": true - } -} -``` - -## Related - -**Docs** - -- [CLI commands](cli-commands.md) -- [data](data.md) -- [options](options.md) -- [plugins](plugins.md) - -**Libraries** - -- [base-data][] -- [base-option][] -- [base-pipeline][] -- [templates][] -- [verb-toc][] - - -[base-data]: https://github.com/node-base/base-data -[base-option]: https://github.com/node-base/base-option -[verb-toc]: https://github.com/verbose/verb-toc -[base-pipeline]: https://github.com/jonschlinkert/base-pipeline -[templates]: https://github.com/jonschlinkert/templates \ No newline at end of file diff --git a/docs/src/content/configuration-and-data.md b/docs/src/content/configuration-and-data.md deleted file mode 100644 index f21e396d..00000000 --- a/docs/src/content/configuration-and-data.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Configuration and data ---- - -_(WIP)_ - -## Overview - -**Persisted** - -* `app.store.data`: global config store -* `app.local.data`: project config store -* `app.pkg.data`: package.json config -* `app.questions[key].answer.data`: every question has its own data store, with one answer per directory, per locale - as well as one default answer per locale. - -**In-memory** - -- `app.answers.data` -- `app.cache.data` - - -## Comparison - -Verb keeps "data" on two different objects, depending on your needs. - -| **Storage object** | **Description** | **Methods** | -| --- | --- | --- | -| `app.cache.data` | in-memory | `app.data()` (method) | -| `app.store.data` | persisted | `app.store` (object with methods) | diff --git a/docs/src/content/context.md b/docs/src/content/context.md deleted file mode 100644 index 40bcaa52..00000000 --- a/docs/src/content/context.md +++ /dev/null @@ -1,15 +0,0 @@ -# Context - -> When templates are rendered, "context" is the data object used by the rendering engine to resolve variables in template strings to their actual values. - -Typically, the _context object_ is created dynamically at render time by merging multiple source objects together (before being passed to the rendering engine). Thus, it only exists in memory. - -For example, data from [front-matter][] is often merged with `locals` to create the context object. - -**How context is created** - -When a template is rendered a context object is created and passed to the rendering engine for that template. This object is created any or all of the following data sources, depending on options or customizations: - -- **dynamic data**: data that is dynamically generated at runtime or only exists at certain points during runtime, like pagination info, destination file paths or URLs, permalinks, navigation variables, an so on. -- **locals**: an object that is passed to the `render` method -- **globals**: an object that is intended to be universally made available to all templates, and is created from some "globally" stored data object. {{upper name}} Stores global data on `{{name}}.cache.data`. See the [data][] docs for more details. \ No newline at end of file diff --git a/docs/src/content/create.md b/docs/src/content/create.md deleted file mode 100644 index f634f1a9..00000000 --- a/docs/src/content/create.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: create ---- - -The `.create` method is used for creating custom "view collections". - -**Params** - -- `name` **{String}**: the name of the collection to create -- `options` **{Object}**: options to use when creating the collection - -**Example** - -To create a "pages" collection: - -```js -app.create('pages'); -``` - -A few things happened when the `.create` method was used: - -- The `page` and `pages` methods were decorated onto verb (verb automatically detects inflections - plural and singular forms) -- A `pages` object was added to `verb.views` for caching views, so when pages are created they can be found on `verb.views.pages` - -## FAQ - -- View collections are instances of the [Views](Views.md) class. -- When the `.create` method is called, verb invokes the [Views](Views.md) class to create an instance of `Views` \ No newline at end of file diff --git a/docs/src/content/data.md b/docs/src/content/data.md deleted file mode 100644 index 2f720291..00000000 --- a/docs/src/content/data.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: verb -title: Data -engine: hbs -description: "Learn how to get, set and delete data for templates, options and more." -related: ['#store'] -reflinks: ['base-store', 'base-data'] ---- - -Verb keeps "data" on two different objects, depending on your needs. - -| **Storage object** | **Description** | **Methods** | -| --- | --- | --- | -| `verb.cache.data` | Kept in-memory | `verb.data()` (function) | -| `verb.store.data` | Persisted to disk | `verb.store` (object with methods) | - -## verb.data - -```js -verb.data({a: 'b'}); -// or -verb.data('a', 'b'); -console.log(verb.cache.data); -//=> {a: 'b'} -``` - -The `verb.data` method is powered by the [base-data][] plugin. Visit [that project][base-data] to see all available features and options. - -## verb.store - -```js -verb.store.set({a: 'b'}); -// or -verb.store.set('a', 'b'); - -console.log(verb.store.data); -//=> {a: 'b'} -``` - -The `verb.store` object is powered by the [base-store][] plugin. Visit [that project][base-store] to see all available features and options. diff --git a/docs/src/content/features.md b/docs/src/content/features.md deleted file mode 100644 index 04cd85a6..00000000 --- a/docs/src/content/features.md +++ /dev/null @@ -1,26 +0,0 @@ -# Features - -## Fast - -Assemble is fast. Depending on setup, plugins, customizations and other factors, a small site with 10-15 pages can be generated in ~300 ms. - -## Flexible - -Assemble gives you complete control over how and where you build files. If you've used [gulp][], you'll already be familiar with Assembles file-sytem API. - -## Template collections - -Assemble has rich support for template collections -- assemble supports template "types": `renderable`, `layout` and `partial`. Defined when a collection is created, types add special behavior and decorate methods related to each type onto the collection and its views (e.g. `partials` will be passed to the rendering engine _as partials_, helpers are created for getting partials, etc). Collections can use one or more type. -- assemble supports instance plugins (like metalsmith), so you can add do anything you want to an instance -- assemble collections can use plugins -- assemble views (templates) can use plugins (assemble views are vinyl files) -- assemble supports pipeline plugins (like gulp), so you can do in-stream transformations on files (views) - -## Template engines - -- assemble allows you to use any template engine, and allows you to use more than one template engine during the same build. - -## Middleware and routes - -- assemble supports middleware, similar to express. diff --git a/docs/src/content/fs.md b/docs/src/content/fs.md deleted file mode 100644 index bd858fd1..00000000 --- a/docs/src/content/fs.md +++ /dev/null @@ -1,10 +0,0 @@ -# File system - -_(TODO)_ - -**Example** - -```js -app.src('*.hbs') - .pipe(app.dest('site/')) -``` diff --git a/docs/src/content/middleware.md b/docs/src/content/middleware.md deleted file mode 100644 index 98d1aa32..00000000 --- a/docs/src/content/middleware.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -name: verb -title: Middleware -engine: hbs -description: "" -related: ['en-route'] -reflinks: ['en-route'] ---- - -## What is middleware? - -A "middleware" is a function that has access to the [view](view.md) object, and a callback function that represents the `next` middleware in the application’s build cycle. - -Middleware functions can perform the following tasks: - -- Execute any code. -- Make changes to the [view](view.md) object. -- Call the `next` middleware function in the stack. - -### Router methods and handlers - -- **Router methods**: similar to the router METHODS in [express][], but instead of representing [HTTP METHODS][verbs], here router methods represent significant points during the build cycle. - -- **Handler**: The `.handle` method - - -- middleware is "registered" by a router method - - - -## Build lifecycle - -The build lifecycle describes the various stages, events and tranformations that occur from start to finish. - -## Built-in handlers - -- `onLoad` -- `preRender` -- `preCompile` -- `preLayout` -- `onLayout` -- `postLayout` -- `onMerge` -- `postCompile` -- `postRender` - -### Pipeline handlers - -Assemble adds the following handlers, called after a `view` enters assemble's ([vinyl][]) `.src` stream: - -- `onStream`: called by the `.toStream` method upon adding a view to the `.src` stream -- `preWrite`: called by the `.dest` method before writing `view.contents` to the file system -- `postWrite`: called by the `.dest` method after writing `view.contents` to the file system - -### Custom handlers - -... - - -## Caveats - -Their are many advantages to using middleware as a part of your application's build cycle, but there is also at least one common potential pitfalls you should be aware of: - -**A middleware will only when called by its handler** - -This means, for instance, that if you compile a view using the `.compile` method, and you never call the `.render` method, then the `.render` middleware handler will **not be called**, thus any custom `.preRender` or `.postRender` middleware you're registered will not be run. - -In most cases, this should be acceptable and considered correct. However, there will be cases where this behavior causes confusion, but we know of at least a couple of ways to work around it. - -### Workarounds - -One solution to the "why wasn't my middleware called?" problem is to call the "missing" handlers directly. For instance, continuing with the previous example, if you need a `.render` middleware to be called, you could: - -- Call the missing handlers directly, or -- Create noops to fill in build cycle gaps and trigger the handlers - -**Call the missing handlers directly** - -```js -app.preCompile(/./, function(view, next) { - app.handle('preRender', view, next); -}); - -app.postCompile(/./, function(view, next) { - app.handle('postRender', view, next); -}); -``` - -This solution brings its own set of complications. In particular, now the `.preRender` method is being called after `.preCompile`, when `.preRender` should be called first. Since middleware are executed in the order in which they are defined, as long as you register all other `.preCompile` middleware after the one in the example, you should be okay. But the following workaround might be a better solution whenever possible. - -**Create noops to trigger handlers** - -A more reliable solution is to create noops that will trigger missing handlers, ideally without any negative side effects. - -For example, instead of skipping `.render` altogether (because we only want to `.compile` a view), we could create a basic custom engine, or override the render method on a registered engine so that `.render` will not alter `view.contents`, allowing us to call `.render` instead of `.compile`. - -For example, a basic noop engine might look like this: - -```js -app.engine('foo', function(view, locals, cb) { - cb(null, view); -}); -``` - -Or, to override the `.render` method on a registered engine: - -```js -var engine = app.engine('foo'); -engine.render = function(view, locals, cb) { - cb(null, view); -}; -``` - -**** - -## Middleware guidelines - -Guidelines for authoring durable, reliable middleware: - -- middleware should do one thing, and do it well -- middleware should not rely on other middleware, with rare exception -- middleware should be able to run before or after any other middleware called by the same handler. For example, if two different `.onLoad` middleware are registered, either should be able to run first or last, without effecting the results of either middleware. - - -## Router methods - -Router methods are similar to the router METHODS in [express][], but instead of representing [HTTP METHODS][verbs], the router methods here represent significant points or "stages" during the build. - -**Summary** - -- `onLoad`: Immediately after a view is loaded, as a last step just before adding the view to a collection. -- `preLayout`: Immediately before the first [layout][] in a [layout-stack][] is applied to a view. -- `onLayout`: Called after each [layout][] in a [layout-stack][] is applied. -- `postLayout`: Called after all [layouts][] have been applied to a view. -- `onMerge`: Called directly before [partials][] collections are merged onto the [context][]. -- `preCompile`: Called before compiling a view. -- `postCompile`: Called after compiling a view. -- `preRender`: Called before rendering a view. -- `postRender`: Called after rendering a view. - - -## Methods - -### onLoad - -Immediately after a view is loaded, as a last step just before adding the view to a collection. - -**Example** - -Parse [YAML Front Matter][yaml] and add the parsed data object to `view.data`: - -```js -var matter = require('parser-front-matter'); -app.onLoad(/\.hbs$/, function(view, next) { - matter.parse(view, next); -}); -``` - -### preLayout - -Immediately before the first [layout][] in a [layout-stack][] is applied to a view. - -```js -app.preLayout(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### onLayout - -Called after each [layout][] in a [layout-stack][] is applied. - -```js -app.onLayout(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### postLayout - -Called after all [layouts][] have been applied to a view. - -```js -app.postLayout(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### onMerge - -Called directly before [partials][] collections are merged onto the [context][]. - -```js -app.onMerge(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### preCompile - -Called before compiling a view. - -```js -app.preCompile(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### postCompile - -Called after compiling a view. - -```js -app.postCompile(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### preRender - -Called before rendering a view. - -```js -app.preRender(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - -### postRender - -Called after rendering a view. - -```js -app.postRender(/\.hbs$/, function(view, next) { - // do something with `view` - next(); -}); -``` - - -[yaml]: https://en.wikipedia.org/wiki/YAML -[verbs]: http://expressjs.com/api.html#router.METHOD - -{%= reflinks(['express']) %} - - - - - -(Some wording in this document was borrowed from [express's middleware docs][express]) - -[express]: http://expressjs.com/en/guide/using-middleware.html \ No newline at end of file diff --git a/docs/src/content/naming-conventions.md b/docs/src/content/naming-conventions.md deleted file mode 100644 index ea4c909b..00000000 --- a/docs/src/content/naming-conventions.md +++ /dev/null @@ -1,13 +0,0 @@ -# Naming conventions - - -- `name` -- `configfile`: `assemblefile`, `verfile`, `generator` -- `appname` -- `varname` -- `method` -- `ctor` - -- `singular` -- `plural` - diff --git a/docs/src/content/plugins.md b/docs/src/content/plugins.md deleted file mode 100644 index cb1ff3b0..00000000 --- a/docs/src/content/plugins.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -name: verb -title: Plugins -engine: hbs -description: "" -related: ['assemble-core'] -reflinks: [] ---- - -## Plugin types - -- **instance plugins**: Instance plugins are functions that are invoked by the `.use()` method and have access to `app`, `collection`, or `view`, depending on where and how the plugin is registered. -- **pipeline plugins**: Pipeline plugins are registered with `.pipe()` and are used on vinyl `file` objects in a stream (note that all verb "views" are instances of vinyl files) - -### Instance plugins - - - -**Example instance plugin** - -```js -var verb = require('verb'); -var app = verb(); - -app.use(function(app) { - // do stuff to app (verb instance, also exposed as `this) -}); -``` - -### Collection plugins - -Collections themselves are like mini-application instances, and collection plugins are registered and used the same way as instance plugins, with the `.use()` method, but on a specific collection. Collection plugins are called immediately upon instantiation of the collection. - -**Example collection plugin** - -```js -var verb = require('verb'); -var app = verb(); - -// register a plugin to be used on all collections -app.use(function(app) { - // do stuff to app or `this` (the verb instance) - - // return a function to be use used as a collection plugin - return function(collection) { - // do stuff to `collection` - }; -}); -``` - -The `app.create()` method (used for creating custom collections) returns the collection instance. So collection plugins can be chained from create as well. - -```js -app.use(function() { - return function(collection) { - // do stuff to (every) `collection` - } -}); - -app.create('pages') - .use(function(pages) { - // do stuff to `pages` collection - }); - -app.create('posts') - .use(function(posts) { - // do stuff to `posts` collection - }); -``` - -**Example view plugin** - -```js -var verb = require('verb'); -var app = verb(); - -// register a plugin to be used on all views, from all collections -app.use(function(app) { - return function(collection) { - - // return a function from a collection plugin to be used - // as a view plugin - return function(view) { - // do stuff to `view` - }; - }; -}); -``` - -**Register view plugins on specific collections** - -```js -app.use(function(app) { - // do stuff to `app` - return function(collection) { - // do stuff to (every) `collection` - return function(view) { - // do stuff to (every) `view` - }; - }; -}); - -app.create('pages') - .use(function(pages) { - return function(page) { - // do stuff to `page` - }; - }); - -app.create('posts') - .use(function(posts) { - return function(post) { - // do something to `post` - }; - }); -``` - -**Use cases for collection/view plugins** - -Here are just a few examples - -- `permalinks`: You might have a permalink plugin that modifies the `dest` path a particular way for blog `posts`, and a different way for `pages`. You could register the same plugin with both collections, just using different settings/options. Also, you could implement this functionality at the view level or collection level, depending on how granular your plugin needs to be. -- `pagination` -- `groups` and `lists` -- `sorting` - -_(this is for the `templates` docs, but it will help explain how plugins work in `base`. you obviously already know a lot about plugins, so this is for anyone who might find it useful)_ - -# Plugins - -> Overview of how `Templates` plugins work - -## App - -The following example shows a `plugin` that will be invoked by the `.use` method. Example: `app.use(plugin)`. When invoked, the plugin function is passed the application instance (`app`) as its only argument. - -![plugins-app](https://cloud.githubusercontent.com/assets/383994/13402852/311b9d88-dee0-11e5-944b-200ba56f42fe.png) - -The plugin stops there and will not be called again, **unless the plugin returns a function**. If a function is returned it will be pushed onto the `app.fns` array and then called on each collection that is created using the `app.create`, `app.collection`, or `app.list` methods. - -In the next example, a function is returned from the plugin so that it can be called again later. - -## Collection - -![plugins-collection](https://cloud.githubusercontent.com/assets/383994/13402856/34dd996c-dee0-11e5-8def-f0b739ff10ca.png) - -The plugin stops there and will not be called again, **unless the plugin returns a function**. If a function is returned it will be pushed onto the `collection.fns` array and then called on each view that is added to the collection. - -In the next example, we'll return a function inside the `collection` plugin, so that it can be called again later. - -## View - -![plugins-view](https://cloud.githubusercontent.com/assets/383994/13402861/37fc7032-dee0-11e5-8489-04f392cb9370.png) - -End of the line... - -# Short-circuit: "smart plugins" - -> Don't like all the function nesting? Want to register your plugin with `app` but only have it run on specific objects? No problem, just short-circuit the plugin! - -Every class has a boolean `.is*` property that is useful for checking the instance name. For example, the `Templates` class has an `isTemplates` property, view collections have `isViews`, and views have `isView`. (all "apps", like `Templates`, `Assemble`, `Generate`, etc. also have `isApp` as a convenience). - -To make your plugins "smarter" and eliminate the nesting, try the following: - -![plugins-short-circuit](https://cloud.githubusercontent.com/assets/383994/13405180/33cea2a4-deeb-11e5-864d-4fbab7510c22.png) - -## Generators - -![plugins-generators](https://cloud.githubusercontent.com/assets/383994/13413383/184bc5de-df18-11e5-815c-b968d5c676a4.png) diff --git a/docs/src/content/settings.md b/docs/src/content/settings.md deleted file mode 100644 index e6d49c5e..00000000 --- a/docs/src/content/settings.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Settings -related: ['./recipes/3-ways-to-disable-toc'] ---- - -There are multiple ways to define and manage configuration settings in {{name}}. - -- `cli`: command line arguments -- `options`: in-memory options cache -- `data`: in-memory data cache -- `store`: global config store -- `config`: local {{name}} config, defined on the `{{name}}` property in package.json. -- `locals`: template locals, typically passed to the `render` method -- `helpers`: options can be passed as arguments to helpers -- `answers`: answer store -- `view.data`: front-matter -- `view.options`: - -**Why aren't these merged onto one big object?** - -For a couple of reasons: - -1. These objects aren't all available at the same time. For example, command line options might be used to determine which templates to render. We can use front-matter from templates once they're loaded, but not before. -1. Separation of concerns. This allows users to decide order of preference for conflicting options defined in more than one place. - - -## Local config - -Configuration values may be stored on a project-by-project basis by adding a `verb` object to the project's package.json. - -**Example** - -```json -{ - "name": "my-project", - "verb": { - "layout": "default", - "plugins": ["gulp-format-md"] - } -} -``` - -## Global config store - -**API** - -Persist a global configuration value that may be used on any project: - -```js -{{name}}.store.set('foo', 'bar'); -``` - -Get a global configuration value: - -```js -{{name}}.store.get('foo'); -//=> 'bar' -``` - -**CLI** - -Set a global config value via command line: - -```sh -$ verb --set=foo:bar -``` - -Show the value in the command line: - -```sh -$ verb --get=foo -# 'bar' -``` - -Or get the value programmatically: - -```js -{{name}}.store.get('foo'); -//=> 'bar' -``` \ No newline at end of file diff --git a/docs/src/content/stores.md b/docs/src/content/stores.md deleted file mode 100644 index 6e5a0bfa..00000000 --- a/docs/src/content/stores.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Stores ---- - -Verb supports 4 "types" of stores for persisting config values, each is persisted to the `~/.data-store` directory, and all three have API methods for getting/setting data: - -## Store comparison - -The first 3 types are stored in user home: `~/.data-store`, the last type "local configs", is stored locally to a project. - -**Type** | **API** | **Description** ---- | --- | --- -Global defaults | `app.globals` | Generic global defaults, persisted to `~/.data-store/globals/defaults.json`. These defaults are shared by `verb`, `assemble`, `update` and `generate`. -Verb defaults | `app.store` | Verb-specific defaults, persisted to `~/.data-store/app/verb.json` -Project defaults | `app.locals` | Project-specific defaults, persisted to `~/.data-store/project/foo-bar.json`. -Local configs | N/A | Project-specific configuration settings in `verb.json` or the `verb` object in package.json. - -## How config works - -Specific data "wins" over general data. - -```sh -global - # "app" wins over "global" - L app - # "project" wins over "app" - L project - # "local" wins over "project" - L local (verb.json) - L local (package.json) - # Anything explicitly passed on the API should win - L API -``` - -### Peristed config - -Config stores are persisted according the following conventions: - -- **globals**: `~/.data-store/globals/defaults.json` -- **app**: `~/.data-store/app/{app-name}.json` -- **project**: `~/.data-store/app/{app-name}/projects/{project-name}.json` - - -**Examples** - -```sh -# global defaults -~/.data-store/globals/defaults.json - -# app-specific defaults (app => verb) -~/.data-store/app/verb.json - -# verb project configs -~/.data-store/app/verb/projects/foo.json -~/.data-store/app/verb/projects/bar.json -~/.data-store/app/verb/projects/baz.json -``` - - -## Global defaults - -Global defaults are shared across all "apps" and are persisted to: - -```sh -~/.data-store/globals/defaults.json -``` - -**Best for** - -Generic data that can be used in any project, such as "author" `data` to be passed on the context when rendering docs or project readmes: - -```json -{ - "data": { - "username": "jonschlinkert", - "twitter": "jonschlinkert", - "author": { - "name": "Jon Schlinkert", - "url": "https://github.com/jonschlinkert" - } - } -} -``` - -## Verb defaults - -App-specific defaults are persisted to: - -```sh -~/.data-store/app/verb.json -``` - -**Best for** - -Verb-specific configuration settings. For example, you might have certain preferences that should always be used (unless overridden), like: - -```json -{ - "toc": true, - "layout": "default" -} -``` - -## Project defaults - -Project stores are specific to the "app" being run. In this case, `verb` is the app, so project-specific configuration settings are persisted as follows: - -```sh -# app-specific defaults (app => verb) -~/.data-store/app/verb.json - -# verb project configs -~/.data-store/app/verb/projects/foo.json -~/.data-store/app/verb/projects/bar.json -~/.data-store/app/verb/projects/baz.json -``` - -## Local configs - -### verb.json - -Project defaults may be stored locally in `{app-name}.json`. In this case, `app-name` is verb, but the same applies to [assembe][], [generate][], and [update][]. -For example, the following will enable the Table of Contents for a project (if you're using [verb-readme-generator][]) and will add the `fooo` property to the context at render-time: - -```json -{ - "options": { - "toc": true - }, - "data": { - "fooo": "baaar" - } -} -``` - -### package.json - -The exact same configuration and support as `verb.json`, but the object is stored on the `verb` object in `package.json`: - -```json -{ - "name": "my-project", - "description": "It's a really nice project. seriously, it is. it's nice. reaaaly nice.", - "verb": { - "options": { - "toc": true - }, - "data": { - "fooo": "baaar" - } - } -} -``` diff --git a/docs/src/content/tasks.md b/docs/src/content/tasks.md deleted file mode 100644 index 6ec576e5..00000000 --- a/docs/src/content/tasks.md +++ /dev/null @@ -1 +0,0 @@ -# Tasks \ No newline at end of file diff --git a/docs/src/content/templates.md b/docs/src/content/templates.md deleted file mode 100644 index 686dc811..00000000 --- a/docs/src/content/templates.md +++ /dev/null @@ -1,2 +0,0 @@ -# Templates - diff --git a/docs/src/content/terminology.md b/docs/src/content/terminology.md deleted file mode 100644 index 08e48d39..00000000 --- a/docs/src/content/terminology.md +++ /dev/null @@ -1,14 +0,0 @@ -# Terminology - -This document provides summary descriptions of common terms used in {{name}}. Further information may be found by following the links on some of the terms. - - -## Data - -- data -- `globals` -- `locals`: The data object passed to a rendering engine is commonly referred to as `locals` (as opposed to `globals`) -- [context](/context) -- front-matter: - - diff --git a/docs/src/content/verb.config.md b/docs/src/content/verb.config.md deleted file mode 100644 index 94647394..00000000 --- a/docs/src/content/verb.config.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: <%= upper(name) %> Configuration ---- - -### package.json - -> setting options in package.json - -If your package.json file has a `{{name}}` property with an `options` object, {{upper name}} will use it to extend the `{{name}}.options` object. - -**Example** - -```json -{ - "name": "my-project", - "description": "It's awesome, seriously.", - - "{{name}}": { - "options": {} - } -} -``` diff --git a/docs/src/content/verb.options.md b/docs/src/content/verb.options.md deleted file mode 100644 index 1363dd41..00000000 --- a/docs/src/content/verb.options.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: Verb Options -descriptions: Currently supported options that may defined on the `verb.options` object. ---- - -To become familiarized with how to get and set options with verb, see the [Options API][options-api] documentation. - -### options.toc - -Generate a markdown table of contents using [markdown-toc][]. - -Type: `boolean` | `object` - -Default: `undefined` - -**Object** - -When defined as an object, the following sub-options may be defined: - -* `render` - - If `true`, a toc string will be added to the `view.data.toc` property on any views that have a `` tag defined. - - If `false`, all `` tags will simply be stripped, and no TOC will be created. This allows the tag to be added to a document, and rendering and insertion to be controlled programmatically. -* `insert` - - If `true`, the Table of Contents value stored in `view.data.toc` will be inserted into the document by a built-in `postLayout` middleware. - - If `false`, the middleware will simply ignore the `view.data.toc` value and continue. - -_(Note that since `view.data.toc` is a value on the context, TOCs may also/alternatively be inserted via helper or template variable)_. - -**Boolean** - -When `options.toc` is either true or false, the option is converted to an object before being passed to [template-toc][], and both the `render` and `insert` options will be defined using the given value. - -For example, `{toc: false}` normalizes to `{toc: {insert: false, render: false}}`. - -**Examples** - -As a boolean: - -```js -var verb = require('verb'); -var app = verb(); - -app.option('toc', true); -// or "app.enable('toc');" -``` - -As an object: - -```js -app.option({toc: {insert: false, render: true}}); -``` - -**view.options** - -Note that, as with most options, `toc` options may also be defined on `view.options`: - -```js -var app = verb(); - -app.create('pages'); -app.pages.addView('foo', {content: 'bar', options: {toc: false}}); -``` - -[options-api]: ./options-api.md -[markdown-toc]: https://github.com/jonschlinkert/markdown-toc \ No newline at end of file diff --git a/docs/src/flags.md b/docs/src/flags.md deleted file mode 100644 index c818bfb9..00000000 --- a/docs/src/flags.md +++ /dev/null @@ -1,30 +0,0 @@ -# Command line flags - -- `--emit` -- `--layout` -- `--set` -- `--toc` -- `--options` -- `--data` -- `--create` -- `--plugins` -- `--helpers` -- `--tasks` -- `--related` -- `--reflinks` - -## File properties - -- `--base` -- `--basename` -- `--cwd` -- `--dir` -- `--dirname` -- `--ext` -- `--extname` -- `--f` -- `--file` -- `--filename` -- `--path` -- `--root` -- `--stem` diff --git a/docs/src/templates/contributing.md b/docs/src/templates/contributing.md deleted file mode 100644 index d0a1b15d..00000000 --- a/docs/src/templates/contributing.md +++ /dev/null @@ -1,44 +0,0 @@ -# Contributing to {{name}} - -First and foremost, thank you! We appreciate that you want to contribute to {{name}}, your time is valuable, and your contributions mean a lot to us. - -**What does "contributing" mean?** - -Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following: - -- Updating or correcting documentation -- Feature requests -- Bug reports - - -## Issues - -**Before creating an issue** - -Please make sure you're creating one in the right place: - -- do you have a template syntax question? Like how to accomplish something with handlebars? The best place to get answers for this is [stackoverflow.com][], the [handlebars docs](handlebarsjs.com), or the documentation for the template engine you're using. -- Are you having an issue with an {{name}} feature that is powered by an underlying lib? This is sometimes difficult to know, but sometimes it can be pretty easy to find out. For example, if you use a glob pattern somewhere and you found what you believe to be a matching bug, that would probably be an issue for [node-glob][] or [micromatch][] - -**Creating an issue** - -Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue: - -- what version of assemble are you using? -- is the issue helper-related? If so, this issue should probably be opened on the repo related to the helper being used. -- do you have any custom helpers defined? Is the issue related to the helper itself, data (context) being passed to the helper, or actually registering the helper in the first place? -- are you using middleware? -- any plugins? - - -## Above and beyond - -Here are some tips for creating idiomatic issues. Taking just a little bit extra time will make your issue easier to read, easier to resolve, more likely to be found by others who have the same or similar issue in the future. - -- Take some time to learn basic markdown. This [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) is super helpful, as is the GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/). -- Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/). -- use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others -- use syntax highlighting by adding the correct language name after the first "code fence" - -[node-glob]: https://github.com/isaacs/node-glob -[micromatch]: https://github.com/jonschlinkert/micromatch \ No newline at end of file From fe57ad4e218d25ade15c02c06b2f51d507a48655 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 02:36:49 -0400 Subject: [PATCH 241/282] update default templates to ensure no layout is used --- lib/templates/doc.md | 8 -------- lib/templates/post.md | 9 --------- lib/templates/readme.md | 5 +++++ lib/templates/verbfile.js | 3 +++ 4 files changed, 8 insertions(+), 17 deletions(-) delete mode 100644 lib/templates/doc.md delete mode 100644 lib/templates/post.md diff --git a/lib/templates/doc.md b/lib/templates/doc.md deleted file mode 100644 index 03d04c45..00000000 --- a/lib/templates/doc.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: <%= ask("doc.title") %> -layout: nil -dest: ":docs/:filename" -tags: [] ---- - -This is {%= title %} \ No newline at end of file diff --git a/lib/templates/post.md b/lib/templates/post.md deleted file mode 100644 index bbb6e94a..00000000 --- a/lib/templates/post.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: <%= ask("title") %> -date: <%= date() %> -layout: default -dest: ":posts/:filename" -tags: [] ---- - -This is {%= title %} \ No newline at end of file diff --git a/lib/templates/readme.md b/lib/templates/readme.md index a8ea3630..70b5a431 100644 --- a/lib/templates/readme.md +++ b/lib/templates/readme.md @@ -2,3 +2,8 @@ <%= ask("description", "Project description") %> +## Usage + +```js +var {%= varname %} = require('<%= name %>'); +``` diff --git a/lib/templates/verbfile.js b/lib/templates/verbfile.js index 51504378..2dad7982 100644 --- a/lib/templates/verbfile.js +++ b/lib/templates/verbfile.js @@ -1,3 +1,6 @@ +--- +layout: false +--- 'use strict'; module.exports = function(verb) { From 185e23cebbe4e69786c0651d8a01c19613882c5d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 02:36:57 -0400 Subject: [PATCH 242/282] use `.generate` --- lib/commands/tasks.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js index 51507ca0..bf20de83 100644 --- a/lib/commands/tasks.js +++ b/lib/commands/tasks.js @@ -37,7 +37,7 @@ module.exports = function(app, options) { var argv = app.base.get('cache.argv'); var tasks = setTasks(app, options.env.configFile, val, argv); app.base.set('cache.argv.tasks', tasks); - app.generateEach(tasks, next); + app.generate(tasks, next); }; }; @@ -47,29 +47,31 @@ module.exports = function(app, options) { */ function setTasks(app, configFile, tasks, argv) { - if (argv.hasOwnProperty('new')) { - return ['defaults.new:' + argv.new]; - } + var configExists, configTasks; if (argv.init === true) { return []; } tasks = tasks.map(function(task) { + if (task === 'new') { + return 'defaults.new:default'; + } if (task.indexOf('new') === 0) { return 'defaults.' + task; } if (task === 'format') { return 'defaults:format'; } + if (task === 'diff') { + return 'defaults:diff'; + } return task; }); - var configTasks = app.pkg.get('verb.tasks'); - if (tasks.length === 1 && tasks[0] === 'default') { - var configExists = utils.exists(configFile); - + configExists = utils.exists(configFile); + configTasks = app.pkg.get('verb.tasks'); // if a `verbfile.js` or custom configFile exists, return tasks if (configExists) { if (configTasks && configTasks.length) { @@ -83,7 +85,6 @@ function setTasks(app, configFile, tasks, argv) { } var verbmd = utils.exists('.verb.md'); - // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default if (verbmd && !configExists) { return ['verb-readme-generator']; From b32e2554c441cce5c8376e576ed2f07bf2373afd Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 03:09:53 -0400 Subject: [PATCH 243/282] move `contributing.md` to `.github` folder --- contributing.md => .github/contributing.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contributing.md => .github/contributing.md (100%) diff --git a/contributing.md b/.github/contributing.md similarity index 100% rename from contributing.md rename to .github/contributing.md From 98e0b104ffa7cfbc03f531b8933183ecb8f3d98f Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 03:10:29 -0400 Subject: [PATCH 244/282] update lookup patterns, init events --- bin/verb.js | 52 ++++++++++++++++++++++++++++++---------------------- index.js | 32 ++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 1e6cac2d..7450ca7e 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -8,6 +8,22 @@ var utils = require('../lib/utils'); var argv = utils.yargs(process.argv.slice(2)); var Verb = require('..'); +/** + * Listen for errors on all instances + */ + +Verb.on('generate.preInit', function(app) { + app.on('error', function(err) { + console.log(err.stack); + process.exit(1); + }); +}); + +Verb.on('verb.finished', function(app) { + app.emit('done'); + process.exit(); +}); + /** * Initialize verb CLI */ @@ -24,30 +40,22 @@ utils.runner(Verb, {name: 'verb'}, argv, function(err, app, ctx) { }); commands(app, ctx); - app.register('defaults', require('../lib/generator')); - app.option('lookup', lookup(app)); + var config = app.base.get('cache.config') || {}; + + if (!app.generators.defaults) { + app.register('defaults', require('../lib/generator')); + } - app.cli.process(ctx.argv, function(err) { + app.config.process(config, function(err, config) { if (err) app.emit('error', err); - app.emit('done'); - process.exit(); + + app.base.del('cache.config'); + app.base.set('cache.config', config); + + app.cli.process(ctx.argv, function(err) { + if (err) app.emit('error', err); + Verb.emit('verb.finished', app); + }); }); }); -/** - * Custom lookup function for resolving generators - */ - -function lookup(app) { - return function(key) { - var patterns = [key]; - if (!/^verb-([^-]+)-generator/.test(key)) { - patterns.unshift(`verb-${key}-generator`); - } - - if (app.enabled('generators')) { - patterns.push(`generate-${key}`); - } - return patterns; - } -} diff --git a/index.js b/index.js index d5c95dee..2a2e07a8 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ 'use strict'; var Generate = require('generate'); +var utils = require('./lib/utils'); /** * Create a verb application with `options`. @@ -34,22 +35,45 @@ function Verb(options) { */ Generate.extend(Verb); +Generate.on('generate.init', function(app) { + Verb.emit('generate.init', app); +}); + +/** + * Expose custom lookup function for resolving generators + */ + +Verb.lookup = function(app) { + return function(key) { + var patterns = [key]; + if (!/^verb-([^-]+)-generator/.test(key)) { + patterns.unshift(`verb-${key}-generator`); + } + + if (app.enabled('generators')) { + patterns.push(`generate-${key}`); + } + return patterns; + }; +}; /** * Initialize verb data */ Verb.prototype.initVerb = function(opts) { - Verb.emit('preInit', this, this.base); + this.debug('initializing', __filename); + + Verb.emit('verb.preInit', this, this.base); this.data({before: {}, after: {}}); - this.debug('initializing', __filename); this.option('toAlias', function(name) { - return name.replace(/^verb-([^-]+)-generator$/, '$1'); + return name.replace(/^(?:verb-([^-]+)-generator$)|(?:generate-)/, '$1'); }); + this.option('lookup', Verb.lookup(this)); this.data({runner: require('./package')}); - Verb.emit('init', this, this.base); + Verb.emit('verb.postInit', this, this.base); }; /** From bcb76488caa21df52d99983e77d57c8f0667f7bc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 03:11:12 -0400 Subject: [PATCH 245/282] suggest default layout based on lib --- lib/questions.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/questions.js b/lib/questions.js index 34513f4c..9dfbfb50 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -8,7 +8,7 @@ module.exports = function(app, options) { app.questions .set('config.layout', 'What layout would you like to use?', { - default: 'default' + default: defaultLayout(app) }) .set('config.toc', 'Add Table of Contents to README.md?', { type: 'confirm', @@ -68,6 +68,25 @@ module.exports = function(app, options) { }); }; +function defaultLayout(app) { + var layout = app.pkg.get([app._name, 'layout']); + if (typeof layout === 'string' && layout.trim() !== 'default') { + return layout; + } + + var name = app.pkg.get('name'); + if (/^generate-/.test(name)) { + return 'generator'; + } + if (/^update-/.test(name)) { + return 'updater'; + } + if (/^helper-/.test(name)) { + return 'helper'; + } + return 'default'; +} + /** * Build the list of `config.*` options to prompt the user about */ From 58c31ab64ff54038d7743f7a7f892d900f5b6a01 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 11 Jun 2016 03:11:35 -0400 Subject: [PATCH 246/282] adds render task, diffs --- lib/diff.js | 53 +++++++++++++++++++++++++++++++++++ lib/generator.js | 72 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 lib/diff.js diff --git a/lib/diff.js b/lib/diff.js new file mode 100644 index 00000000..019bdfa3 --- /dev/null +++ b/lib/diff.js @@ -0,0 +1,53 @@ +'use strict'; + +var differ = require('diff'); +var through = require('through2'); +var colors = require('ansi-colors'); + +/** + * Output to the console a visual representation of the difference between + * two objects or strings. + * + * @param {Object|String} `a` + * @param {Object|String} `b` + * @api public + */ + +module.exports = function(options) { + options = options || {}; + var cache = {}; + var prev; + + return function(a, b) { + return through.obj(function(file, enc, next) { + if (options.diff === false) { + next(null, file); + return; + } + var contents = file.contents.toString(); + cache[a] = contents; + var str = b ? (cache[b] || b) : prev; + + if (typeof str !== 'undefined') { + diff(contents, cache[b]); + next(); + return; + } + prev = contents; + next(null, file); + }) + }; +}; + +function diff(a, b, method) { + differ[method || 'diffWords'](a, b).forEach(function(stat) { + process.stderr.write(colors[color(stat)](stat.value)); + }); + console.error(); +} + +function color(stat) { + if (stat.removed) return 'red'; + if (stat.added) return 'green'; + return 'gray'; +} diff --git a/lib/generator.js b/lib/generator.js index a87da027..a62c5196 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,13 +1,21 @@ +'use strict'; - -var stack = require('callsite'); var path = require('path'); +var opts = {alias: {diff: 'diffOnly'}, boolean: ['diff']} +var argv = require('yargs-parser')(process.argv.slice(2), opts); var cwd = path.resolve.bind(path, __dirname, 'templates'); +var common = require('common-middleware'); var reflinks = require('./reflinks'); var utils = require('./utils'); +var diff = require('./diff')(argv); + +/** + * Built-in verb tasks + */ module.exports = function(verb, base) { var argv = base.get('cache.argv'); + verb.use(common()); verb.on('view', function(view) { if (view.basename === 'doc.md') { @@ -54,15 +62,42 @@ module.exports = function(verb, base) { verb.src(src) .pipe(reflinks(verb)) + .pipe(diff('before')) .pipe(utils.format()) + .pipe(diff('after', 'before')) .pipe(verb.dest(function(file) { - file.basename = (argv.name || path.basename(src)); - return (dest || path.resolve(path.dirname(src))); + if (argv.name) file.basename = argv.name; + return dest || file.dirname; })) + .on('data', function(file) { + console.log('formatted "%s"', file.relative); + }) .on('error', cb) .on('end', cb); }); + /** + * Render a single `--src` file to the given `--dest` or current working directory. + * + * ```sh + * $ verb defaults:render + * # aliased as + * $ verb render + * ``` + * @name render + * @api public + */ + + verb.task('render', function(cb) { + if (!verb.option('src')) { + verb.emit('error', new Error('Expected a `--src` filepath')); + } else if (!verb.option('dest')) { + verb.build(['dest', 'render'], cb); + } else { + file(verb, verb.option('src'), verb.cwd, cb); + } + }); + /** * Sub-generator with a handful of tasks for generating a file from * a template. @@ -76,17 +111,18 @@ module.exports = function(verb, base) { verb.register('new', function(app) { app.option(verb.options); + app.task('default', ['verbfile']); app.task('verbfile', function(cb) { - file(app, 'verbfile.js', cb); + file(app, 'verbfile.js', null, cb); }); app.task('verbmd', function(cb) { - file(app, '.verb.md', cb); + file(app, '.verb.md', null, cb); }); app.task('readme', function(cb) { - file(app, 'README.md', cb); + file(app, 'README.md', null, cb); }); app.task('prompt-verbmd', function(cb) { @@ -97,7 +133,7 @@ module.exports = function(verb, base) { return; } if (answers.verbmd) { - file(app, '.verb.md', cb); + file(app, '.verb.md', null, cb); return; } cb(); @@ -105,7 +141,7 @@ module.exports = function(verb, base) { }); app.task('doc', function(cb) { - file(verb, 'doc.md', cb); + file(verb, 'doc.md', null, cb); }); }); @@ -140,17 +176,17 @@ module.exports = function(verb, base) { * Generate a file */ -function file(verb, name, cb) { - var dest = verb.option('dest') || verb.cwd; +function file(verb, name, srcBase, cb) { + var dest = path.resolve(verb.option('dest') || verb.cwd); verb.engine('*', require('engine-base')); - verb.src(name, {cwd: cwd()}) + verb.src(name, {cwd: srcBase || cwd()}) .pipe(verb.renderFile('*')) + .pipe(utils.format()) .pipe(verb.conflicts(dest)) - .pipe(verb.dest(dest)) - .on('error', cb) - .on('end', function() { - console.log(name, 'written to', dest); - cb(); - }); + .pipe(verb.dest(function(file) { + console.log('writing', file.relative, 'to', path.relative(verb.cwd, dest)); + return dest; + })) + .on('end', cb); } From 363690c1d1f68942c9b264bc7939e5c6e2c09c62 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 28 Jun 2016 14:46:58 -0400 Subject: [PATCH 247/282] adds macro support, better task handling --- bin/verb.js | 92 +++++++++++---- lib/commands.js | 2 - lib/commands/tasks.js | 99 ---------------- lib/diff.js | 4 +- lib/format.js | 4 +- lib/generator.js | 260 ++++++++++++++++++++++++++++++++++-------- lib/reflinks.js | 18 ++- lib/tasks.js | 74 ++++++++++++ lib/utils.js | 66 +++++++++++ 9 files changed, 440 insertions(+), 179 deletions(-) delete mode 100644 lib/commands/tasks.js create mode 100644 lib/tasks.js diff --git a/bin/verb.js b/bin/verb.js index 7450ca7e..daeb10cc 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,12 +1,20 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; +require('set-blocking')(true); -var generator = require('../lib/generator'); +var util = require('util'); +var path = require('path'); +var Verb = require('..'); var commands = require('../lib/commands'); +var tasks = require('../lib/tasks'); var utils = require('../lib/utils'); -var argv = utils.yargs(process.argv.slice(2)); -var Verb = require('..'); +var args = process.argv.slice(2); +var argv = require('yargs-parser')(args); +if (argv.v) { + console.log('generate v' + Verb.pkg.version); + process.exit(); +} /** * Listen for errors on all instances @@ -19,43 +27,81 @@ Verb.on('generate.preInit', function(app) { }); }); -Verb.on('verb.finished', function(app) { - app.emit('done'); - process.exit(); +/** + * Initialize CLI + */ + +Verb.on('generate.postInit', function(app) { + if (app.macros.has(args)) { + app.macros.set(args); + var macro = {}; + macro[args[0]] = args.slice(2).join(' '); + console.log('saved macro:', util.inspect(macro)); + process.exit(); + } + + var idx = utils.firstIndex(args, ['-D', '--default']); + if (idx !== -1) { + var del = args.indexOf('--del') !== -1; + if (del) { + app.base.store.del('defaultTask'); + } else { + args.splice(idx, 1); + app.base.store.set('defaultTask', args); + } + } }); /** - * Initialize verb CLI + * Initialize Runner */ -utils.runner(Verb, {name: 'verb'}, argv, function(err, app, ctx) { - if (err) { - console.log(err.stack); - process.exit(1); - } +var options = {name: 'verb'}; - app.on('error', function(err) { - console.log(err.stack); - process.exit(1); - }); +utils.runner(Verb, options, argv, function(err, app, runnerContext) { + if (err) handleErr(app, err); - commands(app, ctx); - var config = app.base.get('cache.config') || {}; + app.set('cache.runnerContext', runnerContext); + commands(app, runnerContext); if (!app.generators.defaults) { app.register('defaults', require('../lib/generator')); } + var ctx = utils.extend({}, runnerContext); + var config = app.get('cache.config') || {}; + ctx.argv.tasks = []; + + app.config.process(config, function(err, config) { - if (err) app.emit('error', err); + if (err) return handleErr(app, err); - app.base.del('cache.config'); - app.base.set('cache.config', config); + app.base.cache.config = config; app.cli.process(ctx.argv, function(err) { - if (err) app.emit('error', err); - Verb.emit('verb.finished', app); + if (err) return handleErr(app, err); + + var arr = tasks(app, ctx, argv); + app.log.success('running tasks:', arr); + app.generate(arr, function(err) { + if (err) return handleErr(app, err); + + app.emit('done'); + process.exit(); + }); }); }); }); +/** + * Handle errors + */ + +function handleErr(app, err) { + if (app && app.base.hasListeners('error')) { + app.base.emit('error', err); + } else { + console.log(err.stack); + process.exit(1); + } +} diff --git a/lib/commands.js b/lib/commands.js index addc7972..d53bfef4 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -3,10 +3,8 @@ var commands = require('./commands/'); module.exports = function(app, options) { - app.debug('adding custom verb commands'); for (var key in commands) { if (commands.hasOwnProperty(key)) { - app.debug('adding command > %s', key); app.cli.map(key, commands[key](app, options)); } } diff --git a/lib/commands/tasks.js b/lib/commands/tasks.js deleted file mode 100644 index bf20de83..00000000 --- a/lib/commands/tasks.js +++ /dev/null @@ -1,99 +0,0 @@ -'use strict'; - -var utils = require('../utils'); - -/** - * Run the given generators and tasks. This flag is unnecessary when - * used with [base-runner][]. - * - * ```sh - * # run task 'foo' - * $ app --tasks foo - * # => {task: ['foo']} - * # run generator 'foo', task 'bar' - * $ app --tasks foo:bar - * # => {task: ['foo:bar']} - * ``` - * @name tasks - * @api public - * @cli public - */ - -module.exports = function(app, options) { - var ran = false; - - return function(val, key, config, next) { - if (config.run === false) { - next(); - return; - } - - if (ran) { - next(); - return; - } - - ran = true; - var argv = app.base.get('cache.argv'); - var tasks = setTasks(app, options.env.configFile, val, argv); - app.base.set('cache.argv.tasks', tasks); - app.generate(tasks, next); - }; -}; - -/** - * Determine the task to run. This is only necessary because we're doing - * some re-routing with verb's small handful of built-in tasks. - */ - -function setTasks(app, configFile, tasks, argv) { - var configExists, configTasks; - - if (argv.init === true) { - return []; - } - - tasks = tasks.map(function(task) { - if (task === 'new') { - return 'defaults.new:default'; - } - if (task.indexOf('new') === 0) { - return 'defaults.' + task; - } - if (task === 'format') { - return 'defaults:format'; - } - if (task === 'diff') { - return 'defaults:diff'; - } - return task; - }); - - if (tasks.length === 1 && tasks[0] === 'default') { - configExists = utils.exists(configFile); - configTasks = app.pkg.get('verb.tasks'); - // if a `verbfile.js` or custom configFile exists, return tasks - if (configExists) { - if (configTasks && configTasks.length) { - app.pkg.logWarning('ignoring tasks defined in package.json:', configTasks); - } - return tasks; - } - - if (configTasks && configTasks.length) { - return configTasks; - } - - var verbmd = utils.exists('.verb.md'); - // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default - if (verbmd && !configExists) { - return ['verb-readme-generator']; - } - - // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` - if (!verbmd) { - return ['defaults.new:prompt-verbmd']; - } - } - return tasks; -} diff --git a/lib/diff.js b/lib/diff.js index 019bdfa3..07506c6e 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -2,7 +2,7 @@ var differ = require('diff'); var through = require('through2'); -var colors = require('ansi-colors'); +var utils = require('./utils'); /** * Output to the console a visual representation of the difference between @@ -41,7 +41,7 @@ module.exports = function(options) { function diff(a, b, method) { differ[method || 'diffWords'](a, b).forEach(function(stat) { - process.stderr.write(colors[color(stat)](stat.value)); + process.stderr.write(utils.log[color(stat)](stat.value)); }); console.error(); } diff --git a/lib/format.js b/lib/format.js index 747a11a4..18d9b0d2 100644 --- a/lib/format.js +++ b/lib/format.js @@ -1,8 +1,8 @@ 'use strict'; var path = require('path'); -var reflinks = require('../reflinks'); -var utils = require('../utils'); +var reflinks = require('./reflinks'); +var utils = require('./utils'); /** * Format a markdown file using [pretty-remarkable][]. Optionally diff --git a/lib/generator.js b/lib/generator.js index a62c5196..5d91cf97 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,10 +1,13 @@ 'use strict'; var path = require('path'); -var opts = {alias: {diff: 'diffOnly'}, boolean: ['diff']} +var opts = {alias: {diff: 'diffOnly'}, boolean: ['diff']}; var argv = require('yargs-parser')(process.argv.slice(2), opts); var cwd = path.resolve.bind(path, __dirname, 'templates'); -var common = require('common-middleware'); +var middleware = require('common-middleware'); +var extend = require('extend-shallow'); +var DataStore = require('data-store'); +var log = require('log-utils'); var reflinks = require('./reflinks'); var utils = require('./utils'); var diff = require('./diff')(argv); @@ -14,35 +17,16 @@ var diff = require('./diff')(argv); */ module.exports = function(verb, base) { - var argv = base.get('cache.argv'); - verb.use(common()); - - verb.on('view', function(view) { - if (view.basename === 'doc.md') { - view.options.frontMatter = false; - view.layout = false; - } - }); + var common = new DataStore('common-config'); + verb.use(middleware()); /** - * Prompt the user for the `dest` directory to use for generated files. - * This is called by the [new]() task. - * - * ```sh - * $ verb defaults:dest - * ``` - * @name dest - * @api public + * Listen for errors */ - verb.task('dest', function(cb) { - verb.question('dest', 'Destination directory?', {default: verb.cwd}); - if (verb.option('dest')) return cb(); - verb.ask('dest', {save: false}, function(err, answers) { - if (err) return cb(err); - verb.option('dest', path.resolve(verb.cwd, answers.dest)); - cb(); - }); + verb.on('error', function(err) { + console.error(err); + process.exit(1); }); /** @@ -94,55 +78,236 @@ module.exports = function(verb, base) { } else if (!verb.option('dest')) { verb.build(['dest', 'render'], cb); } else { - file(verb, verb.option('src'), verb.cwd, cb); + file(verb, verb.option('src'), { dest: verb.cwd }, cb); } }); /** - * Sub-generator with a handful of tasks for generating a file from - * a template. + * The `new` sub-generator has a handful of tasks for quickly generating a file from + * a template. Tasks on the sub-generator are called with `verb new:foo`, where `foo` + * is the name of the task to run. * - * ```sh - * $ verb new - * ``` * @name new * @api public */ verb.register('new', function(app) { app.option(verb.options); + + /** + * On all generators, the `default` task is executed when no other task name + * is given. Thus, on the `new` sub-generator, the `new:default` task is an alias that allows + * you to execute the `new:verbfile` task with the following command: + * + * ```sh + * $ verb new + * # or, if you prefer verbose commands + * $ verb new:default + * ``` + * @name new:default + * @api public + */ + app.task('default', ['verbfile']); + /** + * Generate a `verbfile.js` in the current working directory. + * + * ```sh + * $ verb new:verbfile + * ``` + * @name new:verbfile + * @api public + */ + app.task('verbfile', function(cb) { file(app, 'verbfile.js', null, cb); }); + /** + * Generate a `.verb.md` file in the current working directory. + * + * ```sh + * $ verb new:verbmd + * ``` + * @name new:verbmd + * @api public + */ + app.task('verbmd', function(cb) { - file(app, '.verb.md', null, cb); + file(app, '_verb.md', null, cb); + }); + + /** + * Generate a `.verbrc.json` file in the current working directory. + * + * ```sh + * $ verb new:verbmd + * ``` + * @name new:verbmd + * @api public + */ + + app.task('rc', function(cb) { + file(app, '_verbrc.json', null, cb); }); + /** + * Generate a `README.md` in the current working directory (the task will prompt + * for project `name` and `description`). + * + * ```sh + * $ verb new:readme + * ``` + * @name new:readme + * @api public + */ + app.task('readme', function(cb) { file(app, 'README.md', null, cb); }); - app.task('prompt-verbmd', function(cb) { + /** + * Prompts the user to add a `.verb.md` (this task runs automatically when the + * `verb` command is given if `verbfile.js` and `.verb.md` are both missing from the + * current working directory): + * + * ```sh + * $ verb new:prompt-verbmd + * ``` + * @name new:prompt-verbmd + * @api public + */ + + app.task('prompt-verbmd', {silent: true}, function(cb) { app.confirm('verbmd', 'Looks like .verb.md is missing, want to add one?'); - app.ask('verbmd', function(err, answers) { + app.ask('verbmd', {save: false}, function(err, answers) { if (err) { cb(err); return; } + if (answers.verbmd) { - file(app, '.verb.md', null, cb); + app.build('verbmd', cb); + } else { + cb(); + } + }); + }); + }); + + /** + * Display a help menu of available commands and flags. + * + * ```sh + * $ verb help + * ``` + * @name help + * @api public + */ + + verb.task('init', { silent: true }, function(cb) { + verb.question('init', 'Would you like to use defaults, or choose settings?', { + type: 'list', + choices: ['defaults', 'choose'], + all: false + }); + + verb.ask('init', {save: false}, function(err, answers) { + if (err) { + cb(err); + return; + } + + switch (answers.init) { + case 'defaults': + console.log(verb.globals.data); + break; + case 'choose': + default: { + console.log('foo'); + break; + } + } + + cb(); + }); + }); + + /** + * Save personal defaults in user home. + */ + + verb.register('store', function(gen) { + gen.enable('silent'); + + gen.task('del', function(cb) { + var keys = ['name', 'username', 'twitter', 'email']; + keys.forEach(function(key) { + console.log(log.red(' Deleted:'), key, common.get(key)); + common.del(keys); + }); + cb(); + }); + + gen.task('show', function(cb) { + var keys = ['name', 'username', 'twitter', 'email']; + console.log(); + keys.forEach(function(key) { + console.log(key + ': ' + log.cyan(common.get(key))); + }); + console.log(); + cb(); + }); + + gen.task('me', function(cb) { + console.log(); + console.log(' Answers to the following questions will be stored in:', log.bold('~/.common-config.json')); + console.log(' The stored values will be used later in (your) templates.'); + console.log(` To skip a question, just hit ${log.bold('')}`); + console.log(); + + gen.question('common.name', 'What is your name?'); + gen.question('common.username', 'GitHub username?'); + gen.question('common.url', 'GitHub URL?'); + gen.question('common.twitter', 'Twitter username?'); + gen.question('common.email', 'Email address?'); + + gen.ask('common', {save: false}, function(err, answers) { + if (err) return cb(err); + + if (!answers.common) { + cb(); return; } + + var vals = []; + for (var key in answers.common) { + if (answers.common.hasOwnProperty(key)) { + var val = answers.common[key]; + common.set(key, val); + vals.push(log.green(key + ': ' + val)); + } + } + + console.log(); + console.log(' Saved:'); + console.log(); + console.log(' ', vals.join('\n ')); + console.log(); + console.log(' To delete these values, run:'); + console.log(); + console.log(log.bold(' $ gen store:del')); + console.log(); + console.log(' To update these values, run:'); + console.log(); + console.log(log.bold(' $ gen store:me')); + console.log(); cb(); }); }); - app.task('doc', function(cb) { - file(verb, 'doc.md', null, cb); - }); + gen.task('default', ['me']); }); /** @@ -169,23 +334,28 @@ module.exports = function(verb, base) { * @api public */ - verb.task('default', argv.tasks); + verb.task('default', ['help']); }; /** * Generate a file */ -function file(verb, name, srcBase, cb) { - var dest = path.resolve(verb.option('dest') || verb.cwd); +function file(verb, src, options, cb) { + var defaults = { cwd: cwd(), dest: verb.cwd }; + var opts = extend({}, defaults, options); + var dest = path.resolve(opts.dest); verb.engine('*', require('engine-base')); - verb.src(name, {cwd: srcBase || cwd()}) + verb.src(src, {cwd: opts.cwd, layout: null}) .pipe(verb.renderFile('*')) .pipe(utils.format()) .pipe(verb.conflicts(dest)) .pipe(verb.dest(function(file) { - console.log('writing', file.relative, 'to', path.relative(verb.cwd, dest)); + if (opts.name) file.basename = opts.name; + file.basename = file.basename.replace(/^_/, '.'); + file.basename = file.basename.replace(/^\$/, ''); + verb.log.success('created', file.relative); return dest; })) .on('end', cb); diff --git a/lib/reflinks.js b/lib/reflinks.js index 37f293e3..aaf488da 100644 --- a/lib/reflinks.js +++ b/lib/reflinks.js @@ -7,19 +7,25 @@ var utils = require('./utils'); * package.json if enabled by the user. */ -module.exports = function(app) { - var arr = app.pkg.get('verb.reflinks') || []; - var re = /(\[[\w._-]+?\]\[\])/g; +module.exports = function(app, options) { + options = options || {}; + + var arr = []; + if (Array.isArray(options.reflinks)) { + arr = options.reflinks; + } + + var re = /(\[[-\w._]+?\]\[\])/g; var count = 0; return utils.through.obj(function(file, enc, next) { - var options = app.option('reflinks') || {}; + var existing = app.option('reflinks') || {}; var matches = file.content.match(re); if (matches && matches.length) { matches.forEach(function(match) { var idx = match.indexOf(']'); - var name = match.slice(1, idx).trim(); + var name = match.slice(1, idx).trim().toLowerCase(); if (arr.indexOf(name) === -1) { arr.push(name); count++; @@ -32,7 +38,7 @@ module.exports = function(app) { return; } - if (file.basename === '.verb.md') { + if (options.save === true) { save(app, arr); } diff --git a/lib/tasks.js b/lib/tasks.js new file mode 100644 index 00000000..35c07bbd --- /dev/null +++ b/lib/tasks.js @@ -0,0 +1,74 @@ +'use strict'; + +var utils = require('./utils'); + +module.exports = function(app, ctx, argv) { + if (argv.init === true) { + return []; + } + + var configFile = ctx.env.configFile; + var configExists; + var configTasks; + + // determine the tasks to run (returns the first value that isn't `["default"]` or `[]`) + var tasks = utils.getTasks(configFile, [ + argv._, // command line + ctx.pkgConfig.tasks, // set in package.json + app.store.get('defaultTasks') // stored user-defined "default" tasks + ]); + + tasks = tasks.map(function(task) { + var isDefaults = false; + + if (task.indexOf('new') === 0 || task.indexOf('store') === 0) { + isDefaults = true; + task = 'defaults.' + task; + + } else if (task === 'init') { + isDefaults = true; + task = 'defaults:init'; + + } else if (task === 'format') { + isDefaults = true; + task = 'defaults:format'; + + } else if (task === 'diff') { + isDefaults = true; + task = 'defaults:diff'; + } + if (isDefaults === true) { + app.enable('silent'); + } + return task; + }); + + if (tasks.length === 1 && tasks[0] === 'default') { + configExists = utils.exists(configFile); + configTasks = app.pkg.get('verb.tasks'); + + // if a `verbfile.js` or custom configFile exists, return tasks + if (configExists) { + if (configTasks && configTasks.length) { + app.pkg.logWarning('ignoring tasks defined in package.json:', configTasks); + } + return tasks; + } + + if (configTasks && configTasks.length) { + return configTasks; + } + + var verbmd = utils.exists('.verb.md'); + // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default + if (verbmd && !configExists) { + return ['verb-readme-generator']; + } + + // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` + if (!verbmd) { + return ['defaults.new:prompt-verbmd'].concat(tasks); + } + } + return tasks; +}; diff --git a/lib/utils.js b/lib/utils.js index be8d74c2..2747097e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,5 +1,7 @@ 'use strict'; +var fs = require('fs'); +var path = require('path'); var utils = require('lazy-cache')(require); var fn = require; require = utils; @@ -9,17 +11,81 @@ require = utils; */ require('base-runner', 'runner'); +require('config-file'); require('extend-shallow', 'extend'); require('fs-exists-sync', 'exists'); require('generate'); require('get-value', 'get'); require('gulp-format-md', 'format'); +require('log-utils', 'log'); require('reflinks'); require('set-value', 'set'); require('through2', 'through'); require('yargs-parser', 'yargs'); require = fn; +utils.getConfig = function(app, name) { + var runtimeConfig = path.resolve(app.cwd, name); + var config = utils.configFile('.verbrc.json') || {}; + + if (utils.exists(runtimeConfig)) { + var rc = JSON.parse(fs.readFileSync(runtimeConfig, 'utf8')); + config = utils.extend({}, config, rc); + app.base.set('cache.config', config); + } +}; + +utils.getTasks = function(configFile, arrays) { + arrays = utils.arrayify(arrays); + var tasks = []; + + if (configFile) { + tasks = utils.arrayify(arrays[0]); + return tasks.length >= 1 ? tasks : ['default']; + } + + for (var i = 0; i < arrays.length; i++) { + var arr = utils.arrayify(arrays[i]); + // if `default` task is defined, continue + if (arr.length === 1 && arr[0] === 'default') { + continue; + } + // if nothing is defined, continue + if (arr.length === 0) { + continue; + } + tasks = arr; + break; + } + return tasks; +}; + +utils.arrayify = function(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; +}; + +utils.firstIndex = function(arr, items) { + items = utils.arrayify(items); + var idx = -1; + for (var i = 0; i < arr.length; i++) { + if (items.indexOf(arr[i]) !== -1) { + idx = i; + break; + } + } + return idx; +}; + +utils.logger = function(prop, color) { + color = color || 'dim'; + return function(msg) { + var rest = [].slice.call(arguments, 1); + return console.log + .bind(console, utils.log.timestamp, utils.log[prop]) + .apply(console, [utils.log[color](msg), ...rest]); + }; +}; + /** * Expose `utils` modules */ From d8b4bb28d0eec83bf7f82e0bdda89132ea1acfb7 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 28 Jun 2016 14:48:44 -0400 Subject: [PATCH 248/282] adds default templates --- lib/templates/.verb.md | 5 ----- lib/templates/_verb.md | 5 +++++ lib/templates/_verbrc.json | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) delete mode 100644 lib/templates/.verb.md create mode 100644 lib/templates/_verb.md create mode 100644 lib/templates/_verbrc.json diff --git a/lib/templates/.verb.md b/lib/templates/.verb.md deleted file mode 100644 index 383b2629..00000000 --- a/lib/templates/.verb.md +++ /dev/null @@ -1,5 +0,0 @@ -## Usage - -```js -var {%= varname %} = require('{%= name %}'); -``` diff --git a/lib/templates/_verb.md b/lib/templates/_verb.md new file mode 100644 index 00000000..6ce05fd7 --- /dev/null +++ b/lib/templates/_verb.md @@ -0,0 +1,5 @@ +## Usage + +```js +var {%= alias %} = require('{%= name %}'); +``` diff --git a/lib/templates/_verbrc.json b/lib/templates/_verbrc.json new file mode 100644 index 00000000..77f8fd50 --- /dev/null +++ b/lib/templates/_verbrc.json @@ -0,0 +1,7 @@ +{ + "data": {}, + "helpers": [], + "options": {}, + "plugins": [], + "tasks": [] +} \ No newline at end of file From 42109b5891ff6de6491cf24fac3a568cda39a961 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 28 Jun 2016 14:48:55 -0400 Subject: [PATCH 249/282] remove unused var --- bin/verb.js | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/verb.js b/bin/verb.js index daeb10cc..9745821b 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -4,7 +4,6 @@ process.env.GENERATE_CLI = true; require('set-blocking')(true); var util = require('util'); -var path = require('path'); var Verb = require('..'); var commands = require('../lib/commands'); var tasks = require('../lib/tasks'); From 8825bedb95f275acd0987a7b8a29ceb6dc038608 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 28 Jun 2016 14:49:07 -0400 Subject: [PATCH 250/282] update deps --- package.json | 77 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 39914d94..ae0dad76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "verb", - "description": "Documentation build system for GitHub projects, with full support for gulp and assemble plugins. Built verb can be used to create documentation generators, themes, documentation websites and much more!", + "description": "Documentation build system for GitHub projects, powered by node.js. Verb has full support for gulp and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more!", "version": "0.9.0", "homepage": "https://github.com/verbose/verb", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", @@ -12,7 +12,8 @@ "files": [ "bin", "index.js", - "lib" + "lib", + "utils.js" ], "main": "index.js", "preferGlobal": true, @@ -26,55 +27,65 @@ "test": "mocha" }, "dependencies": { - "ansi-colors": "^0.1.0", - "base-runner": "^0.7.0", + "base-runner": "^0.8.0", + "common-middleware": "^0.2.9", + "config-file": "^0.3.2", + "data-store": "^0.16.0", "debug": "^2.2.0", + "diff": "^2.2.3", + "engine-base": "^0.1.2", "export-files": "^2.1.1", "extend-shallow": "^2.0.1", "fs-exists-sync": "^0.1.0", - "generate": "^0.5.4", + "generate": "^0.7.3", + "get-value": "^2.0.6", "gulp-format-md": "^0.1.9", - "isobject": "^2.1.0", "lazy-cache": "^2.0.1", - "memoize-path": "^0.1.2", - "reflinks": "^0.2.6", + "log-utils": "^0.1.4", + "reflinks": "^0.2.7", + "set-blocking": "^2.0.0", "set-value": "^0.3.3", "through2": "^2.0.1", - "unset-value": "^0.1.1", + "vinyl": "^1.1.1", "yargs-parser": "^2.4.0" }, "devDependencies": { - "async": "^1.5.2", - "base-store": "^0.4.2", + "async-each": "^1.0.0", + "base-store": "^0.4.4", "buffer-equal": "^1.0.0", "consolidate": "^0.14.1", + "default-resolution": "^2.0.0", "define-property": "^0.2.5", - "engine-base": "^0.1.2", + "delete": "^0.3.2", "engine-handlebars": "^0.8.0", - "event-stream": "^3.3.2", - "expect": "^1.19.0", + "event-stream": "^3.3.3", + "expect": "^1.20.1", "generate-foo": "^0.1.5", "generator-util": "^0.2.9", - "get-value": "^2.0.5", - "global-modules": "^0.2.1", - "graceful-fs": "^4.1.3", + "global-modules": "^0.2.2", + "graceful-fs": "^4.1.4", "gulp": "^3.9.1", "gulp-eslint": "^2.0.0", - "gulp-istanbul": "^0.10.4", + "gulp-istanbul": "^1.0.0", "gulp-mocha": "^2.2.0", + "gulp-reflinks": "^0.1.0", + "gulp-unused": "^0.1.2", "helper-example": "^0.1.0", "is-buffer": "^1.1.3", "kind-of": "^3.0.3", "load-pkg": "^3.0.1", - "mocha": "^2.4.5", + "mocha": "^2.5.3", "parser-front-matter": "^1.3.0", + "readable-stream": "^2.1.4", "resolve-glob": "^0.1.8", "rimraf": "^2.5.2", - "should": "^8.3.1", + "should": "^9.0.2", "sinon": "^1.17.4", "spawn-commands": "^0.3.1", "swig": "^1.4.2", - "vinyl": "^1.1.1" + "templates": "^0.22.5", + "verb-readme-generator": "^0.1.14", + "vinyl-fs": "^2.4.3" }, "keywords": [ "verb" @@ -87,7 +98,7 @@ }, "verb": { "toc": false, - "layout": "default-dev", + "layout": "minimal", "plugins": [ "gulp-format-md" ], @@ -99,19 +110,23 @@ ] }, "reflinks": [ - "assemble", - "base-runner", - "generate", "gulp", + "bach", + "generate", + "base", + "assemble", + "update", "handlebars", "lodash", - "pretty-remarkable", "swig", - "update", - "verb", - "verb-readme-generator" + "consolidate", + "templates", + "template-helpers", + "handlebars-helpers", + "verb-readme-generator", + "verb" ], - "task": [ + "tasks": [ "readme" ], "sections": false, @@ -119,4 +134,4 @@ "reflinks": true } } -} \ No newline at end of file +} From 2a738ed5019a1bb46e9208b579ec4e86d29aa0fb Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 28 Jun 2016 14:49:24 -0400 Subject: [PATCH 251/282] logger, ctor events --- index.js | 127 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index 2a2e07a8..83554506 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ var Generate = require('generate'); var utils = require('./lib/utils'); +var pkg = require('./package'); /** * Create a verb application with `options`. @@ -35,8 +36,105 @@ function Verb(options) { */ Generate.extend(Verb); -Generate.on('generate.init', function(app) { - Verb.emit('generate.init', app); + +Generate.on('generate.preInit', function(app) { + Verb.emit('generate.preInit', app); +}); + +Generate.on('generate.postInit', function(app) { + Verb.emit('generate.postInit', app); +}); + +/** + * Initialize verb data + */ + +Verb.prototype.initVerb = function(opts) { + Verb.emit('verb.preInit', this, this.base); + var self = this; + + /** + * Data + */ + + this.data('before', {}); + this.data('after', {}); + this.data('runner', { + name: 'verb', + version: pkg.version, + homepage: pkg.homepage + }); + + /** + * Options + */ + + this.option('lookup', Verb.lookup(this)); + this.option('toAlias', Verb.toAlias); + this.option('help', { + command: 'verb', + configname: 'verbfile', + appname: 'verb' + }); + + /** + * Listeners + */ + + this.on('option', function(key, val) { + if (key === 'dest') self.cwd = val; + }); + + this.on('ask', function(answerVal, answerKey, question) { + if (typeof answerVal === 'undefined') { + var segs = answerKey.split('author.'); + if (segs.length > 1) { + self.questions.answers[answerKey] = self.common.get(segs.pop()); + } + } + }); + + /** + * Middleware + */ + + this.preWrite(/(^|\/)[$_]/, function(file, next) { + file.basename = file.basename.replace(/^_/, '.'); + file.basename = file.basename.replace(/^\$/, ''); + next(); + }); + + Verb.emit('verb.postInit', this, this.base); +}; + +/** + * Expose logging methods + */ + +Object.defineProperty(Verb.prototype, 'log', { + configurable: true, + get: function() { + function log() { + return console.log.bind(console, utils.log.timestamp).apply(console, arguments); + } + log.warn = function(msg) { + return utils.logger('warning', 'yellow').apply(null, arguments); + }; + + log.success = function() { + return utils.logger('success', 'green').apply(null, arguments); + }; + + log.info = function() { + return utils.logger('info', 'cyan').apply(null, arguments); + }; + + log.error = function() { + return utils.logger('error', 'red').apply(null, arguments); + }; + log.__proto__ = utils.log; + return log; + } }); /** @@ -49,8 +147,7 @@ Verb.lookup = function(app) { if (!/^verb-([^-]+)-generator/.test(key)) { patterns.unshift(`verb-${key}-generator`); } - - if (app.enabled('generators')) { + if (app.enabled('generate')) { patterns.push(`generate-${key}`); } return patterns; @@ -58,29 +155,19 @@ Verb.lookup = function(app) { }; /** - * Initialize verb data + * Convert the given `name` to the `alias` to be used in the + * command line. */ -Verb.prototype.initVerb = function(opts) { - this.debug('initializing', __filename); - - Verb.emit('verb.preInit', this, this.base); - this.data({before: {}, after: {}}); - - this.option('toAlias', function(name) { - return name.replace(/^(?:verb-([^-]+)-generator$)|(?:generate-)/, '$1'); - }); - - this.option('lookup', Verb.lookup(this)); - this.data({runner: require('./package')}); - Verb.emit('verb.postInit', this, this.base); +Verb.toAlias = function(name) { + return name.replace(/^(?:verb-([^-]+)-generator$)|(?:generate-)/, '$1'); }; /** - * Expose static `is*` methods from Templates + * Expose `pkg` as a static property */ -Generate._.plugin.is(Verb); +Verb.pkg = pkg; /** * Expose `Verb` From 6e7dad176ec956a19ef030fad34a056fcd02af29 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 28 Jun 2016 14:54:10 -0400 Subject: [PATCH 252/282] update tests --- test/_suite.js | 21 + test/app.applyLayout.js | 87 -- test/app.cli.js | 4 +- test/app.collection.compile.js | 52 - test/app.collection.js | 177 --- test/app.collection.render.js | 161 --- test/app.compile.js | 54 - test/app.copy.js | 33 - test/app.create.js | 244 ---- test/app.data.js | 96 -- test/app.dest.js | 1092 --------------- test/app.doc.js | 2 +- test/app.docs.js | 4 +- test/app.engines.js | 160 --- test/app.env.js | 65 - test/app.events.js | 199 --- test/app.extendWith.js | 268 ++-- test/app.generate.js | 560 ++++++-- test/app.generateEach.js | 598 +++++++- test/app.generator.js | 210 +-- test/app.generator.tasks.js | 46 - test/app.get-set.js | 74 - test/app.getGenerator.js | 52 +- test/app.handle.js | 37 - test/app.handleOnce.js | 39 - test/app.handlers.js | 74 - test/app.include.js | 2 +- test/app.includes.js | 2 +- test/app.js | 121 -- test/app.layout.js | 3 +- test/app.layouts.js | 3 +- test/app.list.compile.js | 46 - test/app.list.js | 106 -- test/app.list.render.js | 157 --- test/app.lookupGenerator.js | 61 + test/app.lookups.js | 119 -- test/app.matchGenerator.js | 57 + test/app.mergePartials.js | 107 -- test/app.middleware.js | 62 - test/app.onLoad.js | 50 - test/app.option.js | 100 -- test/app.options.initTemplates.js | 24 - test/app.page.js | 9 +- test/app.pages.js | 15 +- test/app.partial.js | 3 +- test/app.partials.js | 3 +- test/app.questions.js | 350 ++--- test/app.register.js | 47 +- test/app.render.js | 180 --- test/app.renderFile.js | 135 -- test/app.route.js | 95 -- test/app.src.js | 349 ----- test/app.store.js | 74 +- test/app.symlink.js | 398 ------ test/app.task.js | 132 +- test/app.toAlias.js | 41 + test/app.toStream.js | 64 - test/app.use.js | 283 ---- test/app.view.compile.js | 40 - test/app.view.render.js | 94 -- test/app.viewTypes.js | 54 - test/app.views.js | 29 - test/collection.engines.js | 179 --- test/collection.events.js | 29 - test/collection.getView.js | 34 - test/collection.isType.js | 26 - test/collection.js | 573 -------- test/collection.options.js | 27 - test/collection.render.js | 162 --- test/collection.routes.js | 130 -- test/collection.src.js | 322 ----- test/collection.use.js | 245 ---- test/fixtures/generator.js | 2 +- test/fixtures/generators/a/generator.js | 2 - test/fixtures/generators/a/package.json | 2 +- test/fixtures/generators/b/index.js | 2 +- test/fixtures/generators/b/package.json | 2 +- test/fixtures/generators/c/package.json | 2 +- test/fixtures/generators/e/package.json | 2 +- test/fixtures/generators/qux/generator.js | 4 - test/fixtures/generators/qux/package.json | 6 +- test/fixtures/generators2.js | 2 +- test/fixtures/not-exposed.js | 8 +- test/fixtures/one/generator.js | 3 +- test/fixtures/one/package.json | 2 +- test/fixtures/package.json | 9 + test/fixtures/three/four/five/generator.js | 2 +- test/fixtures/three/four/five/package.json | 2 +- test/fixtures/three/four/generator.js | 2 +- test/fixtures/three/four/package.json | 2 +- test/fixtures/three/generator.js | 2 +- test/fixtures/three/package.json | 2 +- test/fixtures/two/generator.js | 16 +- test/fixtures/two/package.json | 2 +- test/fixtures/verbfile.js | 2 +- test/fixtures/vinyl/test-symlink-dir | 1 - .../vinyl/test-symlink-dir}/suchempty | 0 test/generators.env.js | 99 +- test/generators.events.js | 87 +- test/group.js | 153 -- test/handlers.js | 131 -- test/helpers.js | 1090 --------------- test/integration/instance.js | 7 - test/is.js | 69 - test/item.js | 1215 ---------------- test/layouts.js | 238 ---- test/list.deleteItem.js | 73 - test/list.getIndex.js | 105 -- test/list.js | 715 ---------- test/list.render.js | 144 -- test/list.use.js | 158 --- test/partials.js | 204 --- test/renameKey.js | 460 ------ test/render.js | 72 - test/routes.js | 100 -- test/runner.js | 111 ++ test/store.js | 220 --- test/support/spy.js | 3 +- test/view.content.js | 31 - test/view.events.js | 30 - test/view.isType.js | 26 - test/view.js | 1159 ---------------- test/view.methods.js | 41 - test/view.option.js | 31 - test/view.render.js | 68 - test/view.set.js | 35 - test/view.use.js | 106 -- test/viewTypes.js | 35 - test/views.getView.js | 62 - test/views.js | 558 -------- test/views.use.js | 158 --- test/vinyl.js | 13 - test/vinyl/LICENSE | 20 - test/vinyl/dest-modes.js | 483 ------- test/vinyl/dest-times.js | 223 --- test/vinyl/dest.js | 1229 ----------------- test/vinyl/file-operations.js | 917 ------------ test/vinyl/fixtures/bom-utf16be.txt | Bin 244 -> 0 bytes test/vinyl/fixtures/bom-utf16le.txt | Bin 244 -> 0 bytes test/vinyl/fixtures/bom-utf8.txt | 1 - test/vinyl/fixtures/foo/bar/baz.txt | 1 - test/vinyl/fixtures/test.coffee | 1 - test/vinyl/not-owned.js | 75 - test/vinyl/not-owned/not-owned.txt | 1 - test/vinyl/spy.js | 32 - test/vinyl/src.js | 514 ------- test/vinyl/symlink.js | 455 ------ 147 files changed, 2169 insertions(+), 19222 deletions(-) create mode 100644 test/_suite.js delete mode 100644 test/app.applyLayout.js delete mode 100644 test/app.collection.compile.js delete mode 100644 test/app.collection.js delete mode 100644 test/app.collection.render.js delete mode 100644 test/app.compile.js delete mode 100644 test/app.copy.js delete mode 100644 test/app.create.js delete mode 100644 test/app.data.js delete mode 100644 test/app.dest.js delete mode 100644 test/app.engines.js delete mode 100644 test/app.env.js delete mode 100644 test/app.events.js delete mode 100644 test/app.generator.tasks.js delete mode 100644 test/app.get-set.js delete mode 100644 test/app.handle.js delete mode 100644 test/app.handleOnce.js delete mode 100644 test/app.handlers.js delete mode 100644 test/app.js delete mode 100644 test/app.list.compile.js delete mode 100644 test/app.list.js delete mode 100644 test/app.list.render.js create mode 100644 test/app.lookupGenerator.js delete mode 100644 test/app.lookups.js create mode 100644 test/app.matchGenerator.js delete mode 100644 test/app.mergePartials.js delete mode 100644 test/app.middleware.js delete mode 100644 test/app.onLoad.js delete mode 100644 test/app.option.js delete mode 100644 test/app.options.initTemplates.js delete mode 100644 test/app.render.js delete mode 100644 test/app.renderFile.js delete mode 100644 test/app.route.js delete mode 100644 test/app.src.js delete mode 100644 test/app.symlink.js create mode 100644 test/app.toAlias.js delete mode 100644 test/app.toStream.js delete mode 100644 test/app.use.js delete mode 100644 test/app.view.compile.js delete mode 100644 test/app.view.render.js delete mode 100644 test/app.viewTypes.js delete mode 100644 test/app.views.js delete mode 100644 test/collection.engines.js delete mode 100644 test/collection.events.js delete mode 100644 test/collection.getView.js delete mode 100644 test/collection.isType.js delete mode 100644 test/collection.js delete mode 100644 test/collection.options.js delete mode 100644 test/collection.render.js delete mode 100644 test/collection.routes.js delete mode 100644 test/collection.src.js delete mode 100644 test/collection.use.js create mode 100644 test/fixtures/package.json delete mode 120000 test/fixtures/vinyl/test-symlink-dir rename test/{vinyl/fixtures/wow => fixtures/vinyl/test-symlink-dir}/suchempty (100%) delete mode 100644 test/group.js delete mode 100644 test/handlers.js delete mode 100644 test/helpers.js delete mode 100644 test/integration/instance.js delete mode 100644 test/is.js delete mode 100644 test/item.js delete mode 100644 test/layouts.js delete mode 100644 test/list.deleteItem.js delete mode 100644 test/list.getIndex.js delete mode 100644 test/list.js delete mode 100644 test/list.render.js delete mode 100644 test/list.use.js delete mode 100644 test/partials.js delete mode 100644 test/renameKey.js delete mode 100644 test/render.js delete mode 100644 test/routes.js create mode 100644 test/runner.js delete mode 100644 test/store.js delete mode 100644 test/view.content.js delete mode 100644 test/view.events.js delete mode 100644 test/view.isType.js delete mode 100644 test/view.js delete mode 100644 test/view.methods.js delete mode 100644 test/view.option.js delete mode 100644 test/view.render.js delete mode 100644 test/view.set.js delete mode 100644 test/view.use.js delete mode 100644 test/viewTypes.js delete mode 100644 test/views.getView.js delete mode 100644 test/views.js delete mode 100644 test/views.use.js delete mode 100644 test/vinyl.js delete mode 100644 test/vinyl/LICENSE delete mode 100644 test/vinyl/dest-modes.js delete mode 100644 test/vinyl/dest-times.js delete mode 100644 test/vinyl/dest.js delete mode 100644 test/vinyl/file-operations.js delete mode 100644 test/vinyl/fixtures/bom-utf16be.txt delete mode 100644 test/vinyl/fixtures/bom-utf16le.txt delete mode 100644 test/vinyl/fixtures/bom-utf8.txt delete mode 100644 test/vinyl/fixtures/foo/bar/baz.txt delete mode 100644 test/vinyl/fixtures/test.coffee delete mode 100644 test/vinyl/not-owned.js delete mode 100644 test/vinyl/not-owned/not-owned.txt delete mode 100644 test/vinyl/spy.js delete mode 100644 test/vinyl/src.js delete mode 100644 test/vinyl/symlink.js diff --git a/test/_suite.js b/test/_suite.js new file mode 100644 index 00000000..8a136aaf --- /dev/null +++ b/test/_suite.js @@ -0,0 +1,21 @@ +'use strict'; + +var generate = require('..'); +var runner = require('base-test-runner')(); +var suite = require('base-test-suite'); + +/** + * Run the tests in `base-test-suite` + */ + +runner.on('templates', function(file) { + var fn = require(file.path); + if (typeof fn === 'function') { + fn(generate); + } else { + throw new Error('expected ' + file.path + ' to export a function'); + } +}); + +runner.addFiles('templates', suite.test.templates); +runner.addFiles('templates', suite.test['assemble-core']); diff --git a/test/app.applyLayout.js b/test/app.applyLayout.js deleted file mode 100644 index c62d83f6..00000000 --- a/test/app.applyLayout.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; -var page = { - content: '<%= name %>', - layout: 'default.tmpl', - locals: { - name: 'Halle' - } -}; - -describe('app.applyLayout', function() { - describe('rendering', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('layout', { viewType: 'layout' }); - app.create('page'); - }); - - it('should throw an error when a layout cannot be found:', function(cb) { - app.layout('fofof.tmpl', {content: '..'}); - app.page('a.tmpl', page) - .render(function(err) { - assert(/layouts/.test(err.message)); - cb(); - }); - }); - - it('should emit an error when a layout cannot be found:', function(cb) { - app.layout('fofof.tmpl', {content: '..'}); - app.on('error', function(err) { - assert(/layouts/.test(err.message)); - cb(); - }); - - app.page('a.tmpl', page) - .render(function() { - }); - }); - - it('should throw an error - layout defined but no layouts registered:', function(cb) { - app.page('a.tmpl', page) - .render(function(err) { - assert(/layouts/.test(err.message)); - cb(); - }); - }); - - it('should emit an error - layout defined but no layouts registered:', function(cb) { - app.on('error', function(err) { - assert(/layouts/.test(err.message)); - cb(); - }); - app.page('a.tmpl', page) - .render(function() { - }); - }); - - it('should wrap a view with a layout (view.render):', function(cb) { - app.layout('default.tmpl', {content: 'before {% body %} after'}); - app.page('a.tmpl', page) - .render(function(err) { - if (err) return cb(err); - cb(); - }); - }); - - it('should wrap a view with a layout (app.render):', function(cb) { - app.layout('default.tmpl', {content: 'before {% body %} after'}); - app.page('a.tmpl', page); - - var view = app.pages.getView('a.tmpl'); - app.render(view, function(err, res) { - if (err) return cb(err); - assert(res.contents.toString() === 'before Halle after'); - cb(); - }); - }); - }); -}); - diff --git a/test/app.cli.js b/test/app.cli.js index 2d7c6952..f341a2cf 100644 --- a/test/app.cli.js +++ b/test/app.cli.js @@ -1,10 +1,8 @@ 'use strict'; require('mocha'); -require('should'); var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); +var App = require('..'); var app; describe('app.cli', function() { diff --git a/test/app.collection.compile.js b/test/app.collection.compile.js deleted file mode 100644 index e6d91831..00000000 --- a/test/app.collection.compile.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Views = App.Views; -var views; - -describe('app.collection.compile', function() { - beforeEach(function() { - views = new Views(); - }); - - it('should throw an error when an engine cannot be found:', function() { - views.addView('foo.bar', {content: '<%= name %>'}); - var page = views.getView('foo.bar'); - (function() { - views.compile(page); - }).should.throw('Views#compile cannot find an engine for: .bar'); - }); - - it('should compile a template:', function() { - views.engine('tmpl', require('engine-base')); - views.addView('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); - - var page = views.getView('a.tmpl'); - var view = views.compile(page); - assert.equal(typeof view.fn, 'function'); - }); - - it('should compile a template by name:', function() { - views.engine('tmpl', require('engine-base')); - views.addView('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); - - var view = views.compile('a.tmpl'); - assert.equal(typeof view.fn, 'function'); - }); - - it('should throw an error when a callback is given:', function() { - views.engine('md', require('engine-base')); - views.addView('foo.md', {content: '<%= name %>'}); - var page = views.getView('foo.md'); - (function() { - views.compile(page, function() {}); - }).should.throw('Views#compile is sync and does not take a callback function'); - - (function() { - views.compile(page, {}, function() {}); - }).should.throw('Views#compile is sync and does not take a callback function'); - }); -}); diff --git a/test/app.collection.js b/test/app.collection.js deleted file mode 100644 index 9116953c..00000000 --- a/test/app.collection.js +++ /dev/null @@ -1,177 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var assert = require('assert'); -var define = require('define-property'); -var support = require('./support'); -var App = support.resolve(); -var Collection = App.Collection; -var app; - -describe('app.collection', function() { - describe('method', function() { - beforeEach(function() { - app = new App(); - }); - - it('should expose the collection method', function() { - assert.equal(typeof app.collection, 'function'); - }); - - it('should return a new collection', function() { - var collection = app.collection(); - assert.equal(typeof collection, 'object'); - }); - - it('should have isCollection property', function() { - var collection = app.collection(); - assert.equal(collection.isCollection, true); - }); - }); - - describe('adding views', function() { - beforeEach(function() { - app = new App() - .use(function() { - return function() { - define(this, 'count', { - get: function() { - return Object.keys(this.views).length; - }, - set: function() { - throw new Error('count is a read-only getter and cannot be defined.'); - } - }); - }; - }); - - app.engine('tmpl', require('engine-base')); - app.create('pages'); - }); - - it('should load a view onto the respective collection:', function() { - app.pages('test/fixtures/pages/a.hbs'); - app.views.pages.should.have.property('test/fixtures/pages/a.hbs'); - }); - - it('should allow collection methods to be chained:', function() { - app - .pages('test/fixtures/pages/a.hbs') - .pages('test/fixtures/pages/b.hbs') - .pages('test/fixtures/pages/c.hbs'); - - app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' - ]); - }); - - it('should expose the `option` method:', function() { - app.pages.option('foo', 'bar') - .pages('test/fixtures/pages/a.hbs') - .pages('test/fixtures/pages/b.hbs') - .pages('test/fixtures/pages/c.hbs'); - - app.pages.options.should.have.property('foo', 'bar'); - app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' - ]); - }); - - it('should expose the `option` method:', function() { - app.pages.option('foo', 'bar') - .pages('test/fixtures/pages/a.hbs') - .pages('test/fixtures/pages/b.hbs') - .pages('test/fixtures/pages/c.hbs'); - - assert.equal(app.pages.count, 3); - }); - }); - - describe('addItem', function() { - beforeEach(function() { - app = new App(); - }); - - it('should add items to a collection', function() { - var pages = app.collection({Collection: Collection}); - pages.addItem('foo'); - pages.addItem('bar'); - pages.addItem('baz'); - - pages.items.hasOwnProperty('foo'); - pages.items.hasOwnProperty('bar'); - pages.items.hasOwnProperty('baz'); - }); - - it('should create a collection from an existing collection:', function() { - var pages = app.collection({Collection: Collection}); - pages.addItem('foo'); - pages.addItem('bar'); - pages.addItem('baz'); - - var posts = app.collection(pages); - posts.items.hasOwnProperty('foo'); - posts.items.hasOwnProperty('bar'); - posts.items.hasOwnProperty('baz'); - }); - }); - - describe('rendering views', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('pages'); - app.cache.data = {}; - }); - - it('should render a view with inherited app.render', function(cb) { - app.page('test/fixtures/templates/a.tmpl') - .use(function(view) { - view.contents = fs.readFileSync(view.path); - }) - .set('data.name', 'Brian') - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'Brian'); - cb(); - }); - }); - }); -}); - -describe('collection singular method', function() { - describe('create', function() { - beforeEach(function() { - app = new App(); - }); - - it('should add a pluralized collection from singular name', function() { - app.create('page'); - assert.equal(typeof app.views.pages, 'object'); - }); - }); - - describe('adding views', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - it('should add a view to the created collection:', function() { - app.page('test/fixtures/pages/a.hbs'); - assert.equal(typeof app.views.pages['test/fixtures/pages/a.hbs'], 'object'); - }); - - it('should expose the `option` method:', function() { - app.pages.option('foo', 'bar'); - app.pages.options.should.have.property('foo', 'bar'); - }); - }); -}); diff --git a/test/app.collection.render.js b/test/app.collection.render.js deleted file mode 100644 index 58dd3b36..00000000 --- a/test/app.collection.render.js +++ /dev/null @@ -1,161 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var each = require('async-each'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var pages, app; - -describe('app.collection.render', function() { - describe('rendering', function() { - beforeEach(function() { - app = App(); - pages = app.create('pages'); - app.engine('tmpl', require('engine-base')); - pages.engine('tmpl', require('engine-base')); - }); - - it('should throw an error when no callback is given:', function(cb) { - try { - app.pages.render({}); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'Views#render is async and expects a callback function'); - cb(); - } - }); - - it('should throw an error when an engine is not defined:', function(cb) { - pages.addView('foo.bar', { content: '<%= name %>' }); - var page = pages.getView('foo.bar'); - - app.pages.render(page, function(err) { - assert(err.message === 'Views#render cannot find an engine for: .bar'); - cb(); - }); - }); - - it('should use helpers defined on app to render a view:', function(cb) { - var locals = {name: 'Halle'}; - app.helper('upper', function(str) { - return str.toUpperCase(str) + 'app'; - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - app.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.content === 'a HALLEapp b'); - cb(); - }); - }); - - it('should use helpers defined on app to render a view with collection.render:', function(cb) { - var locals = {name: 'Halle'}; - app.helper('upper', function(str) { - return str.toUpperCase(str) + 'app'; - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - pages.helper('upper', app._.helpers.sync.upper); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.content === 'a HALLEapp b'); - cb(); - }); - }); - - it('should use helpers when rendering a view:', function(cb) { - var locals = {name: 'Halle'}; - pages.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - assert(res.content === 'a HALLE b'); - cb(); - }); - }); - - it('should render a template when contents is a buffer:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function(err, view) { - if (err) return cb(err); - assert(view.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a template when content is a string:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function(err, view) { - if (err) return cb(err); - assert(view.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a view from its path:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - - pages.render('a.tmpl', function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use a plugin for rendering:', function(cb) { - pages.engine('tmpl', require('engine-base')); - pages.option('engine', 'tmpl'); - - pages.addViews({ - 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, - 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, - 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, - 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, - 'e': {content: '<%= title %>', locals: {title: 'eee'}}, - 'f': {content: '<%= title %>', locals: {title: 'fff'}}, - 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, - 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, - 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}} - }); - - pages.use(function(collection) { - collection.option('pager', false); - - collection.renderEach = function(cb) { - var list = new List(collection); - each(list.items, function(item, next) { - collection.render(item, next); - }, cb); - }; - }); - - pages.renderEach(function(err, items) { - if (err) return cb(err); - assert(items[0].content === 'aaa'); - assert(items[9].content === 'jjj'); - assert(items.length === 10); - cb(); - }); - }); - }); -}); diff --git a/test/app.compile.js b/test/app.compile.js deleted file mode 100644 index 2b8d585e..00000000 --- a/test/app.compile.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.compile', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - }); - - it('should throw an error when an engine cannot be found:', function() { - app.page('foo.bar', {content: '<%= name %>'}); - var page = app.pages.getView('foo.bar'); - (function() { - app.compile(page); - }).should.throw('Templates#compile cannot find an engine for: .bar'); - }); - - it('should compile a template:', function() { - app.engine('tmpl', require('engine-base')); - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); - - var page = app.pages.getView('a.tmpl'); - var view = app.compile(page); - assert.equal(typeof view.fn, 'function'); - }); - - it('should compile a template by name:', function() { - app.engine('tmpl', require('engine-base')); - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', a: 'b'}); - - var view = app.compile('a.tmpl'); - assert.equal(typeof view.fn, 'function'); - }); - - it('should throw an error when a callback is given:', function() { - app.engine('md', require('engine-base')); - app.page('foo.md', {content: '<%= name %>'}); - var page = app.pages.getView('foo.md'); - (function() { - app.compile(page, function() { - }); - }).should.throw('Templates#compile is sync and does not take a callback function'); - - (function() { - app.compile(page, {}, function() { - }); - }).should.throw('Templates#compile is sync and does not take a callback function'); - }); -}); diff --git a/test/app.copy.js b/test/app.copy.js deleted file mode 100644 index dcc96b0f..00000000 --- a/test/app.copy.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -require('mocha'); -var path = require('path'); -var assert = require('assert'); -var rimraf = require('rimraf'); -var App = require('..'); -var app; - -var fixtures = path.join(__dirname, 'fixtures/copy/*.txt'); -var actual = path.join(__dirname, 'actual'); - -describe('app.copy', function() { - beforeEach(function(cb) { - rimraf(actual, cb); - app = new App(); - }); - - afterEach(function(cb) { - rimraf(actual, cb); - }); - - describe('streams', function() { - it('should copy files', function(cb) { - app.copy(fixtures, path.join(__dirname, 'actual')) - .on('error', cb) - .on('data', function(file) { - assert.equal(typeof file, 'object'); - }) - .on('end', cb); - }); - }); -}); diff --git a/test/app.create.js b/test/app.create.js deleted file mode 100644 index 9f88049e..00000000 --- a/test/app.create.js +++ /dev/null @@ -1,244 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.create', function() { - describe('inflections', function() { - beforeEach(function() { - app = new App(); - }); - - it('should expose the create method', function() { - assert.equal(typeof app.create, 'function'); - }); - - it('should add a collection to `views`', function() { - app.create('pages'); - assert.equal(typeof app.views.pages, 'object'); - assert.equal(typeof app.pages, 'function'); - }); - - it('should add a pluralized collection to `views`', function() { - app.create('page'); - assert.equal(typeof app.views.pages, 'object'); - assert.equal(typeof app.page, 'function'); - }); - }); - - describe('renderable views', function() { - beforeEach(function() { - app = new App(); - app.create('pages'); - app.create('partials', {viewType: 'partial'}); - app.create('layout', {viewType: 'layout'}); - }); - - it('should add renderable views when no type is defined', function() { - app.pages.addView('foo', {content: 'bar'}); - assert(app.views.pages.hasOwnProperty('foo')); - }); - - it('should add view collection name to view._name', function() { - app.pages.addView('foo', {content: 'bar'}); - assert.equal(app.views.pages.foo._name, 'page'); - }); - - it('should add partial views when partial type is defined', function() { - app.partials.addView('abc', {content: 'xyz'}); - assert(app.views.partials.hasOwnProperty('abc')); - }); - - it('should add layout views when layout type is defined', function() { - app.layouts.addView('foo', {content: 'bar'}); - assert(app.views.layouts.hasOwnProperty('foo')); - }); - - it('should set viewType on renderable views', function() { - app.pages.addView('foo', {content: 'bar'}); - var view = app.pages.getView('foo'); - assert(view.isType('renderable')); - assert(!view.isType('layout')); - assert(!view.isType('partial')); - }); - - it('should set viewType on partial views', function() { - app.partials.addView('foo', {content: 'bar'}); - var view = app.partials.getView('foo'); - assert(view.isType('partial')); - assert(!view.isType('layout')); - assert(!view.isType('renderable')); - }); - - it('should set viewType on layout views', function() { - app.layouts.addView('foo', {content: 'bar'}); - var view = app.layouts.getView('foo'); - assert(view.isType('layout')); - assert(!view.isType('renderable')); - assert(!view.isType('partial')); - }); - }); - - describe('custom constructors', function() { - beforeEach(function() { - var Vinyl = require('vinyl'); - Vinyl.prototype.custom = function(key) { - this[key] = 'nonsense'; - return this; - }; - app = new App({View: Vinyl}); - app.create('pages'); - }); - - it('should create views from key-value pairs:', function() { - app.page('a.hbs', {path: 'a.hbs', content: 'a'}); - app.page('b.hbs', {path: 'b.hbs', content: 'b'}); - app.page('c.hbs', {path: 'c.hbs', content: 'c'}); - var a = app.pages.getView('a.hbs'); - a.custom('foo'); - a.foo.should.equal('nonsense'); - }); - }); - - describe('custom instances', function() { - it('should create views from custom `View` and `Views` instance/ctor:', function() { - var Vinyl = require('vinyl'); - Vinyl.prototype.read = function(file) { - return fs.readFileSync(file.path); - }; - - var Views = App.Views; - var views = new Views({View: Vinyl}); - - views.addView('a.hbs', {path: 'a.hbs', content: 'a'}); - views.addView('b.hbs', {path: 'b.hbs', content: 'b'}); - views.addView('c.hbs', {path: 'c.hbs', content: 'c'}); - - app = new App(); - app.create('pages', views); - - var a = app.pages.getView('a.hbs'); - assert(a instanceof Vinyl); - assert(Vinyl.isVinyl(a)); - assert.equal(typeof a.read, 'function'); - - views.addView('d.hbs', {path: 'd.hbs', content: 'd'}); - var d = app.pages.getView('d.hbs'); - assert(d instanceof Vinyl); - assert(Vinyl.isVinyl(d)); - }); - }); - - describe('chaining', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - it('should create views from key-value pairs:', function() { - app.page('a.hbs', {content: 'a'}); - app.page('b.hbs', {content: 'b'}); - app.page('c.hbs', {content: 'c'}); - app.views.pages.should.have.properties(['a.hbs', 'b.hbs', 'c.hbs']); - assert.equal(app.views.pages['a.hbs'].contents.toString(), 'a'); - }); - - it('should create views from file paths:', function() { - app.page('test/fixtures/pages/a.hbs'); - app.page('test/fixtures/pages/b.hbs'); - app.page('test/fixtures/pages/c.hbs'); - - app.views.pages.should.have.properties([ - 'test/fixtures/pages/a.hbs', - 'test/fixtures/pages/b.hbs', - 'test/fixtures/pages/c.hbs' - ]); - }); - }); - - describe('instance', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - }); - - it('should return the collection instance', function() { - var collection = app.create('pages'); - assert(collection instanceof App.Views); - - collection.option('renameKey', function(key) { - return path.basename(key); - }); - collection - .use(function(views) { - views.read = function(name) { - var view = this.getView(name); - if (!view.contents) { - view.contents = fs.readFileSync(view.path); - } - }; - }); - - collection.addView('test/fixtures/templates/a.tmpl'); - collection.read('a.tmpl'); - assert.equal(collection.getView('a.tmpl').contents.toString(), '<%= name %>'); - }); - }); - - describe('viewType', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - }); - - it('should add collection to the given viewType', function() { - app.create('layout', {viewType: 'layout'}); - assert.equal(app.layouts.options.viewType[0], 'layout'); - }); - - it('should add a collection to multiple viewTypes', function() { - app.create('foo', {viewType: ['layout', 'renderable']}); - assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); - }); - }); - - describe('events', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - }); - - it('should emit `create` when a collection is created:', function() { - app.on('postCreate', function(collection) { - if (collection.options.plural === 'layouts') { - collection.options.foo = 'bar'; - } - }); - - app.create('layout'); - app.layout('one', {path: 'two', contents: '...'}); - assert.equal(app.layouts.options.foo, 'bar'); - }); - }); - - describe('collection instantiation', function() { - it('should expose collection instance methods that are created after instantiation on the app collection loader', function() { - app.create('pages'); - app.pages.use(function(collection) { - collection.define('foo', function(msg) { - return 'foo ' + msg; - }); - }); - - assert(app.pages.foo); - assert.equal(typeof app.pages.foo, 'function'); - }); - }); -}); diff --git a/test/app.data.js b/test/app.data.js deleted file mode 100644 index 41808b62..00000000 --- a/test/app.data.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.data', function() { - beforeEach(function() { - app = new App(); - }); - - it('should set a key-value pair on cache.data:', function() { - app.data('a', 'b'); - assert(app.cache.data.a === 'b'); - }); - - it('should set an object on cache.data:', function() { - app.data({c: 'd'}); - assert(app.cache.data.c === 'd'); - }); - - it('should load data from a file onto cache.data:', function() { - app.data('test/fixtures/data/a.json'); - assert(app.cache.data.a.one.a === 'aaa'); - }); - - it('should load a glob of data onto cache.data:', function() { - app.data('test/fixtures/data/*.json'); - assert(app.cache.data.a.one.a === 'aaa'); - assert(app.cache.data.b.two.b === 'bbb'); - assert(app.cache.data.c.three.c === 'ccc'); - }); - - it('should use `namespace` defined on global opts:', function() { - app.option('namespace', function(key) { - return 'prefix_' + path.basename(key, path.extname(key)); - }); - app.data('test/fixtures/data/*.json'); - assert(app.cache.data.prefix_a.one.a === 'aaa'); - assert(app.cache.data.prefix_b.two.b === 'bbb'); - assert(app.cache.data.prefix_c.three.c === 'ccc'); - }); - - it('should use `namespace` defined on data opts:', function() { - app.data('test/fixtures/data/*.json', { - namespace: function(key) { - return 'prefix_' + path.basename(key, path.extname(key)); - } - }); - assert(app.cache.data.prefix_a.one.a === 'aaa'); - assert(app.cache.data.prefix_b.two.b === 'bbb'); - assert(app.cache.data.prefix_c.three.c === 'ccc'); - }); - - it('should use `renameKey` defined on data opts:', function() { - app.data('test/fixtures/data/*.json', { - renameKey: function(key) { - return 'prefix_' + path.basename(key, path.extname(key)); - } - }); - assert(app.cache.data.prefix_a.one.a === 'aaa'); - assert(app.cache.data.prefix_b.two.b === 'bbb'); - assert(app.cache.data.prefix_c.three.c === 'ccc'); - }); - - it('should extend `cache.data`', function() { - app.data({a: 'aaa', b: 'bbb', c: 'ccc'}); - app.data({x: 'xxx', y: 'yyy', z: 'zzz'}); - assert(app.cache.data.a === 'aaa'); - assert(app.cache.data.b === 'bbb'); - assert(app.cache.data.c === 'ccc'); - assert(app.cache.data.x === 'xxx'); - assert(app.cache.data.y === 'yyy'); - assert(app.cache.data.z === 'zzz'); - }); - - it('should extend the `cache.data` object when the first param is a string.', function() { - app.data('foo', {x: 'xxx', y: 'yyy', z: 'zzz'}); - app.data('bar', {a: 'aaa', b: 'bbb', c: 'ccc'}); - assert(app.cache.data.foo.x === 'xxx'); - assert(app.cache.data.bar.a === 'aaa'); - }); - - it('should be chainable.', function() { - app - .data({x: 'xxx', y: 'yyy', z: 'zzz'}) - .data({a: 'aaa', b: 'bbb', c: 'ccc'}); - - assert(app.cache.data.x === 'xxx'); - assert(app.cache.data.a === 'aaa'); - }); -}); diff --git a/test/app.dest.js b/test/app.dest.js deleted file mode 100644 index 0086bf7e..00000000 --- a/test/app.dest.js +++ /dev/null @@ -1,1092 +0,0 @@ -'use strict'; - -var os = require('os'); -var path = require('path'); -var fs = require('graceful-fs'); -var rimraf = require('rimraf'); -var isWindows = (os.platform() === 'win32'); - -require('mocha'); -var should = require('should'); -var assert = require('assert'); -var expect = require('expect'); - -var spies = require('./support/spy'); -var chmodSpy = spies.chmodSpy; -var statSpy = spies.statSpy; - -var App = require('..'); -var app; - -var bufferStream; -var bufEqual = require('buffer-equal'); -var through = require('through2'); -var File = require('vinyl'); - -var actual = path.join(__dirname, 'actual'); - -var wipeOut = function(cb) { - app = new App(); - spies.setError('false'); - statSpy.reset(); - chmodSpy.reset(); - expect.restoreSpies(); - rimraf(path.join(__dirname, 'actual/'), cb); -}; - -var dataWrap = function(fn) { - return function(data, enc, cb) { - fn(data); - cb(); - }; -}; - -var MASK_MODE = parseInt('777', 8); - -function masked(mode) { - return mode & MASK_MODE; -} - -describe('app.dest', function() { - beforeEach(wipeOut); - afterEach(wipeOut); - - it('should explode on invalid folder (empty)', function(cb) { - var stream; - try { - stream = app.dest(); - } catch (err) { - assert(err && typeof err === 'object'); - should.not.exist(stream); - cb(); - } - }); - - it('should explode on invalid folder (empty string)', function(cb) { - var stream; - try { - stream = app.dest(''); - } catch (err) { - assert(err && typeof err === 'object'); - should.not.exist(stream); - cb(); - } - }); - - it('should pass through writes with cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - cb(); - }; - - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should pass through writes with default cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - cb(); - }; - - var stream = app.dest(path.join(__dirname, 'actual/')); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not write null files', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'actual'); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(false); - cb(); - }; - - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with relative cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'actual'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - cb(); - }; - - var stream = app.dest('actual/', {cwd: path.relative(process.cwd(), __dirname)}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with function and relative cwd', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'actual'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - cb(); - }; - - var stream = app.dest(function(file) { - should.exist(file); - file.should.equal(expectedFile); - return './actual'; - }, {cwd: path.relative(process.cwd(), __dirname)}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - cb(); - }; - - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write streaming files to the right folder', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('655', 8); - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - stat: { - mode: expectedMode - } - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - cb(); - }; - - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - setTimeout(function() { - contentStream.write(expectedContents); - contentStream.end(); - }, 100); - stream.end(); - }); - - it('should write directories to the right folder', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - mode: expectedMode - } - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).isDirectory().should.equal(true); - masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - cb(); - }; - - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should allow piping multiple dests in streaming mode', function(cb) { - var inputPath1 = path.join(__dirname, 'actual/multiple-first'); - var inputPath2 = path.join(__dirname, 'actual/multiple-second'); - var inputBase = path.join(__dirname, 'actual/'); - var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var stream1 = app.dest('actual/', {cwd: __dirname}); - var stream2 = app.dest('actual/', {cwd: __dirname}); - var content = fs.readFileSync(srcPath); - var rename = through.obj(function(file, _, next) { - file.path = inputPath2; - this.push(file); - next(); - }); - - stream1.on('data', function(file) { - file.path.should.equal(inputPath1); - }); - - stream1.pipe(rename).pipe(stream2); - stream2.on('data', function(file) { - file.path.should.equal(inputPath2); - }).once('end', function() { - fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); - fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); - cb(); - }); - - var file = new File({ - base: inputBase, - path: inputPath1, - cwd: __dirname, - contents: content - }); - - stream1.write(file); - stream1.end(); - }); - - it('should write new files with the default user mode', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('0666', 8) & (~process.umask()); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - cb(); - }; - - chmodSpy.reset(); - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write new files with the specified mode', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('744', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - masked(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - cb(); - }; - - chmodSpy.reset(); - var stream = app.dest('actual/', { cwd: __dirname, mode: expectedMode }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should update file mode to match the vinyl mode', function(cb) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var startMode = parseInt('0655', 8); - var expectedMode = parseInt('0722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var onEnd = function() { - assert.equal(masked(fs.lstatSync(expectedPath).mode), expectedMode); - cb(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, startMode); - - var stream = app.dest('actual/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should use different modes for files and directories', function(cb) { - var inputBase = path.join(__dirname, 'fixtures/vinyl'); - var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); - var expectedBase = path.join(__dirname, 'actual/wow'); - var expectedDirMode = parseInt('755', 8); - var expectedFileMode = parseInt('655', 8); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function() { - masked(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); - masked(buffered[0].stat.mode).should.equal(expectedFileMode); - cb(); - }; - - var stream = app.dest('actual/', { - cwd: __dirname, - mode: expectedFileMode, - dirMode: expectedDirMode - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should change to the specified base as string', function(cb) { - var inputBase = path.join(__dirname, 'fixtures/vinyl'); - var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); - - var firstFile = new File({ - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function() { - buffered[0].base.should.equal(inputBase); - cb(); - }; - - var stream = app.dest('actual/', { - cwd: __dirname, - base: inputBase - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should change to the specified base as function', function(cb) { - var inputBase = path.join(__dirname, 'fixtures/vinyl'); - var inputPath = path.join(__dirname, 'fixtures/vinyl/wow/suchempty'); - - var firstFile = new File({ - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function() { - buffered[0].base.should.equal(inputBase); - cb(); - }; - - var stream = app.dest('actual/', { - cwd: __dirname, - base: function(file) { - should.exist(file); - file.path.should.equal(inputPath); - return inputBase; - } - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should report IO errors', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, 0); - - var stream = app.dest('actual/', {cwd: __dirname}); - stream.on('error', function(err) { - err.code.should.equal('EACCES'); - cb(); - }); - stream.write(expectedFile); - }); - - it('should report stat errors', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - spies.setError(function(mod, fn) { - if (fn === 'fstat' && typeof arguments[2] === 'number') { - return new Error('stat error'); - } - }); - - var stream = app.dest('actual/', {cwd: __dirname}); - stream.on('error', function(err) { - err.message.should.equal('stat error'); - cb(); - }); - stream.write(expectedFile); - }); - - it('should report fchmod errors', function(cb) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('722', 8); - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function() { - var callback = arguments[arguments.length - 1]; - callback(new Error('mocked error')); - }); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - var stream = app.dest('actual/', { cwd: __dirname }); - stream.on('error', function(err) { - assert(err); - assert.equal(fchmodSpy.calls.length, 1); - cb(); - }); - stream.write(expectedFile); - }); - - it('should not fchmod a matching file', function(cb) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('711', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var stream = app.dest('actual/', { cwd: __dirname }); - stream.on('end', function() { - assert.equal(fchmodSpy.calls.length, 0); - assert.equal(masked(fs.lstatSync(expectedPath).mode), expectedMode); - cb(); - }); - stream.write(expectedFile); - stream.end(); - }); - - it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(cb) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - - var inputPath = path.join(__dirname, 'fixtures/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/'); - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, 'actual'); - var expectedMode = parseInt('3722', 8); - var normalMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: normalMode - } - }); - - var onEnd = function() { - assert.equal(fchmodSpy.calls.length, 0); - cb(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - var stream = app.dest('actual/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should not overwrite files with overwrite option set to false', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedBase = path.join(__dirname, 'actual'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); - cb(); - }; - - // Write expected file which should not be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = app.dest('actual/', {cwd: __dirname, overwrite: false}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should overwrite files with overwrite option set to true', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, 'actual/test.coffee'); - var expectedBase = path.join(__dirname, 'actual'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); - cb(); - }; - - // This should be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = app.dest('actual/', {cwd: __dirname, overwrite: true}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should create symlinks when the `symlink` attribute is set on the file', function(cb) { - var inputPath = path.join(__dirname, 'fixtures/vinyl/test-create-dir-symlink'); - var inputBase = path.join(__dirname, 'fixtures/vinyl/'); - var inputRelativeSymlinkPath = 'wow'; - - var expectedPath = path.join(__dirname, 'actual/test-create-dir-symlink'); - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null - }); - - // `src()` adds this side-effect with `keepSymlinks` option set to false - inputFile.symlink = inputRelativeSymlinkPath; - - var onEnd = function() { - fs.readlink(buffered[0].path, function() { - buffered[0].symlink.should.equal(inputFile.symlink); - buffered[0].path.should.equal(expectedPath); - cb(); - }); - }; - - var stream = app.dest('actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should emit finish event', function(cb) { - var srcPath = path.join(__dirname, 'fixtures/vinyl/test.coffee'); - var stream = app.dest('actual/', {cwd: __dirname}); - - stream.once('finish', function() { - cb(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: new Buffer('1234567890') - }); - - stream.write(file); - stream.end(); - }); -}); - -describe('dest', function() { - beforeEach(function(cb) { - app = new App(); - rimraf(actual, cb); - }); - - afterEach(function(cb) { - rimraf(actual, cb); - }); - - describe('streams', function() { - it('should return a stream', function(cb) { - var stream = app.dest(path.join(__dirname, 'fixtures/')); - should.exist(stream); - should.exist(stream.on); - cb(); - }); - - it('should return an output stream that writes files', function(cb) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt')); - var outstream = app.dest(actual); - instream.pipe(outstream); - - outstream.on('error', cb); - outstream.on('data', function(file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); - String(file.contents).should.equal('Hello world!'); - }); - outstream.on('end', function() { - fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { - should.not.exist(err); - should.exist(contents); - String(contents).should.equal('Hello world!'); - cb(); - }); - }); - }); - - it('should return an output stream that does not write non-read files', function(cb) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt'), {read: false}); - var outstream = app.dest(actual); - instream.pipe(outstream); - - outstream.on('error', cb); - outstream.on('data', function(file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); - }); - - outstream.on('end', function() { - fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { - should.exist(err); - should.not.exist(contents); - cb(); - }); - }); - }); - - it('should return an output stream that writes streaming files', function(cb) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt'), {buffer: false}); - var outstream = instream.pipe(app.dest(actual)); - - outstream.on('error', cb); - outstream.on('data', function(file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); - }); - outstream.on('end', function() { - fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { - should.not.exist(err); - should.exist(contents); - String(contents).should.equal('Hello world!'); - cb(); - }); - }); - }); - - it('should return an output stream that writes streaming files to new directories', function(cb) { - testWriteDir({}, cb); - }); - - it('should return an output stream that writes streaming files to new directories (buffer: false)', function(cb) { - testWriteDir({buffer: false}, cb); - }); - - it('should return an output stream that writes streaming files to new directories (read: false)', function(cb) { - testWriteDir({read: false}, cb); - }); - - it('should return an output stream that writes streaming files to new directories (read: false, buffer: false)', function(cb) { - testWriteDir({buffer: false, read: false}, cb); - }); - - }); - - describe('ext', function() { - beforeEach(function() { - app = new App(); - app.set('ext', '.txt'); - }); - - afterEach(function() { - app.set('ext', '.html'); - }); - - it('should return a stream', function(cb) { - var stream = app.dest(path.join(__dirname, 'fixtures/')); - should.exist(stream); - should.exist(stream.on); - cb(); - }); - - it('should return an output stream that writes files', function(cb) { - var instream = app.src(path.join(__dirname, 'fixtures/copy/e*.txt')); - var outstream = app.dest(actual); - instream.pipe(outstream); - - outstream.on('error', cb); - outstream.on('data', function(file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); - String(file.contents).should.equal('Hello world!'); - }); - outstream.on('end', function() { - fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { - should.not.exist(err); - should.exist(contents); - String(contents).should.equal('Hello world!'); - cb(); - }); - }); - }); - - it('should return an output stream that does not write non-read files', function(cb) { - var instream = app.src(path.join(__dirname, 'fixtures/dest/*.txt'), {read: false}); - var outstream = app.dest(actual); - instream.pipe(outstream); - - outstream.on('error', cb); - outstream.on('data', function(file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(actual, 'example.txt')); - }); - - outstream.on('end', function() { - fs.readFile(path.join(actual, 'example.txt'), function(err, contents) { - should.exist(err); - should.not.exist(contents); - cb(); - }); - }); - }); - }); - - function testWriteDir(srcOptions, cb) { - var instream = app.src(path.join(__dirname, 'fixtures/generic'), srcOptions); - var outstream = instream.pipe(app.dest(actual)); - - outstream.on('error', cb); - outstream.on('data', function(file) { - // data should be re-emitted correctly - should.exist(file); - should.exist(file.path); - path.join(file.path, '').should.equal(path.join(actual, 'generic')); - }); - - outstream.on('end', function() { - fs.exists(path.join(actual, 'generic'), function(exists) { - /* jshint expr: true */ - should(exists).be.ok; - /* jshint expr: false */ - cb(); - }); - }); - } -}); - diff --git a/test/app.doc.js b/test/app.doc.js index 08da88fd..abf08797 100644 --- a/test/app.doc.js +++ b/test/app.doc.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app; -describe('app.doc', function() { +describe('app', function() { beforeEach(function() { app = generate(); app.create('docs'); diff --git a/test/app.docs.js b/test/app.docs.js index b5bb5172..9500eab9 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app; -describe('app.docs', function() { +describe('app', function() { beforeEach(function() { app = generate(); app.create('docs'); @@ -19,7 +19,7 @@ describe('app.docs', function() { 'b.hbs': {path: 'b.hbs', content: 'b'}, 'c.hbs': {path: 'c.hbs', content: 'c'}, }); - assert(Object.keys(app.views.docs).length === 3); + assert.each(Object.keys(app.views.docs).length, 3); }); }); }); diff --git a/test/app.engines.js b/test/app.engines.js deleted file mode 100644 index 562aeeb8..00000000 --- a/test/app.engines.js +++ /dev/null @@ -1,160 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.engines', function() { - beforeEach(function() { - app = new App(); - }); - - it('should throw an error when engine name is invalid:', function() { - (function() { - app.engine(null, {}); - }).should.throw('expected engine ext to be a string or array.'); - }); - - it('should register an engine to the given extension', function() { - app.engine('hbs', function() {}); - assert(typeof app.engines['.hbs'] === 'object'); - }); - - it('should set an engine with the given extension', function() { - var hbs = function() {}; - hbs.render = function() {}; - hbs.renderFile = function() {}; - app.engine('hbs', hbs); - assert(app.engines['.hbs']); - assert(app.engines['.hbs'].renderFile); - assert(app.engines['.hbs'].render); - }); - - it('should get an engine:', function() { - app.engine('hbs', function() {}); - var hbs = app.engine('hbs'); - assert(typeof hbs === 'object'); - assert(hbs.hasOwnProperty('render')); - assert(hbs.hasOwnProperty('compile')); - }); - - it('should return undefined if no engine is found:', function() { - var hbs = app.getEngine(); - assert.equal(typeof hbs, 'undefined'); - }); - - it('should register multiple engines to the given extension', function() { - app.engine(['hbs', 'md'], function() {}); - assert(typeof app.engines['.hbs'] === 'object'); - assert(typeof app.engines['.md'] === 'object'); - }); -}); - -describe('engines', function() { - beforeEach(function() { - app = new App(); - app.create('pages'); - app.pages('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); - app.pages('bar.tmpl', {content: 'A <%= letter %> {{ letter }} C'}); - }); - - it('should register an engine:', function() { - app.engine('a', {render: function() {}}); - app.engines.should.have.property('.a'); - }); - - it('should use custom delimiters:', function(cb) { - app.engine('tmpl', require('engine-base'), { - delims: ['{{', '}}'] - }); - app.render('foo.tmpl', {letter: 'B'}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('A <%= letter %> B C'); - cb(); - }); - }); - - it('should override individual delims values:', function(cb) { - app.engine('tmpl', require('engine-base'), { - interpolate: /\{{([^}]+)}}/g, - evaluate: /\{{([^}]+)}}/g, - escape: /\{{-([^}]+)}}/g - }); - app.render('bar.tmpl', {letter: 'B'}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('A <%= letter %> B C'); - cb(); - }); - }); - - it('should get an engine:', function() { - app.engine('a', { - render: function() {} - }); - var a = app.engine('a'); - a.should.have.property('render'); - }); -}); - -describe('engine selection:', function() { - beforeEach(function(cb) { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.engine('hbs', require('engine-handlebars')); - app.create('pages'); - cb(); - }); - - it('should get the engine from file extension:', function(cb) { - app.page('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on the collection:', function(cb) { - app.create('posts', {engine: 'hbs'}); - - app.post('a', {content: '{{a}}', locals: {a: 'b'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on the view:', function(cb) { - app.create('posts'); - app.post('a', {content: '{{a}}', engine: 'hbs', locals: {a: 'b'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on `view.data`:', function(cb) { - app.create('posts'); - app.post('a', {content: '{{a}}', locals: {a: 'b'}, data: {engine: 'hbs'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on render locals:', function(cb) { - app.create('posts'); - app.post('a', {content: '{{a}}', locals: {a: 'b'}}) - .render({engine: 'hbs'}, function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); -}); diff --git a/test/app.env.js b/test/app.env.js deleted file mode 100644 index af87a498..00000000 --- a/test/app.env.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -require('mocha'); -var path = require('path'); -var assert = require('assert'); -var Generate = require('..'); -var generate; - -var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); - -describe('app.env', function() { - describe('createEnv', function() { - beforeEach(function() { - generate = new Generate(); - generate.option('toAlias', function(name) { - return name.replace(/^generate-/, ''); - }); - }); - - it('should add an env object to the instance', function() { - var fn = function() {}; - generate.createEnv('foo', fn); - assert(generate.env); - }); - - it('should take options as the second arg', function() { - var fn = function() {}; - generate.createEnv('foo', {}, fn); - assert(generate.env); - }); - - it('should prime `env` if it doesn\'t exist', function() { - var fn = function() {}; - delete generate.env; - generate.createEnv('foo', {}, fn); - assert(generate.env); - }); - - it('should add an alias to the env object', function() { - var fn = function() {}; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.alias, 'foo'); - }); - - it('should not use `prefix` when function is passed', function() { - var fn = function() {}; - delete generate.prefix; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.name, 'foo'); - }); - - it('should use not use custom `prefix` when function is passed', function() { - var fn = function() {}; - generate.prefix = 'whatever'; - generate.createEnv('foo', {}, fn); - assert.equal(generate.env.name, 'foo'); - }); - - it('should try to resolve a path passed as the second arg', function() { - generate.createEnv('generate-foo', fixtures('verbfile.js')); - assert.equal(generate.env.alias, 'foo'); - assert.equal(generate.env.name, 'generate-foo'); - }); - }); -}); diff --git a/test/app.events.js b/test/app.events.js deleted file mode 100644 index fb95c980..00000000 --- a/test/app.events.js +++ /dev/null @@ -1,199 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.events', function() { - describe('events', function() { - beforeEach(function() { - app = new App(); - }); - - it('should listen for an event:', function() { - app = new App(); - app.on('foo', function() {}); - assert(Array.isArray(app._callbacks['$foo'])); - }); - - it('should emit an event:', function(cb) { - app = new App(); - app.on('foo', function(val) { - assert.equal(val, 'bar'); - cb(); - }); - assert(Array.isArray(app._callbacks['$foo'])); - app.emit('foo', 'bar'); - }); - - it('should listen for `view` events:', function(cb) { - app = new App(); - var count = 0; - - app.on('view', function(view) { - view.foo = 'bar'; - count++; - }); - - var view = app.view({path: 'a', content: 'b'}); - assert.equal(view.foo, 'bar'); - assert.equal(count, 1); - cb(); - }); - }); - - describe('onLoad', function() { - beforeEach(function() { - app = new App(); - }); - - describe('app.collection', function() { - it('should emit a `view` event when view is created', function(cb) { - var collection = app.collection(); - - app.on('view', function(view) { - assert.equal(view.path, 'blog/foo.js'); - cb(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert.equal(view.path, 'blog/foo.js'); - next(); - }); - - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - - it('should emit an onLoad event when view is created', function(cb) { - var collection = app.collection(); - - app.on('onLoad', function(view) { - assert.equal(view.path, 'blog/foo.js'); - cb(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert.equal(view.path, 'blog/foo.js'); - next(); - }); - - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - - it('should not emit an onLoad event when view is created and `app.options.onLoad` is `false', function(cb) { - var emitted = false; - var handled = false; - var collection = app.collection(); - app.options.onLoad = false; - - app.on('onLoad', function(view) { - emitted = true; - }); - - app.onLoad('blog/:title', function(view, next) { - handled = true; - next(); - }); - - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - setImmediate(function() { - assert.equal(typeof collection.views.whatever, 'object'); - assert.equal(emitted, false); - assert.equal(handled, false); - cb(); - }); - }); - - it('should not emit an onLoad event when view is created and `collection.options.onLoad` is `false', function(cb) { - var emitted = false; - var handled = false; - var collection = app.collection(); - collection.options.onLoad = false; - - app.on('onLoad', function(view) { - emitted = true; - }); - - app.onLoad('blog/:title', function(view, next) { - handled = true; - next(); - }); - - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - setImmediate(function() { - assert.equal(typeof collection.views.whatever, 'object'); - assert.equal(emitted, false); - assert.equal(handled, false); - cb(); - }); - }); - - it('should not emit an onLoad event when view is created and `view.options.onLoad` is `false', function(cb) { - var emitted = false; - var handled = false; - var collection = app.collection(); - - app.on('onLoad', function(view) { - emitted = true; - }); - - app.onLoad('blog/:title', function(view, next) { - handled = true; - next(); - }); - - collection.addView('whatever', { - options: { - onLoad: false - }, - path: 'blog/foo.js', - content: 'bar baz' - }); - - setImmediate(function() { - assert.equal(typeof collection.views.whatever, 'object'); - assert.equal(emitted, false); - assert.equal(handled, false); - cb(); - }); - }); - }); - - describe('view collections', function() { - it('should emit a view event when view is created', function(cb) { - app.create('posts'); - - app.on('view', function(view) { - assert.equal(view.path, 'blog/foo.js'); - cb(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert.equal(view.path, 'blog/foo.js'); - next(); - }); - - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - - it('should emit an onLoad event when view is created', function(cb) { - app.create('posts'); - - app.on('onLoad', function(view) { - assert.equal(view.path, 'blog/foo.js'); - cb(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert.equal(view.path, 'blog/foo.js'); - next(); - }); - - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - }); - }); -}); diff --git a/test/app.extendWith.js b/test/app.extendWith.js index 14c7e780..49472494 100644 --- a/test/app.extendWith.js +++ b/test/app.extendWith.js @@ -1,73 +1,140 @@ 'use strict'; require('mocha'); +require('generate-foo/generator.js'); + var path = require('path'); var assert = require('assert'); -var gm = require('global-modules'); +var npm = require('npm-install-global'); var utils = require('generator-util'); -var commands = require('spawn-commands'); -var Generate = require('..'); -var generate; -require('generate-foo'); +var gm = require('global-modules'); +var isAbsolute = require('is-absolute'); +var resolve = require('resolve'); +var Base = require('..'); +var base; var fixture = path.resolve.bind(path, __dirname, 'fixtures/generators'); -function install(name, cb) { - commands({ - args: ['install', '-g', '--silent', name], - cmd: 'npm' - }, cb); +function resolver(search, app) { + try { + if (isAbsolute(search.name)) { + search.name = require.resolve(search.name); + } else { + search.name = resolve.sync(search.name, {basedir: gm}); + } + search.app = app.register(search.name, search.name); + } catch (err) {} } -describe('app.extendWith', function() { +describe('.extendWith', function() { before(function(cb) { if (!utils.exists(path.resolve(gm, 'generate-bar'))) { - install('generate-bar', cb); + npm.install('generate-bar', cb); } else { cb(); } }); beforeEach(function() { - generate = new Generate(); - generate.option('toAlias', function(name) { + base = new Base(); + base.option('toAlias', function(name) { return name.replace(/^generate-/, ''); }); + + base.on('unresolved', resolver); }); it('should throw an error when a generator is not found', function(cb) { - generate.register('foo', function(app) { - app.extendWith('fofoofofofofof'); - }); - try { - generate.getGenerator('foo'); + base.register('foo', function(app) { + app.extendWith('fofoofofofofof'); + }); + + base.getGenerator('foo'); cb(new Error('expected an error')); } catch (err) { - assert.equal(err.message, 'cannot find generator fofoofofofofof'); + assert.equal(err.message, 'cannot find generator: "fofoofofofofof"'); cb(); } }); + it('should extend a generator with settings in the default generator', function(cb) { + var count = 0; + + base.register('foo', function(app) { + app.task('default', function(next) { + assert.equal(app.options.foo, 'bar'); + assert.equal(app.cache.data.foo, 'bar'); + count++; + next(); + }); + }); + + base.register('default', function(app) { + app.data({foo: 'bar'}); + app.option({foo: 'bar'}); + }); + + base.generate('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should not extend tasks by default', function(cb) { + var count = 0; + + base.register('foo', function(app) { + app.task('default', function(next) { + assert(app.tasks.hasOwnProperty('default')); + assert(!app.tasks.hasOwnProperty('a')); + assert(!app.tasks.hasOwnProperty('b')); + assert(!app.tasks.hasOwnProperty('c')); + count++; + next(); + }); + }); + + base.register('default', function(app) { + app.task('a', function(next) { + next(); + }); + app.task('b', function(next) { + next(); + }); + app.task('c', function(next) { + next(); + }); + }); + + base.generate('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + it('should get a named generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + + base.register('foo', function(app) { app.extendWith('bar'); count++; }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); assert.equal(count, 1); cb(); }); it('should extend a generator with a named generator', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -79,17 +146,17 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should extend a generator with an array of generators', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -101,24 +168,24 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('a', function() {}); }); - generate.register('baz', function(app) { + base.register('baz', function(app) { app.task('b', function() {}); }); - generate.register('qux', function(app) { + base.register('qux', function(app) { app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); describe('invoke generators', function(cb) { it('should extend with a generator instance', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { var bar = app.getGenerator('bar'); app.extendWith(bar); @@ -128,18 +195,18 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.isBar = true; app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke a named generator', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.extendWith('bar'); assert(app.tasks.hasOwnProperty('a')); @@ -148,19 +215,19 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); }); describe('extend generators', function(cb) { it('should extend a generator with a generator invoked by name', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -172,17 +239,17 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should extend a generator with a generator invoked by alias', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -194,18 +261,18 @@ describe('app.extendWith', function() { cb(); }); - generate.register('generate-qux', function(app) { + base.register('generate-qux', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - var qux = generate.getGenerator('qux'); - generate.getGenerator('foo'); + base.getGenerator('qux'); + base.getGenerator('foo'); }); it('should extend with a generator invoked by filepath', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -217,11 +284,11 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should extend with a generator invoked from node_modules by name', function(cb) { - generate.register('abc', function(app) { + base.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -233,11 +300,46 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('abc'); + base.getGenerator('abc'); + }); + + it('should extend with a generator invoked from node_modules by name on a default instance', function() { + var app = new Base(); + + app.on('unresolved', resolver); + app.option('toAlias', function(name) { + return name.replace(/^generate-/, ''); + }); + + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.extendWith('generate-foo'); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); + }); + + it('should use a generator from node_modules as a plugin', function() { + var app = new Base(); + + app.option('toAlias', function(name) { + return name.replace(/^generate-/, ''); + }); + + assert(!app.tasks.a); + assert(!app.tasks.b); + assert(!app.tasks.c); + + app.use(require('generate-foo')); + assert(app.tasks.a); + assert(app.tasks.b); + assert(app.tasks.c); }); it('should extend with a generator invoked from global modules by name', function(cb) { - generate.register('zzz', function(app) { + base.register('zzz', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -249,13 +351,13 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('zzz'); + base.getGenerator('zzz'); }); it('should extend with a generator invoked from global modules by alias', function(cb) { - generate.register('generate-bar'); + base.register('generate-bar'); - generate.register('zzz', function(app) { + base.register('zzz', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -267,13 +369,13 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('zzz'); + base.getGenerator('zzz'); }); }); describe('sub-generators', function(cb) { it('should invoke sub-generators', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('one', function(app) { app.task('a', function() {}); }); @@ -289,11 +391,11 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke a sub-generator on the base instance', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.extendWith('bar.sub'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -301,7 +403,7 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.register('sub', function(sub) { sub.task('a', function() {}); sub.task('b', function() {}); @@ -309,11 +411,11 @@ describe('app.extendWith', function() { }); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke a sub-generator from node_modules by name', function(cb) { - generate.register('abc', function(app) { + base.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -325,17 +427,17 @@ describe('app.extendWith', function() { cb(); }); - generate.register('xyz', function(app) { + base.register('xyz', function(app) { app.extendWith('generate-foo'); }); - generate.getGenerator('abc'); + base.getGenerator('abc'); }); it('should invoke a sub-generator from node_modules by alias', function(cb) { - generate.register('generate-foo'); + base.register('generate-foo'); - generate.register('abc', function(app) { + base.register('abc', function(app) { assert(!app.tasks.a); assert(!app.tasks.b); assert(!app.tasks.c); @@ -347,15 +449,15 @@ describe('app.extendWith', function() { cb(); }); - generate.register('xyz', function(app) { + base.register('xyz', function(app) { app.extendWith('foo'); }); - generate.getGenerator('abc'); + base.getGenerator('abc'); }); it('should invoke an array of sub-generators', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('one', function(app) { app.task('a', function() {}); }); @@ -370,11 +472,11 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke sub-generators from sub-generators', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('one', function(sub) { sub.register('a', function(a) { a.task('a', function() {}); @@ -395,11 +497,11 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke an array of sub-generators from sub-generators', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('one', function(sub) { sub.register('a', function(a) { a.task('a', function() {}); @@ -419,11 +521,11 @@ describe('app.extendWith', function() { cb(); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke sub-generator that invokes another generator', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.extendWith('bar'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -431,21 +533,21 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.extendWith('baz'); }); - generate.register('baz', function(app) { + base.register('baz', function(app) { app.task('a', function() {}); app.task('b', function() {}); app.task('c', function() {}); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke sub-generator that invokes another sub-generator', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.extendWith('bar.sub'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -453,13 +555,13 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.register('sub', function(sub) { sub.extendWith('baz.sub'); }); }); - generate.register('baz', function(app) { + base.register('baz', function(app) { app.register('sub', function(sub) { sub.task('a', function() {}); sub.task('b', function() {}); @@ -467,11 +569,11 @@ describe('app.extendWith', function() { }); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); it('should invoke sub-generator that invokes another sub-generator', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.extendWith('bar.sub'); assert(app.tasks.hasOwnProperty('a')); assert(app.tasks.hasOwnProperty('b')); @@ -479,13 +581,13 @@ describe('app.extendWith', function() { cb(); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.register('sub', function(sub) { sub.extendWith('baz.sub'); }); }); - generate.register('baz', function(app) { + base.register('baz', function(app) { app.register('sub', function(sub) { sub.task('a', function() {}); sub.task('b', function() {}); @@ -493,7 +595,7 @@ describe('app.extendWith', function() { }); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); }); }); }); diff --git a/test/app.generate.js b/test/app.generate.js index 843ad25b..b1403d96 100644 --- a/test/app.generate.js +++ b/test/app.generate.js @@ -2,61 +2,160 @@ require('mocha'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var config = require('base-config-process'); +var Base = require('..'); +var base; -describe('app.generate', function() { +describe('.generate', function() { beforeEach(function() { - generate = new Generate(); + base = new Base(); + }); + + describe('config.process', function(cb) { + it('should run tasks when the base-config plugin is used', function(cb) { + base.use(config()); + var count = 0; + base.task('default', function(next) { + count++; + next(); + }); + + base.generate('default', function(err) { + assert(!err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should handle errors when the base-config plugin is used', function(cb) { + base.use(config()); + var count = 0; + base.task('default', function(next) { + count++; + next(new Error('fooo')); + }); + + base.generate('default', function(err) { + assert.equal(err.message, 'fooo'); + assert.equal(count, 1); + cb(); + }); + }); + + it('should handle config errors when the base-config plugin is used', function(cb) { + base.use(config()); + var count = 0; + + base.config.map('foo', function(val, key, config, next) { + count++; + next(new Error('fooo')); + }); + + base.set('cache.config', {foo: true}); + base.task('default', function(next) { + count--; + next(); + }); + + base.generate('default', function(err) { + assert(err); + assert.equal(err.message, 'fooo'); + assert.equal(count, 1); + cb(); + }); + }); }); describe('generators', function(cb) { it('should throw an error when a generator is not found', function(cb) { - generate.generate('fdsslsllsfjssl', function(err) { + base.generate('fdsslsllsfjssl', function(err) { assert(err); assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); cb(); }); }); + it('should *not* throw an error when the default task is not found', function(cb) { + base.register('foo', function() {}); + base.generate('foo:default', function(err) { + assert(!err); + cb(); + }); + }); + + it('should not throw an error when a default generator is not found', function(cb) { + base.generate('default', function(err) { + assert(!err); + cb(); + }); + }); + + it('should not throw an error when a default task and default generator is not found', function(cb) { + base.generate('default:default', function(err) { + assert(!err); + cb(); + }); + }); + // special case it('should throw an error when a generator is not found in argv.cwd', function(cb) { - generate.option('cwd', 'foo/bar/baz'); - generate.generate('sflsjljskksl', function(err) { + base.option('cwd', 'foo/bar/baz'); + base.generate('sflsjljskksl', function(err) { assert(err); assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz"', err.message); cb(); }); }); - it('should not reformat error messages that are not about invalid tasks', function(cb) { - generate.task('default', function(cb) { - cb(new Error('whatever')); + it('should throw an error when a task is not found (task array)', function(cb) { + var fn = console.error; + var res = []; + console.error = function(msg) { + res.push(msg); + }; + base.register('fdsslsllsfjssl', function() {}); + base.generate('fdsslsllsfjssl', ['foo'], function(err) { + console.error = fn; + if (err) return cb(err); + assert.equal(res[0], 'Cannot find task: "foo" in generator: "fdsslsllsfjssl"'); + cb(); }); + }); - generate.generate('default', function(err) { - assert(err); - assert.equal(err.message, 'whatever'); + it('should throw an error when a task is not found (task string)', function(cb) { + var fn = console.error; + var res = []; + console.error = function(msg) { + res.push(msg); + }; + base.register('fdsslsllsfjssl', function() {}); + base.generate('fdsslsllsfjssl:foo', function(err) { + console.error = fn; + if (err) return cb(err); + assert.equal(res[0], 'Cannot find task: "foo" in generator: "fdsslsllsfjssl"'); cb(); }); }); - it('should throw an error when a task is not found', function(cb) { - generate.register('fdsslsllsfjssl', function() {}); - generate.generate('fdsslsllsfjssl', ['foo'], function(err) { + it('should not reformat error messages that are not about invalid tasks', function(cb) { + base.task('default', function(cb) { + cb(new Error('whatever')); + }); + + base.generate('default', function(err) { assert(err); - assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + assert.equal(err.message, 'whatever'); cb(); }); }); it('should not throw an error when the default task is not defined', function(cb) { - generate.register('foo', function() {}); - generate.register('bar', function() {}); - generate.generate('foo', ['default'], function(err) { + base.register('foo', function() {}); + base.register('bar', function() {}); + base.generate('foo', ['default'], function(err) { if (err) return cb(err); - generate.generate('bar', function(err) { + base.generate('bar', function(err) { if (err) return cb(err); cb(); @@ -65,58 +164,58 @@ describe('app.generate', function() { }); it('should run a task on the instance', function(cb) { - generate.task('abc123', function(next) { + base.task('abc123', function(next) { next(); }); - generate.generate('abc123', function(err) { + base.generate('abc123', function(err) { assert(!err); cb(); }); }); it('should run same-named task instead of a generator', function(cb) { - generate.register('123xyz', function(app) { + base.register('123xyz', function(app) { cb(new Error('expected the task to run first')); }); - generate.task('123xyz', function() { + base.task('123xyz', function() { cb(); }); - generate.generate('123xyz', function(err) { + base.generate('123xyz', function(err) { assert(!err); }); }); it('should run a task instead of a generator with a default task', function(cb) { - generate.register('123xyz', function(app) { + base.register('123xyz', function(app) { app.task('default', function() { cb(new Error('expected the task to run first')); }); }); - generate.task('123xyz', function() { + base.task('123xyz', function() { cb(); }); - generate.generate('123xyz', function(err) { + base.generate('123xyz', function(err) { assert(!err); }); }); it('should run a task on a same-named generator when the task is specified', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.task('foo', function() { + base.task('foo', function() { cb(new Error('expected the generator to run')); }); - generate.generate('foo:default', function(err) { + base.generate('foo:default', function(err) { assert(!err); assert.equal(count, 1); cb(); @@ -125,25 +224,25 @@ describe('app.generate', function() { it('should run an array of tasks that includes a same-named generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('baz', function(next) { count++; next(); }); }); - generate.task('foo', function() { + base.task('foo', function() { cb(new Error('expected the generator to run')); }); - generate.generate(['foo:default', 'bar:baz'], function(err) { + base.generate(['foo:default', 'bar:baz'], function(err) { assert(!err); assert.equal(count, 2); cb(); @@ -151,29 +250,29 @@ describe('app.generate', function() { }); it('should run a generator from a task with the same name', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function() { cb(); }); }); - generate.task('foo', function(cb) { - generate.generate('foo', cb); + base.task('foo', function(cb) { + base.generate('foo', cb); }); - generate.build('foo', function(err) { + base.build('foo', function(err) { if (err) cb(err); - }) + }); }); it('should run the default task on a generator', function(cb) { - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { next(); }); }); - generate.generate('foo', function(err) { + base.generate('foo', function(err) { assert(!err); cb(); }); @@ -181,20 +280,20 @@ describe('app.generate', function() { it('should run a stringified array of tasks on the instance', function(cb) { var count = 0; - generate.task('a', function(next) { + base.task('a', function(next) { count++; next(); }); - generate.task('b', function(next) { + base.task('b', function(next) { count++; next(); }); - generate.task('c', function(next) { + base.task('c', function(next) { count++; next(); }); - generate.generate('a,b,c', function(err) { + base.generate('a,b,c', function(err) { assert.equal(count, 3); assert(!err); cb(); @@ -203,20 +302,20 @@ describe('app.generate', function() { it('should run an array of tasks on the instance', function(cb) { var count = 0; - generate.task('a', function(next) { + base.task('a', function(next) { count++; next(); }); - generate.task('b', function(next) { + base.task('b', function(next) { count++; next(); }); - generate.task('c', function(next) { + base.task('c', function(next) { count++; next(); }); - generate.generate(['a', 'b', 'c'], function(err) { + base.generate(['a', 'b', 'c'], function(err) { if (err) return cb(err); assert.equal(count, 3); assert(!err); @@ -224,47 +323,210 @@ describe('app.generate', function() { }); }); + it('should run the default task on a registered generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.generate('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of generators', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.register('bar', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.generate(['foo', 'bar'], function(err) { + if (err) return cb(err); + assert.equal(count, 2); + cb(); + }); + }); + + it('should run the default task on the default generator', function(cb) { + var count = 0; + base.register('default', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.generate(function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task on a registered generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('abc', function(next) { + count++; + next(); + }); + }); + + base.generate('foo:abc', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('a', function(next) { + count++; + next(); + }); + + app.task('b', function(next) { + count++; + next(); + }); + + app.task('c', function(next) { + count++; + next(); + }); + }); + + base.generate('foo:a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + it('should run the default tasks on an array of generators', function(cb) { var count = 0; - generate.register('a', function(app) { + base.register('a', function(app) { this.task('default', function(cb) { count++; cb(); }); }); - generate.register('b', function(app) { + base.register('b', function(app) { this.task('default', function(cb) { count++; cb(); }); }); - generate.register('c', function(app) { + base.register('c', function(app) { this.task('default', function(cb) { count++; cb(); }); }); - generate.generate(['a', 'b', 'c'], function(err) { + base.generate(['a', 'b', 'c'], function(err) { if (err) return cb(err); assert.equal(count, 3); assert(!err); cb(); }); }); + }); + + describe('options', function(cb) { + it('should pass options to generator.options', function(cb) { + var count = 0; + base.register('default', function(app, base, env, options) { + app.task('default', function(next) { + count++; + assert.equal(app.options.foo, 'bar'); + next(); + }); + }); + + base.generate({foo: 'bar'}, function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should expose options on generator options', function(cb) { + var count = 0; + base.register('default', function(app, base, env, options) { + app.task('default', function(next) { + count++; + assert.equal(options.foo, 'bar'); + next(); + }); + }); + + base.generate({foo: 'bar'}, function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should not mutate options on parent instance', function(cb) { + var count = 0; + base.register('default', function(app, base, env, options) { + app.task('default', function(next) { + count++; + assert.equal(options.foo, 'bar'); + assert(!base.options.foo); + next(); + }); + }); + base.generate({foo: 'bar'}, function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + }); + + describe('default tasks', function(cb) { it('should run the default task on the default generator', function(cb) { var count = 0; - generate.register('default', function(app) { + base.register('default', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generate(function(err) { + base.generate(function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -273,23 +535,25 @@ describe('app.generate', function() { it('should run the default task on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generate('foo', function(err) { + base.generate('foo', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); }); }); + }); + describe('specified tasks', function(cb) { it('should run the specified task on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -301,7 +565,7 @@ describe('app.generate', function() { }); }); - generate.generate('foo', ['abc'], function(err) { + base.generate('foo', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -310,7 +574,7 @@ describe('app.generate', function() { it('should run an array of tasks on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -332,7 +596,7 @@ describe('app.generate', function() { }); }); - generate.generate('foo', 'a,b,c', function(err) { + base.generate('foo', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -340,10 +604,10 @@ describe('app.generate', function() { }); }); - describe('generate sub-generators', function(cb) { + describe('sub-generators', function(cb) { it('should run the default task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -357,16 +621,39 @@ describe('app.generate', function() { }); }); - generate.generate('foo.sub', function(err) { + base.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); }); }); - it('should run the specified task on a registered sub-generator', function(cb) { + it('should run the specified task string on a registered sub-generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + base.generate('foo.sub:abc', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the specified task array on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -380,16 +667,49 @@ describe('app.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + base.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); }); }); + it('should run an of stringified-tasks on a registered sub-generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + base.generate('foo.bar:a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + it('should run an array of tasks on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -413,7 +733,7 @@ describe('app.generate', function() { }); }); - generate.generate('foo.bar', ['a', 'b', 'c'], function(err) { + base.generate('foo.bar', ['a', 'b', 'c'], function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -422,7 +742,7 @@ describe('app.generate', function() { it('should run an multiple tasks on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -446,28 +766,85 @@ describe('app.generate', function() { }); }); - generate.generate('foo.bar', 'a,b,c', function(err) { + base.generate('foo.bar', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); }); }); + + it('should run an multiple tasks on a registered sub-generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + base.register('qux', function(app) { + app.register('fez', function(fez) { + fez.task('default', function(next) { + count++; + next(); + }); + + fez.task('a', function(next) { + count++; + next(); + }); + + fez.task('b', function(next) { + count++; + next(); + }); + + fez.task('c', function(next) { + count++; + next(); + }); + }); + }); + + base.generate(['foo.bar:a,b,c', 'qux.fez:a,b,c'], function(err) { + if (err) return cb(err); + assert.equal(count, 6); + cb(); + }); + }); }); describe('cross-generator', function(cb) { it('should run a generator from another generator', function(cb) { var res = ''; - generate.register('foo', function(app, two) { + base.register('foo', function(app, two) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'foo > sub > default '; - generate.generate('bar.sub', next); + base.generate('bar.sub', next); }); }); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'bar > sub > default '; @@ -476,7 +853,7 @@ describe('app.generate', function() { }); }); - generate.generate('foo.sub', function(err) { + base.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(res, 'foo > sub > default bar > sub > default '); cb(); @@ -485,7 +862,7 @@ describe('app.generate', function() { it('should run the specified task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -499,7 +876,7 @@ describe('app.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + base.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -509,34 +886,39 @@ describe('app.generate', function() { describe('events', function(cb) { it('should emit generate', function(cb) { - generate.on('generate', function() { - cb(); + var count = 0; + + base.on('generate', function() { + count++; }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); }); sub.task('abc', function(next) { + count++; next(); }); }); }); - generate.generate('foo.sub', ['abc'], function(err) { + base.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); + assert.equal(count, 2); + cb(); }); }); it('should expose the generator alias as the first parameter', function(cb) { - generate.on('generate', function(name) { + base.on('generate', function(name) { assert.equal(name, 'sub'); cb(); }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -548,18 +930,18 @@ describe('app.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + base.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); it('should expose the tasks array as the second parameter', function(cb) { - generate.on('generate', function(name, tasks) { + base.on('generate', function(name, tasks) { assert.deepEqual(tasks, ['abc']); cb(); }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -571,7 +953,7 @@ describe('app.generate', function() { }); }); - generate.generate('foo.sub', ['abc'], function(err) { + base.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); }); }); diff --git a/test/app.generateEach.js b/test/app.generateEach.js index 74542f3c..1964250f 100644 --- a/test/app.generateEach.js +++ b/test/app.generateEach.js @@ -2,48 +2,269 @@ require('mocha'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var config = require('base-config-process'); +var Base = require('..'); +var base; -describe('app.generateEach', function() { +describe('.generate', function() { beforeEach(function() { - generate = new Generate(); + base = new Base(); + }); + + describe('config.process', function(cb) { + it('should run tasks when the base-config plugin is used', function(cb) { + base.use(config()); + var count = 0; + base.task('default', function(next) { + count++; + next(); + }); + + base.generate('default', function(err) { + assert(!err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run handle errors when the base-config plugin is used', function(cb) { + base.use(config()); + var count = 0; + base.task('default', function(next) { + count++; + next(new Error('fooo')); + }); + + base.generate('default', function(err) { + assert.equal(err.message, 'fooo'); + assert.equal(count, 1); + cb(); + }); + }); + + it('should handle config errors when the base-config plugin is used', function(cb) { + base.use(config()); + var count = 0; + base.config.map('foo', function(val, key, config, next) { + count++; + next(new Error('fooo')); + }); + + base.set('cache.config', {foo: true}); + + base.task('default', function(next) { + count--; + next(); + }); + + base.generate('default', function(err) { + assert(err); + assert.equal(err.message, 'fooo'); + assert.equal(count, 1); + cb(); + }); + }); }); describe('generators', function(cb) { it('should throw an error when a generator is not found', function(cb) { - generate.generateEach('fdsslsllsfjssl', function(err) { + base.generate('fdsslsllsfjssl', function(err) { assert(err); assert.equal('Cannot find generator: "fdsslsllsfjssl"', err.message); cb(); }); }); + it('should *not* throw an error when the default task is not found', function(cb) { + base.register('foo', function() {}); + base.generate('foo:default', function(err) { + assert(!err); + cb(); + }); + }); + + it('should not throw an error when a default generator is not found', function(cb) { + base.generate('default', function(err) { + assert(!err); + cb(); + }); + }); + + it('should not throw an error when a default task and default generator is not found', function(cb) { + base.generate('default:default', function(err) { + assert(!err); + cb(); + }); + }); + // special case it('should throw an error when a generator is not found in argv.cwd', function(cb) { - generate.option('cwd', 'foo/bar/baz'); - generate.generateEach('sflsjljskksl', function(err) { + base.option('cwd', 'foo/bar/baz'); + base.generate('sflsjljskksl', function(err) { assert(err); - assert.equal(err.message, 'Cannot find generator: "sflsjljskksl" in "foo/bar/baz"'); + assert.equal('Cannot find generator: "sflsjljskksl" in "foo/bar/baz"', err.message); cb(); }); }); - it('should throw an error when a task is not found', function(cb) { - generate.register('fdsslsllsfjssl', function() {}); - generate.generateEach('fdsslsllsfjssl:foo', function(err) { + it('should not reformat error messages that are not about invalid tasks', function(cb) { + base.task('default', function(cb) { + cb(new Error('whatever')); + }); + + base.generate('default', function(err) { assert(err); - assert.equal('Cannot find task: "foo" in generator: "fdsslsllsfjssl"', err.message); + assert.equal(err.message, 'whatever'); cb(); }); }); + it('should not throw an error when the default task is not defined', function(cb) { + base.register('foo', function() {}); + base.register('bar', function() {}); + base.generate('foo', ['default'], function(err) { + if (err) return cb(err); + + base.generate('bar', function(err) { + if (err) return cb(err); + + cb(); + }); + }); + }); + it('should run a task on the instance', function(cb) { - generate.task('foo', function(next) { + base.task('abc123', function(next) { next(); }); - generate.generateEach('foo', function(err) { + base.generate('abc123', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run same-named task instead of a generator', function(cb) { + base.register('123xyz', function(app) { + cb(new Error('expected the task to run first')); + }); + + base.task('123xyz', function() { + cb(); + }); + + base.generate('123xyz', function(err) { + assert(!err); + }); + }); + + it('should run a task instead of a generator with a default task', function(cb) { + base.register('123xyz', function(app) { + app.task('default', function() { + cb(new Error('expected the task to run first')); + }); + }); + base.task('123xyz', function() { + cb(); + }); + base.generate('123xyz', function(err) { + assert(!err); + }); + }); + + it('should run a task on a same-named generator when the task is specified', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.task('foo', function() { + cb(new Error('expected the generator to run')); + }); + + base.generate('foo:default', function(err) { + assert(!err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks that includes a same-named generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.register('bar', function(app) { + app.task('baz', function(next) { + count++; + next(); + }); + }); + + base.task('foo', function() { + cb(new Error('expected the generator to run')); + }); + + base.generate(['foo:default', 'bar:baz'], function(err) { + assert(!err); + assert.equal(count, 2); + cb(); + }); + }); + + it('should run a generator from a task with the same name', function(cb) { + base.register('foo', function(app) { + app.task('default', function() { + cb(); + }); + }); + + base.task('foo', function(cb) { + base.generate('foo', cb); + }); + + base.build('foo', function(err) { + if (err) cb(err); + }); + }); + + it('should run the default task on a generator', function(cb) { + base.register('foo', function(app) { + app.task('default', function(next) { + next(); + }); + }); + + base.generate('foo', function(err) { + assert(!err); + cb(); + }); + }); + + it('should run a stringified array of tasks on the instance', function(cb) { + var count = 0; + base.task('a', function(next) { + count++; + next(); + }); + base.task('b', function(next) { + count++; + next(); + }); + base.task('c', function(next) { + count++; + next(); + }); + + base.generate('a,b,c', function(err) { + assert.equal(count, 3); assert(!err); cb(); }); @@ -51,20 +272,20 @@ describe('app.generateEach', function() { it('should run an array of tasks on the instance', function(cb) { var count = 0; - generate.task('a', function(next) { + base.task('a', function(next) { count++; next(); }); - generate.task('b', function(next) { + base.task('b', function(next) { count++; next(); }); - generate.task('c', function(next) { + base.task('c', function(next) { count++; next(); }); - generate.generateEach('a,b,c', function(err) { + base.generate(['a', 'b', 'c'], function(err) { if (err) return cb(err); assert.equal(count, 3); assert(!err); @@ -74,14 +295,14 @@ describe('app.generateEach', function() { it('should run the default task on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generateEach('foo', function(err) { + base.generate('foo', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -90,21 +311,21 @@ describe('app.generateEach', function() { it('should run an array of generators', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generateEach(['foo', 'bar'], function(err) { + base.generate(['foo', 'bar'], function(err) { if (err) return cb(err); assert.equal(count, 2); cb(); @@ -113,14 +334,14 @@ describe('app.generateEach', function() { it('should run the default task on the default generator', function(cb) { var count = 0; - generate.register('default', function(app) { + base.register('default', function(app) { app.task('default', function(next) { count++; next(); }); }); - generate.generateEach(function(err) { + base.generate(function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -129,7 +350,7 @@ describe('app.generateEach', function() { it('should run the specified task on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -141,7 +362,7 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo:abc', function(err) { + base.generate('foo:abc', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -150,7 +371,7 @@ describe('app.generateEach', function() { it('should run an array of tasks on a registered generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { count++; next(); @@ -172,7 +393,180 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo:a,b,c', function(err) { + base.generate('foo:a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + + it('should run the default tasks on an array of generators', function(cb) { + var count = 0; + base.register('a', function(app) { + this.task('default', function(cb) { + count++; + cb(); + }); + }); + + base.register('b', function(app) { + this.task('default', function(cb) { + count++; + cb(); + }); + }); + + base.register('c', function(app) { + this.task('default', function(cb) { + count++; + cb(); + }); + }); + + base.generate(['a', 'b', 'c'], function(err) { + if (err) return cb(err); + assert.equal(count, 3); + assert(!err); + cb(); + }); + }); + }); + + describe('options', function(cb) { + it('should pass options to generator.options', function(cb) { + var count = 0; + base.register('default', function(app, base, env, options) { + app.task('default', function(next) { + count++; + assert.equal(app.options.foo, 'bar'); + next(); + }); + }); + + base.generate({foo: 'bar'}, function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should expose options on generator options', function(cb) { + var count = 0; + base.register('default', function(app, base, env, options) { + app.task('default', function(next) { + count++; + assert.equal(options.foo, 'bar'); + next(); + }); + }); + + base.generate({foo: 'bar'}, function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should not mutate options on parent instance', function(cb) { + var count = 0; + base.register('default', function(app, base, env, options) { + app.task('default', function(next) { + count++; + assert.equal(options.foo, 'bar'); + assert(!base.options.foo); + next(); + }); + }); + + base.generate({foo: 'bar'}, function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + }); + + describe('default tasks', function(cb) { + it('should run the default task on the default generator', function(cb) { + var count = 0; + base.register('default', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.generate(function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run the default task on a registered generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + }); + + base.generate('foo', function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + }); + + describe('specified tasks', function(cb) { + it('should run the specified task on a registered generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('abc', function(next) { + count++; + next(); + }); + }); + + base.generate('foo', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an array of tasks on a registered generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.task('default', function(next) { + count++; + next(); + }); + + app.task('a', function(next) { + count++; + next(); + }); + + app.task('b', function(next) { + count++; + next(); + }); + + app.task('c', function(next) { + count++; + next(); + }); + }); + + base.generate('foo', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -183,7 +577,7 @@ describe('app.generateEach', function() { describe('sub-generators', function(cb) { it('should run the default task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -197,16 +591,16 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.sub', function(err) { + base.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); }); }); - it('should run the specified task on a registered sub-generator', function(cb) { + it('should run the specified task string on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -220,16 +614,105 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.sub:abc', function(err) { + base.generate('foo.sub:abc', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); }); }); + it('should run the specified task array on a registered sub-generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.register('sub', function(sub) { + sub.task('default', function(next) { + count++; + next(); + }); + + sub.task('abc', function(next) { + count++; + next(); + }); + }); + }); + + base.generate('foo.sub', ['abc'], function(err) { + if (err) return cb(err); + assert.equal(count, 1); + cb(); + }); + }); + + it('should run an of stringified-tasks on a registered sub-generator', function(cb) { + var count = 0; + base.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + base.generate('foo.bar:a,b,c', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + it('should run an array of tasks on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { + app.register('bar', function(bar) { + bar.task('default', function(next) { + count++; + next(); + }); + + bar.task('a', function(next) { + count++; + next(); + }); + + bar.task('b', function(next) { + count++; + next(); + }); + + bar.task('c', function(next) { + count++; + next(); + }); + }); + }); + + base.generate('foo.bar', ['a', 'b', 'c'], function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + + it('should run an multiple tasks on a registered sub-generator', function(cb) { + var count = 0; + base.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -253,7 +736,7 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.bar:a,b,c', function(err) { + base.generate('foo.bar', 'a,b,c', function(err) { if (err) return cb(err); assert.equal(count, 3); cb(); @@ -262,7 +745,7 @@ describe('app.generateEach', function() { it('should run an multiple tasks on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('bar', function(bar) { bar.task('default', function(next) { count++; @@ -286,7 +769,7 @@ describe('app.generateEach', function() { }); }); - generate.register('qux', function(app) { + base.register('qux', function(app) { app.register('fez', function(fez) { fez.task('default', function(next) { count++; @@ -310,7 +793,7 @@ describe('app.generateEach', function() { }); }); - generate.generateEach(['foo.bar:a,b,c', 'qux.fez:a,b,c'], function(err) { + base.generate(['foo.bar:a,b,c', 'qux.fez:a,b,c'], function(err) { if (err) return cb(err); assert.equal(count, 6); cb(); @@ -322,16 +805,16 @@ describe('app.generateEach', function() { it('should run a generator from another generator', function(cb) { var res = ''; - generate.register('foo', function(app, two) { + base.register('foo', function(app, two) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'foo > sub > default '; - generate.generateEach('bar.sub', next); + base.generate('bar.sub', next); }); }); }); - generate.register('bar', function(app) { + base.register('bar', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { res += 'bar > sub > default '; @@ -340,7 +823,7 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.sub', function(err) { + base.generate('foo.sub', function(err) { if (err) return cb(err); assert.equal(res, 'foo > sub > default bar > sub > default '); cb(); @@ -349,7 +832,7 @@ describe('app.generateEach', function() { it('should run the specified task on a registered sub-generator', function(cb) { var count = 0; - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { count++; @@ -363,7 +846,7 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.sub:abc', function(err) { + base.generate('foo.sub:abc', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -373,34 +856,39 @@ describe('app.generateEach', function() { describe('events', function(cb) { it('should emit generate', function(cb) { - generate.on('generate', function() { - cb(); + var count = 0; + + base.on('generate', function() { + count++; }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); }); sub.task('abc', function(next) { + count++; next(); }); }); }); - generate.generateEach('foo.sub:abc', function(err) { + base.generate('foo.sub', ['abc'], function(err) { if (err) return cb(err); + assert.equal(count, 2); + cb(); }); }); it('should expose the generator alias as the first parameter', function(cb) { - generate.on('generate', function(name) { + base.on('generate', function(name) { assert.equal(name, 'sub'); cb(); }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -412,18 +900,18 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.sub:abc', function(err) { + base.generate('foo.sub:abc', function(err) { if (err) return cb(err); }); }); it('should expose the tasks array as the second parameter', function(cb) { - generate.on('generate', function(name, tasks) { + base.on('generate', function(name, tasks) { assert.deepEqual(tasks, ['abc']); cb(); }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.register('sub', function(sub) { sub.task('default', function(next) { next(); @@ -435,7 +923,7 @@ describe('app.generateEach', function() { }); }); - generate.generateEach('foo.sub:abc', function(err) { + base.generate('foo.sub:abc', function(err) { if (err) return cb(err); }); }); diff --git a/test/app.generator.js b/test/app.generator.js index 6ef3fcb4..9b0a51b1 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -1,59 +1,63 @@ 'use strict'; +require('mocha'); var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var verb; +var Base = require('..'); +var base; var fixtures = path.resolve.bind(path, __dirname, 'fixtures'); -describe('app.generator', function() { +describe('.generator', function() { beforeEach(function() { - verb = new Verb(); - verb.option('toAlias', function(name) { - return name.replace(/^generate-/, ''); + base = new Base(); + + base.option('toAlias', function(key) { + return key.replace(/^generate-(.*)/, '$1'); }); }); - describe('get generator', function() { - it('should get a generator by full name name', function() { - var gen = verb.getGenerator('generate-foo'); + describe('generator', function() { + it('should get a generator by alias', function() { + base.register('generate-foo', require('generate-foo')); + var gen = base.getGenerator('foo'); assert(gen); - assert.equal(gen.env.alias, 'foo'); assert.equal(gen.env.name, 'generate-foo'); - }); - - it('should get a generator by aliased name', function() { - var gen = verb.getGenerator('generate-foo'); - assert(gen); assert.equal(gen.env.alias, 'foo'); - assert.equal(gen.env.name, 'generate-foo'); }); - it('should get a generator by alias', function() { - var gen = verb.getGenerator('generate-foo'); + it('should get a generator using a custom lookup function', function() { + base.register('generate-foo', function() {}); + base.register('generate-bar', function() {}); + + var gen = base.getGenerator('foo', { + lookup: function(key) { + return ['generate-' + key, 'verb-' + key + '-generator', key]; + } + }); + assert(gen); - assert.equal(gen.env.alias, 'foo'); assert.equal(gen.env.name, 'generate-foo'); + assert.equal(gen.env.alias, 'foo'); }); }); describe('register > function', function() { it('should register a generator function by name', function() { - verb.generator('foo', function() {}); - assert(verb.generators.hasOwnProperty('foo')); + base.generator('foo', function() {}); + assert(base.generators.hasOwnProperty('foo')); }); it('should register a generator function by alias', function() { - verb.generator('generate-abc', function() {}); - assert(verb.generators.hasOwnProperty('generate-abc')); + base.generator('generate-abc', function() {}); + assert(base.generators.hasOwnProperty('generate-abc')); }); }); describe('get > alias', function() { it('should get a generator by alias', function() { - verb.generator('generate-abc', function() {}); - var abc = verb.generator('abc'); + base.generator('generate-abc', function() {}); + var abc = base.generator('abc'); assert(abc); assert.equal(typeof abc, 'object'); }); @@ -61,8 +65,8 @@ describe('app.generator', function() { describe('get > name', function() { it('should get a generator by name', function() { - verb.generator('generate-abc', function() {}); - var abc = verb.generator('generate-abc'); + base.generator('generate-abc', function() {}); + var abc = base.generator('generate-abc'); assert(abc); assert.equal(typeof abc, 'object'); }); @@ -70,62 +74,62 @@ describe('app.generator', function() { describe('generators', function() { it('should invoke a registered generator when `getGenerator` is called', function(cb) { - verb.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function() {}); cb(); }); - verb.getGenerator('foo'); + base.getGenerator('foo'); }); it('should expose the generator instance on `app`', function(cb) { - verb.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(next) { assert.equal(app.get('a'), 'b'); next(); - }) + }); }); - var foo = verb.getGenerator('foo'); + var foo = base.getGenerator('foo'); foo.set('a', 'b'); foo.build('default', function(err) { if (err) return cb(err); - cb() + cb(); }); }); it('should expose the "base" instance on `base`', function(cb) { - verb.set('x', 'z'); - verb.register('foo', function(app, base) { + base.set('x', 'z'); + base.register('foo', function(app, base) { app.task('default', function(next) { - assert.equal(verb.get('x'), 'z'); + assert.equal(base.get('x'), 'z'); next(); - }) + }); }); - var foo = verb.getGenerator('foo'); + var foo = base.getGenerator('foo'); foo.set('a', 'b'); foo.build('default', function(err) { if (err) return cb(err); - cb() + cb(); }); }); it('should expose the "env" object on `env`', function(cb) { - verb.register('foo', function(app, base, env) { + base.register('foo', function(app, base, env) { app.task('default', function(next) { assert.equal(env.alias, 'foo'); next(); - }) + }); }); - verb.getGenerator('foo').build('default', function(err) { + base.getGenerator('foo').build('default', function(err) { if (err) return cb(err); - cb() + cb(); }); }); it('should expose an app\'s generators on app.generators', function(cb) { - verb.register('foo', function(app) { + base.register('foo', function(app) { app.register('a', function() {}); app.register('b', function() {}); @@ -134,45 +138,45 @@ describe('app.generator', function() { cb(); }); - verb.getGenerator('foo'); + base.getGenerator('foo'); }); - it('should expose all root generators on verb.generators', function(cb) { - verb.register('foo', function(app, b) { + it('should expose all root generators on base.generators', function(cb) { + base.register('foo', function(app, b) { b.generators.hasOwnProperty('foo'); b.generators.hasOwnProperty('bar'); b.generators.hasOwnProperty('baz'); cb(); }); - verb.register('bar', function(app, base) {}); - verb.register('baz', function(app, base) {}); - verb.getGenerator('foo'); + base.register('bar', function(app, base) {}); + base.register('baz', function(app, base) {}); + base.getGenerator('foo'); }); }); describe('cross-generators', function() { it('should get a generator from another generator', function(cb) { - verb.register('foo', function(app, b) { + base.register('foo', function(app, b) { var bar = b.getGenerator('bar'); assert(bar); cb(); }); - verb.register('bar', function(app, base) {}); - verb.register('baz', function(app, base) {}); - verb.getGenerator('foo'); + base.register('bar', function(app, base) {}); + base.register('baz', function(app, base) {}); + base.getGenerator('foo'); }); it('should set options on another generator instance', function(cb) { - verb.generator('foo', function(app) { + base.generator('foo', function(app) { app.task('default', function(next) { assert.equal(app.option('abc'), 'xyz'); next(); }); }); - verb.generator('bar', function(app, b) { + base.generator('bar', function(app, b) { var foo = b.getGenerator('foo'); foo.option('abc', 'xyz'); foo.build(function(err) { @@ -185,30 +189,23 @@ describe('app.generator', function() { describe('generators > filepath', function() { it('should register a generator function from a file path', function() { - var one = verb.generator('one', fixtures('one/verbfile.js')); - assert(verb.generators.hasOwnProperty('one')); - assert(typeof verb.generators.one === 'object'); - assert.deepEqual(verb.generators.one, one); - }); - - it('should register an instance from a file path', function() { - var two = verb.generator('two', fixtures('two/generator.js')); - assert(verb.generators.hasOwnProperty('two')); - assert(typeof verb.generators.two === 'object'); - assert.deepEqual(verb.generators.two, two); + var one = base.generator('one', fixtures('one/generator.js')); + assert(base.generators.hasOwnProperty('one')); + assert(typeof base.generators.one === 'object'); + assert.deepEqual(base.generators.one, one); }); it('should get a registered generator by name', function() { - var one = verb.generator('one', fixtures('one/generator.js')); - var two = verb.generator('two', fixtures('two/generator.js')); - assert.deepEqual(verb.generator('one'), one); - assert.deepEqual(verb.generator('two'), two); + var one = base.generator('one', fixtures('one/generator.js')); + var two = base.generator('two', fixtures('two/generator.js')); + assert.deepEqual(base.generator('one'), one); + assert.deepEqual(base.generator('two'), two); }); }); describe('tasks', function() { it('should expose a generator\'s tasks on app.tasks', function(cb) { - verb.register('foo', function(app) { + base.register('foo', function(app) { app.task('a', function() {}); app.task('b', function() {}); assert(app.tasks.a); @@ -216,22 +213,81 @@ describe('app.generator', function() { cb(); }); - verb.getGenerator('foo'); + base.getGenerator('foo'); }); it('should get tasks from another generator', function(cb) { - verb.register('foo', function(app, b) { + base.register('foo', function(app, b) { var baz = b.getGenerator('baz'); var task = baz.tasks.aaa; assert(task); cb(); }); - verb.register('bar', function(app, base) {}); - verb.register('baz', function(app, base) { + base.register('bar', function(app, base) {}); + base.register('baz', function(app, base) { app.task('aaa', function() {}); }); - verb.getGenerator('foo'); + base.getGenerator('foo'); + }); + }); + + describe('namespace', function() { + it('should expose `app.namespace`', function(cb) { + base.generator('foo', function(app) { + assert(typeof app.namespace, 'string'); + cb(); + }); + }); + + it('should create namespace from generator alias', function(cb) { + base.generator('generate-foo', function(app) { + assert.equal(app.namespace, base._name + '.foo'); + cb(); + }); + }); + + it('should create sub-generator namespace from parent namespace and alias', function(cb) { + var name = base._name; + base.generator('generate-foo', function(app) { + assert.equal(app.namespace, name + '.foo'); + + app.generator('generate-bar', function(bar) { + assert.equal(bar.namespace, name + '.foo.bar'); + + bar.generator('generate-baz', function(baz) { + assert.equal(baz.namespace, name + '.foo.bar.baz'); + + baz.generator('generate-qux', function(qux) { + assert.equal(qux.namespace, name + '.foo.bar.baz.qux'); + cb(); + }); + }); + }); + }); + }); + + it('should expose namespace on `this`', function(cb) { + var name = base._name; + + base.generator('generate-foo', function(app, first) { + assert.equal(this.namespace, base._name + '.foo'); + + this.generator('generate-bar', function() { + assert.equal(this.namespace, base._name + '.foo.bar'); + + this.generator('generate-baz', function() { + assert.equal(this.namespace, base._name + '.foo.bar.baz'); + + this.generator('generate-qux', function() { + assert.equal(this.namespace, base._name + '.foo.bar.baz.qux'); + assert.equal(app.namespace, base._name + '.foo'); + assert.equal(first.namespace, base._name); + cb(); + }); + }); + }); + }); }); }); }); diff --git a/test/app.generator.tasks.js b/test/app.generator.tasks.js deleted file mode 100644 index 8955d2c4..00000000 --- a/test/app.generator.tasks.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var Generate = require('..'); -var generate; - -describe('app.tasks', function() { - it('should register as a plugin', function() { - var generate = new Generate(); - assert(generate.registered.hasOwnProperty('base-generators-tasks')); - }); -}); - -describe('tasks', function() { - beforeEach(function() { - generate = new Generate(); - }); - - describe('hasTask', function() { - it('should return true if a task exists', function() { - generate.task('foo', function() {}); - assert(generate.hasTask('foo')); - }); - - it('should return false if a task does not exist', function() { - generate.task('foo', function() {}); - assert(!generate.hasTask('bar')); - }); - }); - - describe('.stringifyTasks', function() { - it('should create a generator-task string', function() { - assert.equal(generate.stringifyTasks('foo'), 'foo'); - assert.equal(generate.stringifyTasks('foo', function() {}), 'foo'); - assert.equal(generate.stringifyTasks('foo', []), 'foo'); - assert.equal(generate.stringifyTasks('foo:a,b,c'), 'foo:a,b,c'); - assert.equal(generate.stringifyTasks('foo.bar:a,b,c'), 'foo.bar:a,b,c'); - assert.equal(generate.stringifyTasks('foo.bar', 'a,b,c'), 'foo.bar:a,b,c'); - assert.equal(generate.stringifyTasks('foo', 'a,b,c'), 'foo:a,b,c'); - assert.equal(generate.stringifyTasks('foo', ['a', 'b', 'c']), 'foo:a,b,c'); - assert.equal(generate.stringifyTasks(['foo', 'bar'], ['a', 'b']), 'foo.bar:a,b'); - assert.equal(generate.stringifyTasks(['foo', 'bar'], 'a,b,c'), 'foo.bar:a,b,c'); - }); - }); -}); diff --git a/test/app.get-set.js b/test/app.get-set.js deleted file mode 100644 index c4ebda33..00000000 --- a/test/app.get-set.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.get-set', function() { - beforeEach(function() { - app = new App(); - }); - - it('should set a value', function() { - app.set('a', 'b'); - assert.equal(app.get('a'), 'b'); - }); - - it('should set properties on the instance.', function() { - app.set('a', 'b'); - assert.equal(app.a, 'b'); - }); - - it('should allow an object to be set directly.', function() { - app.set({x: 'y'}); - assert.equal(app.x, 'y'); - assert.equal(app.get('x'), 'y'); - }); - - it('should set nested properties on the instance.', function() { - app.set('c', {d: 'e'}); - assert.equal(app.get('c').d, 'e'); - }); - - it('should use dot notation to `set` values.', function() { - app.set('h.i', 'j'); - assert.deepEqual(app.get('h'), {i: 'j'}); - }); - - it('should use dot notation to `get` values.', function() { - app.set('h', {i: 'j'}); - assert.equal(app.get('h.i'), 'j'); - }); - - it('should return `this` for chaining', function() { - assert.equal(app.set('a', 'b'), app); - app - .set('aa', 'bb') - .set('bb', 'cc') - .set('cc', 'dd'); - assert.equal(app.get('aa'), 'bb'); - assert.equal(app.get('bb'), 'cc'); - assert.equal(app.get('cc'), 'dd'); - }); - - it('should return undefined when not set', function() { - assert.equal(app.set('a', undefined), app); - }); -}); - -describe('app.get()', function() { - beforeEach(function() { - app = new App(); - }); - - it('should return undefined when no set', function() { - assert(app.get('a') === undefined); - }); - - it('should otherwise return the value', function() { - app.set('a', 'b'); - assert.equal(app.get('a'), 'b'); - }); -}); diff --git a/test/app.getGenerator.js b/test/app.getGenerator.js index 4a013c5b..0300fafd 100644 --- a/test/app.getGenerator.js +++ b/test/app.getGenerator.js @@ -2,40 +2,45 @@ var path = require('path'); var assert = require('assert'); -var Generate = require('..'); -var generate +var Base = require('..'); +var base; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('app.getGenerator', function() { +describe('.generator', function() { beforeEach(function() { - generate = new Generate(); + base = new Base(); }); it('should get a generator from the base instance', function() { - generate.register('abc', function() {}); - var generator = generate.getGenerator('abc'); + base.register('abc', function() {}); + var generator = base.getGenerator('abc'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'abc'); }); + it('should fail when a generator is not found', function() { + var generator = base.getGenerator('whatever'); + assert(!generator); + }); + it('should get a generator from the base instance from a nested generator', function() { - generate.register('abc', function() {}); - generate.register('xyz', function(app) { + base.register('abc', function() {}); + base.register('xyz', function(app) { app.register('sub', function(sub) { - var generator = generate.getGenerator('abc'); + var generator = base.getGenerator('abc'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'abc'); }); }); - generate.getGenerator('xyz'); + base.getGenerator('xyz'); }); it('should get a generator from the base instance using `this`', function() { - generate.register('abc', function() {}); - generate.register('xyz', function(app) { + base.register('abc', function() {}); + base.register('xyz', function(app) { app.register('sub', function(sub) { var generator = this.getGenerator('abc'); assert(generator); @@ -43,12 +48,12 @@ describe('app.getGenerator', function() { assert.equal(generator.name, 'abc'); }); }); - generate.getGenerator('xyz'); + base.getGenerator('xyz'); }); it('should get a base generator from "app" from a nested generator', function() { - generate.register('abc', function() {}); - generate.register('xyz', function(app) { + base.register('abc', function() {}); + base.register('xyz', function(app) { app.register('sub', function(sub) { var generator = app.getGenerator('abc'); assert(generator); @@ -56,22 +61,22 @@ describe('app.getGenerator', function() { assert.equal(generator.name, 'abc'); }); }); - generate.getGenerator('xyz'); + base.getGenerator('xyz'); }); it('should get a nested generator', function() { - generate.register('abc', function(abc) { - abc.register('def', function() {}); + base.register('abc', function(abc) { + abc.register('def', function(def) {}); }); - var generator = generate.getGenerator('abc.def'); + var generator = base.getGenerator('abc.def'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'def'); }); it('should get a deeply nested generator', function() { - generate.register('abc', function(abc) { + base.register('abc', function(abc) { abc.register('def', function(def) { def.register('ghi', function(ghi) { ghi.register('jkl', function(jkl) { @@ -81,15 +86,16 @@ describe('app.getGenerator', function() { }); }); - var generator = generate.getGenerator('abc.def.ghi.jkl.mno'); + var generator = base.getGenerator('abc.def.ghi.jkl.mno'); assert(generator); assert.equal(typeof generator, 'object'); assert.equal(generator.name, 'mno'); }); it('should get a generator that was registered by path', function() { - generate.register('a', fixtures('generators/a')); - var generator = generate.getGenerator('a'); + base.register('a', fixtures('generators/a')); + var generator = base.getGenerator('a'); + assert(generator); assert(generator.tasks); assert(generator.tasks.hasOwnProperty('default')); diff --git a/test/app.handle.js b/test/app.handle.js deleted file mode 100644 index 442a836a..00000000 --- a/test/app.handle.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.handle', function() { - beforeEach(function() { - app = new App(); - app.create('pages'); - app.handlers(['foo']); - }); - - it('should support custom handle methods:', function(cb) { - var page = app.page('foo', {contents: null}); - - app.handle('foo', page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.path, 'string'); - cb(); - }); - }); - - it('should not blow up if `options.handled` does not exist:', function(cb) { - var page = app.page('foo', {contents: null}); - delete page.options.handled; - - app.handle('foo', page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.path, 'string'); - cb(); - }); - }); -}); diff --git a/test/app.handleOnce.js b/test/app.handleOnce.js deleted file mode 100644 index a17d9a40..00000000 --- a/test/app.handleOnce.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.handle', function() { - beforeEach(function() { - app = new App(); - app.create('pages'); - app.handlers(['foo']); - }); - - it('should support custom handle methods:', function(cb) { - var page = app.page('foo', {contents: null}); - - app.handleOnce('foo', page, function(err, view) { - if (err) return cb(err); - - assert(typeof view.path === 'string'); - cb(); - }); - }); - - it('should not blow up if `options.handled` does not exist:', function(cb) { - var page = app.page('foo', {contents: null}); - delete page.options.handled; - - app.handleOnce('foo', page, function(err, view) { - if (err) return cb(err); - - assert(typeof view.path === 'string'); - cb(); - }); - }); -}); diff --git a/test/app.handlers.js b/test/app.handlers.js deleted file mode 100644 index 21cee5c7..00000000 --- a/test/app.handlers.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -require('should'); -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); -var resolve = require('resolve-glob'); -var support = require('./support'); -var App = support.resolve(); -var app; - -function decorateViews(views) { - var fn = views.decorateView; - views.decorateView = function() { - var view = fn.apply(fn, arguments); - view.read = function() { - if (!this.contents) { - this.contents = fs.readFileSync(this.path); - } - }; - return view; - }; - views.loader = function(pattern) { - var files = resolve.sync(pattern); - return files.reduce(function(acc, fp) { - acc[fp] = {path: fp}; - return acc; - }, {}); - }; - return views; -} - -describe('app.handlers', function() { - describe('custom handlers', function() { - beforeEach(function() { - app = new App(); - app.create('pages') - .use(decorateViews) - .option('renameKey', function(key) { - return path.basename(key); - }); - }); - - it('should add custom middleware handlers:', function() { - app.handler('foo'); - app.router.should.have.property('foo'); - assert.equal(typeof app.router.foo, 'function'); - }); - - it('should add custom middleware handlers:', function() { - app.handler('foo'); - app.handler('bar'); - - app.foo(/./, function(view, next) { - view.one = 'aaa'; - next(); - }); - - app.bar(/./, function(view, next) { - view.two = 'zzz'; - next(); - }); - - app.page('abc', {content: '...'}) - .use(function(view) { - app.handleView('foo', view); - app.handleView('bar', view); - }); - - app.views.pages.abc.should.have.property('one', 'aaa'); - app.views.pages.abc.should.have.property('two', 'zzz'); - }); - }); -}); diff --git a/test/app.include.js b/test/app.include.js index 8ae616b4..cdf58bbf 100644 --- a/test/app.include.js +++ b/test/app.include.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app, len; -describe('app.include', function() { +describe('app', function() { beforeEach(function() { app = generate(); if (typeof app.include === 'undefined') { diff --git a/test/app.includes.js b/test/app.includes.js index 758d09ff..390660d6 100644 --- a/test/app.includes.js +++ b/test/app.includes.js @@ -6,7 +6,7 @@ var assert = require('assert'); var generate = require('..'); var app, len; -describe('app.includes', function() { +describe('app', function() { beforeEach(function() { app = generate(); if (typeof app.include === 'undefined') { diff --git a/test/app.js b/test/app.js deleted file mode 100644 index 2ba1759b..00000000 --- a/test/app.js +++ /dev/null @@ -1,121 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Base = App.Base; -var app; - -describe('app', function() { - describe('constructor', function() { - it('should create an instance of App:', function() { - app = new App(); - assert(app instanceof App); - }); - - it('should new up without new:', function() { - app = App(); - assert(app instanceof App); - }); - }); - - describe('static methods', function() { - it('should expose `extend`:', function() { - assert(typeof App.extend === 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - app = new App(); - }); - - it('should expose `set`', function() { - assert(typeof app.set === 'function'); - }); - it('should expose `get`', function() { - assert(typeof app.get === 'function'); - }); - it('should expose `visit`', function() { - assert(typeof app.visit === 'function'); - }); - it('should expose `define`', function() { - assert(typeof app.define === 'function'); - }); - it('should expose `views`', function() { - assert(typeof app.views === 'object'); - }); - }); - - describe('instance', function() { - beforeEach(function() { - app = new App(); - }); - - it('should set a value on the instance:', function() { - app.set('a', 'b'); - assert(app.a === 'b'); - }); - - it('should get a value from the instance:', function() { - app.set('a', 'b'); - assert(app.get('a') === 'b'); - }); - }); - - describe('initialization', function() { - it('should listen for errors:', function(cb) { - app = new App(); - app.on('error', function(err) { - assert(err.message === 'foo'); - cb(); - }); - app.emit('error', new Error('foo')); - }); - - it('should expose constructors from `lib`:', function() { - app = new App(); - app.expose('Collection'); - assert(typeof app.Collection === 'function'); - }); - - it('should update constructors after init:', function() { - var Group = App.Group; - function MyGroup() { - Base.call(this); - } - Base.extend(MyGroup); - - app = new App(); - assert.equal(app.Group, Group); - assert.equal(app.get('Group'), Group); - app.option('Group', MyGroup); - assert.equal(app.Group, MyGroup); - assert.equal(app.get('Group'), MyGroup); - }); - - it('should mixin prototype methods defined on options:', function() { - app = new App({ - mixins: { - foo: function() {} - } - }); - assert(typeof app.foo === 'function'); - delete App.prototype.foo; - }); - - it('should expose `_` on app:', function() { - app = new App(); - assert(typeof app._ === 'object'); - }); - - it('should not re-add `_` in init:', function() { - app = new App(); - app._.foo = 'bar'; - app.initTemplates(); - assert(app._.foo === 'bar'); - }); - }); -}); diff --git a/test/app.layout.js b/test/app.layout.js index b07cabc2..f7631579 100644 --- a/test/app.layout.js +++ b/test/app.layout.js @@ -1,12 +1,11 @@ 'use strict'; require('mocha'); -require('should'); var assert = require('assert'); var assemble = require('..'); var app; -describe('app.layout', function() { +describe('.layout()', function() { beforeEach(function() { app = assemble(); if (!app.layout) { diff --git a/test/app.layouts.js b/test/app.layouts.js index 37e3c75e..d731a0b4 100644 --- a/test/app.layouts.js +++ b/test/app.layouts.js @@ -1,12 +1,11 @@ 'use strict'; require('mocha'); -require('should'); var assert = require('assert'); var assemble = require('..'); var app; -describe('app.layouts', function() { +describe('.layouts()', function() { beforeEach(function() { app = assemble(); if (!app.layout) { diff --git a/test/app.list.compile.js b/test/app.list.compile.js deleted file mode 100644 index 2efa2235..00000000 --- a/test/app.list.compile.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var list; - -describe('app.list.compile', function() { - beforeEach(function() { - list = new List(); - list.engine('tmpl', require('engine-base')); - }); - - it('should compile an item:', function() { - var buffer = new Buffer('a b c'); - var item = list.addItem('a.tmpl', {contents: buffer}) - .compile(); - - assert(typeof item.fn === 'function'); - }); - - it('should use the compiled function to render:', function() { - var buffer = new Buffer('a <%= title %> c'); - var item = list.addItem('a.tmpl', {contents: buffer}) - .compile(); - - assert(item.fn({title: 'z'})); - assert(typeof item.fn({title: 'z'}) === 'string'); - assert(item.fn({title: 'z'}) === 'a z c'); - }); - - it('should compile a view by name:', function() { - var buffer = new Buffer('a <%= title %> c'); - list.addItem('a.tmpl', {contents: buffer}); - - var item = list.compile('a.tmpl'); - - assert(item.fn({title: 'z'})); - assert(typeof item.fn({title: 'z'}) === 'string'); - assert(item.fn({title: 'z'}) === 'a z c'); - }); -}); - diff --git a/test/app.list.js b/test/app.list.js deleted file mode 100644 index b1b0a4be..00000000 --- a/test/app.list.js +++ /dev/null @@ -1,106 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var app; - -describe('app.list', function() { - describe('method', function() { - beforeEach(function() { - app = new App(); - }); - - it('should expose the list method', function() { - assert(typeof app.list === 'function'); - }); - - it('should return a new list', function() { - var list = app.list(); - assert(typeof list === 'object'); - }); - - it('should have isList property', function() { - var list = app.list(); - assert(list.isList === true); - }); - }); - - describe('adding items', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('pages'); - }); - - it('should add an item to a list:', function() { - app.pages('test/fixtures/pages/a.hbs'); - var list = app.list(); - list.addItem(app.pages.getView('test/fixtures/pages/a.hbs')); - assert(list.hasItem('test/fixtures/pages/a.hbs')); - }); - - it('should expose the `option` method from a list:', function() { - var list = app.list(); - list.option('a', 'b'); - assert(list.options); - assert(list.options.a === 'b'); - }); - }); - - describe('addItem', function() { - beforeEach(function() { - app = new App(); - }); - - it('should add items to a list', function() { - var pages = app.list({List: List}); - pages.addItem('foo'); - pages.addItem('bar'); - pages.addItem('baz'); - - pages.items.hasOwnProperty('foo'); - pages.items.hasOwnProperty('bar'); - pages.items.hasOwnProperty('baz'); - }); - - it('should create a list from an existing list:', function() { - var pages = app.list({List: List}); - pages.addItem('foo'); - pages.addItem('bar'); - pages.addItem('baz'); - - var posts = app.list(pages); - posts.items.hasOwnProperty('foo'); - posts.items.hasOwnProperty('bar'); - posts.items.hasOwnProperty('baz'); - }); - }); - - describe('rendering items', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('pages'); - }); - - it('should render a item with inherited app.render', function(cb) { - app.page('test/fixtures/templates/a.tmpl') - .use(function(item) { - if (!item.content) { - item.contents = fs.readFileSync(item.path); - } - }) - .set('data.name', 'Brian') - .render(function(err, res) { - if (err) return cb(err); - assert(res.contents.toString() === 'Brian'); - cb(); - }); - }); - }); -}); diff --git a/test/app.list.render.js b/test/app.list.render.js deleted file mode 100644 index f0c97eb1..00000000 --- a/test/app.list.render.js +++ /dev/null @@ -1,157 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var each = require('async-each'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var pages, app; - -describe('app.list.render', function() { - describe('rendering', function() { - beforeEach(function() { - app = App(); - pages = app.create('pages'); - app.engine('tmpl', require('engine-base')); - pages.engine('tmpl', require('engine-base')); - }); - - it('should throw an error when no callback is given:', function() { - (function() { - app.pages.render({}); - }).should.throw('Views#render is async and expects a callback function'); - }); - - it('should throw an error when an engine is not defined:', function(cb) { - pages.addView('foo.bar', {content: '<%= name %>'}); - var page = pages.getView('foo.bar'); - - app.pages.render(page, function(err) { - assert(err.message === 'Views#render cannot find an engine for: .bar'); - cb(); - }); - }); - - it('should use helpers defined on app to render a view:', function(cb) { - var locals = {name: 'Halle'}; - app.helper('upper', function(str) { - return str.toUpperCase(str) + 'app'; - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - app.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.content === 'a HALLEapp b'); - cb(); - }); - }); - - it('should use helpers defined on app to render a view with collection.render:', function(cb) { - var locals = {name: 'Halle'}; - app.helper('upper', function(str) { - return str.toUpperCase(str) + 'app'; - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - pages.helper('upper', app._.helpers.sync.upper); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.content === 'a HALLEapp b'); - cb(); - }); - }); - - it('should use helpers when rendering a view:', function(cb) { - var locals = {name: 'Halle'}; - pages.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - assert(res.content === 'a HALLE b'); - cb(); - }); - }); - - it('should render a template when contents is a buffer:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function(err, view) { - if (err) return cb(err); - assert(view.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a template when content is a string:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function(err, view) { - if (err) return cb(err); - assert(view.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a view from its path:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - - pages.render('a.tmpl', function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use a plugin for rendering:', function(cb) { - pages.engine('tmpl', require('engine-base')); - pages.option('engine', 'tmpl'); - - pages.addViews({ - 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, - 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, - 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, - 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, - 'e': {content: '<%= title %>', locals: {title: 'eee'}}, - 'f': {content: '<%= title %>', locals: {title: 'fff'}}, - 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, - 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, - 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}} - }); - - pages.use(function(collection) { - collection.option('pager', false); - - collection.renderEach = function(cb) { - var list = new List(collection); - each(list.items, function(item, next) { - collection.render(item, next); - }, cb); - }; - }); - - pages.renderEach(function(err, items) { - if (err) return cb(err); - assert(items[0].content === 'aaa'); - assert(items[9].content === 'jjj'); - assert(items.length === 10); - cb(); - }); - }); - }); -}); diff --git a/test/app.lookupGenerator.js b/test/app.lookupGenerator.js new file mode 100644 index 00000000..7b4d1590 --- /dev/null +++ b/test/app.lookupGenerator.js @@ -0,0 +1,61 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Base = require('..'); +var base; + +describe('.lookupGenerator', function() { + beforeEach(function() { + base = new Base(); + + base.option('toAlias', function(key) { + return key.replace(/^generate-(.*)/, '$1'); + }); + }); + + it('should get a generator using a custom lookup function', function() { + base.register('generate-foo', function() {}); + base.register('generate-bar', function() {}); + var gen = base.lookupGenerator('foo', function(key) { + return ['generate-' + key, 'verb-' + key + '-generator', key]; + }); + + assert(gen); + assert.equal(gen.env.name, 'generate-foo'); + assert.equal(gen.env.alias, 'foo'); + }); + + it('should get a generator using a custom lookup function passed on options', function() { + base.register('generate-foo', function() {}); + base.register('generate-bar', function() {}); + + var gen = base.getGenerator('foo', { + lookup: function(key) { + return ['generate-' + key, 'verb-' + key + '-generator', key]; + } + }); + + assert(gen); + assert.equal(gen.env.name, 'generate-foo'); + assert.equal(gen.env.alias, 'foo'); + }); + + it('should return undefined when nothing is found', function() { + var gen = base.lookupGenerator('fofofofofo', function(key) { + return ['generate-' + key, 'verb-' + key + '-generator', key]; + }); + + assert(!gen); + }); + + it('should throw an error when a function is not passed', function(cb) { + try { + base.lookupGenerator('foo'); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'expected `fn` to be a lookup function'); + cb(); + } + }); +}); diff --git a/test/app.lookups.js b/test/app.lookups.js deleted file mode 100644 index 0a28bf00..00000000 --- a/test/app.lookups.js +++ /dev/null @@ -1,119 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); -var resolve = require('resolve-glob'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.lookups', function() { - beforeEach(function() { - app = new App(); - app.option('renameKey', function(key) { - return path.basename(key); - }); - app.create('pages') - .use(function(pages) { - pages.on('addViews', function(glob) { - var files = resolve.sync(glob); - files.forEach(function(fp) { - pages.addView(fp, {path: fp}); - }); - pages.loaded = true; - }); - return function(view) { - view.read = function() { - this.contents = fs.readFileSync(this.path); - }; - return view; - }; - }); - - app.pages('test/fixtures/templates/*.tmpl'); - }); - - describe('getView', function() { - it('should find a view', function() { - var view = app.getView('pages', 'a.tmpl'); - assert(typeof view.path === 'string'); - }); - - it('should find a view using the renameKey function', function() { - var view = app.getView('pages', 'test/fixtures/templates/a.tmpl'); - assert(typeof view.path === 'string'); - }); - - it('should return undefined when nothing is found', function() { - var view = app.getView('pages', 'test/fixtures/templates/foo.tmpl'); - assert.equal(typeof view, 'undefined'); - }); - - it('should return undefined when name is a directory', function() { - var view = app.getView('pages', 'test/fixtures/templates'); - assert(typeof view === 'undefined'); - }); - - it('should find a view using a glob pattern', function() { - var view = app.getView('pages', 'a', function(key) { - return key + '.tmpl'; - }); - assert(typeof view.path === 'string'); - }); - }); - - describe('getViews', function() { - it('should return the collection object if passed:', function() { - var views = app.getViews(app.views.pages); - assert(Object.keys(views).length > 1); - }); - - it('should return the specified collection with the plural name:', function() { - var views = app.getViews('pages'); - assert(Object.keys(views).length > 1); - }); - - it('should return the specified collection with the singular name:', function() { - var views = app.getViews('page'); - assert(Object.keys(views).length > 1); - }); - - it('should return null when the collection is not found:', function() { - (function() { - app.getViews('nada'); - }).should.throw('getViews cannot find collection: nada'); - }); - }); - - describe('find', function() { - it('should return null when a view is not found:', function() { - (function() { - app.find({}); - }).should.throw('expected name to be a string.'); - }); - - it('should find a view by collection name:', function() { - var view = app.find('a.tmpl', 'pages'); - assert(typeof view.path === 'string'); - }); - - it('should find a view by collection name:', function() { - app = new App(); - app.option('renameKey', function(key) { - return path.basename(key); - }); - app.create('pages'); - app.page('a/b/c.md', {content: '...'}); - var view = app.find('a/b/c.md'); - assert(typeof view.path === 'string'); - }); - - it('should find a view without a collection name:', function() { - var view = app.find('a.tmpl'); - assert(typeof view.path === 'string'); - }); - }); -}); diff --git a/test/app.matchGenerator.js b/test/app.matchGenerator.js new file mode 100644 index 00000000..922d5de9 --- /dev/null +++ b/test/app.matchGenerator.js @@ -0,0 +1,57 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var spawn = require('cross-spawn'); +var exists = require('fs-exists-sync'); +var Base = require('..'); +var base; + +describe('.matchGenerator', function() { + before(function(cb) { + if (!exists(path.resolve(__dirname, '../node_modules/generate-foo'))) { + spawn('npm', ['install', '--global', 'generate-foo'], {stdio: 'inherit'}) + .on('error', cb) + .on('close', function(code, err) { + cb(err, code); + }); + } else { + cb(); + } + }); + + beforeEach(function() { + base = new Base(); + + base.option('toAlias', function(key) { + return key.replace(/^generate-(.*)/, '$1'); + }); + }); + + it('should match a generator by name', function() { + base.register('generate-foo'); + + var gen = base.matchGenerator('generate-foo'); + assert(gen); + assert.equal(gen.env.name, 'generate-foo'); + assert.equal(gen.env.alias, 'foo'); + }); + + it('should match a generator by alias', function() { + base.register('generate-foo'); + + var gen = base.matchGenerator('foo'); + assert(gen); + assert.equal(gen.env.name, 'generate-foo'); + assert.equal(gen.env.alias, 'foo'); + }); + + it('should match a generator by path', function() { + base.register('generate-foo'); + var gen = base.matchGenerator(require.resolve('generate-foo')); + assert(gen); + assert.equal(gen.env.name, 'generate-foo'); + assert.equal(gen.env.alias, 'foo'); + }); +}); diff --git a/test/app.mergePartials.js b/test/app.mergePartials.js deleted file mode 100644 index 01c9a17b..00000000 --- a/test/app.mergePartials.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.mergePartials', function() { - beforeEach(function() { - app = new App(); - }); - - it('should merge multiple partials collections onto one collection:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials(); - actual.should.have.property('partials'); - actual.partials.should.have.properties(['a', 'b', 'c']); - }); - - it('should keep partials collections on separate collections:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.not.have.property('partials'); - actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); - }); - - it('should emit `mergePartials`:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - var arr = []; - - app.on('onMerge', function(view) { - arr.push(view.content); - }); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.not.have.property('partials'); - actual.should.eql({ foos: { a: 'aaa' }, bars: { b: 'bbb' }, bazs: { c: 'ccc' } }); - arr.should.eql(['aaa', 'bbb', 'ccc']); - }); - - it('should handle `onMerge` middleware:', function() { - var opts = { viewType: 'partial' }; - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.onMerge(/./, function(view, next) { - view.content += ' onMerge'; - next(); - }); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.eql({ - foos: {a: 'aaa onMerge'}, - bars: {b: 'bbb onMerge'}, - bazs: {c: 'ccc onMerge'} - }); - }); - - it('should skip views with `nomerge=true`:', function() { - var opts = { viewType: 'partial' }; - - app.create('foo', opts); - app.create('bar', opts); - app.create('baz', opts); - - app.onMerge(/[ab]/, function(view, next) { - view.options.nomerge = true; - next(); - }); - - app.foo('a', {path: 'a', content: 'aaa'}); - app.bar('b', {path: 'b', content: 'bbb'}); - app.baz('c', {path: 'c', content: 'ccc'}); - - var actual = app.mergePartials({mergePartials: false}); - actual.should.eql({ bazs: { c: 'ccc' } }); - }); -}); - diff --git a/test/app.middleware.js b/test/app.middleware.js deleted file mode 100644 index 437d17ec..00000000 --- a/test/app.middleware.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.middleware', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('pages'); - }); - - it('should call the all method for every middleware method:', function() { - var i = 0; - app.all(/./, function(view, next) { - assert(typeof view.path === 'string'); - i++; - next(); - }); - - assert(i === 0); - app.page('foo.tmpl', {content: 'foo'}); - assert(i === 1); - }); - - it('should call the onLoad method when a view is loaded:', function() { - var i = 0; - app.onLoad(/./, function(view, next) { - assert(typeof view.path === 'string'); - i++; - next(); - }); - - assert(i === 0); - app.page('foo.tmpl', {content: 'foo'}); - assert(i === 1); - }); - - it('should emit an event when a handler is called:', function(cb) { - var i = 0; - app.on('onLoad', function() { - i++; - }); - app.on('preRender', function() { - i++; - }); - app.on('preCompile', function() { - i++; - }); - - app.page('foo.tmpl', {content: 'foo'}) - .render(function(err) { - if (err) return cb(err); - assert.equal(i, 3); - cb(); - }); - }); -}); diff --git a/test/app.onLoad.js b/test/app.onLoad.js deleted file mode 100644 index dac994ed..00000000 --- a/test/app.onLoad.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.onLoad', function() { - beforeEach(function() { - app = new App(); - }); - - describe('app.collection', function() { - it('should emit an onLoad when view is created', function(cb) { - var collection = app.collection(); - - app.on('onLoad', function(view) { - assert(view.path === 'blog/foo.js'); - cb(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); - next(); - }); - - collection.addView('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - }); - - describe('view collections', function() { - it('should emit an onLoad when view is created', function(cb) { - app.create('posts'); - - app.on('onLoad', function(view) { - assert(view.path === 'blog/foo.js'); - cb(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert(view.path === 'blog/foo.js'); - next(); - }); - - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - }); -}); diff --git a/test/app.option.js b/test/app.option.js deleted file mode 100644 index bc9d855c..00000000 --- a/test/app.option.js +++ /dev/null @@ -1,100 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.option', function() { - beforeEach(function() { - app = new App(); - }); - - it('should set a key-value pair on options:', function() { - app.option('a', 'b'); - assert.equal(app.options.a, 'b'); - }); - - it('should set an object on options:', function() { - app.option({c: 'd'}); - assert.equal(app.options.c, 'd'); - }); - - it('should set an option.', function() { - app.option('a', 'b'); - assert(app.options.hasOwnProperty('a')); - }); - - it('should get an option.', function() { - app.option('a', 'b'); - assert.equal(app.option('a'), 'b'); - }); - - it('should extend the `options` object.', function() { - app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - assert.equal(app.option('x'), 'xxx'); - assert.equal(app.option('y'), 'yyy'); - assert.equal(app.option('z'), 'zzz'); - }); - - it('options should be on the `options` object.', function() { - app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - assert.equal(app.options.x, 'xxx'); - assert.equal(app.options.y, 'yyy'); - assert.equal(app.options.z, 'zzz'); - }); - - it('should be chainable.', function() { - app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - app.option({a: 'aaa', b: 'bbb', c: 'ccc'}); - - assert.equal(app.option('x'), 'xxx'); - assert.equal(app.option('a'), 'aaa'); - }); - - it('should extend the `options` object when the first param is a string.', function() { - app.option('foo', {x: 'xxx', y: 'yyy', z: 'zzz'}); - app.option('bar', {a: 'aaa', b: 'bbb', c: 'ccc'}); - - assert(app.option('foo').hasOwnProperty('x')); - assert(app.option('bar').hasOwnProperty('a')); - - assert(app.options.foo.hasOwnProperty('x')); - assert(app.options.bar.hasOwnProperty('a')); - }); - - it('should set an option.', function() { - app.option('a', 'b'); - assert(app.options.hasOwnProperty('a')); - }); - - it('should get an option.', function() { - app.option('a', 'b'); - assert.equal(app.option('a'), 'b'); - }); - - it('should extend the `options` object.', function() { - app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - assert.equal(app.option('x'), 'xxx'); - assert.equal(app.option('y'), 'yyy'); - assert.equal(app.option('z'), 'zzz'); - }); - - it('options should be on the `options` object.', function() { - app.option({x: 'xxx', y: 'yyy', z: 'zzz'}); - assert.equal(app.options.x, 'xxx'); - assert.equal(app.options.y, 'yyy'); - assert.equal(app.options.z, 'zzz'); - }); - - it('should be chainable.', function() { - app - .option({x: 'xxx', y: 'yyy', z: 'zzz'}) - .option({a: 'aaa', b: 'bbb', c: 'ccc'}); - - assert.equal(app.option('x'), 'xxx'); - assert.equal(app.option('a'), 'aaa'); - }); -}); diff --git a/test/app.options.initTemplates.js b/test/app.options.initTemplates.js deleted file mode 100644 index 20ac2f09..00000000 --- a/test/app.options.initTemplates.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.options.initTemplates', function() { - it('should call `options.initTemplates` before any instance methods are called', function() { - app = new App({ - initTemplates: function(app) { - this.on('create', function(name, options) { - options.viewType = 'partial'; - }); - } - }); - - app.create('includes'); - app.include('one', {path: 'two', contents: '...'}); - assert(app.includes.isType('partial')); - }); -}); diff --git a/test/app.page.js b/test/app.page.js index 0b279bd1..0f314f1b 100644 --- a/test/app.page.js +++ b/test/app.page.js @@ -1,12 +1,11 @@ 'use strict'; require('mocha'); -var path = require('path'); var assert = require('assert'); var assemble = require('..'); var app; -describe('app.page', function() { +describe('.page()', function() { beforeEach(function() { app = assemble(); if (!app.pages) { @@ -19,14 +18,14 @@ describe('app.page', function() { app.page('a.hbs', {path: 'a.hbs', contents: new Buffer('a')}); app.page('b.hbs', {path: 'b.hbs', contents: new Buffer('b')}); app.page('c.hbs', {path: 'c.hbs', contents: new Buffer('c')}); - assert.equal(Object.keys(app.views.pages).length, 3); + assert(Object.keys(app.views.pages).length === 3); }); }); - + describe('load', function() { it('should load a page from a non-glob filepath', function() { app.page('test/fixtures/pages/a.hbs'); - assert.equal(Object.keys(app.views.pages).length, 1); + assert(Object.keys(app.views.pages).length === 1); }); }); }); diff --git a/test/app.pages.js b/test/app.pages.js index dbc6ec6e..1d324ac0 100644 --- a/test/app.pages.js +++ b/test/app.pages.js @@ -1,19 +1,20 @@ 'use strict'; require('mocha'); -require('should'); var assert = require('assert'); -var Verb = require('..'); +var generate = require('..'); var app; -describe('app.pages', function() { +describe('.pages()', function() { beforeEach(function() { - app = new Verb({cli: true, cwd: process.cwd()}); - app.create('pages'); + app = generate({cli: true}); + if (!app.pages) { + app.create('pages'); + } }); describe('add pages', function() { - it('should add pages to `app.views.pages`', function() { + it('should add pages to `app.views.pages`:', function() { app.pages({ 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, @@ -24,7 +25,7 @@ describe('app.pages', function() { }); describe('load pages', function() { - it('should load pages onto `app.views.pages`', function() { + it('should load pages onto `app.views.pages`:', function() { app.pages('test/fixtures/pages/*.hbs'); assert.equal(Object.keys(app.views.pages).length, 3); }); diff --git a/test/app.partial.js b/test/app.partial.js index 27789a5e..ba6ca577 100644 --- a/test/app.partial.js +++ b/test/app.partial.js @@ -1,12 +1,11 @@ 'use strict'; require('mocha'); -require('should'); var assert = require('assert'); var assemble = require('..'); var app; -describe('app.partial', function() { +describe('.partial()', function() { beforeEach(function() { app = assemble(); if (!app.partials) { diff --git a/test/app.partials.js b/test/app.partials.js index a92886df..9622fcfe 100644 --- a/test/app.partials.js +++ b/test/app.partials.js @@ -1,12 +1,11 @@ 'use strict'; require('mocha'); -require('should'); var assert = require('assert'); var assemble = require('..'); var app; -describe('app.partials', function() { +describe('.partials()', function() { beforeEach(function() { app = assemble(); if (!app.partials) { diff --git a/test/app.questions.js b/test/app.questions.js index 8f93588c..eb9d6708 100644 --- a/test/app.questions.js +++ b/test/app.questions.js @@ -5,6 +5,8 @@ process.env.NODE_ENV = 'test'; require('mocha'); var fs = require('fs'); var assert = require('assert'); +var questions = require('base-questions'); +var config = require('base-config-process'); var store = require('base-store'); var App = require('..'); var app, base, site; @@ -12,10 +14,14 @@ var app, base, site; describe('app.questions', function() { describe('plugin', function() { beforeEach(function() { - base = new App({cli: true}); + base = new App(); + base.use(config()); + base.use(questions()); base.use(store('base-questions-tests/base')); - app = new App({cli: true}); + app = new App(); + app.use(config()); + app.use(questions()); app.use(store('base-questions-tests/app')); }); @@ -41,213 +47,247 @@ describe('app.questions', function() { }); }); - describe('app.ask', function() { - beforeEach(function() { - app = new App({cli: true}); - app.use(store('base-questions-tests/ask')); - }); + if (process.env.TRAVIS) { + return; - afterEach(function() { - app.store.del({force: true}); - app.questions.clear(); - app.cache.data = {}; - }); + describe('app.ask', function() { + beforeEach(function() { + app = new App(); + app.use(config()); + app.use(questions()); + app.use(store('base-questions-tests/ask')); + }); - it('should store a question:', function() { - app.question('a', 'b'); - assert(app.questions); - assert(app.questions.cache); - assert(app.questions.cache.a); - assert.equal(app.questions.cache.a.name, 'a'); - assert.equal(app.questions.cache.a.message, 'b'); - }); + afterEach(function() { + app.store.del({force: true}); + app.questions.clear(); + app.cache.data = {}; + }); - it('should ask a question defined on `ask`', function(cb) { - app.data('name', 'Brian Woodward'); + it.skip('should force all questions to be asked', function(cb) { + app.questions.option('init', 'author'); + app.ask({force: true}, function(err, answers) { + console.log(answers) + cb(); + }); + }); - app.ask('name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.name, 'Brian Woodward'); - cb(); + it('should store a question:', function() { + app.question('a', 'b'); + assert(app.questions); + assert(app.questions.cache); + assert(app.questions.cache.a); + assert.equal(app.questions.cache.a.name, 'a'); + assert.equal(app.questions.cache.a.message, 'b'); }); - }); - it('should ask a question and use a `cache.data` value to answer:', function(cb) { - app.question('a', 'this is a question'); - app.data('a', 'b'); + it.skip('should re-init a specific question:', function(cb) { + this.timeout(20000); + app.question('a', 'b'); + app.question('c', 'd'); + app.question('e', 'f'); + app.data({a: 'b'}); - app.ask('a', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.a, 'b'); + app.questions.get('e') + .force() - app.data('a', 'zzz'); - app.ask('a', function(err, answers) { + app.ask(function(err, answers) { + console.log(answers); + cb(); + }); + }); + + it('should ask a question defined on `ask`', function(cb) { + app.data('name', 'Brian Woodward'); + + app.ask('name', function(err, answers) { if(err) return cb(err) - assert.equal(answers.a, 'zzz'); + assert.equal(answers.name, 'Brian Woodward'); cb(); - }) + }); }); - }); - it('should ask a question and use a `store.data` value to answer:', function(cb) { - app.question('a', 'this is another question'); - app.store.set('a', 'c'); - app.ask('a', function(err, answers) { - if (err) return cb(err); - assert(answers); - assert.equal(answers.a, 'c'); - cb(); - }) - }); + it('should ask a question and use a `cache.data` value to answer:', function(cb) { + app.question('a', 'this is a question'); + app.data('a', 'b'); - it('should ask a question and use a config value to answer:', function(cb) { - app.question('a', 'b'); - app.config.process({data: {a: 'foo'}}, function(err) { - if (err) return cb(err); + app.ask('a', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.a, 'b'); + + app.data('a', 'zzz'); + app.ask('a', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.a, 'zzz'); + cb(); + }) + }); + }); + it('should ask a question and use a `store.data` value to answer:', function(cb) { + app.question('a', 'this is another question'); app.store.set('a', 'c'); - - app.ask('a', function(err, answer) { + app.ask('a', function(err, answers) { if (err) return cb(err); - assert(answer); - assert.equal(answer.a, 'foo'); + assert(answers); + assert.equal(answers.a, 'c'); cb(); - }); + }) }); - }); - it('should prefer `cache.data` to `store.data`', function(cb) { - app.question('a', 'b'); - app.data('a', 'b'); - app.store.set('a', 'c'); - - app.ask('a', function(err, answer) { - if (err) return cb(err); - assert(answer); - assert.equal(answer.a, 'b'); - cb(); - }) - }); + it('should ask a question and use a config value to answer:', function(cb) { + app.question('a', 'b'); + app.config.process({data: {a: 'foo'}}, function(err) { + if (err) return cb(err); - it('should update data with data loaded by config', function(cb) { - app.question('a', 'this is a question'); - app.data('a', 'b'); + app.store.set('a', 'c'); - app.config.process({data: {a: 'foo'}}, function(err) { - if (err) return cb(err); + app.ask('a', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.a, 'foo'); + cb(); + }); + }); + }); + + it('should prefer `cache.data` to `store.data`', function(cb) { + app.question('a', 'b'); + app.data('a', 'b'); + app.store.set('a', 'c'); app.ask('a', function(err, answer) { if (err) return cb(err); - assert(answer); - assert.equal(answer.a, 'foo'); + assert.equal(answer.a, 'b'); cb(); - }); + }) }); - }); - }); - describe('session data', function() { - before(function() { - site = new App({cli: true}); - site.use(store('base-questions-tests/site')); + it('should update data with data loaded by config', function(cb) { + app.question('a', 'this is a question'); + app.data('a', 'b'); - app = new App({cli: true}); - app.use(store('base-questions-tests/ask')); - }); + app.config.process({data: {a: 'foo'}}, function(err) { + if (err) return cb(err); - after(function() { - site.store.del({force: true}); - site.questions.clear(); + app.ask('a', function(err, answer) { + if (err) return cb(err); - app.store.del({force: true}); - app.questions.clear(); + assert(answer); + assert.equal(answer.a, 'foo'); + cb(); + }); + }); + }); }); - it('[app] should ask a question and use a `cache.data` value to answer:', function(cb) { - app.question('package.name', 'this is a question'); - app.data('package.name', 'base-questions'); + describe('session data', function() { + before(function() { + site = new App(); + site.use(config()); + site.use(questions()); + site.use(store('base-questions-tests/site')); + + app = new App(); + app.use(config()); + app.use(questions()); + app.use(store('base-questions-tests/ask')); + }); - app.ask('package.name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.package.name, 'base-questions'); + after(function() { + site.store.del({force: true}); + site.questions.clear(); + + app.store.del({force: true}); + app.questions.clear(); + }); + + it('[app] should ask a question and use a `cache.data` value to answer:', function(cb) { + app.question('package.name', 'this is a question'); + app.data('package.name', 'base-questions'); - app.data('package.name', 'foo-bar-baz'); app.ask('package.name', function(err, answers) { if(err) return cb(err) - assert.equal(answers.package.name, 'foo-bar-baz'); - cb(); - }) + assert.equal(answers.package.name, 'base-questions'); + + app.data('package.name', 'foo-bar-baz'); + app.ask('package.name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.package.name, 'foo-bar-baz'); + cb(); + }) + }); }); - }); - it('[site] should ask a question and use a `cache.data` value to answer:', function(cb) { - site.question('package.name', 'this is a question'); - site.data('package.name', 'base-questions'); + it('[site] should ask a question and use a `cache.data` value to answer:', function(cb) { + site.question('package.name', 'this is a question'); + site.data('package.name', 'base-questions'); - site.ask('package.name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.package.name, 'base-questions'); - - site.data('package.name', 'foo-bar-baz'); site.ask('package.name', function(err, answers) { if(err) return cb(err) - assert.equal(answers.package.name, 'foo-bar-baz'); + assert.equal(answers.package.name, 'base-questions'); + + site.data('package.name', 'foo-bar-baz'); + site.ask('package.name', function(err, answers) { + if(err) return cb(err) + assert.equal(answers.package.name, 'foo-bar-baz'); + cb(); + }) + }); + }); + + it('[app] should ask a question and use a `store.data` value to answer:', function(cb) { + app.question('author.name', 'this is another question'); + app.store.set('author.name', 'Brian Woodward'); + app.ask('author.name', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.author.name, 'Brian Woodward'); cb(); }) }); - }); - it('[app] should ask a question and use a `store.data` value to answer:', function(cb) { - app.question('author.name', 'this is another question'); - app.store.set('author.name', 'Brian Woodward'); - app.ask('author.name', function(err, answers) { - if (err) return cb(err); - assert(answers); - assert.equal(answers.author.name, 'Brian Woodward'); - cb(); - }) - }); - - it('[site] should ask a question and use a `store.data` value to answer:', function(cb) { - site.question('author.name', 'this is another question'); - site.store.set('author.name', 'Jon Schlinkert'); - site.ask('author.name', function(err, answers) { - if (err) return cb(err); - assert(answers); - assert.equal(answers.author.name, 'Brian Woodward'); - cb(); - }) - }); + it('[site] should ask a question and use a `store.data` value to answer:', function(cb) { + site.question('author.name', 'this is another question'); + site.store.set('author.name', 'Jon Schlinkert'); + site.ask('author.name', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.author.name, 'Brian Woodward'); + cb(); + }) + }); - it('[app] should ask a question and use a config value to answer:', function(cb) { - app.question('foo', 'Username?'); - app.config.process({data: {foo: 'jonschlinkert'}}, function(err) { - if (err) return cb(err); + it('[app] should ask a question and use a config value to answer:', function(cb) { + app.question('foo', 'Username?'); + app.config.process({data: {foo: 'jonschlinkert'}}, function(err) { + if (err) return cb(err); - app.store.set('foo', 'doowb'); + app.store.set('foo', 'doowb'); - app.ask('foo', function(err, answer) { - if (err) return cb(err); - assert(answer); - assert.equal(answer.foo, 'jonschlinkert'); - cb(); + app.ask('foo', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.foo, 'jonschlinkert'); + cb(); + }); }); }); - }); - - it('[site] should ask a question and use a config value to answer:', function(cb) { - site.question('foo', 'Username?'); - site.config.process({data: {foo: 'doowb'}}, function(err) { - if (err) return cb(err); - site.ask('foo', function(err, answer) { + it('[site] should ask a question and use a config value to answer:', function(cb) { + site.question('foo', 'Username?'); + site.config.process({data: {foo: 'doowb'}}, function(err) { if (err) return cb(err); - assert(answer); - assert.equal(answer.foo, 'doowb'); - cb(); + + site.ask('foo', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.foo, 'doowb'); + cb(); + }); }); }); }); - }); + } }); diff --git a/test/app.register.js b/test/app.register.js index ca99e85d..90e5b3bf 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -3,25 +3,19 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Generate = require('..'); +var generators = require('base-generators'); +var Base = require('..'); var base; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('app.register', function() { - it('should register as a plugin', function() { - var base = new Generate(); - assert(base.registered.hasOwnProperty('base-generators')); - }); -}); - describe('.register', function() { beforeEach(function() { - base = new Generate(); + base = new Base(); }); describe('function', function() { - it('should register a generator function', function() { + it('should register a generator a function', function() { base.register('foo', function() {}); var foo = base.getGenerator('foo'); assert(foo); @@ -38,7 +32,7 @@ describe('.register', function() { assert(generator.tasks.hasOwnProperty('default')); }); - it('should get a sub-generator from a generator registered as a function', function() { + it('should get a generator from a generator registered as a function', function() { base.register('foo', function(foo) { foo.register('bar', function(bar) {}); }); @@ -134,7 +128,7 @@ describe('.register', function() { }); }); - describe('.toAlias', function() { + describe('alias', function() { it('should use a custom function to create the alias', function() { base.option('toAlias', function(name) { return name.slice(name.lastIndexOf('-') + 1); @@ -164,8 +158,8 @@ describe('.register', function() { }); it('should register a generator from a configfile filepath', function() { - base.register('generate-abc', fixtures('generators/a/verbfile.js')); - assert(base.generators.hasOwnProperty('generate-abc')); + base.register('base-abc', fixtures('generators/a/generator.js')); + assert(base.generators.hasOwnProperty('base-abc')); }); it('should throw when a generator does not expose the instance', function(cb) { @@ -173,8 +167,7 @@ describe('.register', function() { base.register('not-exposed', require(fixtures('not-exposed.js'))); cb(new Error('expected an error')); } catch (err) { - var fp = path.resolve(__dirname, '../node_modules/not-exposed/generator.js'); - assert.equal(err.message, 'Cannot find module \'' + fp + '\''); + assert.equal(err.message, `cannot resolve: 'not-exposed'`); cb(); } }); @@ -182,21 +175,21 @@ describe('.register', function() { describe('instance', function() { it('should register an instance', function() { - base.register('base-inst', new Generate()); + base.register('base-inst', new Base()); assert(base.generators.hasOwnProperty('base-inst')); }); it('should get a generator that was registered as an instance', function() { - var foo = new Generate(); + var foo = new Base(); foo.task('default', function() {}); base.register('foo', foo); assert(base.getGenerator('foo')); }); it('should register multiple instances', function() { - var foo = new Generate(); - var bar = new Generate(); - var baz = new Generate(); + var foo = new Base(); + var bar = new Base(); + var baz = new Base(); base.register('foo', foo); base.register('bar', bar); base.register('baz', baz); @@ -206,15 +199,17 @@ describe('.register', function() { }); it('should get tasks from a generator that was registered as an instance', function() { - var foo = new Generate(); + var foo = new Base(); foo.task('default', function() {}); base.register('foo', foo); var generator = base.getGenerator('foo'); + assert(generator); assert(generator.tasks.hasOwnProperty('default')); }); it('should get sub-generators from a generator registered as an instance', function() { - var foo = new Generate(); + var foo = new Base(); + foo.use(generators()); foo.register('bar', function() {}); base.register('foo', foo); var generator = base.getGenerator('foo.bar'); @@ -222,10 +217,14 @@ describe('.register', function() { }); it('should get tasks from sub-generators registered as an instance', function() { - var foo = new Generate(); + var foo = new Base(); + foo.use(generators()); + + foo.options.isFoo = true; foo.register('bar', function(bar) { bar.task('whatever', function() {}); }); + base.register('foo', foo); var generator = base.getGenerator('foo.bar'); assert(generator.tasks); diff --git a/test/app.render.js b/test/app.render.js deleted file mode 100644 index d3632429..00000000 --- a/test/app.render.js +++ /dev/null @@ -1,180 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.render', function() { - beforeEach(function() { - app = new App(); - app.engine('hbs', require('engine-handlebars')); - app.engine('tmpl', require('engine-base')); - app.create('partials', {viewType: 'partial'}); - app.create('page'); - }); - - it('should throw an error when no callback is given:', function(cb) { - try { - app.render(); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'Templates#render is async and expects a callback function'); - cb(); - } - }); - - it('should throw an error when an engine is not defined:', function(cb) { - app.page('foo.bar', {content: '<%= name %>'}); - var page = app.pages.getView('foo.bar'); - - app.render(page, function(err) { - assert(err.message === 'Templates#render cannot find an engine for: .bar'); - cb(); - }); - }); - - it('should use helpers to render a view:', function(cb) { - var locals = {name: 'Halle'}; - - app.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.contents.toString() === 'a HALLE b'); - cb(); - }); - }); - - it('should use global app data in template.', function(cb) { - app.data({name: 'CCC'}); - app.page('a.tmpl', {content: 'a <%= name %> b'}); - app.render('a.tmpl', function(err, res) { - if (err) return cb(err); - res.content.should.equal('a CCC b'); - cb(); - }); - }); - - it('should use page data in template.', function(cb) { - app.data({name: 'CCC'}); - app.page('a.tmpl', {content: 'a <%= name %> b', data: {name: 'DDD'}}); - app.render('a.tmpl', function(err, res) { - if (err) return cb(err); - res.content.should.equal('a DDD b'); - cb(); - }); - }); - - it('should use passed in locals in template.', function(cb) { - app.data({name: 'CCC'}); - app.page('a.tmpl', {content: 'a <%= name %> b', data: {name: 'DDD'}}); - app.render('a.tmpl', {name: 'EEE'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('a EEE b'); - cb(); - }); - }); - - it('should render the same template twice with a helper', function(cb) { - app.partial('button.tmpl', {content: '<%= name %>'}); - app.page('a.tmpl', {content: 'a <%= partial("button.tmpl", {name: "foo"}) %> <%= partial("button.tmpl", {name: "bar"}) %> b'}); - - app.pages.getView('a.tmpl') - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a foo bar b'); - cb(); - }); - }); - - it('should render the same template multiple times with different engines', function(cb) { - app.partial('button.tmpl', {content: '<%= name %>'}); - app.partial('foo.hbs', {content: '{{name}}'}); - - app.page('a.hbs', {content: 'a <%= partial("button.tmpl", {name: "foo"}) %> <%= partial("button.tmpl", {name: "bar"}) %> {{partial "foo.hbs" name="one"}} {{partial "foo.hbs" name="two"}} b'}); - - var view = app.pages.getView('a.hbs'); - - app.render(view, function(err, res) { - if (err) return cb(err); - - res.engine = 'tmpl'; - - app.render(res, function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a foo bar one two b'); - cb(); - }); - }); - }); - - it('should render the same template multiple times with different engines', function(cb) { - app.partial('button.tmpl', {content: '{{title}}', engine: 'hbs'}); - app.partial('foo.hbs', {content: '{{title}}'}); - - app.page('a.hbs', { - content: 'a <%= partial("button.tmpl", {title: "foo"}) %> <%= partial("button.tmpl", {title: "bar"}) %> {{partial "foo.hbs" title="one"}} {{partial "foo.hbs" title="two"}} b' - }); - - var view = app.pages.getView('a.hbs'); - - app.render(view, function(err, res) { - if (err) return cb(err); - - res.engine = 'tmpl'; - - app.render(res, function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a foo bar one two b'); - cb(); - }); - }); - }); - - it('should use helpers when rendering a view:', function(cb) { - var locals = {name: 'Halle'}; - app.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - app.page('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, res) { - if (err) return cb(err); - assert(res.contents.toString() === 'a HALLE b'); - cb(); - }); - }); - - it('should render a template when contents is a buffer:', function(cb) { - app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = app.pages.getView('a.tmpl'); - - app.render(view, function(err, view) { - if (err) return cb(err); - assert(view.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a template when content is a string:', function(cb) { - app.pages('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = app.pages.getView('a.tmpl'); - - app.render(view, function(err, view) { - if (err) return cb(err); - assert(view.contents.toString() === 'b'); - cb(); - }); - }); -}); diff --git a/test/app.renderFile.js b/test/app.renderFile.js deleted file mode 100644 index 2fbe52d1..00000000 --- a/test/app.renderFile.js +++ /dev/null @@ -1,135 +0,0 @@ -'use strict'; - -var assemble = require('..'); -var assert = require('assert'); -var should = require('should'); -var path = require('path'); -var app; - -describe('app.renderFile', function() { - beforeEach(function() { - app = assemble(); - app.engine('hbs', require('engine-handlebars')); - app.engine('*', require('engine-base')); - - app.create('files', {engine: '*'}); - app.file('a', {content: 'this is <%= title() %>'}); - app.file('b', {content: 'this is <%= title() %>'}); - app.file('c', {content: 'this is <%= title() %>'}); - - app.option('renameKey', function(key) { - return path.basename(key, path.extname(key)); - }); - - app.helper('title', function() { - var view = this.context.view; - var key = view.key; - var ctx = this.context[key]; - if (ctx && ctx.title) return ctx.title; - return key; - }); - }); - - it('should render views from src', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); - var files = []; - - stream.pipe(app.renderFile()) - .on('error', cb) - .on('data', function(file) { - files.push(file); - }) - .on('end', function() { - assert.equal(files[0].basename, 'a.hbs'); - assert.equal(files[1].basename, 'b.hbs'); - assert.equal(files[2].basename, 'c.hbs'); - cb(); - }); - }); - - it('should render views with the engine that matches the file extension', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); - var files = []; - - stream.pipe(app.renderFile()) - .on('error', cb) - .on('data', function(file) { - files.push(file); - }) - .on('end', function() { - assert(/

a<\/h1>/.test(files[0].contents.toString())); - assert(/

b<\/h1>/.test(files[1].contents.toString())); - assert(/

c<\/h1>/.test(files[2].contents.toString())); - cb(); - }); - }); - - it('should render views from src with the engine passed on the opts', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); - var files = []; - - stream.pipe(app.renderFile({engine: '*'})) - .on('error', cb) - .on('data', function(file) { - files.push(file); - }) - .on('end', function() { - assert(/

a<\/h2>/.test(files[0].contents.toString())); - assert(/

b<\/h2>/.test(files[1].contents.toString())); - assert(/

c<\/h2>/.test(files[2].contents.toString())); - cb(); - }); - }); - - it('should use the context passed on the opts', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/pages/*.hbs')); - var files = []; - - stream.pipe(app.renderFile({a: {title: 'foo'}})) - .on('error', cb) - .on('data', function(file) { - files.push(file); - }) - .on('end', function() { - assert(/

foo<\/h1>/.test(files[0].contents.toString())); - assert(/

b<\/h1>/.test(files[1].contents.toString())); - assert(/

c<\/h1>/.test(files[2].contents.toString())); - cb(); - }); - }); - - it('should render the files in a collection', function(cb) { - var files = []; - app.toStream('files') - .pipe(app.renderFile()) - .on('error', cb) - .on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - files.push(file); - }) - .on('end', function() { - assert(/this is a/.test(files[0].contents.toString())); - assert(/this is b/.test(files[1].contents.toString())); - assert(/this is c/.test(files[2].contents.toString())); - assert.equal(files.length, 3); - cb(); - }); - }); - - it('should handle engine errors', function(cb) { - app.create('notdefined', {engine: '*'}); - app.notdefined('foo', {content: '<%= bar %>'}); - app.toStream('notdefined') - .pipe(app.renderFile()) - .on('error', function(err) { - assert.equal(typeof err, 'object'); - assert.equal(err.message, 'bar is not defined'); - cb(); - }) - .on('end', function() { - cb(new Error('expected renderFile to handle the error.')); - }); - }); -}); diff --git a/test/app.route.js b/test/app.route.js deleted file mode 100644 index bc4ebaf4..00000000 --- a/test/app.route.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.route', function() { - beforeEach(function() { - app = new App(); - }); - - describe('routes', function() { - it('should create a route for the given path:', function(cb) { - app = new App(); - app.create('posts'); - - app.on('all', function(msg) { - assert.equal(msg, 'cb'); - cb(); - }); - - app.route('blog/:title') - .all(function(view, next) { - app.emit('all', 'cb'); - next(); - }); - - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - - it('should emit events when a route method is called:', function(cb) { - app = new App(); - app.create('posts'); - - app.on('onLoad', function(view) { - assert.equal(view.path, 'blog/foo.js'); - cb(); - }); - - app.param('title', function(view, next, title) { - assert.equal(title, 'foo.js'); - next(); - }); - - app.onLoad('blog/:title', function(view, next) { - assert.equal(view.path, 'blog/foo.js'); - next(); - }); - - app.post('whatever', {path: 'blog/foo.js', content: 'bar baz'}); - }); - - it('should emit errors', function(cb) { - app = new App(); - app.create('posts'); - - app.on('error', function(err) { - assert.equal(err.message, "'foo.js' == 'fo.js'"); - cb(); - }); - - // wrong... - app.param('title', function(view, next, title) { - assert.equal(title, 'fo.js'); - next(); - }); - - app.onLoad('/blog/:title', function(view, next) { - assert.equal(view.path, '/blog/foo.js'); - next(); - }); - - app.post('whatever', {path: '/blog/foo.js', content: 'bar baz'}); - }); - - it('should have path property', function() { - var route = new app.Route('/blog/:year/:month/:day/:slug').all([ - function() {} - ]); - route.path.should.equal('/blog/:year/:month/:day/:slug'); - }); - - it('should have stack property', function() { - var route = new app.Route('/blog/:year/:month/:day/:slug').all([ - function() {} - ]); - - route.stack.should.be.instanceof(Array); - route.stack.should.have.length(1); - }); - }); -}); diff --git a/test/app.src.js b/test/app.src.js deleted file mode 100644 index 88e7e39f..00000000 --- a/test/app.src.js +++ /dev/null @@ -1,349 +0,0 @@ -'use strict'; - -var App = require('..'); -var path = require('path'); -var assert = require('assert'); -var should = require('should'); -var app; - -describe('src()', function() { - beforeEach(function() { - app = new App(); - }); - - it('should return a stream', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); - assert(stream); - assert.equal(typeof stream.on, 'function'); - assert.equal(typeof stream.pipe, 'function'); - cb(); - }); - - it('should return an input stream from a flat glob', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', cb); - }); - - it('should add files to an existing collection', function(cb) { - app.create('files'); - app.files('foo', {content: 'this is content'}); - var stream = app.src(path.join(__dirname, 'fixtures/test.coffee'), {collection: 'files'}); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - assert.equal(Object.keys(app.views.files).length, 2); - cb(); - }); - }); - - it('should extend file.options with src options', function(cb) { - app.create('files'); - app.file('foo', {content: 'this is content'}); - var stream = app.src(path.join(__dirname, 'fixtures/test.coffee'), {layout: 'default'}); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - assert.equal(file.options.layout, 'default'); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should add files to a new specified collection', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/test.coffee'), {collection: 'docs'}); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - assert.equal(Object.keys(app.views.docs).length, 1); - cb(); - }); - }); - - it('should return an input stream from a flat glob', function(cb) { - var stream = app.src(path.join(__dirname, './fixtures/*.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, './fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should return an input stream for multiple globs', function(cb) { - var globArray = [ - path.join(__dirname, './fixtures/generic/run.dmc'), - path.join(__dirname, './fixtures/generic/test.dmc') - ]; - var stream = app.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(2); - files[0].path.should.equal(globArray[0]); - files[1].path.should.equal(globArray[1]); - cb(); - }); - }); - - it('should return an input stream for multiple globs with negation', function(cb) { - var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); - var globArray = [ - path.join(__dirname, 'fixtures/generic/*.dmc'), - '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), - ]; - var stream = app.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(1); - files[0].path.should.equal(expectedPath); - cb(); - }); - }); - - it('should return an input stream with no contents when read is false', function(cb) { - app.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}) - .on('error', cb) - .on('data', function(file) { - assert(file); - assert(file.path); - assert(!file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - }) - .on('end', cb) - }); - - it('should not blow up when no files are matched', function(cb) { - app.src(['test.js', 'foo/*.js']) - .on('error', cb) - .on('data', function() {}) - .on('end', cb) - }); - - it('should return an input stream with contents as stream when buffer is false', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/*.coffee'), {buffer: false}); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - var buf = ''; - file.contents.on('data', function(d) { - buf += d; - }); - file.contents.on('end', function() { - buf.should.equal('Hello world!'); - cb(); - }); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - }); - }); - - it('should return an input stream from a deep glob', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/**/*.jade')); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); - String(file.contents).should.equal('test template'); - }); - stream.on('end', cb); - }); - - it('should return an input stream from a deeper glob', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/**/*.dmc')); - var a = 0; - stream.on('error', cb); - stream.on('data', function() { - ++a; - }); - stream.on('end', function() { - a.should.equal(2); - cb(); - }); - }); - - it('should return a file stream from a flat path', function(cb) { - var a = 0; - var stream = app.src(path.join(__dirname, 'fixtures/test.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - ++a; - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - a.should.equal(1); - cb(); - }); - }); - - it('should return a stream', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); - assert(stream); - assert(stream.on); - cb(); - }); - - it('should return an input stream from a flat glob', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/*.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', cb); - }); - - it('should return an input stream for multiple globs', function(cb) { - var globArray = [ - path.join(__dirname, 'fixtures/generic/run.dmc'), - path.join(__dirname, 'fixtures/generic/test.dmc') - ]; - var stream = app.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(2); - files[0].path.should.equal(globArray[0]); - files[1].path.should.equal(globArray[1]); - cb(); - }); - }); - - it('should return an input stream for multiple globs, with negation', function(cb) { - var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); - var globArray = [ - path.join(__dirname, 'fixtures/generic/*.dmc'), - '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), - ]; - var stream = app.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(1); - files[0].path.should.equal(expectedPath); - cb(); - }); - }); - - it('should return an input stream with no contents when read is false', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}); - stream.on('error', cb); - stream.on('data', function(file) { - assert(file); - assert(file.path); - assert(!file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - }); - stream.on('end', cb); - }); - - it('should return an input stream from a deep glob', function(cb) { - app.src(path.join(__dirname, 'fixtures/**/*.jade')) - .on('error', cb) - .on('data', function(file) { - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); - String(file.contents).should.equal('test template'); - }) - .on('end', function() { - cb(); - }); - }); - - it('should return an input stream from a deeper glob', function(cb) { - var stream = app.src(path.join(__dirname, 'fixtures/**/*.dmc')); - var a = 0; - stream.on('error', cb); - stream.on('data', function() { - ++a; - }); - stream.on('end', function() { - a.should.equal(2); - cb(); - }); - }); - - it('should return a file stream from a flat path', function(cb) { - var a = 0; - var stream = app.src(path.join(__dirname, 'fixtures/test.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - ++a; - assert(file); - assert(file.path); - assert(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - a.should.equal(1); - cb(); - }); - }); -}); diff --git a/test/app.store.js b/test/app.store.js index 5ec07e9f..aa745e09 100644 --- a/test/app.store.js +++ b/test/app.store.js @@ -1,19 +1,17 @@ 'use strict'; require('mocha'); -require('should'); var fs = require('fs'); var path = require('path'); var assert = require('assert'); var store = require('base-store'); -var support = require('./support'); -var generate = support.resolve(); +var App = require('..'); var app; -describe('app.store', function() { +describe('store', function() { describe('methods', function() { beforeEach(function() { - app = generate({cli: true}); + app = new App(); app.use(store()); app.store.create('app-data-tests'); }); @@ -26,42 +24,42 @@ describe('app.store', function() { it('should `.set()` a value on the store', function() { app.store.set('one', 'two'); - app.store.data.one.should.equal('two'); + assert.equal(app.store.data.one, 'two'); }); it('should `.set()` an object', function() { app.store.set({four: 'five', six: 'seven'}); - app.store.data.four.should.equal('five'); - app.store.data.six.should.equal('seven'); + assert.equal(app.store.data.four, 'five'); + assert.equal(app.store.data.six, 'seven'); }); it('should `.set()` a nested value', function() { app.store.set('a.b.c.d', {e: 'f'}); - app.store.data.a.b.c.d.e.should.equal('f'); + assert.equal(app.store.data.a.b.c.d.e, 'f'); }); it('should `.union()` a value on the store', function() { app.store.union('one', 'two'); - app.store.data.one.should.eql(['two']); + assert.deepEqual(app.store.data.one, ['two']); }); it('should not union duplicate values', function() { app.store.union('one', 'two'); - app.store.data.one.should.eql(['two']); + assert.deepEqual(app.store.data.one, ['two']); app.store.union('one', ['two']); - app.store.data.one.should.eql(['two']); + assert.deepEqual(app.store.data.one, ['two']); }); it('should concat an existing array:', function() { app.store.union('one', 'a'); - app.store.data.one.should.eql(['a']); + assert.deepEqual(app.store.data.one, ['a']); app.store.union('one', ['b']); - app.store.data.one.should.eql(['a', 'b']); + assert.deepEqual(app.store.data.one, ['a', 'b']); app.store.union('one', ['c', 'd']); - app.store.data.one.should.eql(['a', 'b', 'c', 'd']); + assert.deepEqual(app.store.data.one, ['a', 'b', 'c', 'd']); }); it('should return true if a key `.has()` on the store', function() { @@ -122,24 +120,24 @@ describe('app.store', function() { it('should `.get()` a stored value', function() { app.store.set('three', 'four'); - app.store.get('three').should.equal('four'); + assert.equal(app.store.get('three'), 'four'); }); it('should `.get()` a nested value', function() { app.store.set({a: {b: {c: 'd'}}}); - app.store.get('a.b.c').should.equal('d'); + assert.equal(app.store.get('a.b.c'), 'd'); }); it('should `.del()` a stored value', function() { app.store.set('a', 'b'); app.store.set('c', 'd'); - app.store.data.should.have.property('a'); - app.store.data.should.have.property('c'); + assert(app.store.data.hasOwnProperty('a')); + assert(app.store.data.hasOwnProperty('c')); app.store.del('a'); app.store.del('c'); - app.store.data.should.not.have.property('a'); - app.store.data.should.not.have.property('c'); + assert(!app.store.data.hasOwnProperty('a')); + assert(!app.store.data.hasOwnProperty('c')); }); it('should `.del()` multiple stored values', function() { @@ -147,14 +145,14 @@ describe('app.store', function() { app.store.set('c', 'd'); app.store.set('e', 'f'); app.store.del(['a', 'c', 'e']); - app.store.data.should.eql({}); + assert.deepEqual(app.store.data, {}); }); }); }); -describe('app.store.create', function() { +describe('create', function() { beforeEach(function() { - app = generate({cli: true}); + app = new App({cli: true}); app.use(store()); app.store.create('abc'); @@ -203,7 +201,7 @@ describe('app.store.create', function() { app.store.create('foo'); var dir = path.dirname(app.store.path); - assert.equal(app.store.foo.path, path.join(dir, 'verb/foo.json')); + assert.equal(app.store.foo.path, path.join(dir, 'generate/foo.json')); app.store.foo.set('a', 'b'); app.store.foo.del({force: true}); }); @@ -225,7 +223,7 @@ describe('app.store.create', function() { describe('events', function() { beforeEach(function() { - app = generate({cli: true}); + app = new App({cli: true}); app.use(store()); app.store.create('abc'); }); @@ -243,7 +241,7 @@ describe('events', function() { }); app.store.set({a: {b: {c: 'd'}}}); - keys.should.eql(['a']); + assert.deepEqual(keys, ['a']); }); it('should emit `set` when a key/value pair is set:', function() { @@ -254,7 +252,7 @@ describe('events', function() { }); app.store.set('a', 'b'); - keys.should.eql(['a']); + assert.deepEqual(keys, ['a']); }); it('should emit `set` when an object value is set:', function() { @@ -265,7 +263,7 @@ describe('events', function() { }); app.store.set('a', {b: 'c'}); - keys.should.eql(['a']); + assert.deepEqual(keys, ['a']); }); it('should emit `set` when an array of objects is passed:', function(cb) { @@ -276,7 +274,7 @@ describe('events', function() { }); app.store.set([{a: 'b'}, {c: 'd'}]); - keys.should.eql(['a', 'c']); + assert.deepEqual(keys, ['a', 'c']); cb(); }); @@ -294,13 +292,13 @@ describe('events', function() { it('should emit `del` when a value is delted:', function(cb) { app.store.on('del', function(keys) { - keys.should.eql('a'); + assert.deepEqual(keys, 'a'); assert.equal(typeof app.store.get('a'), 'undefined'); cb(); }); app.store.set('a', {b: 'c'}); - app.store.get('a').should.eql({b: 'c'}); + assert.deepEqual(app.store.get('a'), {b: 'c'}); app.store.del('a'); }); @@ -317,17 +315,21 @@ describe('events', function() { app.store.set('e', 'f'); app.store.del({force: true}); - arr.should.eql(['a', 'c', 'e']); + assert.deepEqual(arr, ['a', 'c', 'e']); cb(); }); - it('should throw an error if force is not passed', function() { + it('should throw an error if force is not passed', function(cb) { app.store.set('a', 'b'); app.store.set('c', 'd'); app.store.set('e', 'f'); - (function() { + try { app.store.del(); - }).should.throw('options.force is required to delete the entire cache.'); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'options.force is required to delete the entire cache.'); + cb(); + } }); }); diff --git a/test/app.symlink.js b/test/app.symlink.js deleted file mode 100644 index 1bf164f1..00000000 --- a/test/app.symlink.js +++ /dev/null @@ -1,398 +0,0 @@ -'use strict'; - -require('mocha'); -var should = require('should'); -var fs = require('graceful-fs'); -var path = require('path'); -var rimraf = require('rimraf'); -var bufEqual = require('buffer-equal'); -var through = require('through2'); -var File = require('vinyl'); -var assemble = require('..'); -var spies = require('./support/spy'); -var chmodSpy = spies.chmodSpy; -var statSpy = spies.statSpy; -var app, bufferStream; - -var wipeOut = function(cb) { - rimraf(path.join(__dirname, './actual/'), cb); - spies.setError('false'); - statSpy.reset(); - chmodSpy.reset(); - app = assemble(); -}; - -var dataWrap = function(fn) { - return function(data, enc, cb) { - fn(data); - cb(); - }; -}; - -var realMode = function(n) { - return n & parseInt('777', 8); -}; - -describe('app.symlink', function() { - beforeEach(wipeOut); - afterEach(wipeOut); - - it('should pass through writes with cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - cb(); - }; - - var stream = app.symlink('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should pass through writes with default cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - cb(); - }; - - var stream = app.symlink(path.join(__dirname, './actual/')); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should make link to the right folder with relative cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedBase = path.join(__dirname, './actual'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - fs.readlinkSync(expectedPath).should.equal(inputPath); - cb(); - }; - - var stream = app.symlink('./actual/', {cwd: path.relative(process.cwd(), __dirname)}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with function and relative cwd', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedBase = path.join(__dirname, './actual'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - fs.readlinkSync(expectedPath).should.equal(inputPath); - cb(); - }; - - var stream = app.symlink(function(file) { - should.exist(file); - file.should.equal(expectedFile); - return './actual'; - }, {cwd: path.relative(process.cwd(), __dirname)}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = parseInt('655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - fs.readlinkSync(expectedPath).should.equal(inputPath); - cb(); - }; - - var stream = app.symlink('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write streaming files to the right folder', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = parseInt('655', 8); - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - stat: { - mode: expectedMode - } - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - fs.readlinkSync(expectedPath).should.equal(inputPath); - cb(); - }; - - var stream = app.symlink('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - setImmediate(function() { - contentStream.write(expectedContents); - contentStream.end(); - }); - stream.end(); - }); - - it('should write directories to the right folder', function(cb) { - var inputPath = path.join(__dirname, './fixtures/wow'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './actual/wow'); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = parseInt('655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - mode: expectedMode - } - }); - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(__dirname, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.readlinkSync(expectedPath).should.equal(inputPath); - fs.lstatSync(expectedPath).isDirectory().should.equal(false); - fs.statSync(expectedPath).isDirectory().should.equal(true); - cb(); - }; - - var stream = app.symlink('./actual/', {cwd: __dirname}); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should use different modes for files and directories', function(cb) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - var expectedBase = path.join(__dirname, './actual/wow'); - var expectedDirMode = parseInt('755', 8); - var expectedFileMode = parseInt('655', 8); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function() { - realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); - realMode(buffered[0].stat.mode).should.equal(expectedFileMode); - cb(); - }; - - var stream = app.symlink('./actual/', { - cwd: __dirname, - mode: expectedFileMode, - dirMode: expectedDirMode - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should change to the specified base', function(cb) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath) - }); - - var onEnd = function() { - buffered[0].base.should.equal(inputBase); - cb(); - }; - - var stream = app.symlink('./actual/', { - cwd: __dirname, - base: inputBase - }); - - var buffered = []; - bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should report IO errors', function(cb) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './actual'); - var expectedMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode - } - }); - - fs.mkdirSync(expectedBase); - fs.chmodSync(expectedBase, 0); - - var stream = app.symlink('./actual/', {cwd: __dirname}); - stream.on('error', function(err) { - err.code.should.equal('EACCES'); - cb(); - }); - stream.write(expectedFile); - }); - - ['end', 'finish'].forEach(function(eventName) { - it('should emit ' + eventName + ' event', function(cb) { - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var stream = app.symlink('./actual/', {cwd: __dirname}); - - stream.on(eventName, function() { - cb(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: new Buffer('1234567890') - }); - - stream.write(file); - stream.end(); - }); - }); -}); diff --git a/test/app.task.js b/test/app.task.js index 8a3a334d..68ad2123 100644 --- a/test/app.task.js +++ b/test/app.task.js @@ -1,47 +1,90 @@ 'use strict'; +require('mocha'); var assert = require('assert'); -var App = require('..'); -var app; +var Base = require('..'); +var base; -describe('app.task', function() { +describe('.generate', function() { beforeEach(function() { - app = new App(); + base = new Base(); }); it('should register a task', function() { var fn = function(cb) { cb(); }; - app.task('default', fn); - assert.equal(typeof app.tasks.default, 'object'); - assert.equal(app.tasks.default.fn, fn); + base.task('default', fn); + assert.equal(typeof base.tasks.default, 'object'); + assert.equal(base.tasks.default.fn, fn); }); - it('should register a task with an array of dependencies', function() { - app.task('default', ['foo', 'bar'], function(cb) { + it('should register a task with an array of dependencies', function(cb) { + var count = 0; + base.task('foo', function(next) { + count++; + next(); + }); + base.task('bar', function(next) { + count++; + next(); + }); + base.task('default', ['foo', 'bar'], function(next) { + count++; + next(); + }); + assert.equal(typeof base.tasks.default, 'object'); + assert.deepEqual(base.tasks.default.deps, ['foo', 'bar']); + base.build('default', function(err) { + if (err) return cb(err); + assert.equal(count, 3); + cb(); + }); + }); + + it('should run a glob of tasks', function(cb) { + var count = 0; + base.task('foo', function(next) { + count++; + next(); + }); + base.task('bar', function(next) { + count++; + next(); + }); + base.task('baz', function(next) { + count++; + next(); + }); + base.task('qux', function(next) { + count++; + next(); + }); + base.task('default', ['b*']); + assert.equal(typeof base.tasks.default, 'object'); + base.build('default', function(err) { + if (err) return cb(err); + assert.equal(count, 2); cb(); }); - assert.equal(typeof app.tasks.default, 'object'); - assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); }); it('should register a task with a list of strings as dependencies', function() { - app.task('default', 'foo', 'bar', function(cb) { + base.task('default', 'foo', 'bar', function(cb) { cb(); }); - assert.equal(typeof app.tasks.default, 'object'); - assert.deepEqual(app.tasks.default.deps, ['foo', 'bar']); + assert.equal(typeof base.tasks.default, 'object'); + assert.deepEqual(base.tasks.default.deps, ['foo', 'bar']); }); it('should run a task', function(cb) { var count = 0; - app.task('default', function(cb) { + base.task('default', function(cb) { count++; cb(); }); - app.build('default', function(err) { + base.build('default', function(err) { if (err) return cb(err); assert.equal(count, 1); cb(); @@ -49,22 +92,16 @@ describe('app.task', function() { }); it('should throw an error when a task with unregistered dependencies is run', function(cb) { - var count = 0; - app.task('default', ['foo', 'bar'], function(cb) { - count++; - cb(); - }); - - app.build('default', function(err) { - if (!err) return cb(new Error('Expected an error to be thrown.')); - assert.equal(count, 0); + base.task('default', ['foo', 'bar']); + base.build('default', function(err) { + assert(err); cb(); }); }); it('should throw an error when `.build` is called without a callback function.', function() { try { - app.build('default'); + base.build('default'); throw new Error('Expected an error to be thrown.'); } catch (err) { } @@ -72,25 +109,23 @@ describe('app.task', function() { it('should emit task events', function(cb) { var events = []; - app.on('task:starting', function(task) { + base.on('task:starting', function(task) { events.push('starting.' + task.name); }); - app.on('task:finished', function(task) { + base.on('task:finished', function(task) { events.push('finished.' + task.name); }); - app.on('task:error', function(err, task) { - assert(err); + base.on('task:error', function(e, task) { events.push('error.' + task.name); }); - - app.task('foo', function(cb) { + base.task('foo', function(cb) { cb(); }); - app.task('bar', ['foo'], function(cb) { + base.task('bar', ['foo'], function(cb) { cb(); }); - app.task('default', ['bar']); - app.build('default', function(err) { + base.task('default', ['bar']); + base.build('default', function(err) { if (err) return cb(err); assert.deepEqual(events, [ 'starting.default', @@ -105,52 +140,49 @@ describe('app.task', function() { }); it('should emit an error event when an error is passed back in a task', function(cb) { - app.on('error', function(err) { + base.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - app.task('default', function(cb) { + base.task('default', function(cb) { return cb(new Error('This is an error')); }); - app.build('default', function(err) { + base.build('default', function(err) { if (err) return cb(); cb(new Error('Expected an error')); }); }); it('should emit an error event when an error is thrown in a task', function(cb) { - var errors = 0; - app.on('error', function(err) { - errors++; + base.on('error', function(err) { assert(err); assert.equal(err.message, 'This is an error'); }); - app.task('default', function(cb) { + base.task('default', function(cb) { cb(new Error('This is an error')); }); - app.build('default', function(err) { - assert.equal(errors, 1); - if (err) return cb(); - cb(new Error('Expected an error')); + base.build('default', function(err) { + assert(err); + cb(); }); }); it('should run dependencies before running the dependent task.', function(cb) { var seq = []; - app.task('foo', function(cb) { + base.task('foo', function(cb) { seq.push('foo'); cb(); }); - app.task('bar', function(cb) { + base.task('bar', function(cb) { seq.push('bar'); cb(); }); - app.task('default', ['foo', 'bar'], function(cb) { + base.task('default', ['foo', 'bar'], function(cb) { seq.push('default'); cb(); }); - app.build('default', function(err) { + base.build('default', function(err) { if (err) return cb(err); assert.deepEqual(seq, ['foo', 'bar', 'default']); cb(); diff --git a/test/app.toAlias.js b/test/app.toAlias.js new file mode 100644 index 00000000..e5f8bf44 --- /dev/null +++ b/test/app.toAlias.js @@ -0,0 +1,41 @@ +'use strict'; + +require('mocha'); +var assert = require('assert'); +var Base = require('..'); +var base; + +describe('.toAlias', function() { + beforeEach(function() { + base = new Base(); + }); + + it('should not create an alias when no prefix is given', function() { + assert.equal(base.toAlias('foo-bar'), 'foo-bar'); + }); + + it('should create an alias using the `options.toAlias` function', function() { + var alias = base.toAlias('one-two-three', { + toAlias: function(name) { + return name.slice(name.lastIndexOf('-') + 1); + } + }); + assert.equal(alias, 'three'); + }); + + it('should create an alias using the given function', function() { + var alias = base.toAlias('one-two-three', function(name) { + return name.slice(name.lastIndexOf('-') + 1); + }); + assert.equal(alias, 'three'); + }); + + it('should create an alias using base.options.toAlias function', function() { + base.options.toAlias = function(name) { + return name.slice(name.lastIndexOf('-') + 1); + }; + + var alias = base.toAlias('one-two-three'); + assert.equal(alias, 'three'); + }); +}); diff --git a/test/app.toStream.js b/test/app.toStream.js deleted file mode 100644 index b87dc122..00000000 --- a/test/app.toStream.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -var assemble = require('..'); -var assert = require('assert'); -var should = require('should'); -var app; - -describe('app.toStream', function() { - beforeEach(function() { - app = assemble(); - app.create('pages'); - app.page('a', {content: 'this is A'}); - app.page('b', {content: 'this is B'}); - app.page('c', {content: 'this is C'}); - - app.create('posts'); - app.post('x', {content: 'this is X'}); - app.post('y', {content: 'this is Y'}); - app.post('z', {content: 'this is Z'}); - }); - - it('should return a stream', function(cb) { - var stream = app.toStream(); - should.exist(stream); - should.exist(stream.on); - cb(); - }); - - it('should return a stream for a collection', function(cb) { - var stream = app.toStream('pages'); - should.exist(stream); - should.exist(stream.on); - cb(); - }); - - it('should stack handle multiple collections', function(cb) { - var files = []; - app.toStream('pages') - .pipe(app.toStream('posts')) - .on('data', function(file) { - files.push(file); - }) - .on('end', function() { - assert.equal(files.length, 6); - cb(); - }); - }); - - it('should push each item in the collection into the stream', function(cb) { - var files = []; - app.toStream('pages') - .on('error', cb) - .on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - files.push(file.path); - }) - .on('end', function() { - assert.equal(files.length, 3); - cb(); - }); - }); -}); diff --git a/test/app.use.js b/test/app.use.js deleted file mode 100644 index 2fc683bd..00000000 --- a/test/app.use.js +++ /dev/null @@ -1,283 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Views = App.Views; -var View = App.View; -var app; - -describe('app.use', function() { - beforeEach(function() { - app = new App(); - }); - - it('should expose the instance to `use`:', function(cb) { - app.use(function(inst) { - assert(inst instanceof App); - cb(); - }); - }); - - it('should be chainable:', function(cb) { - app.use(function(inst) { - assert(inst instanceof App); - }) - .use(function(inst) { - assert(inst instanceof App); - }) - .use(function(inst) { - assert(inst instanceof App); - cb(); - }); - }); - - it('should pass to collection `use` if a function is returned:', function() { - app.use(function(inst) { - assert(inst instanceof App); - return function(collection) { - collection.foo = collection.addView; - assert(collection instanceof Views); - return collection; - }; - }); - - app.create('pages') - .foo({path: 'a.md', content: '...'}) - .addView({path: 'b.md', content: '...'}) - .addView({path: 'c.md', content: '...'}); - - assert(app.views.pages.hasOwnProperty('a.md')); - assert(app.views.pages.hasOwnProperty('b.md')); - assert(app.views.pages.hasOwnProperty('c.md')); - }); - - it('should be chainable when a collection function is returned:', function() { - app - .use(function(inst) { - assert(inst instanceof App); - return function(collection) { - collection.foo = collection.addView; - assert(collection instanceof Views); - return collection; - }; - }) - .use(function(inst) { - assert(inst instanceof App); - return function(collection) { - collection.bar = collection.addView; - assert(collection instanceof Views); - return collection; - }; - }) - .use(function(inst) { - assert(inst instanceof App); - return function(collection) { - collection.baz = collection.addView; - assert(collection instanceof Views); - return collection; - }; - }); - - var pages = app.create('pages'); - - pages.foo({path: 'a.md', content: '...'}); - pages.bar({path: 'b.md', content: '...'}); - pages.baz({path: 'c.md', content: '...'}); - - assert(app.views.pages.hasOwnProperty('a.md')); - assert(app.views.pages.hasOwnProperty('b.md')); - assert(app.views.pages.hasOwnProperty('c.md')); - }); - - it('should pass to view `use` if collection.use returns a function:', function() { - app.use(function(inst) { - assert(inst instanceof App); - - return function(collection) { - assert(collection instanceof Views); - collection.foo = collection.addView; - - return function(view) { - assert(view instanceof View); - view.foo = collection.addView.bind(collection); - return view; - }; - }; - }); - - app.create('pages') - .foo({path: 'a.md', content: '...'}) - .foo({path: 'b.md', content: '...'}) - .foo({path: 'c.md', content: '...'}); - - assert(app.views.pages.hasOwnProperty('a.md')); - assert(app.views.pages.hasOwnProperty('b.md')); - assert(app.views.pages.hasOwnProperty('c.md')); - }); - - it('should be chainable when a view function is returned:', function() { - app - .use(function(inst) { - assert(inst instanceof App); - - return function(collection) { - assert(collection instanceof Views); - collection.foo = collection.addView; - - return function(view) { - assert(view instanceof View); - view.a = collection.addView.bind(collection); - return view; - }; - }; - }) - .use(function(inst) { - assert(inst instanceof App); - - return function(collection) { - assert(collection instanceof Views); - collection.bar = collection.addView; - - return function(view) { - assert(view instanceof View); - view.b = collection.addView.bind(collection); - return view; - }; - }; - }) - .use(function(inst) { - assert(inst instanceof App); - - return function(collection) { - assert(collection instanceof Views); - collection.baz = collection.addView; - - return function(view) { - assert(view instanceof View); - view.c = collection.addView.bind(collection); - return view; - }; - }; - }); - - var pages = app.create('pages'); - - pages.foo({path: 'a.md', content: '...'}); - pages.bar({path: 'b.md', content: '...'}); - pages.baz({path: 'c.md', content: '...'}) - .a({path: 'x.md', content: '...'}) - .b({path: 'y.md', content: '...'}) - .c({path: 'z.md', content: '...'}); - - assert(app.views.pages.hasOwnProperty('a.md')); - assert(app.views.pages.hasOwnProperty('b.md')); - assert(app.views.pages.hasOwnProperty('c.md')); - - assert(app.views.pages.hasOwnProperty('x.md')); - assert(app.views.pages.hasOwnProperty('y.md')); - assert(app.views.pages.hasOwnProperty('z.md')); - }); - - it('should work with multiple collections:', function() { - app - .use(function(inst) { - assert(inst instanceof App); - - return function(collection) { - assert(collection instanceof Views); - collection.foo = collection.addView; - - return function(view) { - assert(view instanceof View); - view.a = collection.addView.bind(collection); - return view; - }; - }; - }) - .use(function(inst) { - assert(inst instanceof App); - - return function(collection) { - assert(collection instanceof Views); - collection.bar = collection.addView; - - return function(view) { - assert(view instanceof View); - view.b = collection.addView.bind(collection); - return view; - }; - }; - }) - .use(function(inst) { - assert(inst instanceof App); - assert(this instanceof App); - - return function(collection) { - collection.baz = collection.addView; - assert(collection instanceof Views); - assert(this instanceof Views); - - return function(view) { - assert(this instanceof View); - assert(view instanceof View); - view.c = collection.addView.bind(collection); - return view; - }; - }; - }); - - var pages = app.create('pages'); - - pages.foo({path: 'a.md', content: '...'}); - pages.bar({path: 'b.md', content: '...'}); - pages.baz({path: 'c.md', content: '...'}) - .a({path: 'x.md', content: '...'}) - .b({path: 'y.md', content: '...'}) - .c({path: 'z.md', content: '...'}); - - assert(app.views.pages.hasOwnProperty('a.md')); - assert(app.views.pages.hasOwnProperty('b.md')); - assert(app.views.pages.hasOwnProperty('c.md')); - - assert(app.views.pages.hasOwnProperty('x.md')); - assert(app.views.pages.hasOwnProperty('y.md')); - assert(app.views.pages.hasOwnProperty('z.md')); - - var posts = app.create('posts'); - - posts.foo({path: 'a.md', content: '...'}); - posts.bar({path: 'b.md', content: '...'}); - posts.baz({path: 'c.md', content: '...'}) - .a({path: 'x.md', content: '...'}) - .b({path: 'y.md', content: '...'}) - .c({path: 'z.md', content: '...'}); - - assert(app.views.posts.hasOwnProperty('a.md')); - assert(app.views.posts.hasOwnProperty('b.md')); - assert(app.views.posts.hasOwnProperty('c.md')); - - assert(app.views.posts.hasOwnProperty('x.md')); - assert(app.views.posts.hasOwnProperty('y.md')); - assert(app.views.posts.hasOwnProperty('z.md')); - - var docs = app.create('docs'); - - docs.foo({path: 'a.md', content: '...'}); - docs.bar({path: 'b.md', content: '...'}); - docs.baz({path: 'c.md', content: '...'}) - .a({path: 'x.md', content: '...'}) - .b({path: 'y.md', content: '...'}) - .c({path: 'z.md', content: '...'}); - - assert(app.views.docs.hasOwnProperty('a.md')); - assert(app.views.docs.hasOwnProperty('b.md')); - assert(app.views.docs.hasOwnProperty('c.md')); - - assert(app.views.docs.hasOwnProperty('x.md')); - assert(app.views.docs.hasOwnProperty('y.md')); - assert(app.views.docs.hasOwnProperty('z.md')); - }); -}); diff --git a/test/app.view.compile.js b/test/app.view.compile.js deleted file mode 100644 index 7616ce25..00000000 --- a/test/app.view.compile.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.view.compile', function() { - describe('compile method', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - it('should compile a view:', function() { - var buffer = new Buffer('a b c'); - var view = app.page('a.tmpl', {contents: buffer}) - .compile(); - assert(typeof view.fn === 'function'); - }); - - it('should compile a view with settings:', function() { - var buffer = new Buffer('a b c'); - var view = app.page('a.tmpl', {contents: buffer}) - .compile({foo: 'bar'}); - assert(typeof view.fn === 'function'); - }); - - it('should compile a view with isAsync flag:', function() { - var buffer = new Buffer('a b c'); - var view = app.page('a.tmpl', {contents: buffer}) - .compile(true); - assert(typeof view.fn === 'function'); - }); - }); -}); - diff --git a/test/app.view.render.js b/test/app.view.render.js deleted file mode 100644 index 064c4c3e..00000000 --- a/test/app.view.render.js +++ /dev/null @@ -1,94 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.view.render', function() { - describe('rendering', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - it('should use helpers to render a view:', function(cb) { - var locals = {name: 'Halle'}; - - app.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - var buffer = new Buffer('a <%= upper(name) %> b'); - app.page('a.tmpl', {contents: buffer, locals: locals}) - .render(function(err, res) { - if (err) return cb(err); - - assert(res.contents.toString() === 'a HALLE b'); - cb(); - }); - }); - - it('should support helpers as an array:', function(cb) { - var locals = {name: 'Halle'}; - - app.helpers([ - { - lower: function(str) { - return str.toLowerCase(str); - } - } - ]); - - var buffer = new Buffer('a <%= lower(name) %> b'); - app.page('a.tmpl', {contents: buffer, locals: locals}) - .render(function(err, res) { - if (err) return cb(err); - - assert(res.contents.toString() === 'a halle b'); - cb(); - }); - }); - - it('should support helpers as an object:', function(cb) { - var locals = {name: 'Halle'}; - - app.helpers({ - prepend: function(prefix, str) { - return prefix + str; - } - }); - - var buffer = new Buffer('a <%= prepend("foo ", name) %> b'); - app.page('a.tmpl', {contents: buffer, locals: locals}) - .render(function(err, res) { - if (err) return cb(err); - assert(res.contents.toString() === 'a foo Halle b'); - cb(); - }); - }); - - it('should use the engine defined on view options:', function(cb) { - app.engine('hbs', require('engine-handlebars')); - var locals = {name: 'Halle'}; - - app.helpers({ - prepend: function(prefix, str) { - return prefix + str; - } - }); - - var buffer = new Buffer('a {{prepend "foo " name}} b'); - app.page('a.tmpl', {contents: buffer, locals: locals, options: {engine: 'hbs'}}) - .render(function(err, res) { - if (err) return cb(err); - assert(res.contents.toString() === 'a foo Halle b'); - cb(); - }); - }); - }); -}); - diff --git a/test/app.viewTypes.js b/test/app.viewTypes.js deleted file mode 100644 index 982538bf..00000000 --- a/test/app.viewTypes.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.viewTypes', function() { - describe('view types', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - }); - - it('should add collection (plural) to the `viewTypes` object', function() { - app.viewTypes = []; // reset - app.create('foo', {viewType: 'layout'}); - app.create('bar', {viewType: 'layout'}); - assert.deepEqual(app.viewTypes.layout, [ 'foos', 'bars' ]); - - app.create('baz', {viewType: 'renderable'}); - assert.deepEqual(app.viewTypes.renderable, [ 'bazs' ]); - }); - - it('should add collection to the given viewType', function() { - app.create('layout', {viewType: 'layout'}); - assert(app.layouts.options.viewType[0] === 'layout'); - }); - - it('should return true if a collection has the given viewType', function() { - app.create('layout', {viewType: 'layout'}); - assert(app.layouts.isType('layout')); - assert(!app.layouts.isType('partial')); - }); - - it('should add types to the collection', function() { - app.create('layout', {viewType: 'layout'}); - app.layouts.viewType('partial'); - assert(app.layouts.options.viewType[0] === 'layout'); - assert(app.layouts.options.viewType[1] === 'partial'); - }); - - it('should add a collection to multiple viewTypes', function() { - app.create('foo', {viewType: ['layout', 'renderable']}); - assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); - }); - - it('should expose viewType on `view`', function() { - app.create('foo', {viewType: ['layout', 'renderable']}); - var a = app.foo('a', {content: ''}); - assert.deepEqual(app.foos.options.viewType, a.options.viewType); - }); - }); -}); diff --git a/test/app.views.js b/test/app.views.js deleted file mode 100644 index aaa0f264..00000000 --- a/test/app.views.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('app.views', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('partials', {viewType: 'partial'}); - app.create('pages'); - }); - - it('should add a view to a collection', function() { - app.partial('foo', {content: 'bar'}); - assert(app.views.partials.foo); - }); - - it('should create a helper for a view collection', function() { - app.page('abc', {content: 'xyz'}); - assert(app.views.pages.abc); - assert(app._.helpers.async.page); - }); -}); - diff --git a/test/collection.engines.js b/test/collection.engines.js deleted file mode 100644 index 2dbd9c05..00000000 --- a/test/collection.engines.js +++ /dev/null @@ -1,179 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Views = App.Views; -var collection, pages; - -describe('collection.engines', function() { - beforeEach(function() { - pages = new Views(); - }); - - it('should throw an error when engine name is invalid:', function() { - (function() { - pages.engine(null, {}); - }).should.throw('expected engine ext to be a string or array.'); - }); - - it('should register an engine to the given extension', function() { - pages.engine('hbs', function() {}); - assert(typeof pages.engines['.hbs'] === 'object'); - }); - - it('should set an engine with the given extension', function() { - var hbs = function() {}; - hbs.render = function() {}; - hbs.renderFile = function() {}; - pages.engine('hbs', hbs); - assert(pages.engines['.hbs']); - assert(pages.engines['.hbs'].renderFile); - assert(pages.engines['.hbs'].render); - }); - - it('should get an engine:', function() { - pages.engine('hbs', function() {}); - var hbs = pages.engine('hbs'); - assert(typeof hbs === 'object'); - assert(hbs.hasOwnProperty('render')); - assert(hbs.hasOwnProperty('compile')); - }); - - it('should register multiple engines to the given extension', function() { - pages.engine(['hbs', 'md'], function() {}); - assert(typeof pages.engines['.hbs'] === 'object'); - assert(typeof pages.engines['.md'] === 'object'); - }); -}); - -describe('collection.engines', function() { - beforeEach(function() { - pages = new Views(); - pages.addView('foo.tmpl', {content: 'A <%= letter %> {{= letter }} C'}); - pages.addView('bar.tmpl', {content: 'A <%= letter %> {{ letter }} C'}); - }); - - it('should register an engine:', function() { - pages.engine('a', {render: function() {}}); - pages.engines.should.have.property('.a'); - }); - - it('should use custom delimiters:', function(cb) { - pages.engine('tmpl', require('engine-base'), { - delims: ['{{', '}}'] - }); - - pages.render('foo.tmpl', {letter: 'B'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('A <%= letter %> B C'); - cb(); - }); - }); - - it('should override individual delims values:', function(cb) { - pages.engine('tmpl', require('engine-base'), { - interpolate: /\{{([^}]+)}}/g, - evaluate: /\{{([^}]+)}}/g, - escape: /\{{-([^}]+)}}/g - }); - - pages.render('bar.tmpl', {letter: 'B'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('A <%= letter %> B C'); - cb(); - }); - }); - - it('should get an engine:', function() { - pages.engine('a', { - render: function() {} - }); - var a = pages.engine('a'); - a.should.have.property('render'); - }); -}); - -describe('engine selection:', function() { - beforeEach(function(cb) { - collection = new Views(); - collection.engine('tmpl', require('engine-base')); - collection.engine('hbs', require('engine-handlebars')); - cb(); - }); - - it('should get the engine from file extension:', function(cb) { - var pages = new Views(); - pages.engine('tmpl', require('engine-base')); - pages.engine('hbs', require('engine-handlebars')); - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on the collection:', function(cb) { - var posts = new Views({engine: 'hbs'}); - posts.engine('tmpl', require('engine-base')); - posts.engine('hbs', require('engine-handlebars')); - - posts.addView('a', {content: '{{a}}', locals: {a: 'b'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on the view:', function(cb) { - var posts = new Views(); - posts.engine('tmpl', require('engine-base')); - posts.engine('hbs', require('engine-handlebars')); - posts.addView('a', {content: '{{a}}', engine: 'hbs', locals: {a: 'b'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on view.options:', function(cb) { - var posts = new Views(); - posts.engine('tmpl', require('engine-base')); - posts.engine('hbs', require('engine-handlebars')); - posts.addView('a', {content: '{{a}}', data: {a: 'b'}, options: {engine: 'hbs'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on view.data:', function(cb) { - var posts = new Views(); - posts.engine('tmpl', require('engine-base')); - posts.engine('hbs', require('engine-handlebars')); - posts.addView('a', {content: '{{a}}', locals: {a: 'b'}, data: {engine: 'hbs'}}) - .render(function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); - - it('should use the engine defined on render locals:', function(cb) { - var posts = new Views(); - posts.engine('tmpl', require('engine-base')); - posts.engine('hbs', require('engine-handlebars')); - posts.addView('a', {content: '{{a}}', locals: {a: 'b'}}) - .render({engine: 'hbs'}, function(err, view) { - if (err) return cb(err); - assert(view.content === 'b'); - cb(); - }); - }); -}); diff --git a/test/collection.events.js b/test/collection.events.js deleted file mode 100644 index 5b2608a4..00000000 --- a/test/collection.events.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('collection.events', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - }); - - it('should emit events:', function() { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); - var events = []; - - app.pages.on('option', function(key) { - events.push(key); - }); - - app.pages.option('a', 'b'); - app.pages.option('c', 'd'); - app.pages.option('e', 'f'); - app.pages.option({g: 'h'}); - - events.should.eql(['a', 'c', 'e', 'g']); - }); -}); diff --git a/test/collection.getView.js b/test/collection.getView.js deleted file mode 100644 index fb587230..00000000 --- a/test/collection.getView.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -var path = require('path'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('collection.getView', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - - app.page('foo', {content: 'this is foo'}); - app.page('bar.md', {content: 'this is bar'}); - app.page('a/b/c/baz.md', {content: 'this is baz'}); - app.page('test/fixtures/templates/a.tmpl'); - }); - - it('should get a view by name', function() { - assert(app.pages.getView('foo')); - }); - - it('should get a view with the key modified by the given function', function() { - var view = app.pages.getView('foo.md', function(key) { - return path.basename(key, path.extname(key)); - }); - assert(view); - }); - - it('should get a view by full path', function() { - assert(app.pages.getView('a/b/c/baz.md')); - }); -}); diff --git a/test/collection.isType.js b/test/collection.isType.js deleted file mode 100644 index 423dcdce..00000000 --- a/test/collection.isType.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var View = App.View; -var collection; - -describe('collection.isType', function() { - beforeEach(function() { - collection = new View(); - }); - - it('should expose thie "isType" method', function() { - assert.equal(typeof collection.isType, 'function'); - }); - - it('should return true if a collection is the given "type"', function() { - assert(collection.isType('renderable')); - }); - - it('should return false if a collection is not the given "type"', function() { - assert(!collection.isType('partial')); - }); -}); diff --git a/test/collection.js b/test/collection.js deleted file mode 100644 index 1c4d8411..00000000 --- a/test/collection.js +++ /dev/null @@ -1,573 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var assert = require('assert'); -var typeOf = require('kind-of'); -var isBuffer = require('is-buffer'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var Item = App.Item; -var Collection = App.Collection; -var collection; - -describe('collection', function() { - describe('constructor', function() { - it('should create an instance of Collection', function() { - var collection = new Collection(); - assert(collection instanceof Collection); - assert(typeof collection === 'object'); - }); - - it('should instantiate without new', function() { - var collection = Collection(); - assert(collection instanceof Collection); - assert(typeof collection === 'object'); - }); - }); - - describe('static methods', function() { - it('should expose `extend`', function() { - assert(typeof Collection.extend === 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - collection = new Collection(); - }); - - var methods = [ - 'use', - 'setItem', - 'addItem', - 'addItems', - 'addList', - 'getItem', - 'constructor', - 'set', - 'get', - 'del', - 'define', - 'visit', - 'on', - 'once', - 'off', - 'emit', - 'listeners', - 'hasListeners' - ]; - - methods.forEach(function(method) { - it('should expose ' + method + ' method', function() { - assert(typeof collection[method] === 'function'); - }); - }); - - it('should expose isCollection property', function() { - assert(typeof collection.isCollection === 'boolean'); - }); - - it('should expose queue property', function() { - assert(Array.isArray(collection.queue)); - }); - - it('should expose items property', function() { - assert(typeOf(collection.items) === 'object'); - }); - - it('should expose options property', function() { - assert(typeOf(collection.options) === 'object'); - }); - }); -}); - -describe('methods', function() { - beforeEach(function() { - collection = new Collection(); - }); - - describe('chaining', function() { - it('should allow collection methods to be chained', function() { - collection - .addItems({'a.hbs': {path: 'a.hbs'}}) - .addItems({'b.hbs': {path: 'b.hbs'}}) - .addItems({'c.hbs': {path: 'c.hbs'}}); - - collection.items.should.have.properties([ - 'a.hbs', - 'b.hbs', - 'c.hbs' - ]); - }); - }); - - describe('use', function() { - it('should expose the instance to plugins', function() { - collection - .use(function(inst) { - inst.foo = 'bar'; - }); - - assert(collection.foo === 'bar'); - }); - - it('should expose `item` when the plugin returns a function', function() { - collection - .use(function() { - return function(item) { - item.foo = 'bar'; - }; - }); - - collection.addItem('aaa'); - collection.addItem('bbb'); - collection.addItem('ccc'); - - assert(collection.items.aaa.foo === 'bar'); - assert(collection.items.bbb.foo === 'bar'); - assert(collection.items.ccc.foo === 'bar'); - }); - }); - - describe('get / set', function() { - it('should set a value on the instance', function() { - collection.set('a', 'b'); - assert(collection.a === 'b'); - }); - - it('should get a value from the instance', function() { - collection.set('a', 'b'); - assert(collection.get('a') === 'b'); - }); - }); - - describe('adding items', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should load a item onto the respective collection', function() { - collection.addItem('a.hbs'); - collection.items.should.have.property('a.hbs'); - }); - }); - - describe('item', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should return a single collection item from a key-value pair', function() { - var one = collection.item('one', {content: 'foo'}); - var two = collection.item('two', {content: 'bar'}); - - assert(one instanceof Item); - assert(one instanceof collection.Item); - assert(one.path === 'one'); - assert(two instanceof Item); - assert(two instanceof collection.Item); - assert(two.path === 'two'); - }); - - it('should return a single collection item from an object', function() { - var one = collection.item({path: 'one', content: 'foo'}); - var two = collection.item({path: 'two', content: 'bar'}); - - assert(one instanceof Item); - assert(one.path === 'one'); - assert(two instanceof Item); - assert(two.path === 'two'); - }); - }); - - describe('addItem', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should throw an error when args are invalid', function() { - (function() { - collection.addItem(function() {}); - }).should.throw('expected value to be an object.'); - }); - - it('should add a item to `items`', function() { - collection.addItem('foo'); - collection.items.should.have.property('foo'); - - collection.addItem('one', {content: '...'}); - assert(typeof collection.items.one === 'object'); - assert(isBuffer(collection.items.one.contents)); - }); - - it('should create an instance of `Item`', function() { - collection.addItem('one', {content: '...'}); - assert(collection.items.one instanceof collection.Item); - }); - - it('should allow an `Item` constructor to be passed', function() { - Item.prototype.foo = function(key, value) { - this[key] = value; - }; - collection = new Collection({Item: Item}); - collection.addItem('one', {content: '...'}); - collection.items.one.foo('bar', 'baz'); - assert(collection.items.one.bar === 'baz'); - }); - - it('should allow an instance of `Item` to be passed', function() { - var collection = new Collection({Item: Item}); - var item = new Item({content: '...'}); - collection.addItem('one', item); - item.set('abc', 'xyz'); - assert(collection.items.one instanceof collection.Item); - assert(isBuffer(collection.items.one.contents)); - assert(collection.items.one.abc === 'xyz'); - }); - }); - - describe('deleteItem', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should delete an item from `items` by item instance', function() { - collection.addItem('foo'); - assert(collection.items.hasOwnProperty('foo')); - - collection.addItem('one', {content: '...'}); - assert(collection.items.hasOwnProperty('one')); - assert.equal(Object.keys(collection.items).length, 2); - - var foo = collection.getItem('foo'); - collection.deleteItem(foo); - assert.equal(Object.keys(collection.items).length, 1); - }); - - it('should delete an item from `items` by item `key`', function() { - collection.addItem('foo'); - assert(collection.items.hasOwnProperty('foo')); - - collection.addItem('one', {content: '...'}); - assert(collection.items.hasOwnProperty('one')); - assert.equal(Object.keys(collection.items).length, 2); - - collection.deleteItem('foo'); - assert.equal(Object.keys(collection.items).length, 1); - }); - }); - - describe('addItems', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should add multiple items', function() { - collection.addItems({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - assert(isBuffer(collection.items.one.contents)); - assert(isBuffer(collection.items.two.contents)); - }); - - it('should create items from an instance of Collection', function() { - collection.addItems({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - var pages = new Collection(collection); - assert(isBuffer(pages.items.one.contents)); - assert(isBuffer(pages.items.two.contents)); - }); - - it('should add an array of plain-objects', function() { - collection.addItems([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - assert(isBuffer(collection.items.one.contents)); - assert(isBuffer(collection.items.two.contents)); - }); - - it('should add an array of items', function() { - var list = new List([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - - collection.addItems(list.items); - assert(isBuffer(collection.items.one.contents)); - assert(isBuffer(collection.items.two.contents)); - }); - }); - - describe('addList', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should add a list of items', function() { - collection.addList([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - assert(isBuffer(collection.items.one.contents)); - assert(isBuffer(collection.items.two.contents)); - }); - - it('should add a list of items from the constructor', function() { - var list = new List([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - - collection = new Collection(list); - assert(isBuffer(collection.items.one.contents)); - assert(isBuffer(collection.items.two.contents)); - }); - - it('should throw an error when list is not an array', function() { - var items = new Collection(); - (function() { - items.addList(); - }).should.throw('expected list to be an array.'); - - (function() { - items.addList({}); - }).should.throw('expected list to be an array.'); - - (function() { - items.addList('foo'); - }).should.throw('expected list to be an array.'); - }); - - it('should load an array of items from an event', function() { - var collection = new Collection(); - - collection.on('addList', function(list) { - while (list.length) { - collection.addItem({path: list.pop()}); - } - }); - - collection.addList(['a.txt', 'b.txt', 'c.txt']); - assert(collection.items.hasOwnProperty('a.txt')); - assert(collection.items['a.txt'].path === 'a.txt'); - }); - - it('should load an array of items from the addList callback:', function() { - var collection = new Collection(); - - collection.addList(['a.txt', 'b.txt', 'c.txt'], function(fp) { - return {path: fp}; - }); - assert(collection.items.hasOwnProperty('a.txt')); - assert(collection.items['a.txt'].path === 'a.txt'); - }); - - it('should load an object of items from an event', function() { - var collection = new Collection(); - - collection.on('addItems', function(items) { - for (var key in items) { - collection.addItem('foo/' + key, items[key]); - delete items[key]; - } - }); - - collection.addItems({ - a: {path: 'a.txt'}, - b: {path: 'b.txt'}, - c: {path: 'c.txt'} - }); - - assert(collection.items.hasOwnProperty('foo/a')); - assert(collection.items['foo/a'].path === 'a.txt'); - }); - - it('should signal `loaded` when finished (addItems)', function() { - var collection = new Collection(); - - collection.on('addItems', function(items) { - for (var key in items) { - if (key === 'c') { - collection.loaded = true; - break; - } - collection.addItem('foo/' + key, items[key]); - } - }); - - collection.addItems({ - a: {path: 'a.txt'}, - b: {path: 'b.txt'}, - c: {path: 'c.txt'} - }); - - assert(collection.items.hasOwnProperty('foo/a')); - assert(!collection.items.hasOwnProperty('foo/c')); - assert(collection.items['foo/a'].path === 'a.txt'); - }); - - it('should signal `loaded` when finished (addList)', function() { - var collection = new Collection(); - - collection.on('addList', function(items) { - for (var i = 0; i < items.length; i++) { - var item = items[i]; - if (item.key === 'c') { - collection.loaded = true; - break; - } - item.key = 'foo/' + item.key; - collection.addItem(item.key, item); - } - }); - - collection.addList([ - {key: 'a', path: 'a.txt'}, - {key: 'b', path: 'b.txt'}, - {key: 'c', path: 'c.txt'} - ]); - - assert(collection.items.hasOwnProperty('foo/a')); - assert(collection.items['foo/a'].path === 'a.txt'); - assert(!collection.items.hasOwnProperty('foo/c')); - }); - }); - - describe('getItem', function() { - beforeEach(function() { - collection = new Collection(); - }); - it('should get a item from `items`', function() { - collection.addItem('one', {content: 'aaa'}); - collection.addItem('two', {content: 'zzz'}); - assert(isBuffer(collection.items.one.contents)); - assert(isBuffer(collection.getItem('one').contents)); - assert(collection.getItem('one').contents.toString() === 'aaa'); - assert(collection.getItem('two').contents.toString() === 'zzz'); - }); - }); -}); - -describe('queue', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should emit arguments on addItem', function(cb) { - collection.on('addItem', function(args) { - assert(args[0] === 'a'); - assert(args[1] === 'b'); - assert(args[2] === 'c'); - assert(args[3] === 'd'); - assert(args[4] === 'e'); - cb(); - }); - - collection.addItem('a', 'b', 'c', 'd', 'e'); - }); - - it('should expose the `queue` property for loading items', function() { - collection.queue.push(collection.item('b', {path: 'b'})); - - collection.addItem('a', {path: 'a'}); - assert(collection.items.hasOwnProperty('a')); - assert(collection.items.hasOwnProperty('b')); - }); - - it('should load all items on the queue when addItem is called', function() { - collection.on('addItem', function(args) { - var len = args.length; - var last = args[len - 1]; - if (typeof last === 'string') { - args[len - 1] = { content: last }; - } - }); - - collection.addItem('a.html', 'aaa'); - collection.addItem('b.html', 'bbb'); - collection.addItem('c.html', 'ccc'); - - assert(collection.items.hasOwnProperty('a.html')); - assert(collection.getItem('a.html').content === 'aaa'); - assert(collection.items.hasOwnProperty('b.html')); - assert(collection.getItem('b.html').content === 'bbb'); - assert(collection.items.hasOwnProperty('c.html')); - assert(collection.getItem('c.html').content === 'ccc'); - }); -}); - -describe('options', function() { - describe('option', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should expose the `option` method', function() { - collection.option('foo', 'bar'); - collection.options.should.have.property('foo', 'bar'); - }); - - it('should be chainable', function() { - collection.option('foo', 'bar') - .addItems('a.hbs') - .addItems('b.hbs') - .addItems('c.hbs'); - - collection.options.should.have.property('foo', 'bar'); - collection.items.should.have.properties([ - 'a.hbs', - 'b.hbs', - 'c.hbs' - ]); - }); - - it('should set a key/value pair on options', function() { - collection.option('a', 'b'); - assert(collection.options.a === 'b'); - }); - - it('should set an object on options', function() { - collection.option({c: 'd'}); - assert(collection.options.c === 'd'); - }); - - it('should get an option', function() { - collection.option({c: 'd'}); - var c = collection.option('c'); - assert(c === 'd'); - }); - }); - - describe('options.renameKey', function() { - beforeEach(function() { - collection = new Collection({ - renameKey: function(key) { - return path.basename(key); - } - }); - }); - - it('should use a custom rename key function on item keys', function() { - collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); - assert(collection.items['d.hbs'].contents.toString() === 'foo bar baz'); - }); - - it('should get a item with the renamed key', function() { - collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); - assert(collection.getItem('d.hbs').contents.toString() === 'foo bar baz'); - }); - - it('should get a item with the original key', function() { - collection.addItem('a/b/c/d.hbs', {content: 'foo bar baz'}); - assert(collection.getItem('a/b/c/d.hbs').contents.toString() === 'foo bar baz'); - }); - }); -}); - diff --git a/test/collection.options.js b/test/collection.options.js deleted file mode 100644 index b1cd9f3d..00000000 --- a/test/collection.options.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('collection.options', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - }); - - it('should set an option:', function() { - app.pages.options.should.not.have.property('foo'); - app.pages.option('foo', 'bar'); - app.pages.options.should.have.property('foo'); - }); - - it('should extend options:', function() { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); - app.pages.option('a', 'b'); - app.pages.option('c', 'd'); - app.pages.option('e', 'f'); - app.pages.options.should.have.properties(['a', 'c', 'e']); - }); -}); diff --git a/test/collection.render.js b/test/collection.render.js deleted file mode 100644 index 12ebdcd4..00000000 --- a/test/collection.render.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var each = require('async-each'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var Views = App.Views; -var pages; - -describe('collection.render', function() { - describe('rendering', function() { - beforeEach(function() { - pages = new Views(); - pages.engine('tmpl', require('engine-base')); - }); - - it('should throw an error when no callback is given:', function(cb) { - try { - pages.render(); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'Views#render is async and expects a callback function'); - cb(); - } - }); - - it('should throw an error when an engine is not defined:', function(cb) { - pages.addView('foo.bar', {content: '<%= name %>'}); - var page = pages.getView('foo.bar'); - - pages.render(page, function(err) { - assert.equal(err.message, 'Views#render cannot find an engine for: .bar'); - cb(); - }); - }); - - it('should use helpers to render a view:', function(cb) { - var locals = {name: 'Halle'}; - - pages.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - - assert.equal(res.content, 'a HALLE b'); - cb(); - }); - }); - - it('should use globally defined data to render a view', function(cb) { - pages.data({name: 'Halle'}); - - pages.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b'}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.content === 'a HALLE b'); - cb(); - }); - }); - - it('should use helpers when rendering a view:', function(cb) { - var locals = {name: 'Halle'}; - pages.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - pages.addView('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a HALLE b'); - cb(); - }); - }); - - it('should render a template when contents is a buffer:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function(err, view) { - if (err) return cb(err); - assert.equal(view.contents.toString(), 'b'); - cb(); - }); - }); - - it('should render a template when content is a string:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var view = pages.getView('a.tmpl'); - - pages.render(view, function(err, view) { - if (err) return cb(err); - assert.equal(view.contents.toString(), 'b'); - cb(); - }); - }); - - it('should render a view from its path:', function(cb) { - pages.addView('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - - pages.render('a.tmpl', function(err, view) { - if (err) return cb(err); - assert.equal(view.content, 'b'); - cb(); - }); - }); - - it('should use a plugin for rendering:', function(cb) { - pages.engine('tmpl', require('engine-base')); - pages.option('engine', 'tmpl'); - - pages.addViews({ - 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, - 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, - 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, - 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, - 'e': {content: '<%= title %>', locals: {title: 'eee'}}, - 'f': {content: '<%= title %>', locals: {title: 'fff'}}, - 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, - 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, - 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}} - }); - - pages.use(function(collection) { - collection.option('pager', false); - - collection.renderEach = function(done) { - var list = new List(collection); - - each(list.items, function(item, next) { - collection.render(item, next); - }, done); - }; - }); - - pages.renderEach(function(err, items) { - if (err) return cb(err); - assert.equal(items[0].content, 'aaa'); - assert.equal(items[9].content, 'jjj'); - assert.equal(items.length, 10); - cb(); - }); - }); - }); -}); diff --git a/test/collection.routes.js b/test/collection.routes.js deleted file mode 100644 index 4bfc9b09..00000000 --- a/test/collection.routes.js +++ /dev/null @@ -1,130 +0,0 @@ -'use strict'; - -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app, pages; - -function append(str) { - return function(view, next) { - var content = view.contents.toString(); - view.contents = new Buffer(content + ' ' + str); - next(); - }; -} -function prepend(str) { - return function(view, next) { - var content = view.contents.toString(); - view.contents = new Buffer(str + ' ' + content); - next(); - }; -} - -describe('collection.routes', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - pages = app.create('page'); - }); - - describe('params', function() { - it('should call param function when routing', function(cb) { - pages.param('id', function(view, next, id) { - assert.equal(id, '123'); - next(); - }); - - pages.all('/foo/:id/bar', function(view, next) { - assert.equal(view.options.params.id, '123'); - next(); - }); - - pages.router.handle({ path: '/foo/123/bar' }, cb); - }); - }); - - describe('onLoad middleware', function() { - it('should run when templates are loaded:', function() { - pages.onLoad(/\.tmpl/, prepend('onLoad')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>'}); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('onLoad <%= name %>'); - }); - }); - - describe('preCompile middleware', function() { - it('should run before templates are compiled:', function() { - - }); - }); - - describe('postCompile middleware', function() { - it('should run after templates are compiled:', function() { - - }); - }); - - describe('preRender middleware', function() { - it('should run before templates are rendered:', function(cb) { - pages.preRender(/\.tmpl/, prepend('preRender')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('<%= name %>'); - - page.render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('preRender aaa'); - cb(); - }); - }); - - it('should run before templates are rendered on both collection and app middleware:', function(cb) { - app.preRender(/\.tmpl/, prepend('app::preRender')); - pages.preRender(/\.tmpl/, prepend('collection::preRender')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('<%= name %>'); - - page.render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('collection::preRender app::preRender aaa'); - cb(); - }); - }); - }); - - describe('postRender middleware', function() { - it('should run after templates are rendered:', function(cb) { - pages.postRender(/\.tmpl/, append('postRender')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('<%= name %>'); - - page.render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('aaa postRender'); - cb(); - }); - }); - - it('should run after templates are rendered on both collection and app middleware:', function(cb) { - app.postRender(/\.tmpl/, append('app::postRender')); - pages.postRender(/\.tmpl/, append('collection::postRender')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('<%= name %>'); - - page.render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('aaa app::postRender collection::postRender'); - cb(); - }); - }); - }); -}); diff --git a/test/collection.src.js b/test/collection.src.js deleted file mode 100644 index b600131d..00000000 --- a/test/collection.src.js +++ /dev/null @@ -1,322 +0,0 @@ -'use strict'; - -var path = require('path'); -var assert = require('assert'); -var should = require('should'); -var App = require('..'); -var app, pages, posts; - -describe('collection.src()', function() { - beforeEach(function() { - app = new App(); - - pages = app.create('pages'); - posts = app.create('posts'); - }); - - it('should return a stream', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); - assert(stream); - assert.equal(typeof stream.on, 'function'); - assert.equal(typeof stream.pipe, 'function'); - cb(); - }); - - it('should convert vinyl files to views', function(cb) { - var patterns = path.join(__dirname, 'fixtures/*.coffee'); - pages.src(patterns) - .on('error', cb) - .on('data', function(file) { - assert(file.isView); - }) - .on('end', cb); - }); - - it('should add src files to the collection', function(cb) { - var patterns = path.join(__dirname, 'fixtures/*.coffee'); - pages.src(patterns) - .on('error', cb) - .on('data', function(file) { - assert(pages.views); - assert(Object.keys(pages.views).length === 1); - }) - .on('end', cb); - }); - - it('should work with views added with other methods', function(cb) { - pages.addView('a', {content: '...'}); - pages.addView('b', {content: '...'}); - pages.addView('c', {content: '...'}); - - var patterns = path.join(__dirname, 'fixtures/*.coffee'); - var stream = pages.src(patterns); - stream.on('error', cb); - stream.on('data', function(file) { - assert(pages.views); - assert(Object.keys(pages.views).length === 4); - }); - stream.on('end', cb); - }); - - it('should return an input stream from a flat glob', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should return an input stream for multiple globs', function(cb) { - var globArray = [ - path.join(__dirname, 'fixtures/generic/run.dmc'), - path.join(__dirname, 'fixtures/generic/test.dmc') - ]; - var stream = pages.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(2); - files[0].path.should.equal(globArray[0]); - files[1].path.should.equal(globArray[1]); - cb(); - }); - }); - - it('should return an input stream for multiple globs with negation', function(cb) { - var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); - var globArray = [ - path.join(__dirname, 'fixtures/generic/*.dmc'), - '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), - ]; - var stream = pages.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(1); - files[0].path.should.equal(expectedPath); - cb(); - }); - }); - - it('should return an input stream with no contents when read is false', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should return an input stream with contents as stream when buffer is false', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee'), {buffer: false}); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - var buf = ''; - file.contents.on('data', function(d) { - buf += d; - }); - file.contents.on('end', function() { - buf.should.equal('Hello world!'); - cb(); - }); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - }); - }); - - it('should return an input stream from a deep glob', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/**/*.jade')); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); - String(file.contents).should.equal('test template'); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should return an input stream from a deeper glob', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/**/*.dmc')); - var a = 0; - stream.on('error', cb); - stream.on('data', function() { - ++a; - }); - stream.on('end', function() { - a.should.equal(2); - cb(); - }); - }); - - it('should return a file stream from a flat path', function(cb) { - var a = 0; - var stream = pages.src(path.join(__dirname, 'fixtures/test.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - ++a; - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - a.should.equal(1); - cb(); - }); - }); - - it('should return a stream', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); - should.exist(stream); - should.exist(stream.on); - cb(); - }); - - it('should return an input stream from a flat glob', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should return an input stream for multiple globs', function(cb) { - var globArray = [ - path.join(__dirname, 'fixtures/generic/run.dmc'), - path.join(__dirname, 'fixtures/generic/test.dmc') - ]; - var stream = pages.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(2); - files[0].path.should.equal(globArray[0]); - files[1].path.should.equal(globArray[1]); - cb(); - }); - }); - - it('should return an input stream for multiple globs, with negation', function(cb) { - var expectedPath = path.join(__dirname, 'fixtures/generic/run.dmc'); - var globArray = [ - path.join(__dirname, 'fixtures/generic/*.dmc'), - '!' + path.join(__dirname, 'fixtures/generic/test.dmc'), - ]; - var stream = pages.src(globArray); - - var files = []; - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - files.push(file); - }); - stream.on('end', function() { - files.length.should.equal(1); - files[0].path.should.equal(expectedPath); - cb(); - }); - }); - - it('should return an input stream with no contents when read is false', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/*.coffee'), {read: false}); - stream.on('error', cb); - stream.on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.not.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - }); - stream.on('end', function() { - cb(); - }); - }); - - it('should return an input stream from a deep glob', function(cb) { - pages.src(path.join(__dirname, 'fixtures/**/*.jade')) - .on('error', cb) - .on('data', function(file) { - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test/run.jade')); - String(file.contents).should.equal('test template'); - }) - .on('end', cb); - }); - - it('should return an input stream from a deeper glob', function(cb) { - var stream = pages.src(path.join(__dirname, 'fixtures/**/*.dmc')); - var a = 0; - stream.on('error', cb); - stream.on('data', function() { - ++a; - }); - stream.on('end', function() { - a.should.equal(2); - cb(); - }); - }); - - it('should return a file stream from a flat path', function(cb) { - var a = 0; - var stream = pages.src(path.join(__dirname, 'fixtures/test.coffee')); - stream.on('error', cb); - stream.on('data', function(file) { - ++a; - should.exist(file); - should.exist(file.path); - should.exist(file.contents); - path.join(file.path, '').should.equal(path.join(__dirname, 'fixtures/test.coffee')); - String(file.contents).should.equal('Hello world!'); - }); - stream.on('end', function() { - a.should.equal(1); - cb(); - }); - }); -}); diff --git a/test/collection.use.js b/test/collection.use.js deleted file mode 100644 index 55645834..00000000 --- a/test/collection.use.js +++ /dev/null @@ -1,245 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Collection = App.Collection; -var Item = App.Item; -var collection; - -describe('collection.use', function() { - beforeEach(function() { - collection = new Collection(); - }); - - it('should expose the instance to `use`:', function(cb) { - collection.use(function(inst) { - assert(inst instanceof Collection); - cb(); - }); - }); - - it('should be chainable:', function(cb) { - collection.use(function(inst) { - assert(inst instanceof Collection); - }) - .use(function(inst) { - assert(inst instanceof Collection); - }) - .use(function(inst) { - assert(inst instanceof Collection); - cb(); - }); - }); - - it('should expose the collection to a plugin:', function() { - collection.use(function(items) { - assert(items instanceof Collection); - items.foo = items.addItem.bind(items); - }); - - collection.foo('a', {content: '...'}); - assert(collection.items.hasOwnProperty('a')); - }); - - it('should expose collection when chained:', function() { - collection - .use(function(items) { - assert(items instanceof Collection); - items.foo = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof Collection); - items.bar = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof Collection); - items.baz = items.addItem.bind(items); - }); - - var pages = collection; - - pages.foo({path: 'a', content: '...'}); - pages.bar({path: 'b', content: '...'}); - pages.baz({path: 'c', content: '...'}); - - assert(collection.items.hasOwnProperty('a')); - assert(collection.items.hasOwnProperty('b')); - assert(collection.items.hasOwnProperty('c')); - }); - - it('should work when a custom `Item` constructor is passed:', function() { - collection = new Collection({Item: require('vinyl')}); - collection - .use(function(items) { - assert(items instanceof Collection); - items.foo = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof Collection); - items.bar = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof Collection); - items.baz = items.addItem.bind(items); - }); - - var pages = collection; - - pages.foo({path: 'a', content: '...'}); - pages.bar({path: 'b', content: '...'}); - pages.baz({path: 'c', content: '...'}); - - assert(collection.items.hasOwnProperty('a')); - assert(collection.items.hasOwnProperty('b')); - assert(collection.items.hasOwnProperty('c')); - }); - - it('should pass to item `use` if a function is returned:', function() { - collection.use(function(items) { - assert(items instanceof Collection); - - return function(item) { - item.foo = items.addItem.bind(items); - assert(item instanceof Item); - }; - }); - - collection.addItem('a', {content: '...'}) - .foo({path: 'b', content: '...'}) - .foo({path: 'c', content: '...'}) - .foo({path: 'd', content: '...'}); - - assert(collection.items.hasOwnProperty('a')); - assert(collection.items.hasOwnProperty('b')); - assert(collection.items.hasOwnProperty('c')); - assert(collection.items.hasOwnProperty('d')); - }); - - it('should be chainable when a item function is returned:', function() { - collection - .use(function(items) { - assert(items instanceof Collection); - - return function(item) { - item.foo = items.addItem.bind(items); - assert(item instanceof Item); - }; - }) - .use(function(items) { - assert(items instanceof Collection); - - return function(item) { - item.bar = items.addItem.bind(items); - assert(item instanceof Item); - }; - }) - .use(function(items) { - assert(items instanceof Collection); - - return function(item) { - item.baz = items.addItem.bind(items); - assert(item instanceof Item); - }; - }); - - collection.addItem('a', {content: '...'}) - .foo({path: 'b', content: '...'}) - .bar({path: 'c', content: '...'}) - .baz({path: 'd', content: '...'}); - - assert(collection.items.hasOwnProperty('a')); - assert(collection.items.hasOwnProperty('b')); - assert(collection.items.hasOwnProperty('c')); - assert(collection.items.hasOwnProperty('d')); - }); -}); - -describe('app > collection .use', function() { - var app; - beforeEach(function() { - app = new App(); - }); - - it('should pass plugins down to collections', function(cb) { - var count = 0; - app.use(function(inst) { - return function(collection) { - count++; - }; - }); - - app.create('pages'); - assert.equal(count, 1); - cb(); - }); - - it('should pass plugins down to collections after a collection is created', function(cb) { - var count = 0; - app.create('pages'); - - app.use(function(inst) { - return function(collection) { - count++; - }; - }); - - assert.equal(count, 1); - cb(); - }); - - it('should pass plugins down to every collections', function(cb) { - var count = 0; - app.create('pages'); - app.create('posts'); - app.create('docs'); - - app.use(function(inst) { - return function(collection) { - count++; - }; - }); - - assert.equal(count, 3); - cb(); - }); - - it('should pass plugins all the way down to views', function(cb) { - var count = {pages: 0, posts: 0}; - - app.create('pages'); - app.create('posts'); - - app.pages.addView('foo', {content: 'this is content'}); - app.pages.addView('bar', {content: 'this is content'}); - app.pages.addView('baz', {content: 'this is content'}); - - app.posts.addView('foo', {content: 'this is content'}); - app.posts.addView('bar', {content: 'this is content'}); - app.posts.addView('baz', {content: 'this is content'}); - - // add plugin after adding views and collections - app.use(function(inst) { - return function(collection) { - var name = collection.options.plural; - return function(view) { - count[name]++; - view.count = count[name]; - }; - }; - }); - - assert.equal(app.pages.getView('foo').count, 1); - assert.equal(app.pages.getView('bar').count, 2); - assert.equal(app.pages.getView('baz').count, 3); - assert.equal(count.pages, 3); - - assert.equal(app.posts.getView('foo').count, 1); - assert.equal(app.posts.getView('bar').count, 2); - assert.equal(app.posts.getView('baz').count, 3); - assert.equal(count.posts, 3); - cb(); - }); -}); diff --git a/test/fixtures/generator.js b/test/fixtures/generator.js index 060e8248..3dbcc451 100644 --- a/test/fixtures/generator.js +++ b/test/fixtures/generator.js @@ -22,7 +22,7 @@ module.exports = function(app) { }); }); - app.register('generate-abc', 'test/fixtures/generators/a/verbfile.js'); + app.register('generate-abc', 'test/fixtures/generators/a/generator.js'); app.register('generate-bbb', function(app) { app.task('default', function(cb) { diff --git a/test/fixtures/generators/a/generator.js b/test/fixtures/generators/a/generator.js index c3f4d75e..0198ef6e 100644 --- a/test/fixtures/generators/a/generator.js +++ b/test/fixtures/generators/a/generator.js @@ -1,7 +1,5 @@ 'use strict'; -var path = require('path'); - module.exports = function(app) { app.task('default', function(cb) { console.log('fixtures/a > default'); diff --git a/test/fixtures/generators/a/package.json b/test/fixtures/generators/a/package.json index 36390f08..bb51a953 100644 --- a/test/fixtures/generators/a/package.json +++ b/test/fixtures/generators/a/package.json @@ -3,5 +3,5 @@ "private": true, "version": "0.1.0", "files": ["index.js"], - "main": "verbfile.js" + "main": "generator.js" } diff --git a/test/fixtures/generators/b/index.js b/test/fixtures/generators/b/index.js index 0148fb4d..74da3a69 100644 --- a/test/fixtures/generators/b/index.js +++ b/test/fixtures/generators/b/index.js @@ -7,6 +7,6 @@ 'use strict'; -module.exports = function () { +module.exports = function() { // do stuff }; diff --git a/test/fixtures/generators/b/package.json b/test/fixtures/generators/b/package.json index 177dabf0..95afba0d 100644 --- a/test/fixtures/generators/b/package.json +++ b/test/fixtures/generators/b/package.json @@ -3,5 +3,5 @@ "private": true, "version": "0.1.0", "files": ["index.js"], - "main": "verbfile.js" + "main": "generator.js" } diff --git a/test/fixtures/generators/c/package.json b/test/fixtures/generators/c/package.json index 1d98e02b..753957e6 100644 --- a/test/fixtures/generators/c/package.json +++ b/test/fixtures/generators/c/package.json @@ -3,5 +3,5 @@ "private": true, "version": "0.1.0", "files": ["index.js"], - "main": "verbfile.js" + "main": "generator.js" } diff --git a/test/fixtures/generators/e/package.json b/test/fixtures/generators/e/package.json index d8b7e141..48c57d85 100644 --- a/test/fixtures/generators/e/package.json +++ b/test/fixtures/generators/e/package.json @@ -3,5 +3,5 @@ "private": true, "version": "0.1.0", "files": ["index.js"], - "main": "verbfile.js" + "main": "generator.js" } diff --git a/test/fixtures/generators/qux/generator.js b/test/fixtures/generators/qux/generator.js index 28979eea..887c0581 100644 --- a/test/fixtures/generators/qux/generator.js +++ b/test/fixtures/generators/qux/generator.js @@ -1,9 +1,5 @@ 'use strict'; -/** - * testing... - */ - module.exports = function(app, base, env) { app.task('a', function() {}); app.task('b', function() {}); diff --git a/test/fixtures/generators/qux/package.json b/test/fixtures/generators/qux/package.json index 214aac14..b1ec356a 100644 --- a/test/fixtures/generators/qux/package.json +++ b/test/fixtures/generators/qux/package.json @@ -1,7 +1,7 @@ { - "name": "generator-qux", + "name": "generate-qux", "private": true, "version": "0.1.0", - "files": ["verbfile.js"], - "main": "verbfile.js" + "files": ["generator.js"], + "main": "generator.js" } diff --git a/test/fixtures/generators2.js b/test/fixtures/generators2.js index 294b58ff..e2ddbb04 100644 --- a/test/fixtures/generators2.js +++ b/test/fixtures/generators2.js @@ -5,7 +5,7 @@ var app = new Generate({foo: 'bar'}); app.register('a', function(a, base) { a.register('a1', function(aa) { - aa.register('aaa', require('./generators/f/verbfile.js')); + aa.register('aaa', require('./generators/f/generator.js')); aa.task('default', function(cb) { console.log('aa > default'); cb(); diff --git a/test/fixtures/not-exposed.js b/test/fixtures/not-exposed.js index 065e82e6..5cee793f 100644 --- a/test/fixtures/not-exposed.js +++ b/test/fixtures/not-exposed.js @@ -1,8 +1,8 @@ 'use strict'; -var Generate = require('../..'); -var generate = new Generate(); +var Base = require('../..'); +var base = new Base({isApp: true}); + +base.register('not-exposed', function(app) { -generate.register('not-exposed', function(app) { - }); diff --git a/test/fixtures/one/generator.js b/test/fixtures/one/generator.js index 546d0048..eb51b6d7 100644 --- a/test/fixtures/one/generator.js +++ b/test/fixtures/one/generator.js @@ -1,6 +1,5 @@ 'use strict'; - module.exports = function(app, base, env) { app.task('default', function(cb) { console.log('one > default'); @@ -16,4 +15,4 @@ module.exports = function(app, base, env) { app.task('y', function() {}); app.task('z', function() {}); }); -}; \ No newline at end of file +}; diff --git a/test/fixtures/one/package.json b/test/fixtures/one/package.json index 363625fd..d1d46764 100644 --- a/test/fixtures/one/package.json +++ b/test/fixtures/one/package.json @@ -1,6 +1,6 @@ { "name": "one", - "description": "The most interesting project in the world > Generate", + "description": "The most interesting project in the world > Verb", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/one", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/package.json b/test/fixtures/package.json new file mode 100644 index 00000000..ed77450a --- /dev/null +++ b/test/fixtures/package.json @@ -0,0 +1,9 @@ +{ + "name": "generate-tests", + "version": "0.0.0", + "private": true, + "description": "", + "main": "index.js", + "license": "MIT", + "base": {} +} diff --git a/test/fixtures/three/four/five/generator.js b/test/fixtures/three/four/five/generator.js index 61f8bf67..19b86e81 100644 --- a/test/fixtures/three/four/five/generator.js +++ b/test/fixtures/three/four/five/generator.js @@ -11,4 +11,4 @@ module.exports = function(generate, base) { app.task('y', function() {}); app.task('z', function() {}); }); -}; \ No newline at end of file +}; diff --git a/test/fixtures/three/four/five/package.json b/test/fixtures/three/four/five/package.json index 4bc4c94a..984f0f6b 100644 --- a/test/fixtures/three/four/five/package.json +++ b/test/fixtures/three/four/five/package.json @@ -1,6 +1,6 @@ { "name": "five", - "description": "The most interesting project in the world > Generate", + "description": "The most interesting project in the world > Verb", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/five", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/three/four/generator.js b/test/fixtures/three/four/generator.js index 61f8bf67..19b86e81 100644 --- a/test/fixtures/three/four/generator.js +++ b/test/fixtures/three/four/generator.js @@ -11,4 +11,4 @@ module.exports = function(generate, base) { app.task('y', function() {}); app.task('z', function() {}); }); -}; \ No newline at end of file +}; diff --git a/test/fixtures/three/four/package.json b/test/fixtures/three/four/package.json index 2662ab91..3f25912f 100644 --- a/test/fixtures/three/four/package.json +++ b/test/fixtures/three/four/package.json @@ -1,6 +1,6 @@ { "name": "four", - "description": "The most interesting project in the world > Generate", + "description": "The most interesting project in the world > Verb", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/four", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/three/generator.js b/test/fixtures/three/generator.js index 61f8bf67..19b86e81 100644 --- a/test/fixtures/three/generator.js +++ b/test/fixtures/three/generator.js @@ -11,4 +11,4 @@ module.exports = function(generate, base) { app.task('y', function() {}); app.task('z', function() {}); }); -}; \ No newline at end of file +}; diff --git a/test/fixtures/three/package.json b/test/fixtures/three/package.json index af82c885..12b5fac3 100644 --- a/test/fixtures/three/package.json +++ b/test/fixtures/three/package.json @@ -1,6 +1,6 @@ { "name": "three", - "description": "The most interesting project in the world > Generate", + "description": "The most interesting project in the world > Verb", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/three", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/two/generator.js b/test/fixtures/two/generator.js index 4e86299b..eb9b49ce 100644 --- a/test/fixtures/two/generator.js +++ b/test/fixtures/two/generator.js @@ -1,14 +1,14 @@ 'use strict'; -var Generate = require('../../..'); -var generate = new Generate(); +var Base = require('../../..'); +var base = new Base(); -generate.task('default', function() {}); -generate.task('a', function() {}); -generate.task('b', function() {}); -generate.task('c', function() {}); +base.task('default', function() {}); +base.task('a', function() {}); +base.task('b', function() {}); +base.task('c', function() {}); -generate.register('foo', function(app) { +base.register('foo', function(app) { app.task('x', function() {}); app.task('y', function() {}); app.task('z', function() {}); @@ -18,4 +18,4 @@ generate.register('foo', function(app) { * Expose this instance of `Generate` */ -module.exports = generate; \ No newline at end of file +module.exports = base; diff --git a/test/fixtures/two/package.json b/test/fixtures/two/package.json index 55e28acb..8a02df01 100644 --- a/test/fixtures/two/package.json +++ b/test/fixtures/two/package.json @@ -1,6 +1,6 @@ { "name": "two", - "description": "The most interesting project in the world > Generate", + "description": "The most interesting project in the world > Verb", "version": "0.1.0", "homepage": "https://github.com/jonschlinkert/two", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", diff --git a/test/fixtures/verbfile.js b/test/fixtures/verbfile.js index 060e8248..3dbcc451 100644 --- a/test/fixtures/verbfile.js +++ b/test/fixtures/verbfile.js @@ -22,7 +22,7 @@ module.exports = function(app) { }); }); - app.register('generate-abc', 'test/fixtures/generators/a/verbfile.js'); + app.register('generate-abc', 'test/fixtures/generators/a/generator.js'); app.register('generate-bbb', function(app) { app.task('default', function(cb) { diff --git a/test/fixtures/vinyl/test-symlink-dir b/test/fixtures/vinyl/test-symlink-dir deleted file mode 120000 index 9a17374b..00000000 --- a/test/fixtures/vinyl/test-symlink-dir +++ /dev/null @@ -1 +0,0 @@ -wow \ No newline at end of file diff --git a/test/vinyl/fixtures/wow/suchempty b/test/fixtures/vinyl/test-symlink-dir/suchempty similarity index 100% rename from test/vinyl/fixtures/wow/suchempty rename to test/fixtures/vinyl/test-symlink-dir/suchempty diff --git a/test/generators.env.js b/test/generators.env.js index 8957de39..7c7dd97d 100644 --- a/test/generators.env.js +++ b/test/generators.env.js @@ -3,108 +3,123 @@ require('mocha'); var path = require('path'); var assert = require('assert'); -var Verb = require('..'); -var app; +var Base = require('..'); +var base; +var env; var fixtures = path.resolve.bind(path, __dirname + '/fixtures'); -describe('generators.env', function() { +describe('env', function() { + describe('plugin', function() { + it('should work as a plugin', function() { + base = new Base(); + assert.equal(typeof base.createEnv, 'function'); + }); + }); + describe('createEnv paths', function() { beforeEach(function() { - app = new Verb(); + base = new Base(); }); describe('alias and function', function() { it('should make the alias the exact name when the second arg is a function', function() { var fn = function() {}; - app.createEnv('foo-bar-baz', fn); - assert(app.env); - assert(app.env.alias); - assert.equal(app.env.alias, 'foo-bar-baz'); + var env = base.createEnv('foo-bar-baz', fn); + assert(env); + assert(env.alias); + assert.equal(env.alias, 'foo-bar-baz'); }); it('should not change the name when the second arg is a function', function() { var fn = function() {}; - app.createEnv('foo-bar-baz', fn); - assert(app.env); - assert(app.env.name); - assert.equal(app.env.name, 'foo-bar-baz'); + var env = base.createEnv('foo-bar-baz', fn); + assert(env); + assert(env.name); + assert.equal(env.name, 'foo-bar-baz'); }); }); describe('alias and path', function() { - it('should set a generator by name', function() { - app.createEnv('fofofof', 'generate-foo/verbfile.js'); - assert.equal(app.env.name, 'fofofof'); + it('should set the env.name using the given name', function() { + var env = base.createEnv('foo', 'generate-foo/generator.js'); + assert.equal(env.name, 'foo'); }); it('should not change the name when the second arg is a function', function() { var fn = function() {}; - app.createEnv('foo-bar-baz', fn); - assert(app.env); - assert(app.env.name); - assert.equal(app.env.name, 'foo-bar-baz'); + var env = base.createEnv('foo-bar-baz', fn); + assert(env); + assert(env.name); + assert.equal(env.name, 'foo-bar-baz'); }); }); }); describe('createEnv', function() { beforeEach(function() { - app = new Verb(); + base = new Base(); }); it('should add an env object to the instance', function() { var fn = function() {}; - app.createEnv('foo', fn); - assert(app.env); + env = base.createEnv('foo', fn); + assert(env); }); it('should take options as the second arg', function() { var fn = function() {}; - app.createEnv('foo', {}, fn); - assert(app.env); + env = base.createEnv('foo', {}, fn); + assert(env); }); it('should prime `env` if it doesn\'t exist', function() { var fn = function() {}; - delete app.env; - app.createEnv('foo', {}, fn); - assert(app.env); + env = base.createEnv('foo', {}, fn); + assert(env); }); it('should add an alias to the env object', function() { var fn = function() {}; - app.createEnv('foo', {}, fn); - assert.equal(app.env.alias, 'foo'); + env = base.createEnv('foo', {}, fn); + assert.equal(env.alias, 'foo'); }); it('should not prefix the alias when a function is passed', function() { var fn = function() {}; - delete app.prefix; - app.createEnv('foo', {}, fn); - assert.equal(app.env.name, 'foo'); + delete base.prefix; + env = base.createEnv('foo', {}, fn); + assert.equal(env.name, 'foo'); }); it('should not prefix a custom alias when a function is passed', function() { var fn = function() {}; - app.prefix = 'whatever'; - app.createEnv('foo', {}, fn); - assert.equal(app.env.name, 'foo'); + base.prefix = 'whatever'; + env = base.createEnv('foo', {}, fn); + assert.equal(env.name, 'foo'); + }); + + it('should try to resolve an absolute path passed as the second arg', function() { + env = base.createEnv('foo', fixtures('generator.js')); + assert.equal(env.alias, 'foo'); + assert.equal(env.name, 'foo'); }); - it('should try to resolve a path passed as the second arg', function() { - app.createEnv('verb-foo-generator', fixtures('verbfile.js')); - assert.equal(app.env.alias, 'foo'); - assert.equal(app.env.name, 'verb-foo-generator'); + it('should try to resolve a relative path passed as the second arg', function() { + env = base.createEnv('foo', 'generate-foo/generator.js'); + assert.equal(env.key, 'foo'); + assert.equal(env.alias, 'foo'); + assert.equal(env.name, 'foo'); }); it('should throw an error when the path is not resolved', function(cb) { try { - var env = app.createEnv('foo', fixtures('whatever.js')); - env.fn(); + var env = base.createEnv('foo', fixtures('whatever.js')); + env.invoke(); + env.path; cb(new Error('expected an error')); } catch (err) { - assert.equal(err.message, 'Cannot find module \'' + fixtures('whatever.js/generator.js') + '\''); + assert.equal(err.message, 'cannot resolve: \'' + fixtures('whatever.js') + '\''); cb(); } }); diff --git a/test/generators.events.js b/test/generators.events.js index aa88de69..5f2e9cf0 100644 --- a/test/generators.events.js +++ b/test/generators.events.js @@ -2,60 +2,86 @@ require('mocha'); var assert = require('assert'); -var Generate = require('..'); -var generate; +var Base = require('..'); +var base; -describe('generators.events', function() { +var generators = require('..'); + +describe('generators events', function() { describe('generator', function() { beforeEach(function() { - generate = new Generate(); + base = new Base(); }); it('should emit generator when a generator is registered', function(cb) { - generate = new Generate(); - generate.on('generator', function(name) { - assert.equal(name, 'foo'); + base = new Base(); + base.on('generator', function(generator) { + assert.equal(generator.alias, 'foo'); + cb(); + }); + + base.register('foo', function() {}); + }); + + it('should emit generator when base.generators.get is called', function(cb) { + base = new Base(); + + base.on('generator', function(generator) { + assert.equal(generator.alias, 'foo'); + cb(); + }); + + base.register('foo', function() {}); + base.getGenerator('foo'); + }); + + it('should emit generator.get when base.generators.get is called', function(cb) { + base = new Base(); + base.on('generator', function(generator) { + assert.equal(generator.alias, 'foo'); cb(); }); - generate.register('foo', function() {}); + base.register('foo', function() {}); + base.getGenerator('foo'); }); it('should emit error on base when a base generator emits an error', function(cb) { - generate = new Generate(); + base = new Base(); var called = 0; - generate.on('error', function(err) { + base.on('error', function(err) { assert.equal(err.message, 'whatever'); called++; }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.emit('error', new Error('whatever')); }); - generate.getGenerator('foo'); + base.getGenerator('foo'); assert.equal(called, 1); cb(); }); it('should emit error on base when a base generator throws an error', function(cb) { - generate = new Generate(); + base = new Base(); var called = 0; - generate.on('error', function(err) { + base.on('error', function(err) { assert.equal(err.message, 'whatever'); called++; }); - generate.register('foo', function(app) { + base.register('foo', function(app) { app.task('default', function(cb) { cb(new Error('whatever')); }); }); - generate.getGenerator('foo') + base.getGenerator('foo') .build(function(err) { + assert(err); assert.equal(called, 1); cb(); }); @@ -63,15 +89,15 @@ describe('generators.events', function() { }); it('should emit errors on base from deeply nested generators', function(cb) { - generate = new Generate(); + base = new Base(); var called = 0; - generate.on('error', function(err) { + base.on('error', function(err) { assert.equal(err.message, 'whatever'); called++; }); - generate.register('a', function() { + base.register('a', function() { this.register('b', function() { this.register('c', function() { this.register('d', function() { @@ -83,8 +109,9 @@ describe('generators.events', function() { }); }); - generate.getGenerator('a.b.c.d') + base.getGenerator('a.b.c.d') .build(function(err) { + assert(err); assert.equal(called, 1); cb(); }); @@ -92,19 +119,19 @@ describe('generators.events', function() { }); it('should bubble up errors to all parent generators', function(cb) { - generate = new Generate(); + base = new Base(); var called = 0; - generate.on('error', function(err) { - assert.equal(err.message, 'whatever'); + function count() { called++; - }); + } - function count(err) { + base.on('error', function(err) { + assert.equal(err.message, 'whatever'); called++; - } + }); - generate.register('a', function() { + base.register('a', function() { this.on('error', count); this.register('b', function() { @@ -124,9 +151,11 @@ describe('generators.events', function() { }); }); - generate.getGenerator('a.b.c.d') + base.getGenerator('a.b.c.d') .build(function(err) { - assert.equal(called, 5); + assert(err); + assert.equal(called, 6); + assert.equal(err.message, 'whatever'); cb(); }); }); diff --git a/test/group.js b/test/group.js deleted file mode 100644 index b5eaa8d2..00000000 --- a/test/group.js +++ /dev/null @@ -1,153 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); - -var assert = require('assert'); -var support = require('./support/'); -assert.containEql = support.containEql; -var App = support.resolve(); -var List = App.List; -var Group = App.Group; -var group; - -describe('group', function() { - describe('constructor', function() { - it('should create an instance of Group:', function() { - var group = new Group(); - assert(group instanceof Group); - }); - - it('should instantiate without new', function() { - var group = Group(); - assert(group instanceof Group); - }); - - it('should create an instance of Group with default List:', function() { - var group = new Group(); - assert.deepEqual(group.List, List); - }); - - it('should create an instance of Group with custom List:', function() { - function CustomList() { - List.apply(this, arguments); - } - List.extend(CustomList); - var group = new Group({List: CustomList}); - assert.deepEqual(group.List, CustomList); - }); - }); - - describe('static methods', function() { - it('should expose `extend`:', function() { - assert(typeof Group.extend === 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - group = new Group(); - }); - - it('should expose `use`', function() { - assert(typeof group.use === 'function'); - }); - it('should expose `set`', function() { - assert(typeof group.set === 'function'); - }); - it('should expose `get`', function() { - assert(typeof group.get === 'function'); - }); - it('should expose `visit`', function() { - assert(typeof group.visit === 'function'); - }); - it('should expose `define`', function() { - assert(typeof group.define === 'function'); - }); - }); - - describe('instance', function() { - beforeEach(function() { - group = new Group(); - }); - - it('should expose options:', function() { - assert(typeof group.options === 'object'); - }); - - it('should set a value on the instance:', function() { - group.set('a', 'b'); - assert(group.a === 'b'); - }); - - it('should get a value from the instance:', function() { - group.set('a', 'b'); - assert(group.get('a') === 'b'); - }); - }); - - describe('option', function() { - it('should set options on group.options', function() { - var group = new Group(); - group.option('a', {b: {c: 'd'}}); - assert.equal(group.option('a.b.c'), 'd'); - }); - }); - - describe('get', function() { - it('should get a normal value when not an array', function() { - var group = new Group({'foo': {items: [1, 2, 3]}}); - assert.deepEqual(group.get('foo'), {items: [1, 2, 3]}); - }); - - it('should get an instance of List when value is an array', function() { - var group = new Group({'foo': {items: [{path: 'one.hbs'}, {path: 'two.hbs'}, {path: 'three.hbs'}]}}); - var list = group.get('foo.items'); - assert(list instanceof List); - assert.deepEqual(list.items.length, 3); - }); - - it('should throw an error when trying to use a List method on a non List value', function() { - (function() { - var group = new Group({'foo': {items: [1, 2, 3]}}); - var foo = group.get('foo'); - foo.paginate(); - }).should.throw('paginate can only be used with an array of `List` items.'); - }); - - it('should not override properties already existing on non List values', function(cb) { - var group = new Group({'foo': {items: [1, 2, 3], paginate: function() { - assert(true); - cb(); - }}}); - var foo = group.get('foo'); - foo.paginate(); - }); - }); - - describe('use', function() { - beforeEach(function() { - group = new Group(); - }); - - it('should use plugins on a group:', function() { - group.set('one', {contents: new Buffer('aaa')}); - group.set('two', {contents: new Buffer('zzz')}); - - group - .use(function(group) { - group.options = {}; - }) - .use(function(group) { - group.options.foo = 'bar'; - }) - .use(function() { - this.set('one', 'two'); - }); - - assert(group.one === 'two'); - assert(group.options.foo === 'bar'); - }); - }); -}); - diff --git a/test/handlers.js b/test/handlers.js deleted file mode 100644 index 253cd4d4..00000000 --- a/test/handlers.js +++ /dev/null @@ -1,131 +0,0 @@ -'use strict'; - -var path = require('path'); -var assert = require('assert'); -var should = require('should'); -var rimraf = require('rimraf'); -var File = require('vinyl'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('handlers', function() { - describe('custom handlers', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - }); - - it('should add custom middleware handlers:', function() { - app.handler('foo'); - app.handler('bar'); - - app.pages.use(function() { - return function(view) { - app.handle('foo', view); - app.handle('bar', view); - }; - }); - - app.foo(/a/, function(view, next) { - view.one = 'aaa'; - next(); - }); - - app.bar(/z/, function(view, next) { - view.two = 'zzz'; - next(); - }); - - app.pages('a.txt', {content: 'aaa'}); - app.pages('z.txt', {content: 'zzz'}); - - app.pages.getView('a.txt').should.have.property('one'); - app.pages.getView('a.txt').should.not.have.property('two'); - - app.pages.getView('z.txt').should.not.have.property('one'); - app.pages.getView('z.txt').should.have.property('two'); - }); - }); - - describe('stream', function() { - beforeEach(function() { - app = new App(); - }); - - afterEach(function(cb) { - rimraf(path.join(__dirname, './out-fixtures/'), cb); - }); - - it('should handle onLoad', function(cb) { - var count = 0; - app.onLoad(/./, function(file, next) { - count++; - next(); - }); - - app.src(path.join(__dirname, './fixtures/vinyl/test.coffee')) - .pipe(app.dest('./out-fixtures/', {cwd: __dirname})) - .on('end', function() { - assert.equal(count, 1); - cb(); - }); - }); - - it('should handle preWrite', function(cb) { - var count = 0; - app.preWrite(/./, function(file, next) { - count++; - next(); - }); - - var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var stream = app.dest('./out-fixtures/', { - cwd: __dirname - }); - - stream.once('finish', function() { - assert.equal(count, 1); - cb(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: new Buffer("1234567890") - }); - file.options = {}; - - stream.write(file); - stream.end(); - }); - - it('should handle postWrite', function(cb) { - var count = 0; - app.postWrite(/./, function(file, next) { - count++; - next(); - }); - - var srcPath = path.join(__dirname, './fixtures/vinyl/test.coffee'); - var stream = app.dest('./out-fixtures/', { - cwd: __dirname - }); - - stream.once('finish', function() { - assert.equal(count, 1); - cb(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: new Buffer("1234567890") - }); - file.options = {}; - - stream.write(file); - stream.end(); - }); - }); -}); diff --git a/test/helpers.js b/test/helpers.js deleted file mode 100644 index 3943bdbe..00000000 --- a/test/helpers.js +++ /dev/null @@ -1,1090 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var assert = require('assert'); -var consolidate = require('consolidate'); -var handlebars = require('engine-handlebars'); -var matter = require('parser-front-matter'); -var helpers = require('templates/lib/plugins/helpers'); -var init = require('templates/lib/plugins/init'); -var swig = consolidate.swig; -require('swig'); - -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('helpers', function() { - describe('prototype methods', function() { - beforeEach(function() { - app = new App(); - }); - it('should expose `helper`', function() { - assert.equal(typeof app.helper, 'function'); - }); - it('should expose `asyncHelper`', function() { - assert.equal(typeof app.asyncHelper, 'function'); - }); - }); - - describe('instance', function() { - it('should prime _', function() { - function Foo() { - App.call(this); - init(this); - } - App.extend(Foo); - var foo = new Foo(); - helpers(foo); - assert.equal(typeof foo._, 'object'); - }); - }); - - describe('helpers', function() { - beforeEach(function() { - app = new App(); - }); - - it('should add a sync helper to the `sync` object:', function() { - app.helper('one', function() {}); - assert.equal(typeof app._.helpers.sync.one, 'function'); - }); - - it('should load a glob of sync helper functions:', function() { - app.helpers('test/fixtures/helpers/[a-c].js'); - - assert.equal(typeof app._.helpers.sync.c, 'function'); - assert.equal(typeof app._.helpers.sync.b, 'function'); - assert.equal(typeof app._.helpers.sync.a, 'function'); - }); - - it('should fail gracefully on bad globs:', function(cb) { - try { - app.helpers('test/fixtures/helpers/*.foo'); - cb(); - } catch (err) { - cb(new Error('should not throw an error.')); - } - }); - - it('should add a glob of sync helper objects:', function() { - app.helpers('test/fixtures/helpers/!([a-c]).js'); - assert.equal(typeof app._.helpers.sync.one, 'function'); - assert.equal(typeof app._.helpers.sync.two, 'function'); - assert.equal(typeof app._.helpers.sync.three, 'function'); - }); - - it('should add a glob with mixed helper objects and functions:', function() { - app.helpers('test/fixtures/helpers/*.js'); - assert.equal(typeof app._.helpers.sync.a, 'function'); - assert.equal(typeof app._.helpers.sync.b, 'function'); - assert.equal(typeof app._.helpers.sync.c, 'function'); - assert.equal(typeof app._.helpers.sync.one, 'function'); - assert.equal(typeof app._.helpers.sync.two, 'function'); - assert.equal(typeof app._.helpers.sync.three, 'function'); - }); - - it('should add an object of sync helpers to the `sync` object:', function() { - app.helpers({ - x: function() {}, - y: function() {}, - z: function() {} - }); - - assert.equal(typeof app._.helpers.sync.x, 'function'); - assert.equal(typeof app._.helpers.sync.y, 'function'); - assert.equal(typeof app._.helpers.sync.z, 'function'); - }); - - it('should add a helper "group":', function() { - app.helperGroup('foo', { - x: function() {}, - y: function() {}, - z: function() {} - }); - - assert.equal(typeof app._.helpers.sync.foo.x, 'function'); - assert.equal(typeof app._.helpers.sync.foo.y, 'function'); - assert.equal(typeof app._.helpers.sync.foo.z, 'function'); - }); - }); - - describe('async helpers', function() { - beforeEach(function() { - app = new App(); - }); - - it('should add an async helper to the `async` object:', function() { - app.asyncHelper('two', function() {}); - assert.equal(typeof app._.helpers.async.two, 'function'); - }); - - it('should load a glob of async helper functions:', function() { - app.asyncHelpers('test/fixtures/helpers/[a-c].js'); - assert.equal(typeof app._.helpers.async.a, 'function'); - assert.equal(typeof app._.helpers.async.b, 'function'); - assert.equal(typeof app._.helpers.async.c, 'function'); - }); - - it('should add a glob of async helper objects:', function() { - app.asyncHelpers('test/fixtures/helpers/!([a-c]).js'); - assert.equal(typeof app._.helpers.async.one, 'function'); - assert.equal(typeof app._.helpers.async.two, 'function'); - assert.equal(typeof app._.helpers.async.three, 'function'); - }); - - it('should fail gracefully on bad globs:', function(cb) { - try { - app.asyncHelpers('test/fixtures/helpers/*.foo'); - cb(); - } catch (err) { - cb(new Error('should not throw an error.')); - } - }); - - it('should add a glob with mixed helper objects and functions:', function() { - app.asyncHelpers('test/fixtures/helpers/*.js'); - assert.equal(typeof app._.helpers.async.a, 'function'); - assert.equal(typeof app._.helpers.async.b, 'function'); - assert.equal(typeof app._.helpers.async.c, 'function'); - assert.equal(typeof app._.helpers.async.one, 'function'); - assert.equal(typeof app._.helpers.async.two, 'function'); - assert.equal(typeof app._.helpers.async.three, 'function'); - }); - - it('should add an object of async helpers to the `async` object:', function() { - app.asyncHelpers({ - x: function() {}, - y: function() {}, - z: function() {} - }); - - assert.equal(typeof app._.helpers.async.x, 'function'); - assert.equal(typeof app._.helpers.async.y, 'function'); - assert.equal(typeof app._.helpers.async.z, 'function'); - }); - - it('should add an async helper "group":', function() { - app.helperGroup('foo', { - x: function() {}, - y: function() {}, - z: function() {} - }, true); - - assert.equal(typeof app._.helpers.async.foo.x, 'function'); - assert.equal(typeof app._.helpers.async.foo.y, 'function'); - assert.equal(typeof app._.helpers.async.foo.z, 'function'); - }); - }); -}); - -describe('sync helpers', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - it('should register a helper:', function() { - app.helper('a', function() {}); - app.helper('b', function() {}); - assert(app._.helpers.sync.hasOwnProperty('a')); - assert(app._.helpers.sync.hasOwnProperty('b')); - }); - - it('should use a helper:', function(cb) { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= upper(a) %>', locals: {a: 'bbb'}}); - app.helper('upper', function(str) { - return str.toUpperCase(); - }); - - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - - assert.equal(typeof view.contents.toString(), 'string'); - assert.equal(view.contents.toString(), 'BBB'); - cb(); - }); - }); - - it('should use a namespaced helper:', function(cb) { - app.pages('a.tmpl', { - path: 'a.tmpl', - content: '<%= foo.upper(a) %>', - locals: { - a: 'bbb' - } - }); - - app.helperGroup('foo', { - upper: function(str) { - return str.toUpperCase(); - } - }); - - var page = app.pages.getView('a.tmpl'); - app.render(page, function(err, view) { - if (err) return cb(err); - - assert.equal(typeof view.contents.toString(), 'string'); - assert.equal(view.contents.toString(), 'BBB'); - cb(); - }); - }); -}); - -describe('async helpers', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('partials', {viewType: 'partial'}); - app.create('page'); - }); - - it('should register an async helper:', function() { - app.asyncHelper('a', function() {}); - app.asyncHelper('b', function() {}); - app._.helpers.async.should.have.property('a'); - app._.helpers.async.should.have.property('b'); - }); - - it('should use an async helper:', function(cb) { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= lower(a) %>', locals: {a: 'BBB'}}); - app.asyncHelper('lower', function(str, next) { - if (typeof next !== 'function') return str; - next(null, str.toLowerCase()); - }); - - var page = app.pages.getView('a.tmpl'); - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'bbb'); - cb(); - }); - }); - - it('should use load a file from the file system in a helper', function(cb) { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= getPartial("test/fixtures/posts/a.txt") %>', locals: {a: 'BBB'}}); - app.asyncHelper('getPartial', function(name, next) { - var view = this.app.partials.getView(name); - next(null, view.content); - }); - - var page = app.pages.getView('a.tmpl'); - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert(/AAA/.test(view.content)); - cb(); - }); - }); -}); - -describe('built-in helpers:', function() { - describe('automatically generated helpers for default view types:', function() { - beforeEach(function() { - app = new App({rethrow: false}); - app.engine('md', require('engine-base')); - app.engine('tmpl', require('engine-base')); - app.create('partials', { viewType: 'partial' }); - app.create('pages'); - - // parse front matter - app.onLoad(/./, function(view, next) { - matter.parse(view, next); - }); - }); - - it('should expose front matter to the `partial` helper.', function(cb) { - app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); - app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); - - app.render('b.md', function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo AAA bar'); - cb(); - }); - }); - - it('should emit `helper` when a built-in helper is called', function(cb) { - app.partial('a.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); - app.page('b.md', {path: 'b.md', content: 'foo <%= partial("a.md") %> bar'}); - var count = 0; - - app.once('helper', function(msg) { - assert(msg); - assert.equal(msg, 'partial helper > rendering "a.md"'); - count++; - }); - - app.render('b.md', function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - cb(); - }); - }); - - it('should use helper locals.', function(cb) { - app.partial('abc.md', {content: '<%= name %>', locals: {name: 'BBB'}}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo CCC bar'); - cb(); - }); - }); - - it('should use front matter data.', function(cb) { - app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>'}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo AAA bar'); - cb(); - }); - }); - - it('should prefer helper locals over front-matter', function(cb) { - app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo CCC bar'); - cb(); - }); - }); - - it('should use partial locals:', function(cb) { - app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); - - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) - .render({name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo EEE bar'); - cb(); - }); - }); - - it('should use locals from the `view.render` method:', function(cb) { - app.partial('abc.md', {content: '<%= name %>', locals: {name: 'EEE'}}); - - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}) - .render({name: 'DDD'}, function(err, res) { - if (err) return cb(err); - - res.content.should.equal('foo EEE bar'); - cb(); - }); - }); - - it('should use locals from the `app.render` method:', function(cb) { - app.partial('abc.md', {content: '<%= name %>'}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo DDD bar'); - cb(); - }); - }); - - it('should use a `helperContext` function from app.options', function(cb) { - app.option('helperContext', function(view, locals) { - return { name: 'blah' }; - }); - - app.partial('abc.md', {content: '<%= name %>'}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo blah bar'); - cb(); - }); - }); - - it('should return an empty string when the partial is missing.', function(cb) { - app.partial('abc.md', {content: '---\nname: "AAA"\n---\n<%= name %>', locals: {name: 'BBB'}}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("def.md", { name: "CCC" }) %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - assert(err); - assert.equal(err.message, 'helper "partial" cannot find "def.md"'); - cb(); - }); - }); - }); - - describe('helper context:', function() { - beforeEach(function() { - app = new App({rethrow: false}); - app.engine(['tmpl', 'md'], require('engine-base')); - app.create('partial', { viewType: 'partial' }); - app.create('page'); - - // parse front matter - app.onLoad(/./, function(view, next) { - matter.parse(view, next); - }); - }); - - it('should expose a "this" object', function(cb) { - app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); - app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); - var count = 0; - app.option('helper.foo', {some: 'opt'}); - - app.helper('foo', function() { - assert(this); - count++; - return 'foo'; - }); - - app.render('xyz.md', function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - res.content.should.equal('a foo b'); - cb(); - }); - }); - - it('should expose a "this.helper" object', function(cb) { - app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); - app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); - var count = 0; - app.option('helper.foo', {some: 'opt'}); - - app.helper('foo', function() { - assert(this.helper); - assert.equal(typeof this.helper, 'object'); - count++; - return 'foo'; - }); - - app.render('xyz.md', function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - res.content.should.equal('a foo b'); - cb(); - }); - }); - - it('should expose a "this.options" object', function(cb) { - app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); - app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); - var count = 0; - app.option('helper.foo', {some: 'opt'}); - - app.helper('foo', function() { - assert(this.options); - assert.equal(typeof this.options, 'object'); - count++; - return 'foo'; - }); - - app.render('xyz.md', function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - res.content.should.equal('a foo b'); - cb(); - }); - }); - - it('should expose a "this.context" object', function(cb) { - app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); - app.page('xyz.md', {path: 'xyz.md', content: 'a <%= foo() %> b'}); - var count = 0; - app.option('helper.foo', {some: 'opt'}); - - app.helper('foo', function() { - assert(this.context); - assert.equal(typeof this.context, 'object'); - count++; - return 'foo'; - }); - - app.render('xyz.md', function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - res.content.should.equal('a foo b'); - cb(); - }); - }); - - it('should prefer helper locals over view locals.', function(cb) { - app.partial('abc.md', {content: '<%= name %>', name: 'BBB'}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md", { name: "CCC" }) %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo CCC bar'); - cb(); - }); - }); - - it('should give preference to view locals over render locals.', function(cb) { - app.partial('abc.md', {content: '<%= name %>', locals: {name: 'BBB'}}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - - var page = app.pages.getView('xyz.md'); - - app.render(page, {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo BBB bar'); - cb(); - }); - }); - - it('should use render locals when other locals are not defined.', function(cb) { - app.partial('abc.md', {content: '<%= name %>'}); - app.page('xyz.md', {path: 'xyz.md', content: 'foo <%= partial("abc.md") %> bar'}); - - app.render('xyz.md', {name: 'DDD'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo DDD bar'); - cb(); - }); - }); - }); - - describe('user-defined engines:', function() { - beforeEach(function() { - app = new App({rethrow: false}); - app.create('partial', { viewType: 'partial' }); - app.create('page'); - - // parse front matter - app.onLoad(/./, function(view, next) { - matter.parse(view, next); - }); - }); - - it('should use the `partial` helper with handlebars.', function(cb) { - app.engine(['tmpl', 'md'], require('engine-base')); - app.engine('hbs', handlebars); - - app.partial('title.hbs', {content: '{{name}}', locals: {name: 'BBB'}}); - app.page('a.hbs', {path: 'a.hbs', content: 'foo {{{partial "title.hbs" this}}} bar'}); - - app.render('a.hbs', {name: 'Halle Nicole'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('foo Halle Nicole bar'); - cb(); - }); - }); - - it('should use the `partial` helper with any engine.', function(cb) { - app.engine('hbs', handlebars); - app.engine('md', handlebars); - app.engine('swig', swig); - app.engine('tmpl', require('engine-base')); - - /** - * Partial - */ - - app.partial('a.hbs', { - content: '---\nname: "AAA"\n---\n{{name}}', - locals: { - name: 'BBB' - } - }); - - /** - * Pages - */ - - app.page('a.hbs', { - path: 'a.hbs', - content: '{{author}}', - locals: { - author: 'Halle Nicole' - } - }); - app.page('b.tmpl', { - path: 'b.tmpl', - content: '<%= author %>', - locals: { - author: 'Halle Nicole' - } - }); - app.page('d.swig', { - path: 'd.swig', - content: '{{author}}', - locals: { - author: 'Halle Nicole' - } - }); - app.page('e.swig', { - path: 'e.swig', - content: '{{author}}', - locals: { - author: 'Halle Nicole' - } - }); - app.page('f.hbs', { - content: '{{author}}', - locals: { - author: 'Halle Nicole' - } - }); - app.page('g.md', { - content: '---\nauthor: Brian Woodward\n---\n{{author}}', - locals: { - author: 'Halle Nicole' - } - }); - app.page('with-partial.hbs', { - path: 'with-partial.hbs', - content: '{{{partial "a.hbs" custom.locals}}}' - }); - - app.on('error', function(err) { - cb(err); - }); - - var locals = {custom: {locals: {name: 'Halle Nicole' }}}; - app.render('a.hbs', locals, function(err, res) { - if (err) { - app.emit('error', err); - return; - } - res.content.should.equal('Halle Nicole'); - }); - - app.render('with-partial.hbs', locals, function(err, res) { - if (err) { - app.emit('error', err); - return; - } - res.content.should.equal('Halle Nicole'); - }); - - var page = app.pages.getView('g.md'); - locals.author = page.data.author || locals.author; - page.render(locals, function(err, res) { - if (err) { - app.emit('error', err); - return; - } - res.content.should.equal('Brian Woodward'); - cb(null, res.content); - }); - }); - }); -}); - -describe('helper debug', function() { - beforeEach(function() { - app = new App(); - app.create('pages'); - app.engine('hbs', require('engine-handlebars')); - app.engine('md', require('engine-base')); - }); - - it('should expose a `debug` method on the context', function(cb) { - var count = 0; - app.helper('foo', function(str) { - assert.equal(typeof this.debug, 'function'); - this.debug('rendering "%s"', str); - count++; - return str; - }); - - app.page('doc.md', {content: 'a <%= foo("some string") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a some string b'); - cb(); - }); - }); -}); - -describe('helpers integration', function() { - beforeEach(function() { - app = new App(); - app.create('pages'); - app.engine('hbs', require('engine-handlebars')); - app.engine('md', require('engine-base')); - }); - - describe('.helpers()', function() { - it('should add helpers and use them in templates.', function(cb) { - app.helpers({ - upper: function(str) { - return str.toUpperCase(); - } - }); - - app.page('doc.md', {content: 'a <%= upper(name) %> b'}) - .render({name: 'Halle'}, function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a HALLE b'); - cb(); - }); - }); - }); - - describe('helper options:', function() { - it('should expose global options to helpers:', function(cb) { - app.helper('cwd', function(fp) { - return path.join(this.options.cwd, fp); - }); - - app.option('cwd', 'foo/bar'); - - app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a foo/bar/baz b'); - cb(); - }); - }); - - it('should expose helper-specific options to helpers:', function(cb) { - app.helper('cwd', function(fp) { - return path.join(this.options.cwd, fp); - }); - - app.option('helper.cwd', 'foo/bar'); - - app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a foo/bar/baz b'); - cb(); - }); - }); - - it('should prefer helper options over global options:', function(cb) { - app.helper('cwd', function(fp) { - return path.join(this.options.cwd, fp); - }); - - app.option('cwd', 'one/two'); - app.option('helper.cwd', 'foo/bar'); - - app.page('doc.md', {content: 'a <%= cwd("baz") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a foo/bar/baz b'); - cb(); - }); - }); - - it('should expose a `merge` method on context options', function(cb) { - var count = 0; - app.helper('foo', function(str) { - assert.equal(typeof this.options.merge, 'function'); - count++; - return str; - }); - - app.page('doc.md', {content: 'a <%= foo("foo") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a foo b'); - cb(); - }); - }); - - it('should merge the given object onto context options', function(cb) { - var count = 0; - app.helper('foo', function(options) { - this.options.merge(options); - count++; - return this.options.one; - }); - - app.page('doc.md', {content: 'a <%= foo({one: "two"}) %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a two b'); - cb(); - }); - }); - - it('should merge a list of objects onto context options', function(cb) { - var count = 0; - app.helper('foo', function(locals, options) { - this.options.merge(locals, options); - - count++; - return this.options.abc + this.options.one; - }); - - app.page('doc.md', {content: 'a <%= foo({abc: "def"}, {one: "two"}) %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a deftwo b'); - cb(); - }); - }); - - it('should merge the handlebars "hash" object onto context options', function(cb) { - var count = 0; - app.helper('foo', function(options) { - this.options.merge(options); - count++; - return this.options.abc; - }); - - app.page('doc.hbs', {content: 'a {{foo abc="xyz"}} b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a xyz b'); - cb(); - }); - }); - - it('should expose a `get` method on context options', function(cb) { - var count = 0; - app.helper('foo', function(str) { - assert.equal(typeof this.options.get, 'function'); - count++; - return str; - }); - - app.page('doc.md', {content: 'a <%= foo("foo") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a foo b'); - cb(); - }); - }); - - it('should expose a `set` method on context options', function(cb) { - var count = 0; - app.helper('foo', function(str) { - assert.equal(typeof this.options.set, 'function'); - count++; - return str; - }); - - app.page('doc.md', {content: 'a <%= foo("foo") %> b'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(count, 1); - assert.equal(res.content, 'a foo b'); - cb(); - }); - }); - }); - - describe('options.helpers', function() { - it('should register helpers passed on the options:', function(cb) { - app.option({ - helpers: { - upper: function(str) { - return str.toUpperCase(); - }, - foo: function(str) { - return 'foo' + str; - } - } - }); - - app.page('doc.md', {content: 'a <%= upper(name) %> <%= foo("bar") %> b'}) - .render({name: 'Halle'}, function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a HALLE foobar b'); - cb(); - }); - }); - }); - - describe('options.helpers', function() { - it('should add helpers and use them in templates.', function(cb) { - app.options.helpers = { - upper: function(str) { - return str.toUpperCase(); - }, - foo: function(str) { - return 'foo' + str; - } - }; - - app.page('doc.md', {content: 'a <%= upper(name) %> b'}) - .render({name: 'Halle'}, function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'a HALLE b'); - cb(); - }); - }); - }); -}); - -describe('collection helpers', function() { - beforeEach(function() { - app = new App(); - app.create('posts'); - app.create('pages', {engine: 'hbs'}); - app.create('partials', {viewType: 'partial', engine: 'hbs'}); - app.create('snippet', {viewType: 'partial'}); - app.engine('hbs', require('engine-handlebars')); - app.helper('log', function(ctx) { - // console.log(ctx); - }); - }); - - describe('plural', function() { - it('should get the given collection as a block helper', function(cb) { - app.post('a.hbs', {content: 'foo'}); - app.post('b.hbs', {content: 'bar'}); - app.post('c.hbs', {content: 'baz'}); - - app.partial('list.hbs', { - content: '{{#posts}}{{#each items}}{{content}}{{/each}}{{/posts}}' - }); - - app.page('index.hbs', { - content: '{{> list.hbs }}' - }) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'foobarbaz'); - cb(); - }); - }); - - it('should get the given collection as a helper expression', function(cb) { - app.post('a.hbs', {content: 'foo'}); - app.post('b.hbs', {content: 'bar'}); - app.post('c.hbs', {content: 'baz'}); - - app.helper('list', function(list) { - return list.items.map(function(item) { - return '- ' + item.content; - }).join('\n'); - }); - - app.partial('list.hbs', { - content: '{{list (posts)}}' - }); - - app.page('index.hbs', { - content: '{{> list.hbs }}' - }) - .render(function(err, res) { - if (err) return cb(err); - try { - assert.equal(res.content, '- foo\n- bar\n- baz'); - } catch (err) { - return cb(err); - } - cb(); - }); - }); - }); - - describe('single', function() { - it('should get a view from an unspecified collection', function(cb) { - app.post('a.hbs', {content: 'post-a'}); - app.post('b.hbs', {content: 'post-b'}); - - app.page('one', {content: '{{view "a.hbs"}}'}) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.content, 'post-a'); - cb(); - }); - }); - - it('should return an empty string if not found', function(cb) { - app.page('one', {content: '{{view "foo.hbs"}}'}) - .render(function(err, view) { - if (err) return cb(err); - assert.equal(view.content, ''); - cb(); - }); - }); - - it('should handle engine errors', function(cb) { - app.post('foo.hbs', {content: '{{one "two"}}'}); - app.page('one', {content: '{{posts "foo.hbs"}}'}) - .render(function(err) { - assert(err); - assert.equal(typeof err, 'object'); - assert.equal(typeof err.message, 'string'); - assert(/Missing helper: "one"/.test(err.message)); - cb(); - }); - }); - - it('should handle engine errors2', function(cb) { - app.engine('tmpl', require('engine-base')); - app.create('foo', {viewType: 'partial', engine: 'tmpl'}); - app.create('bar', {engine: 'tmpl'}); - - app.foo('a.tmpl', {path: 'a.tmpl', content: '<%= blah.bar %>'}); - app.bar('b.tmpl', {content: '<%= foo("a.tmpl") %>'}) - .render(function(err) { - assert(err); - assert.equal(typeof err, 'object'); - assert(/blah is not defined/.test(err.message)); - cb(); - }); - }); - - it('should work with non-handlebars engine', function(cb) { - app.engine('tmpl', require('engine-base')); - app.create('foo', {engine: 'tmpl'}); - app.create('bar', {engine: 'tmpl'}); - - app.foo('a.tmpl', {content: 'foo-a'}); - app.bar('b.tmpl', {content: 'bar-b'}); - - app.bar('one', {content: '<%= view("a.tmpl", "foos", { render: true }) %>'}) - .render(function(err, foo) { - if (err) return cb(err); - - assert.equal(foo.content, 'foo-a'); - - app.bar('two', {content: '<%= view("b.tmpl", "bars", { render: true }) %>'}) - .render(function(err, bar) { - if (err) return cb(err); - assert.equal(bar.content, 'bar-b'); - cb(); - }); - }); - }); - - it('should render a specific view from the given collection', function(cb) { - app.post('a.hbs', {content: 'post-a'}); - app.post('b.hbs', {content: 'post-b'}); - app.post('c.hbs', {content: 'post-c'}); - app.page('a.hbs', {content: 'page-a'}); - app.page('b.hbs', {content: 'page-b'}); - app.page('c.hbs', {content: 'page-c'}); - - app.page('one', {content: '{{view "a.hbs" "posts" render=true}}'}) - .render(function(err, post) { - if (err) return cb(err); - - assert.equal(post.content, 'post-a'); - - app.page('two', {content: '{{view "b.hbs" "pages" render=true}}'}) - .render(function(err, page) { - if (err) return cb(err); - assert.equal(page.content, 'page-b'); - cb(); - }); - }); - - }); - }); -}); diff --git a/test/integration/instance.js b/test/integration/instance.js deleted file mode 100644 index 2cc22723..00000000 --- a/test/integration/instance.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.extendWith('verb-readme-generator'); - app.helper('example', require('helper-example')); - app.task('default', ['readme']); -}; diff --git a/test/is.js b/test/is.js deleted file mode 100644 index bfac324f..00000000 --- a/test/is.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); - -describe('is', function() { - describe('isApp', function() { - it('should return true if value is an instance of App', function() { - var app = new App(); - assert(App.isApp(app)); - }); - it('should return false if value is not an instance of App', function() { - assert(!App.isApp('foo')); - }); - }); - - describe('isCollection', function() { - it('should return true if value is an instance of Collection', function() { - var collection = new App.Collection(); - assert(App.isCollection(collection)); - }); - it('should return false if value is not an instance of Collection', function() { - assert(!App.isCollection('foo')); - }); - }); - - describe('isViews', function() { - it('should return true if value is an instance of Views', function() { - var collection = new App.Views(); - assert(App.isViews(collection)); - }); - it('should return false if value is not an instance of Views', function() { - assert(!App.isViews('foo')); - }); - }); - - describe('isList', function() { - it('should return true if value is an instance of List', function() { - var collection = new App.List(); - assert(App.isList(collection)); - }); - it('should return false if value is not an instance of List', function() { - assert(!App.isList('foo')); - }); - }); - - describe('isView', function() { - it('should return true if value is an instance of View', function() { - var collection = new App.View(); - assert(App.isView(collection)); - }); - it('should return false if value is not an instance of View', function() { - assert(!App.isView('foo')); - }); - }); - - describe('isItem', function() { - it('should return true if value is an instance of Item', function() { - var collection = new App.Item(); - assert(App.isItem(collection)); - }); - it('should return false if value is not an instance of Item', function() { - assert(!App.isItem('foo')); - }); - }); -}); - diff --git a/test/item.js b/test/item.js deleted file mode 100644 index 82f24f0c..00000000 --- a/test/item.js +++ /dev/null @@ -1,1215 +0,0 @@ -'use strict'; - -require('mocha'); -var should = require('should'); -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var assert = require('assert'); -var es = require('event-stream'); -var Stream = require('stream'); -var support = require('./support'); -var App = support.resolve(); -var Item = App.Item; -var item; - -describe('Item', function() { - describe('instance', function() { - it('should create an instance of Item:', function() { - item = new Item(); - assert(item instanceof Item); - }); - - it('should instantiate without new:', function() { - item = Item(); - assert(item instanceof Item); - }); - - it('inspect should not double name `Stream` when ctor is `Stream`', function(cb) { - var fn = console.log; - var count = 0; - console.log = function(val) { - console.log = fn; - assert.deepEqual(val.inspect(), '>'); - count++; - }; - var val = new Stream(); - item = new Item({contents: val}); - console.log(item); - assert.equal(count, 1); - cb(); - }); - }); - - describe('static methods', function() { - it('should expose `extend`:', function() { - assert.equal(typeof Item.extend, 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - item = new Item(); - }); - - it('should expose `set`:', function() { - assert.equal(typeof item.set, 'function'); - }); - it('should expose `get`:', function() { - assert.equal(typeof item.get, 'function'); - }); - it('should expose `del`:', function() { - assert.equal(typeof item.del, 'function'); - }); - it('should expose `define`:', function() { - assert.equal(typeof item.define, 'function'); - }); - it('should expose `visit`:', function() { - assert.equal(typeof item.visit, 'function'); - }); - }); - - describe('properties', function() { - it('should expose a `_name` property', function() { - item = new Item({}); - assert(item._name); - }); - - it('should use `Item` as the default `_name`', function() { - item = new Item({}); - assert.equal(item._name, 'item'); - }); - - it('should allow `_name` to be set after instantiation', function() { - item = new Item({}); - item._name = 'foo'; - assert.equal(item._name, 'foo'); - }); - - it('should expose an `options` property', function() { - item = new Item({}); - assert.deepEqual(item.options, {}); - assert(item.hasOwnProperty('options')); - }); - - it('should add `options` when passed on the constructor', function() { - item = new Item({options: {foo: 'bar'}}); - assert.equal(item.options.foo, 'bar'); - }); - - it('should expose a `data` property', function() { - item = new Item({app: {}}); - assert.deepEqual(item.data, {}); - assert(item.hasOwnProperty('data')); - }); - - it('should add `data` when passed on the constructor', function() { - item = new Item({data: {foo: 'bar'}}); - assert.equal(item.data.foo, 'bar'); - }); - - it('should add `locals` when passed on the constructor', function() { - item = new Item({locals: {foo: 'bar'}}); - assert.equal(item.locals.foo, 'bar'); - }); - }); - - describe('set', function() { - it('should set properties on the object', function() { - item = new Item(); - item.set('foo', 'bar'); - assert.equal(item.foo, 'bar'); - }); - }); - - describe('get', function() { - it('should get properties from the object', function() { - item = new Item(); - item.set('foo', 'bar'); - assert.equal(item.get('foo'), 'bar'); - }); - }); - - describe('cwd', function() { - it('should get properties from the object', function() { - item = new Item({cwd: 'test/fixtures'}); - assert.equal(item.cwd, 'test/fixtures'); - }); - }); - - describe('clone', function() { - it('should clone the item:', function() { - item = new Item({content: 'foo'}); - item.set({path: 'foo/bar'}); - item.set('options.one', 'two'); - var clone = item.clone(); - assert(clone.contents); - clone.set('baz', 'quux'); - clone.set('options.three', 'four'); - assert.equal(clone.get('foo'), item.get('foo')); - assert.equal(clone.get('baz'), 'quux'); - assert(!item.get('baz')); - // not deep cloned - assert.equal(clone.get('options.three'), 'four'); - assert.equal(item.get('options.three'), 'four'); - }); - - it('should deep clone the entire object', function() { - item = new Item({content: 'foo'}); - item.set({path: 'foo/bar'}); - item.set('options.one', 'two'); - var clone = item.clone({deep: true}); - clone.set('options.three', 'four'); - assert.equal(item.get('options.one'), 'two'); - assert.equal(clone.get('options.one'), 'two'); - assert.equal(clone.get('options.three'), 'four'); - - assert.equal(typeof item.get('options.three'), 'undefined'); - }); - }); - - describe('visit', function() { - it('should visit all properties on an object and call the specified method', function() { - item = new Item(); - var obj = { - foo: 'bar', - bar: 'baz', - baz: 'bang' - }; - item.visit('set', obj); - assert.equal(item.get('foo'), 'bar'); - assert.equal(item.get('bar'), 'baz'); - assert.equal(item.get('baz'), 'bang'); - }); - - it('should visit all properties on all objects in an array and call the specified method', function() { - item = new Item(); - var arr = [{foo: 'bar', bar: 'baz', baz: 'bang'}]; - item.visit('set', arr); - assert.equal(item.get('foo'), 'bar'); - assert.equal(item.get('bar'), 'baz'); - assert.equal(item.get('baz'), 'bang'); - }); - }); -}); - -/** - * The following unit tests are from Vinyl - * Since we inherit vinyl in Item, we need - * to ensure that these still pass. - */ - -describe('Item', function() { - describe('isVinyl()', function() { - it('should return true on a vinyl object', function(cb) { - item = new Item(); - assert.equal(Item.isVinyl(item), true); - cb(); - }); - it('should return false on a normal object', function(cb) { - assert.equal(Item.isVinyl({}), false); - cb(); - }); - it('should return false on a null object', function(cb) { - assert.equal(Item.isVinyl({}), false); - cb(); - }); - }); - - describe('constructor()', function() { - it('should default cwd to process.cwd', function(cb) { - item = new Item(); - item.cwd.should.equal(process.cwd()); - cb(); - }); - - it('should default base to cwd', function(cb) { - var cwd = '/'; - item = new Item({cwd: cwd}); - item.base.should.equal(cwd); - cb(); - }); - - it('should default base to cwd even when none is given', function(cb) { - item = new Item(); - item.base.should.equal(process.cwd()); - cb(); - }); - - it('should default path to null', function(cb) { - item = new Item(); - should.not.exist(item.path); - cb(); - }); - - it('should default history to []', function(cb) { - item = new Item(); - item.history.should.eql([]); - cb(); - }); - - it('should default stat to null', function(cb) { - item = new Item(); - should.not.exist(item.stat); - cb(); - }); - - it('should default contents to null', function(cb) { - item = new Item(); - should.not.exist(item.contents); - cb(); - }); - - it('should set base to given value', function(cb) { - var val = '/'; - item = new Item({base: val}); - item.base.should.equal(val); - cb(); - }); - - it('should set cwd to given value', function(cb) { - var val = '/'; - item = new Item({cwd: val}); - item.cwd.should.equal(val); - cb(); - }); - - it('should set path to given value', function(cb) { - var val = '/test.coffee'; - item = new Item({path: val}); - item.path.should.equal(val); - item.history.should.eql([val]); - cb(); - }); - - it('should set history to given value', function(cb) { - var val = '/test.coffee'; - item = new Item({history: [val]}); - item.path.should.equal(val); - item.history.should.eql([val]); - cb(); - }); - - it('should set stat to given value', function(cb) { - var val = {}; - item = new Item({stat: val}); - item.stat.should.equal(val); - cb(); - }); - - it('should set contents to given value', function(cb) { - var val = new Buffer('test'); - item = new Item({contents: val}); - item.contents.should.equal(val); - cb(); - }); - }); - - describe('isBuffer()', function() { - it('should return true when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - item = new Item({contents: val}); - item.isBuffer().should.equal(true); - cb(); - }); - - it('should return false when the contents are a Stream', function(cb) { - var val = new Stream(); - var item = new Item({contents: val}); - item.isBuffer().should.equal(false); - cb(); - }); - - it('should return false when the contents are a null', function(cb) { - var item = new Item({contents: null}); - item.isBuffer().should.equal(false); - cb(); - }); - }); - - describe('isStream()', function() { - it('should return false when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var item = new Item({contents: val}); - item.isStream().should.equal(false); - cb(); - }); - - it('should return true when the contents are a Stream', function(cb) { - var val = new Stream(); - var item = new Item({contents: val}); - item.isStream().should.equal(true); - cb(); - }); - - it('should return false when the contents are a null', function(cb) { - var item = new Item({contents: null}); - item.isStream().should.equal(false); - cb(); - }); - }); - - describe('isNull()', function() { - it('should return false when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var item = new Item({contents: val}); - item.isNull().should.equal(false); - cb(); - }); - - it('should return false when the contents are a Stream', function(cb) { - var val = new Stream(); - var item = new Item({contents: val}); - item.isNull().should.equal(false); - cb(); - }); - - it('should return true when the contents are a null', function(cb) { - var item = new Item({contents: null}); - item.isNull().should.equal(true); - cb(); - }); - }); - - describe('isDirectory()', function() { - var fakeStat = { - isDirectory: function() { - return true; - } - }; - - it('should return false when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var item = new Item({contents: val, stat: fakeStat}); - item.isDirectory().should.equal(false); - cb(); - }); - - it('should return false when the contents are a Stream', function(cb) { - var val = new Stream(); - var item = new Item({contents: val, stat: fakeStat}); - item.isDirectory().should.equal(false); - cb(); - }); - - it('should return true when the contents are a null', function(cb) { - var item = new Item({contents: null, stat: fakeStat}); - item.isDirectory().should.equal(true); - cb(); - }); - }); - - describe('clone()', function() { - it('should copy all attributes over with Buffer', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Buffer('test') - }; - var item = new Item(options); - var item2 = item.clone(); - - item2.should.not.equal(item, 'refs should be different'); - item2.cwd.should.equal(item.cwd); - item2.base.should.equal(item.base); - item2.path.should.equal(item.path); - item2.contents.should.not.equal(item.contents, 'buffer ref should be different'); - item2.contents.toString('utf8').should.equal(item.contents.toString('utf8')); - cb(); - }); - - it('should copy buffer\'s reference with option contents: false', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.js', - contents: new Buffer('test') - }; - - var item = new Item(options); - - var copy1 = item.clone({ contents: false }); - copy1.contents.should.equal(item.contents); - - var copy2 = item.clone({}); - copy2.contents.should.not.equal(item.contents); - - var copy3 = item.clone({ contents: 'any string' }); - copy3.contents.should.not.equal(item.contents); - - cb(); - }); - - it('should copy all attributes over with Stream', function(cb) { - var contents = new Stream.PassThrough(); - - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: contents - }); - - var item2 = item.clone(); - contents.write(new Buffer('wa')); - - process.nextTick(function() { - contents.write(new Buffer('dup')); - contents.end(); - }); - - item2.should.not.equal(item, 'refs should be different'); - item2.cwd.should.equal(item.cwd); - item2.base.should.equal(item.base); - item2.path.should.equal(item.path); - item2.contents.should.not.equal(item.contents, 'stream ref should not be the same'); - item.contents.pipe(es.wait(function(err, data) { - if (err) return cb(err); - item2.contents.pipe(es.wait(function(err, data2) { - if (err) return cb(err); - data2.should.not.equal(data, 'stream contents ref should not be the same'); - data2.should.eql(data, 'stream contents should be the same'); - })); - })); - cb(); - }); - - it('should copy all attributes over with null', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - var item = new Item(options); - var item2 = item.clone(); - - item2.should.not.equal(item, 'refs should be different'); - item2.cwd.should.equal(item.cwd); - item2.base.should.equal(item.base); - item2.path.should.equal(item.path); - should.not.exist(item2.contents); - cb(); - }); - - it('should properly clone the `stat` property', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.js', - contents: new Buffer('test'), - stat: fs.statSync(__filename) - }; - - var item = new Item(options); - var copy = item.clone(); - - assert(copy.stat.isFile()); - assert(!copy.stat.isDirectory()); - cb(); - }); - - it('should properly clone the `history` property', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.js', - contents: new Buffer('test'), - stat: fs.statSync(__filename) - }; - - var item = new Item(options); - var copy = item.clone(); - - copy.history[0].should.equal(options.path); - copy.path = 'lol'; - item.path.should.not.equal(copy.path); - cb(); - }); - - it('should copy custom properties', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - - var item = new Item(options); - item.custom = { a: 'custom property' }; - var item2 = item.clone(); - - item2.should.not.equal(item, 'refs should be different'); - item2.cwd.should.equal(item.cwd); - item2.base.should.equal(item.base); - item2.path.should.equal(item.path); - item2.custom.should.equal(item.custom); - item2.custom.a.should.equal(item.custom.a); - - cb(); - }); - - it('should copy history', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - - var item = new Item(options); - item.path = '/test/test.js'; - item.path = '/test/test-938di2s.js'; - var item2 = item.clone(); - - item2.history.should.eql([ - '/test/test.coffee', - '/test/test.js', - '/test/test-938di2s.js' - ]); - item2.history.should.not.equal([ - '/test/test.coffee', - '/test/test.js', - '/test/test-938di2s.js' - ]); - item2.path.should.eql('/test/test-938di2s.js'); - - cb(); - }); - - it('should copy all attributes deeply', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - - var item = new Item(options); - item.custom = { a: 'custom property' }; - - var item2 = item.clone(true); - item2.custom.should.eql(item.custom); - item2.custom.should.not.equal(item.custom); - item2.custom.a.should.equal(item.custom.a); - - var item3 = item.clone({ deep: true }); - item3.custom.should.eql(item.custom); - item3.custom.should.not.equal(item.custom); - item3.custom.a.should.equal(item.custom.a); - - var item4 = item.clone(false); - item4.custom.should.eql(item.custom); - item4.custom.should.equal(item.custom); - item4.custom.a.should.equal(item.custom.a); - - var item5 = item.clone({ deep: false }); - item5.custom.should.eql(item.custom); - item5.custom.should.equal(item.custom); - item5.custom.a.should.equal(item.custom.a); - - cb(); - }); - }); - - describe('pipe()', function() { - it('should write to stream with Buffer', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Buffer('test') - }; - var item = new Item(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(options.contents.toString('utf8')); - }); - stream.on('end', function() { - cb(); - }); - var ret = item.pipe(stream); - ret.should.equal(stream, 'should return the stream'); - }); - - it('should pipe to stream with Stream', function(cb) { - var testChunk = new Buffer('test'); - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Stream.PassThrough() - }; - var item = new Item(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - cb(); - }); - var ret = item.pipe(stream); - ret.should.equal(stream, 'should return the stream'); - - item.contents.write(testChunk); - }); - - it('should do nothing with null', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - var item = new Item(options); - var stream = new Stream.PassThrough(); - stream.on('data', function() { - throw new Error('should not write'); - }); - stream.on('end', function() { - cb(); - }); - var ret = item.pipe(stream); - ret.should.equal(stream, 'should return the stream'); - }); - - it('should write to stream with Buffer', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Buffer('test') - }; - var item = new Item(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(options.contents.toString('utf8')); - cb(); - }); - stream.on('end', function() { - throw new Error('should not end'); - }); - var ret = item.pipe(stream, {end: false}); - ret.should.equal(stream, 'should return the stream'); - }); - - it('should pipe to stream with Stream', function(cb) { - var testChunk = new Buffer('test'); - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Stream.PassThrough() - }; - var item = new Item(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - cb(); - }); - stream.on('end', function() { - throw new Error('should not end'); - }); - var ret = item.pipe(stream, {end: false}); - ret.should.equal(stream, 'should return the stream'); - - item.contents.write(testChunk); - }); - - it('should do nothing with null', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - var item = new Item(options); - var stream = new Stream.PassThrough(); - stream.on('data', function() { - throw new Error('should not write'); - }); - stream.on('end', function() { - throw new Error('should not end'); - }); - var ret = item.pipe(stream, {end: false}); - ret.should.equal(stream, 'should return the stream'); - process.nextTick(cb); - }); - }); - - describe('inspect()', function() { - it('should return correct format when no contents and no path', function(cb) { - var item = new Item(); - assert.equal(item.inspect(), ''); - cb(); - }); - - it('should update the name when `_name` is defined', function(cb) { - var item = new Item(); - item._name = 'Foo'; - assert.equal(item.inspect(), ''); - cb(); - }); - - it('should not add duplicate `Stream` name to inspect name', function(cb) { - function PassThroughStream() { - Stream.PassThrough.apply(this, arguments); - } - util.inherits(PassThroughStream, Stream.PassThrough); - var contents = new PassThroughStream(); - - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: contents - }); - - contents.write(new Buffer('foo')); - assert.equal(item.inspect(), '>'); - cb(); - }); - - it('should return correct format when Buffer and no path', function(cb) { - var val = new Buffer('test'); - var item = new Item({ - contents: val - }); - assert.equal(item.inspect(), '>'); - cb(); - }); - - it('should return correct format when Buffer and relative path', function(cb) { - var val = new Buffer('test'); - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: val - }); - assert.equal(item.inspect(), '>'); - cb(); - }); - - it('should return correct format when Buffer and only path and no base', function(cb) { - var val = new Buffer('test'); - var item = new Item({ - cwd: '/', - path: '/test/test.coffee', - contents: val - }); - delete item.base; - assert.equal(item.inspect(), '>'); - cb(); - }); - - it('should return correct format when Stream and relative path', function(cb) { - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Stream.PassThrough() - }); - assert.equal(item.inspect(), '>'); - cb(); - }); - - it('should return correct format when null and relative path', function(cb) { - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }); - assert.equal(item.inspect(), ''); - cb(); - }); - }); - - describe('contents get/set', function() { - it('should work with Buffer', function(cb) { - var val = new Buffer('test'); - var item = new Item(); - item.contents = val; - item.contents.should.equal(val); - cb(); - }); - - it('should work with Stream', function(cb) { - var val = new Stream.PassThrough(); - var item = new Item(); - item.contents = val; - item.contents.should.equal(val); - cb(); - }); - - it('should work with null', function(cb) { - var val = null; - var item = new Item(); - item.contents = val; - assert.equal(item.contents, null); - cb(); - }); - - it('should work with string', function(cb) { - var val = 'test'; - var item = new Item(); - item.contents = val; - assert.deepEqual(item.contents, new Buffer(val)); - cb(); - }); - }); - - describe('relative get/set', function() { - it('should error on set', function(cb) { - var item = new Item(); - try { - item.relative = 'test'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should error on get when no base', function(cb) { - var item = new Item(); - delete item.base; - try { - item.relative; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should error on get when no path', function(cb) { - var item = new Item(); - try { - item.relative; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return a relative path from base', function(cb) { - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.relative.should.equal('test.coffee'); - cb(); - }); - - it('should return a relative path from cwd', function(cb) { - var item = new Item({ - cwd: '/', - path: '/test/test.coffee' - }); - item.relative.should.equal(path.join('test', 'test.coffee')); - cb(); - }); - }); - - describe('dirname get/set', function() { - it('should error on get when no path', function(cb) { - var item = new Item(); - try { - item.dirname; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return the dirname of the path', function(cb) { - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.dirname.should.equal('/test'); - cb(); - }); - - it('should error on set when no path', function(cb) { - var item = new Item(); - try { - item.dirname = '/test'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the dirname of the path', function(cb) { - var item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.dirname = '/test/foo'; - item.path.should.equal('/test/foo/test.coffee'); - cb(); - }); - }); - - describe('stem', function() { - it('should error on get when no path', function(cb) { - item = new Item(); - try { - item.stem; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the stem of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.stem = 'foo'; - item.path.should.equal('/test/foo.coffee'); - cb(); - }); - - it('should get the stem of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.stem.should.equal('test'); - cb(); - }); - - it('should error on set when no path', function(cb) { - item = new Item(); - try { - item.stem = 'test.coffee'; - } catch (err) { - should.exist(err); - cb(); - } - }); - }); - - describe('filename', function() { - it('should error on get when no path', function(cb) { - item = new Item(); - try { - item.filename; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the filename of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.filename = 'foo'; - item.path.should.equal('/test/foo.coffee'); - cb(); - }); - - it('should get the filename of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.filename.should.equal('test'); - cb(); - }); - - it('should stay synchronized with stem', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - assert.equal(item.filename, item.stem); - item.stem = 'foo'; - assert.equal(item.filename, 'foo'); - item.filename = 'bar'; - assert.equal(item.stem, 'bar'); - cb(); - }); - - it('should error on set when no path', function(cb) { - item = new Item(); - try { - item.filename = 'test.coffee'; - } catch (err) { - should.exist(err); - cb(); - } - }); - }); - - describe('basename', function() { - it('should error on get when no path', function(cb) { - item = new Item(); - try { - item.basename; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the basename of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.basename = 'foo.png'; - item.path.should.equal('/test/foo.png'); - cb(); - }); - - it('should get the basename of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.basename.should.equal('test.coffee'); - cb(); - }); - - it('should error on set when no path', function(cb) { - item = new Item(); - try { - item.basename = 'test.coffee'; - } catch (err) { - should.exist(err); - cb(); - } - }); - }); - - describe('extname', function() { - it('should error on get when no path', function(cb) { - item = new Item(); - try { - item.extname; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return the extname of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.extname.should.equal('.coffee'); - cb(); - }); - - it('should error on set when no path', function(cb) { - item = new Item(); - try { - item.extname = '.coffee'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the extname of the path', function(cb) { - item = new Item({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - item.extname = '.png'; - item.path.should.equal('/test/test.png'); - cb(); - }); - }); - - describe('path get/set', function() { - - it('should record history when instantiation', function() { - var item = new Item({ - cwd: '/', - path: '/test/test.coffee' - }); - - item.path.should.eql('/test/test.coffee'); - item.history.should.eql(['/test/test.coffee']); - }); - - it('should record history when path change', function() { - var item = new Item({ - cwd: '/', - path: '/test/test.coffee' - }); - - item.path = '/test/test.js'; - item.path.should.eql('/test/test.js'); - item.history.should.eql(['/test/test.coffee', '/test/test.js']); - - item.path = '/test/test.coffee'; - item.path.should.eql('/test/test.coffee'); - item.history.should.eql(['/test/test.coffee', '/test/test.js', '/test/test.coffee']); - }); - - it('should not record history when set the same path', function() { - var item = new Item({ - cwd: '/', - path: '/test/test.coffee' - }); - - item.path = '/test/test.coffee'; - item.path = '/test/test.coffee'; - item.path.should.eql('/test/test.coffee'); - item.history.should.eql(['/test/test.coffee']); - - // ignore when set empty string - item.path = ''; - item.path.should.eql('/test/test.coffee'); - item.history.should.eql(['/test/test.coffee']); - }); - - it('should throw when set path null in constructor', function() { - (function() { - Item({ - cwd: '/', - path: null - }); - }).should.throw('path should be string'); - }); - - it('should throw when set path null', function() { - item = new Item({ - cwd: '/', - path: 'foo' - }); - - (function() { - item.path = null; - }).should.throw('path should be string'); - }); - }); -}); diff --git a/test/layouts.js b/test/layouts.js deleted file mode 100644 index b14cdea9..00000000 --- a/test/layouts.js +++ /dev/null @@ -1,238 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var each = require('async-each'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('layouts', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('layout', { viewType: 'layout' }); - app.create('partial', { viewType: 'partial' }); - app.create('page'); - }); - - it('should add a layout to a view:', function(cb) { - app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); - app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should use a "default" layout defined on global options', function(cb) { - app.option('layout', 'base'); - - app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); - app.page('a.tmpl', {path: 'a.tmpl', content: 'b'}) - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should use a "default" layout defined on collection options', function(cb) { - app.pages.option('layout', 'base'); - - app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); - app.page('a.tmpl', {path: 'a.tmpl', content: 'b'}) - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should use the "default" layout on layouts', function(cb) { - app.option('layout', 'base'); - - app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); - var foo = app.layout('foo.tmpl', {content: 'b'}); - - app.render(foo, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should not use the "default" layout on partials', function(cb) { - app.option('layout', 'base'); - - app.partial('foo.tmpl', {content: 'c'}); - app.layout('base.tmpl', {content: 'a {% body %} d'}); - - app.page('a.tmpl', {path: 'a.tmpl', content: 'b <%= partial("foo") %>'}) - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c d'); - cb(); - }); - }); - - it('should add a layout to a partial when defined on a partial', function(cb) { - app.option('layout', 'base'); - - app.partial('foo.tmpl', {content: 'c', layout: 'base'}); - app.layout('base.tmpl', {content: 'a {% body %} d'}); - - app.page('a.tmpl', {path: 'a.tmpl', content: 'b <%= partial("foo") %>'}) - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b a c d d'); - cb(); - }); - }); - - it('should add a layout to a layout when defined on a layout', function(cb) { - app.option('layout', 'base'); - - app.partial('foo.tmpl', {content: 'c'}); - app.layout('default.tmpl', {content: 'x {% body %} z'}); - app.layout('base.tmpl', {content: 'a {% body %} d', layout: 'default'}); - - app.page('a.tmpl', {path: 'a.tmpl', content: 'b <%= partial("foo") %>'}) - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'x a b c d z'); - cb(); - }); - }); - - it('should not add a layout when `layoutApplied` is set:', function(cb) { - app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c'}); - app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); - var page = app.pages.getView('a.tmpl'); - page.option('layoutApplied', true); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'b'); - cb(); - }); - }); - - it('should not apply a layout to itself:', function(cb) { - app.layout('base', {path: 'base.tmpl', content: 'a {% body %} c', layout: 'base'}); - app.pages('a.tmpl', {path: 'a.tmpl', content: 'b', layout: 'base'}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should apply nested layouts to a view:', function(cb) { - app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); - app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); - app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); - app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); - - app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); - var page = app.pages.getView('z.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'outter c b a inner a b c outter'); - cb(); - }); - }); - - it('should apply nested layouts to multiple views when layout is defined on data property:', function(cb) { - app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', data: { layout: 'b' }}); - app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', data: { layout: 'c' }}); - app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', data: { layout: 'base' }}); - app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); - - app.pages('x.tmpl', {path: 'x.tmpl', content: 'x inner x', data: { layout: 'a' }}); - app.pages('y.tmpl', {path: 'y.tmpl', content: 'y inner y', data: { layout: 'a' }}); - app.pages('z.tmpl', {path: 'z.tmpl', content: 'z inner z', data: { layout: 'a' }}); - - each(['x', 'y', 'z'], function(key, next) { - var page = app.pages.getView(key + '.tmpl'); - app.render(page, function(err, view) { - if (err) return next(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'outter c b a ' + key + ' inner ' + key + ' a b c outter'); - next(); - }); - }, cb); - }); - - it('should track layout stack history on `layoutStack`:', function(cb) { - app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); - app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); - app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); - app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); - - app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); - var page = app.pages.getView('z.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert(view.layoutStack.length === 4); - assert(typeof view.layoutStack[0] === 'object'); - assert(typeof view.layoutStack[0].depth === 'number'); - cb(); - }); - }); - - it('should track layout stack history on `layoutStack`:', function(cb) { - app.layout('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); - app.layout('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); - app.layout('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); - app.layout('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); - - app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); - var page = app.pages.getView('z.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'outter c b a inner a b c outter'); - cb(); - }); - }); - - it('should get layouts from `layout` viewTypes:', function(cb) { - app.create('section', { viewType: 'layout' }); - app.create('block', { viewType: 'layout' }); - - app.section('a', {path: 'a.tmpl', content: 'a {% body %} a', layout: 'b'}); - app.block('b', {path: 'b.tmpl', content: 'b {% body %} b', layout: 'c'}); - app.section('c', {path: 'c.tmpl', content: 'c {% body %} c', layout: 'base'}); - app.block('base', {path: 'base.tmpl', content: 'outter {% body %} outter'}); - - app.pages('z.tmpl', {path: 'a.tmpl', content: 'inner', layout: 'a'}); - var page = app.pages.getView('z.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'outter c b a inner a b c outter'); - cb(); - }); - }); -}); diff --git a/test/list.deleteItem.js b/test/list.deleteItem.js deleted file mode 100644 index 8a83310e..00000000 --- a/test/list.deleteItem.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var list; - -describe('list.deleteItem', function() { - beforeEach(function() { - list = new List(); - }); - - it('should delete an item from `items` when the key is passed', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - - var a = list.getItem('a'); - list.deleteItem(a); - assert(list.items.length === 2); - - var c = list.getItem('c'); - list.deleteItem(c); - assert(list.items.length === 1); - }); - - it('should delete an item when the item instance is passed', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - - var a = list.getItem('a'); - list.deleteItem(a); - assert(list.keys.length === 2); - }); - - it('should delete the key from `keys` when an item is deleted', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - - var a = list.getItem('a'); - list.deleteItem(a); - assert(list.keys.length === 2); - - list.deleteItem('c'); - assert(list.keys.length === 1); - - assert(list.items[0].key === 'b'); - }); - - it('should remove an item from `items` by key', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - list.deleteItem('c'); - assert(list.items.length === 2); - list.deleteItem('b'); - assert(list.items[0].key === 'a'); - }); - - it('should do nothing when the item does not exist', function() { - assert(list.deleteItem('slfjslslks')); - }); -}); - diff --git a/test/list.getIndex.js b/test/list.getIndex.js deleted file mode 100644 index 362b4177..00000000 --- a/test/list.getIndex.js +++ /dev/null @@ -1,105 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var list; - -describe('list.getIndex', function() { - beforeEach(function() { - list = new List(); - list.items = []; - }); - - it('should get the index of a key when key is not renamed', function() { - list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); - list.addItem('a/b/c/eee.hbs', {content: 'eee'}); - assert.equal(list.getIndex('a/b/c/ddd.hbs'), 0); - assert.equal(list.getIndex('a/b/c/eee.hbs'), 1); - }); - - it('should get the index by path', function() { - list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd'}); - assert.equal(list.getIndex('a/b/c/d.md'), 0); - }); - - it('should get the index by relative path', function() { - list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); - assert.equal(list.getIndex('c/d.md'), 0); - }); - - it('should get the index by stem', function() { - list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); - assert.equal(list.getIndex('d'), 0); - }); - - it('should get the index by basename', function() { - list.addItem('a/b/c/d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); - assert.equal(list.getIndex('d.md'), 0); - }); - - it('should get the index by key', function() { - list.addItem('d.md', {path: 'a/b/c/d.md', content: 'ddd', base: 'a/b'}); - list.getItem('d.md').key = 'foo'; - assert.equal(list.getIndex('foo'), 0); - }); - - it('should get the index of a key for dotfiles', function() { - list.addItem('.gitignore', {content: 'ddd'}); - assert.equal(list.getIndex('.gitignore'), 0); - }); - - it('should throw an error when argument is undefined', function(cb) { - try { - list.getIndex(); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'expected a string or instance of Item'); - cb(); - } - }); - - it('should return `-1` when the item is not found', function() { - assert.equal(list.getIndex('flflflfl'), -1); - }); - - it('should get the correct index for dotfiles', function() { - list.addItem('.DS_Store', {content: '...'}); - list.addItem('.gitignore', {content: 'ddd'}); - list.addItem('.zzz', {content: '...'}); - assert.equal(list.getIndex('.gitignore'), 1); - }); - - it('should get the correct index for dotfiles by their extensions', function() { - list.addItem('a/b/c/.DS_Store', {content: '...'}); - list.addItem('a/b/c/.gitignore', {content: 'ddd'}); - list.addItem('a/b/c/.zzz', {content: '...'}); - assert.equal(list.getIndex('.gitignore'), 1); - }); - - it('should get the correct index for dotfiles by their paths', function() { - list.addItem('a/b/c/.DS_Store', {content: '...'}); - list.addItem('a/b/c/.gitignore', {content: 'ddd'}); - list.addItem('a/b/c/.zzz', {content: '...'}); - assert.equal(list.getIndex('a/b/c/.gitignore'), 1); - }); - - it('should get the index of a key when key is renamed', function() { - list = new List({ - renameKey: function(key) { - return path.basename(key); - } - }); - list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); - list.addItem('a/b/c/eee.hbs', {content: 'eee'}); - assert.equal(list.getIndex('a/b/c/ddd.hbs'), 0); - assert.equal(list.getIndex('ddd.hbs'), 0); - assert.equal(list.getIndex('a/b/c/eee.hbs'), 1); - assert.equal(list.getIndex('eee.hbs'), 1); - }); -}); - diff --git a/test/list.js b/test/list.js deleted file mode 100644 index eaab1cbf..00000000 --- a/test/list.js +++ /dev/null @@ -1,715 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var get = require('get-value'); -var assert = require('assert'); -var each = require('async-each'); -var typeOf = require('kind-of'); -var support = require('./support/'); -var isBuffer = require('is-buffer'); -assert.containEql = support.containEql; -var App = support.resolve(); -var List = App.List; -var Views = App.Views; -var list, views; - -describe('list', function() { - describe('constructor', function() { - it('should create an instance of List', function() { - var list = new List(); - assert(list instanceof List); - }); - - it('should instaniate without `new`', function() { - var list = List(); - assert(list instanceof List); - }); - }); - - describe('static methods', function() { - it('should expose `extend`', function() { - assert(typeof List.extend === 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - list = new List(); - }); - - var methods = [ - 'use', - 'setItem', - 'addItem', - 'addItems', - 'addList', - 'getItem', - 'constructor', - 'set', - 'get', - 'del', - 'define', - 'visit', - 'on', - 'once', - 'off', - 'emit', - 'listeners', - 'hasListeners' - ]; - - methods.forEach(function(method) { - it('should expose the ' + method + ' method', function() { - assert(typeof list[method] === 'function'); - }); - }); - - it('should expose the isList property', function() { - assert(typeof list.isList === 'boolean'); - }); - - it('should expose the keys property', function() { - assert(Array.isArray(list.keys)); - }); - - it('should expose the queue property', function() { - assert(Array.isArray(list.queue)); - }); - - it('should expose the items property', function() { - assert(Array.isArray(list.items)); - }); - - it('should expose the options property', function() { - assert(typeOf(list.options) === 'object'); - }); - }); - - describe('instance', function() { - beforeEach(function() { - list = new List(); - }); - - it('should set a value on the instance', function() { - list.set('a', 'b'); - assert(list.a === 'b'); - }); - - it('should get a value from the instance', function() { - list.set('a', 'b'); - assert(list.get('a') === 'b'); - }); - }); - - describe('use', function() { - beforeEach(function() { - list = new List(); - }); - - it('should expose the instance to plugins', function() { - list - .use(function(inst) { - inst.foo = 'bar'; - }); - - assert(list.foo === 'bar'); - }); - - it('should expose `item` when the plugin returns a function', function() { - list - .use(function() { - return function(item) { - item.foo = 'bar'; - }; - }); - - list.addItem('aaa'); - list.addItem('bbb'); - list.addItem('ccc'); - - assert(list.items[0].foo === 'bar'); - assert(list.items[1].foo === 'bar'); - assert(list.items[2].foo === 'bar'); - }); - }); - - describe('addItem', function() { - beforeEach(function() { - list = new List(); - }); - - it('should add items to a list', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - }); - }); - - describe('deleteItem', function() { - beforeEach(function() { - list = new List(); - }); - - it('should remove an item from `items`', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - var a = list.getItem('a'); - list.deleteItem(a); - assert(list.items.length === 2); - var c = list.getItem('c'); - list.deleteItem(c); - assert(list.items[0].key === 'b'); - }); - - it('should remove an item from `items` by key', function() { - list.addItem('a', {content: '...'}); - list.addItem('b', {content: '...'}); - list.addItem('c', {content: '...'}); - assert(list.items.length === 3); - list.deleteItem('c'); - assert(list.items.length === 2); - list.deleteItem('b'); - assert(list.items[0].key === 'a'); - }); - }); - - describe('addItems', function() { - beforeEach(function() { - list = new List(); - }); - - it('should add an object with multiple items', function() { - list.addItems({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - assert(isBuffer(list.items[0].contents)); - assert(isBuffer(list.items[1].contents)); - }); - - it('should signal `loaded` when finished (addItems)', function() { - list.on('addItems', function(items) { - for (var key in items) { - if (key === 'c') { - list.loaded = true; - break; - } - list.addItem('foo/' + key, items[key]); - } - }); - - list.addItems({ - a: {path: 'a.txt'}, - b: {path: 'b.txt'}, - c: {path: 'c.txt'} - }); - - assert.equal(list.items.length, 2); - assert.equal(list.items[0].key, 'foo/a'); - assert.equal(list.items[0].path, 'a.txt'); - }); - }); - - describe('addList', function() { - beforeEach(function() { - list = new List(); - }); - - it('should add an array with multiple items', function() { - list.addList([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - assert(isBuffer(list.items[0].contents)); - assert(isBuffer(list.items[1].contents)); - }); - - it('should take a callback on `addList`', function() { - function addContents(item) { - item.contents = new Buffer(item.path.charAt(0)); - } - - list.addList([ - { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } } - ], addContents); - - assert(isBuffer(list.items[0].contents)); - assert(isBuffer(list.items[1].contents)); - assert(isBuffer(list.items[2].contents)); - }); - - it('should throw an error when the list is not an array', function() { - function addContents(item) { - item.contents = new Buffer(item.path.charAt(0)); - } - - (function() { - list.addList({ - 'a.md': {locals: { date: '2014-01-01', foo: 'zzz', bar: 1 }}, - 'f.md': {locals: { date: '2014-01-01', foo: 'mmm', bar: 2 }}, - 'd.md': {locals: { date: '2014-01-01', foo: 'xxx', bar: 3 }} - }, addContents); - - assert(isBuffer(list.items[0].contents)); - assert(isBuffer(list.items[1].contents)); - assert(isBuffer(list.items[2].contents)); - }).should.throw('expected list to be an array.'); - }); - - it('should signal `loaded` when finished (addList)', function() { - list.on('addList', function(items) { - var len = items.length; - var i = -1; - - while (++i < len) { - if (items[i].path === 'd.md') { - list.loaded = true; - break; - } - list.addItem('foo/' + items[i].path, items[i]); - } - }); - - list.addList([ - { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } } - ]); - - assert.equal(list.items.length, 2); - assert.equal(list.keys.indexOf('d.md'), -1); - }); - }); - - describe('queue', function() { - beforeEach(function() { - list = new List(); - }); - - it('should emit arguments on addItem', function(cb) { - list.on('addItem', function(args) { - assert(args[0] === 'a'); - assert(args[1] === 'b'); - assert(args[2] === 'c'); - assert(args[3] === 'd'); - assert(args[4] === 'e'); - cb(); - }); - - list.addItem('a', 'b', 'c', 'd', 'e'); - }); - - it('should expose the `queue` property for loading items', function() { - list.queue.push(list.item('b', {path: 'b'})); - - list.addItem('a', {path: 'a'}); - assert(list.items[0].key === 'a'); - assert(list.items[1].key === 'b'); - }); - - it('should load all items on the queue when addItem is called', function() { - list.on('addItem', function(args) { - var len = args.length; - var last = args[len - 1]; - if (typeof last === 'string') { - args[len - 1] = { content: last }; - } - }); - - list.addItem('a.html', 'aaa'); - list.addItem('b.html', 'bbb'); - list.addItem('c.html', 'ccc'); - - assert(list.items[0].path === 'a.html'); - assert(list.getItem('a.html').content === 'aaa'); - assert(list.items[1].path === 'b.html'); - assert(list.getItem('b.html').content === 'bbb'); - assert(list.items[2].path === 'c.html'); - assert(list.getItem('c.html').content === 'ccc'); - }); - }); - - describe('sortBy', function() { - var items = [ - { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } - ]; - - it('should sort a list', function() { - list = new List(); - list.addList(items); - - var compare = function(prop) { - return function(a, b, fn) { - var valA = get(a, prop); - var valB = get(b, prop); - return fn(valA, valB); - }; - }; - - var res = list.sortBy('locals.date', 'doesnt.exist', [ - compare('locals.foo'), - compare('locals.bar') - ]); - - assert.containEql(res.items, [ - { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, - { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } } - ]); - }); - - it('should not sort the (original) instance list `items`', function() { - list = new List(); - list.addList(items); - - var compare = function(prop) { - return function(a, b, fn) { - var valA = get(a, prop); - var valB = get(b, prop); - return fn(valA, valB); - }; - }; - - var res = list.sortBy('locals.date', 'doesnt.exist', [ - compare('locals.foo'), - compare('locals.bar') - ]); - - // should not be sorted - assert.containEql(list.items, items); - - // should be sorted - assert.containEql(res.items, [ - { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, - { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } } - ]); - }); - - it('should pass options to array-sort from the constructor', function() { - list = new List({sort: {reverse: true}}); - list.addList(items); - - var compare = function(prop) { - return function(a, b, fn) { - var valA = get(a, prop); - var valB = get(b, prop); - return fn(valA, valB); - }; - }; - - var res = list.sortBy('locals.date', 'doesnt.exist', [ - compare('locals.foo'), - compare('locals.bar') - ]); - - assert.containEql(res.items, [ - { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, - { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } } - ]); - }); - - it('should pass options to array-sort from the sortBy method', function() { - list = new List(); - list.addList(items); - - var compare = function(prop) { - return function(a, b, fn) { - var valA = get(a, prop); - var valB = get(b, prop); - return fn(valA, valB); - }; - }; - - var res = list.sortBy('locals.date', 'doesnt.exist', [ - compare('locals.foo'), - compare('locals.bar') - ], {reverse: true}); - - assert.containEql(res.items, [ - { key: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { key: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { key: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { key: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, - { key: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { key: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { key: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { key: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { key: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { key: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { key: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { key: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { key: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } } - ]); - }); - }); - - describe('groupBy', function() { - var items = [ - { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { path: 'm.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { path: 'b.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } - ]; - - it('should group a list by a property', function() { - list = new List(); - list.addList(items); - - var res = list.groupBy('locals.foo'); - var keys = ['zzz', 'mmm', 'xxx', 'aaa', 'ccc', 'rrr', 'ttt', 'yyy']; - assert.deepEqual(Object.keys(res), keys); - }); - - it('should group a collection by a property', function() { - list = new List(); - list.addList(items); - - var views = new Views(list); - var res = views.groupBy('locals.foo'); - var keys = ['zzz', 'mmm', 'xxx', 'aaa', 'ccc', 'rrr', 'ttt', 'yyy']; - assert.deepEqual(Object.keys(res), keys); - }); - }); - - describe('sort and group', function() { - var items = [ - { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 3 } }, - { path: 'i.md', locals: { date: '2013-02-01', foo: 'xxx', bar: 5 } }, - { path: 'i.md', locals: { date: '2013-02-01', foo: 'lll', bar: 5 } }, - { path: 'k.md', locals: { date: '2013-03-01', foo: 'xxx', bar: 1 } }, - { path: 'j.md', locals: { date: '2013-02-01', foo: 'xxx', bar: 4 } }, - { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { path: 'm.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { path: 'n.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 7 } }, - { path: 'o.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 7 } }, - { path: 'p.md', locals: { date: '2013-01-01', foo: 'xxx', bar: 7 } }, - { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } - ]; - - it('should group a list by a property', function() { - list = new List(items); - - var context = list - .sortBy('locals.date') - .groupBy(function(view) { - var date = view.locals.date; - view.locals.year = date.slice(0, 4); - view.locals.month = date.slice(5, 7); - view.locals.day = date.slice(8, 10); - return view.locals.year; - }, 'locals.month'); - - var keys = Object.keys(context); - assert(keys[0] === '2012'); - assert(keys[1] === '2013'); - assert(keys[2] === '2014'); - assert(keys[3] === '2015'); - }); - }); - - describe('paginate', function() { - var items = [ - { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, - { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, - { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, - { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, - { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, - { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, - { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, - { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, - { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, - { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, - { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, - { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, - { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } } - ]; - - it('should paginate a list', function() { - list = new List(items); - - var res = list.paginate(); - assert.equal(res.length, 2); - assert.containEql(res[0].items, items.slice(0, 10)); - assert.containEql(res[1].items, items.slice(10)); - }); - - it('should add pager properties', function() { - list = new List({pager: true}); - list.addList(items); - list.items.forEach(function(item, i) { - assert.equal(item.data.pager.index, i); - }); - }); - - it('should render items when pager is `true`', function(cb) { - list = new List({pager: true}); - list.engine('.md', require('engine-base')); - list.addList(items); - each(list.items, function(item, next) { - item.render(next); - }, cb); - }); - - it('should paginate a list with given options', function() { - list = new List(items); - var res = list.paginate({limit: 5}); - - assert.equal(res.length, 3); - assert.containEql(res[0].items, items.slice(0, 5)); - assert.containEql(res[1].items, items.slice(5, 10)); - assert.containEql(res[2].items, items.slice(10)); - }); - }); - - describe('Views instance', function() { - beforeEach(function() { - views = new Views(); - }); - - it('should add views from an instance of Views', function() { - views.addViews({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - - list = new List(views); - assert(isBuffer(list.items[0].contents)); - assert(isBuffer(list.items[1].contents)); - }); - }); - - describe('getIndex', function() { - beforeEach(function() { - list = new List(); - }); - it('should get the index of a key when key is not renamed', function() { - list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); - list.addItem('a/b/c/eee.hbs', {content: 'eee'}); - assert(list.getIndex('a/b/c/ddd.hbs') === 0); - assert(list.getIndex('a/b/c/eee.hbs') === 1); - }); - - it('should get the index of a key when key is renamed', function() { - list = new List({ - renameKey: function(key) { - return path.basename(key); - } - }); - list.addItem('a/b/c/ddd.hbs', {content: 'ddd'}); - list.addItem('a/b/c/eee.hbs', {content: 'eee'}); - assert(list.getIndex('a/b/c/ddd.hbs') === 0); - assert(list.getIndex('ddd.hbs') === 0); - assert(list.getIndex('a/b/c/eee.hbs') === 1); - assert(list.getIndex('eee.hbs') === 1); - }); - }); - - describe('getItem', function() { - beforeEach(function() { - list = new List(); - }); - - it('should get an item from `items`', function() { - list.addItem('one', {content: 'aaa'}); - list.addItem('two', {content: 'zzz'}); - assert(list.items.length === 2); - assert(isBuffer(list.items[0].contents)); - assert(isBuffer(list.getItem('one').contents)); - assert(list.getItem('one').contents.toString() === 'aaa'); - assert(list.getItem('two').contents.toString() === 'zzz'); - }); - - it('should return `undefined` when the item is not found', function() { - assert.equal(typeof list.getItem('flflflfl'), 'undefined'); - }); - }); - - describe('use', function() { - beforeEach(function() { - list = new List(); - }); - - it('should use middleware on a list', function() { - list.addItem('one', {content: 'aaa'}); - list.addItem('two', {content: 'zzz'}); - - list - .use(function() { - this.set('foo', 'bar'); - }) - .use(function() { - this.set('one', 'two'); - }); - - assert(list.one === 'two'); - assert(list.foo === 'bar'); - }); - }); -}); - diff --git a/test/list.render.js b/test/list.render.js deleted file mode 100644 index 6915ac6f..00000000 --- a/test/list.render.js +++ /dev/null @@ -1,144 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var each = require('async-each'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var posts; - -describe('list.render', function() { - describe('rendering', function() { - beforeEach(function() { - posts = new List(); - posts.engine('tmpl', require('engine-base')); - }); - - it('should throw an error when no callback is given:', function(cb) { - try { - posts.render({}); - cb(new Error('expected an error')); - } catch (err) { - console.log(err); - assert.equal(err.message, 'List#render is async and expects a callback function'); - cb(); - } - }); - - it('should throw an error when an engine is not defined:', function(cb) { - posts.addItem('foo.bar', {content: '<%= name %>'}); - var page = posts.getItem('foo.bar'); - - posts.render(page, function(err) { - assert(err.message === 'List#render cannot find an engine for: .bar'); - cb(); - }); - }); - - it('should use helpers to render a item:', function(cb) { - var locals = {name: 'Halle'}; - - posts.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - posts.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = posts.getItem('a.tmpl'); - - posts.render(page, function(err, res) { - if (err) return cb(err); - - assert(res.content === 'a HALLE b'); - cb(); - }); - }); - - it('should use helpers when rendering a item:', function(cb) { - var locals = {name: 'Halle'}; - posts.helper('upper', function(str) { - return str.toUpperCase(str); - }); - - posts.addItem('a.tmpl', {content: 'a <%= upper(name) %> b', locals: locals}); - var page = posts.getItem('a.tmpl'); - - posts.render(page, function(err, res) { - if (err) return cb(err); - assert(res.content === 'a HALLE b'); - cb(); - }); - }); - - it('should render a template when contents is a buffer:', function(cb) { - posts.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var item = posts.getItem('a.tmpl'); - - posts.render(item, function(err, item) { - if (err) return cb(err); - assert(item.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a template when content is a string:', function(cb) { - posts.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - var item = posts.getItem('a.tmpl'); - - posts.render(item, function(err, item) { - if (err) return cb(err); - assert(item.contents.toString() === 'b'); - cb(); - }); - }); - - it('should render a item from its path:', function(cb) { - posts.addItem('a.tmpl', {content: '<%= a %>', locals: {a: 'b'}}); - - posts.render('a.tmpl', function(err, item) { - if (err) return cb(err); - assert(item.content === 'b'); - cb(); - }); - }); - - it('should use a plugin for rendering:', function(cb) { - posts.engine('tmpl', require('engine-base')); - posts.option('engine', 'tmpl'); - - posts.addItems({ - 'a': {content: '<%= title %>', locals: {title: 'aaa'}}, - 'b': {content: '<%= title %>', locals: {title: 'bbb'}}, - 'c': {content: '<%= title %>', locals: {title: 'ccc'}}, - 'd': {content: '<%= title %>', locals: {title: 'ddd'}}, - 'e': {content: '<%= title %>', locals: {title: 'eee'}}, - 'f': {content: '<%= title %>', locals: {title: 'fff'}}, - 'g': {content: '<%= title %>', locals: {title: 'ggg'}}, - 'h': {content: '<%= title %>', locals: {title: 'hhh'}}, - 'i': {content: '<%= title %>', locals: {title: 'iii'}}, - 'j': {content: '<%= title %>', locals: {title: 'jjj'}} - }); - - posts.use(function(collection) { - collection.option('pager', false); - - collection.renderEach = function(cb) { - var list = new List(collection); - - each(list.items, function(item, next) { - collection.render(item, next); - }, cb); - }; - }); - - posts.renderEach(function(err, items) { - if (err) return cb(err); - assert(items[0].content === 'aaa'); - assert(items[9].content === 'jjj'); - assert(items.length === 10); - cb(); - }); - }); - }); -}); diff --git a/test/list.use.js b/test/list.use.js deleted file mode 100644 index e3257b84..00000000 --- a/test/list.use.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var Item = App.Item; -var list; - -describe('list.use', function() { - beforeEach(function() { - list = new List(); - }); - - it('should expose the instance to `use`:', function(cb) { - list.use(function(inst) { - assert(inst instanceof List); - cb(); - }); - }); - - it('should be chainable:', function(cb) { - list.use(function(inst) { - assert(inst instanceof List); - }) - .use(function(inst) { - assert(inst instanceof List); - }) - .use(function(inst) { - assert(inst instanceof List); - cb(); - }); - }); - - it('should expose the list to a plugin:', function() { - list.use(function(items) { - assert(items instanceof List); - items.foo = items.addItem.bind(items); - }); - - list.foo('a', {content: '...'}); - assert(list.hasItem('a')); - }); - - it('should expose list when chained:', function() { - list - .use(function(items) { - assert(items instanceof List); - items.foo = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof List); - items.bar = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof List); - items.baz = items.addItem.bind(items); - }); - - var pages = list; - - pages.foo({path: 'a', content: '...'}); - pages.bar({path: 'b', content: '...'}); - pages.baz({path: 'c', content: '...'}); - - assert(list.hasItem('a')); - assert(list.hasItem('b')); - assert(list.hasItem('c')); - }); - - it('should work when a custom `Item` constructor is passed:', function() { - list = new List({Item: require('vinyl')}); - list - .use(function(items) { - assert(items instanceof List); - items.foo = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof List); - items.bar = items.addItem.bind(items); - }) - .use(function(items) { - assert(items instanceof List); - items.baz = items.addItem.bind(items); - }); - - var pages = list; - - pages.foo({path: 'a', content: '...'}); - pages.bar({path: 'b', content: '...'}); - pages.baz({path: 'c', content: '...'}); - - assert(list.hasItem('a')); - assert(list.hasItem('b')); - assert(list.hasItem('c')); - }); - - it('should pass to item `use` if a function is returned:', function() { - list.use(function(items) { - assert(items instanceof List); - - return function(item) { - item.foo = items.addItem.bind(items); - assert(item.isItem || item.isView); - }; - }); - - list.addItem('a', {content: '...'}) - .foo({path: 'b', content: '...'}) - .foo({path: 'c', content: '...'}) - .foo({path: 'd', content: '...'}); - - assert(list.hasItem('a')); - assert(list.hasItem('b')); - assert(list.hasItem('c')); - assert(list.hasItem('d')); - }); - - it('should be chainable when a item function is returned:', function() { - list - .use(function(items) { - assert(items instanceof List); - - return function(item) { - item.foo = items.addItem.bind(items); - assert(item instanceof Item); - }; - }) - .use(function(items) { - assert(items instanceof List); - - return function(item) { - item.bar = items.addItem.bind(items); - assert(item instanceof Item); - }; - }) - .use(function(items) { - assert(items instanceof List); - - return function(item) { - item.baz = items.addItem.bind(items); - assert(item instanceof Item); - }; - }); - - list.addItem('a', {content: '...'}) - .foo({path: 'b', content: '...'}) - .bar({path: 'c', content: '...'}) - .baz({path: 'd', content: '...'}); - - assert(list.hasItem('a')); - assert(list.hasItem('b')); - assert(list.hasItem('c')); - assert(list.hasItem('d')); - }); -}); diff --git a/test/partials.js b/test/partials.js deleted file mode 100644 index 72b719d8..00000000 --- a/test/partials.js +++ /dev/null @@ -1,204 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app, pages; - -describe('partials', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.engine('hbs', require('engine-handlebars')); - - app.create('partials', { viewType: 'partial' }); - app.create('include', { viewType: 'partial' }); - app.create('layouts', { viewType: 'layout' }); - pages = app.create('page'); - }); - - it('should inject a partial with a helper:', function(cb) { - app.include('base', {path: 'base.tmpl', content: 'xyz'}); - app.pages('a.tmpl', {path: 'a.tmpl', content: 'a <%= include("base") %> c'}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a xyz c'); - cb(); - }); - }); - - it('should inject a partial with a helper on a collection:', function(cb) { - app.include('base', {path: 'base.tmpl', content: 'xyz'}); - pages.engine('.tmpl', require('engine-handlebars')); - pages.helpers(app._.helpers.sync); - pages.asyncHelpers(app._.helpers.async); - pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{include "base" }} c'}); - var page = pages.getView('a.tmpl'); - - pages.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a xyz c'); - cb(); - }); - }); - - it('should use handlebars partial with a helper on a collection:', function(cb) { - app.include('base', {path: 'base.tmpl', content: 'xyz'}); - pages.engine('.tmpl', require('engine-handlebars')); - pages.helpers(app._.helpers.sync); - pages.asyncHelpers(app._.helpers.async); - pages.addView('a.tmpl', {path: 'a.tmpl', content: 'a {{> base }} c'}); - - var page = pages.getView('a.tmpl'); - var locals = app.mergePartials(this.options); - - pages.render(page, locals, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a xyz c'); - cb(); - }); - }); - - it('should use layouts with partials:', function(cb) { - app.layout('default', {path: 'a.tmpl', content: 'a {% body %} c'}); - app.include('b', {path: 'b.tmpl', content: 'b', layout: 'default'}); - app.pages('a.tmpl', {path: 'c.tmpl', content: '<%= include("b") %>'}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should add `layoutApplied` after layout is applied:', function(cb) { - app.layout('default', {path: 'a.tmpl', content: 'a {% body %} c'}); - app.include('b', {path: 'b.tmpl', content: 'b', layout: 'default'}); - app.pages('a.tmpl', {path: 'c.tmpl', content: '<%= include("b") %>'}); - var page = app.pages.getView('a.tmpl'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(app.layouts.getView('default').options.layoutApplied); - assert.equal(view.content, 'a b c'); - cb(); - }); - }); - - it('should pass partials to handlebars:', function(cb) { - app.onMerge(/\.hbs$/, function(view, next) { - app.applyLayout(view); - next(); - }); - - app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); - app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); - app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); - var page = app.pages.getView('a.hbs'); - - app.render(page, function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a foo c'); - cb(); - }); - }); - - it('should only merge in the specified viewTypes:', function(cb) { - app.onMerge(/\.hbs$/, function(view, next) { - app.applyLayout(view); - next(); - }); - - app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); - app.option('mergeTypes', ['includes']); - - app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default'}); - app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); - - app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); - app.pages.getView('a.hbs') - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a foo c'); - cb(); - }); - - }); - - it('should merge the specified viewTypes in the order defined:', function(cb) { - app.onMerge(/\.hbs$/, function(view, next) { - app.applyLayout(view); - next(); - }); - - app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); - app.option('mergeTypes', ['includes', 'partials']); - - app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default'}); - app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); - - app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); - app.pages.getView('a.hbs') - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a bar c'); - cb(); - }); - }); - - it('should not merge in partials with `options.nomerge` defined:', function(cb) { - app.onMerge(/\.hbs$/, function(view, next) { - app.applyLayout(view); - next(); - }); - - app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); - app.option('mergeTypes', ['includes', 'partials']); - - app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default', options: {nomerge: true}}); - app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); - - app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); - app.pages.getView('a.hbs') - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a foo c'); - cb(); - }); - }); - - it('should emit an `onMerge` event:', function(cb) { - app.on('onMerge', function(view) { - app.applyLayout(view); - }); - - app.layout('default', {path: 'a.hbs', content: 'a {% body %} c'}); - app.option('mergeTypes', ['includes', 'partials']); - - app.partial('foo', {path: 'bar.hbs', content: 'bar', layout: 'default'}); - app.include('foo', {path: 'foo.hbs', content: 'foo', layout: 'default'}); - - app.pages('a.hbs', {path: 'c.hbs', content: '{{> foo }}'}); - app.pages.getView('a.hbs') - .render(function(err, view) { - if (err) return cb(err); - assert.equal(typeof view.content, 'string'); - assert.equal(view.content, 'a bar c'); - cb(); - }); - }); -}); diff --git a/test/renameKey.js b/test/renameKey.js deleted file mode 100644 index 9d4d788c..00000000 --- a/test/renameKey.js +++ /dev/null @@ -1,460 +0,0 @@ -'use strict'; - -var path = require('path'); -var support = require('./support'); -var App = support.resolve(); -var app; - -function renameKey(key) { - return path.basename(key, path.extname(key)); -} - -describe('renameKey', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('pages'); - app.create('posts'); - }); - - describe('global options:', function() { - it('should use `renameKey` function defined on global opts:', function() { - app.option('renameKey', renameKey); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.posts.should.have.property('a'); - app.views.posts.should.have.property('b'); - app.views.posts.should.have.property('c'); - app.views.posts.should.have.property('d'); - app.views.posts.should.have.property('e'); - }); - - it('should expose `view` when defined on global opts:', function() { - app.option('renameKey', function(key, view) { - return view.filename; - }); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.posts.should.have.property('a'); - app.views.posts.should.have.property('b'); - app.views.posts.should.have.property('c'); - app.views.posts.should.have.property('d'); - app.views.posts.should.have.property('e'); - }); - - it('should not have conflicts when view name is the collection name:', function() { - app.option('renameKey', renameKey); - - app.post('a/b/c/post.txt', {content: 'this is contents'}); - app.page('a/b/c/page.txt', {content: 'this is contents'}); - - app.views.posts.should.have.property('post'); - app.views.pages.should.have.property('page'); - }); - }); - - describe('create method:', function() { - it('should use `renameKey` option chained from the `create` method:', function() { - app.create('post') - .option('renameKey', function(key) { - return 'posts/' + path.basename(key); - }); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.posts.should.have.property('posts/a.txt'); - app.views.posts.should.have.property('posts/b.txt'); - app.views.posts.should.have.property('posts/c.txt'); - app.views.posts.should.have.property('posts/d.txt'); - app.views.posts.should.have.property('posts/e.txt'); - }); - }); - - describe('create method:', function() { - it('should use `renameKey` defined on the `create` method:', function() { - app.create('post', { - renameKey: function(key) { - return 'posts/' + path.basename(key); - } - }); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.posts.should.have.property('posts/a.txt'); - app.views.posts.should.have.property('posts/b.txt'); - app.views.posts.should.have.property('posts/c.txt'); - app.views.posts.should.have.property('posts/d.txt'); - app.views.posts.should.have.property('posts/e.txt'); - }); - - it('should expose `view` as the second argument', function() { - app.create('post', { - renameKey: function(key, view) { - return 'posts/' + view.basename; - } - }); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.posts.should.have.property('posts/a.txt'); - app.views.posts.should.have.property('posts/b.txt'); - app.views.posts.should.have.property('posts/c.txt'); - app.views.posts.should.have.property('posts/d.txt'); - app.views.posts.should.have.property('posts/e.txt'); - }); - }); - - describe('collections:', function() { - describe('setting:', function() { - it('should get a view with the `renameKey` defined on app.options:', function() { - app.option('renameKey', function(key) { - return 'foo/' + path.basename(key); - }); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.post('a/b/c/c.txt', {content: '...'}); - - app.views.posts.should.have.property('foo/a.txt'); - app.views.posts.should.have.property('foo/b.txt'); - app.views.posts.should.have.property('foo/c.txt'); - }); - - it('should use `renameKey` defined on collection.options:', function() { - app.pages.option('renameKey', function(key) { - return 'page/' + path.basename(key); - }); - - app.posts.option('renameKey', function(key) { - return 'post/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('page/a.txt'); - app.views.pages.should.have.property('page/b.txt'); - app.views.pages.should.have.property('page/c.txt'); - app.views.pages.should.have.property('page/d.txt'); - app.views.pages.should.have.property('page/e.txt'); - - app.views.posts.should.have.property('post/a.txt'); - app.views.posts.should.have.property('post/b.txt'); - app.views.posts.should.have.property('post/c.txt'); - app.views.posts.should.have.property('post/d.txt'); - app.views.posts.should.have.property('post/e.txt'); - }); - - it('should expose `view` when defined on collection.options:', function() { - app.pages.option('renameKey', function(key, view) { - return 'page/' + view.basename; - }); - - app.posts.option('renameKey', function(key, view) { - return 'post/' + view.basename; - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.posts('a/b/c/c.txt', {content: '...'}); - app.post('a/b/c/d.txt', {content: '...'}); - app.post('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('page/a.txt'); - app.views.pages.should.have.property('page/b.txt'); - app.views.pages.should.have.property('page/c.txt'); - app.views.pages.should.have.property('page/d.txt'); - app.views.pages.should.have.property('page/e.txt'); - - app.views.posts.should.have.property('post/a.txt'); - app.views.posts.should.have.property('post/b.txt'); - app.views.posts.should.have.property('post/c.txt'); - app.views.posts.should.have.property('post/d.txt'); - app.views.posts.should.have.property('post/e.txt'); - }); - - it('should use the `collection.renameKey()` method:', function() { - app.pages.renameKey(function(key) { - return 'baz/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('baz/a.txt'); - app.views.pages.should.have.property('baz/b.txt'); - app.views.pages.should.have.property('baz/c.txt'); - app.views.pages.should.have.property('baz/d.txt'); - app.views.pages.should.have.property('baz/e.txt'); - }); - - it('should expose `view` with the `collection.renameKey()` method:', function() { - app.pages.renameKey(function(key, view) { - return 'baz/' + view.basename; - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('baz/a.txt'); - app.views.pages.should.have.property('baz/b.txt'); - app.views.pages.should.have.property('baz/c.txt'); - app.views.pages.should.have.property('baz/d.txt'); - app.views.pages.should.have.property('baz/e.txt'); - }); - - it('should use the `app.renameKey()` method:', function() { - app.renameKey(function(key) { - return 'app/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('app/a.txt'); - app.views.pages.should.have.property('app/b.txt'); - app.views.pages.should.have.property('app/c.txt'); - app.views.pages.should.have.property('app/d.txt'); - app.views.pages.should.have.property('app/e.txt'); - }); - - it('should expose `view` with the `app.renameKey()` method:', function() { - app.renameKey(function(key, view) { - return 'app/' + view.basename; - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('app/a.txt'); - app.views.pages.should.have.property('app/b.txt'); - app.views.pages.should.have.property('app/c.txt'); - app.views.pages.should.have.property('app/d.txt'); - app.views.pages.should.have.property('app/e.txt'); - }); - - it('should prefer collection method over app.options:', function() { - // this works when you switch the order around... - app.pages.renameKey(function pagesRenameKey(key) { - return 'aaa/' + path.basename(key); - }); - app.option('renameKey', function optsRenameKey(key) { - return 'foo/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('aaa/a.txt'); - app.views.pages.should.have.property('aaa/b.txt'); - app.views.pages.should.have.property('aaa/c.txt'); - app.views.pages.should.have.property('aaa/d.txt'); - app.views.pages.should.have.property('aaa/e.txt'); - }); - - it('should prefer collection method over app method:', function() { - app.pages.renameKey(function(key) { - return 'aaa/' + path.basename(key); - }); - app.renameKey(function(key) { - return 'zzz/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('aaa/a.txt'); - app.views.pages.should.have.property('aaa/b.txt'); - app.views.pages.should.have.property('aaa/c.txt'); - app.views.pages.should.have.property('aaa/d.txt'); - app.views.pages.should.have.property('aaa/e.txt'); - }); - - it('should prefer collection options over app.options:', function() { - app.pages.option('renameKey', function(key) { - return 'collection/' + path.basename(key); - }); - app.option('renameKey', function(key) { - return 'app/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('collection/a.txt'); - app.views.pages.should.have.property('collection/b.txt'); - app.views.pages.should.have.property('collection/c.txt'); - app.views.pages.should.have.property('collection/d.txt'); - app.views.pages.should.have.property('collection/e.txt'); - }); - - it('should prefer collection options over app method:', function() { - app.pages.option('renameKey', function(key) { - return 'collection/' + path.basename(key); - }); - app.renameKey(function(key) { - return 'app/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.pages('a/b/c/c.txt', {content: '...'}); - app.page('a/b/c/d.txt', {content: '...'}); - app.page('a/b/c/e.txt', {content: '...'}); - - app.views.pages.should.have.property('collection/a.txt'); - app.views.pages.should.have.property('collection/b.txt'); - app.views.pages.should.have.property('collection/c.txt'); - app.views.pages.should.have.property('collection/d.txt'); - app.views.pages.should.have.property('collection/e.txt'); - }); - - it('should use renameKey on chained methods:', function() { - app.page('test/fixtures/pages/a.txt', { - options: { - renameKey: function foo(key) { - return 'foo/' + path.basename(key); - } - } - }); - - app.page('test/fixtures/pages/a.hbs', { - options: { - renameKey: function bar(key) { - return 'bar/' + path.basename(key); - } - } - }); - - app.views.pages.should.have.properties([ - 'foo/a.txt', - 'bar/a.hbs' - ]); - }); - }); - - describe('getting', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('post'); - app.create('page'); - }); - - it('should get a view with the `renameKey` defined on the `create` method:', function() { - app.create('post', { - renameKey: function createRenameKey(key) { - return 'posts/' + path.basename(key); - } - }); - - app.posts('a/b/c/a.txt', {content: '...'}); - app.posts('a/b/c/b.txt', {content: '...'}); - app.post('a/b/c/c.txt', {content: '...'}); - - app.posts.getView('a.txt').should.have.property('path', 'a/b/c/a.txt'); - app.posts.getView('posts/a.txt').should.have.property('path', 'a/b/c/a.txt'); - }); - - it('should get a view with `renameKey` on collection.options:', function() { - app.pages.option('renameKey', function(key) { - return 'bar/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.page('a/b/c/c.txt', {content: '...'}); - - app.views.pages.should.have.property('bar/a.txt'); - app.views.pages.should.have.property('bar/b.txt'); - app.views.pages.should.have.property('bar/c.txt'); - }); - - it('should get a view with the the `app.renameKey()` method:', function() { - app.renameKey(function(key) { - return 'baz/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.page('a/b/c/c.txt', {content: '...'}); - - app.views.pages.should.have.property('baz/a.txt'); - app.views.pages.should.have.property('baz/b.txt'); - app.views.pages.should.have.property('baz/c.txt'); - }); - - it('should get a view with the the `collection.renameKey()` method:', function() { - app.pages.renameKey(function(key) { - return 'baz/' + path.basename(key); - }); - - app.pages('a/b/c/a.txt', {content: '...'}); - app.pages('a/b/c/b.txt', {content: '...'}); - app.page('a/b/c/c.txt', {content: '...'}); - - app.views.pages.should.have.property('baz/a.txt'); - app.views.pages.should.have.property('baz/b.txt'); - app.views.pages.should.have.property('baz/c.txt'); - }); - }); - }); -}); diff --git a/test/render.js b/test/render.js deleted file mode 100644 index 2854b4a8..00000000 --- a/test/render.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('render', function() { - describe('engine', function() { - var view; - - beforeEach(function() { - app = new App({silent: true}); - app.engine('tmpl', require('engine-base')); - app.create('page'); - view = {contents: new Buffer('a <%= name %> b'), locals: {name: 'Halle'}}; - }); - - it('should render a view from an object:', function(cb) { - app.page('a.tmpl', view) - .render(function(err, res) { - if (err) return cb(err); - assert.equal(res.contents.toString(), 'a Halle b'); - cb(); - }); - }); - - it('should throw an error when a variable is undefined:', function(cb) { - view = {contents: new Buffer('a <%= foo %> b')}; - - app.page('a.tmpl', view) - .render(function(err) { - assert.equal(err.message, 'foo is not defined'); - cb(); - }); - }); - - it('should re-throw an error when rethrow is true:', function(cb) { - view = {contents: new Buffer('a <%= foo %> b')}; - - app = new App({rethrow: true, silent: true}); - app.engine('tmpl', require('engine-base')); - app.create('page'); - - app.page('a.tmpl', view) - .render(function(err) { - assert.equal(err.message, 'foo is not defined'); - cb(); - }); - }); - - it('should emit a re-thrown error when rethrow is true:', function(cb) { - view = {contents: new Buffer('a <%= foo %> b')}; - - app = new App({rethrow: true, silent: false}); - app.engine('tmpl', require('engine-base')); - app.create('page'); - - app.on('error', function(err) { - assert.equal(err.message, 'foo is not defined'); - cb(); - }); - - app.page('a.tmpl', view) - .render(function(err) { - assert.equal(err.message, 'foo is not defined'); - }); - }); - }); -}); diff --git a/test/routes.js b/test/routes.js deleted file mode 100644 index dc45fa7d..00000000 --- a/test/routes.js +++ /dev/null @@ -1,100 +0,0 @@ -'use strict'; - -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -function append(str) { - return function(view, next) { - var content = view.contents.toString(); - view.contents = new Buffer(content + ' ' + str); - next(); - }; -} -function prepend(str) { - return function(view, next) { - var content = view.contents.toString(); - view.contents = new Buffer(str + ' ' + content); - next(); - }; -} - -describe('routes', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - describe('params', function() { - it('should call param function when routing', function(cb) { - app.param('id', function(view, next, id) { - assert.equal(id, '123'); - next(); - }); - - app.all('/foo/:id/bar', function(view, next) { - assert.equal(view.options.params.id, '123'); - next(); - }); - - app.router.handle({ path: '/foo/123/bar' }, cb); - }); - }); - - describe('onLoad middleware', function() { - it('should run when templates are loaded:', function() { - app.onLoad(/\.tmpl/, prepend('onLoad')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>'}); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('onLoad <%= name %>'); - }); - }); - - describe('preCompile middleware', function() { - it('should run before templates are compiled:', function() { - - }); - }); - - describe('postCompile middleware', function() { - it('should run after templates are compiled:', function() { - - }); - }); - - describe('preRender middleware', function() { - it('should run before templates are rendered:', function(cb) { - app.preRender(/\.tmpl/, prepend('preRender')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa'} }); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('<%= name %>'); - - page.render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('preRender aaa'); - cb(); - }); - }); - }); - - describe('postRender middleware', function() { - it('should run after templates are rendered:', function(cb) { - app.postRender(/\.tmpl/, append('postRender')); - app.pages('a.tmpl', { path: 'a.tmpl', content: '<%= name %>', locals: {name: 'aaa' }}); - - var page = app.pages.getView('a.tmpl'); - page.contents.toString().should.equal('<%= name %>'); - - page.render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('aaa postRender'); - cb(); - }); - }); - }); -}); diff --git a/test/runner.js b/test/runner.js new file mode 100644 index 00000000..3546ad11 --- /dev/null +++ b/test/runner.js @@ -0,0 +1,111 @@ +'use strict'; + +require('mocha'); +var path = require('path'); +var assert = require('assert'); +var argv = require('yargs-parser')(process.argv.slice(2)); +var runner = require('base-runner'); +var Generate = require('..'); +var base; + +var fixtures = path.resolve.bind(path, __dirname, 'fixtures'); +var config = { + name: 'foo', + runner: require(fixtures('package.json')), + configName: 'generator', + extensions: { + '.js': null + } +}; + +describe('.runner', function() { + var error = console.error; + + beforeEach(function() { + console.error = function() {}; + }); + + afterEach(function() { + console.error = error; + }); + + describe('errors', function() { + it('should throw an error when a callback is not passed', function(cb) { + try { + runner(); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'expected a callback function'); + cb(); + } + }); + + it('should error when an options object is not passed', function(cb) { + runner(Generate, {}, null, function(err, app, runnerContext) { + assert(err); + assert.equal(err.message, 'expected the third argument to be an options object'); + cb(); + }); + }); + + it('should error when a liftoff config object is not passed', function(cb) { + runner(Generate, null, {}, function(err, app, runnerContext) { + assert(err); + assert.equal(err.message, 'expected the second argument to be a liftoff config object'); + cb(); + }); + }); + + it('should error when a Generate constructor is not passed', function(cb) { + runner(null, {}, {}, function(err, app, runnerContext) { + assert(err); + assert.equal(err.message, 'expected the first argument to be a Base constructor'); + cb(); + }); + }); + }); + + describe('runner', function() { + it('should set "env" on app.cache.runnerContext', function(cb) { + runner(Generate, config, argv, function(err, app, runnerContext) { + if (err) return cb(err); + assert(app.cache.runnerContext.env); + assert.equal(typeof app.cache.runnerContext.env, 'object'); + cb(); + }); + }); + + it('should set "config" on app.cache.runnerContext', function(cb) { + runner(Generate, config, argv, function(err, app, runnerContext) { + if (err) return cb(err); + assert(app.cache.runnerContext.config); + assert.equal(typeof app.cache.runnerContext.config, 'object'); + cb(); + }); + }); + + it('should set the configFile on app.cache.runnerContext.env', function(cb) { + runner(Generate, config, argv, function(err, app, runnerContext) { + if (err) return cb(err); + assert.equal(app.cache.runnerContext.env.configFile, 'generator.js'); + cb(); + }); + }); + + it('should set cwd on the instance', function(cb) { + runner(Generate, config, {cwd: fixtures()}, function(err, app, runnerContext) { + if (err) return cb(err); + assert.equal(app.cwd, fixtures()); + cb(); + }); + }); + + it('should resolve configpath from app.cwd and app.configFile', function(cb) { + runner(Generate, config, {cwd: fixtures()}, function(err, app, runnerContext) { + if (err) return cb(err); + assert.equal(app.cache.runnerContext.env.configPath, path.resolve(__dirname, 'fixtures/generator.js')); + cb(); + }); + }); + }); +}); diff --git a/test/store.js b/test/store.js deleted file mode 100644 index 9664c5f6..00000000 --- a/test/store.js +++ /dev/null @@ -1,220 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); -var App = require('../'); -var app; - -describe('store', function() { - beforeEach(function() { - app = new App(); - }); - - afterEach(function(cb) { - app.store.del({force: true}); - cb(); - }); - - it('should create a store at the given `cwd`', function() { - app = new App({store: {cwd: __dirname + '/actual'}}); - app.store.set('foo', 'bar'); - assert(path.basename(app.store.path) === 'verb.json'); - assert(app.store.data.hasOwnProperty('foo')); - assert(app.store.data.foo === 'bar'); - assert(fs.existsSync(path.join(__dirname, 'actual', 'verb.json'))); - }); - - it('should create a store using the given `indent` value', function() { - app = new App({store: {cwd: __dirname + '/actual', indent: 0}}); - app.store.set('foo', 'bar'); - var contents = fs.readFileSync(path.join(__dirname, 'actual', 'verb.json'), 'utf8'); - assert(contents === '{"foo":"bar"}'); - }); - - it('should set a value on the store', function() { - app.store.set('one', 'two'); - app.store.data.one.should.equal('two'); - }); - - it('should set an object', function() { - app.store.set({four: 'five', six: 'seven'}); - app.store.data.four.should.equal('five'); - app.store.data.six.should.equal('seven'); - }); - - it('should set a nested value', function() { - app.store.set('a.b.c.d', {e: 'f'}); - app.store.data.a.b.c.d.e.should.equal('f'); - }); - - it('should union a value onto an array on the store', function() { - app.store.union('one', 'two'); - app.store.data.one.should.eql(['two']); - }); - - it('should not union duplicate values', function() { - app.store.union('one', 'two'); - app.store.data.one.should.eql(['two']); - - app.store.union('one', ['two']); - app.store.data.one.should.eql(['two']); - }); - - it('should concat an existing array:', function() { - app.store.union('one', 'a'); - app.store.data.one.should.eql(['a']); - - app.store.union('one', ['b']); - app.store.data.one.should.eql(['a', 'b']); - - app.store.union('one', ['c', 'd']); - app.store.data.one.should.eql(['a', 'b', 'c', 'd']); - }); - - it('should return true if a key `.has()` on the store', function() { - app.store.set('foo', 'bar'); - app.store.set('baz', null); - app.store.set('qux', undefined); - - assert(app.store.has('foo')); - assert(app.store.has('baz')); - assert(!app.store.has('bar')); - assert(!app.store.has('qux')); - }); - - it('should return true if a nested key `.has()` on the store', function() { - app.store.set('a.b.c.d', {x: 'zzz'}); - app.store.set('a.b.c.e', {f: null}); - app.store.set('a.b.g.j', {k: undefined}); - - assert(app.store.has('a.b.c.d')); - assert(app.store.has('a.b.c.d.x')); - assert(app.store.has('a.b.c.e')); - assert(app.store.has('a.b.c.e.f')); - assert(app.store.has('a.b.g.j')); - assert(!app.store.has('a.b.bar')); - assert(!app.store.has('a.b.c.d.z')); - assert(!app.store.has('a.b.c.e.z')); - assert(!app.store.has('a.b.g.j.k')); - assert(!app.store.has('a.b.g.j.z')); - }); - - it('should return true if a key exists `.hasOwn()` on the store', function() { - app.store.set('foo', 'bar'); - app.store.set('baz', null); - app.store.set('qux', undefined); - - assert(app.store.hasOwn('foo')); - assert(!app.store.hasOwn('bar')); - assert(app.store.hasOwn('baz')); - assert(app.store.hasOwn('qux')); - }); - - it('should return true if a nested key exists `.hasOwn()` on the store', function() { - app.store.set('a.b.c.d', {x: 'zzz'}); - app.store.set('a.b.c.e', {f: null}); - app.store.set('a.b.g.j', {k: undefined}); - - assert(app.store.hasOwn('a.b.c.d')); - assert(app.store.hasOwn('a.b.c.d.x')); - assert(app.store.has('a.b.c.e.f')); - assert(app.store.hasOwn('a.b.c.e.f')); - assert(app.store.hasOwn('a.b.g.j.k')); - assert(!app.store.hasOwn('a.b.bar')); - assert(!app.store.hasOwn('a.b.c.d.z')); - assert(!app.store.hasOwn('a.b.c.e.bar')); - assert(!app.store.has('a.b.g.j.k')); - assert(!app.store.hasOwn('a.b.g.j.foo')); - }); - - it('should `.get()` a stored value', function() { - app.store.set('three', 'four'); - app.store.get('three').should.equal('four'); - }); - - it('should `.get()` a nested value', function() { - app.store.set({a: {b: {c: 'd'}}}); - app.store.get('a.b.c').should.equal('d'); - }); - - it('should `.del()` a stored value', function() { - app.store.set('a', 'b'); - app.store.set('c', 'd'); - app.store.del('a'); - app.store.should.not.have.property('a'); - }); - - it('should `.del()` multiple stored values', function() { - app.store.set('a', 'b'); - app.store.set('c', 'd'); - app.store.set('e', 'f'); - app.store.del(['a', 'c', 'e']); - app.store.data.should.eql({}); - }); -}); - -describe('events', function() { - beforeEach(function() { - app = new App(); - }); - - afterEach(function(cb) { - app.store.del({force: true}); - cb(); - }); - - it('should emit `set` when an object is set:', function() { - var keys = []; - app.store.on('set', function(key) { - keys.push(key); - }); - - app.store.set({a: {b: {c: 'd'}}}); - keys.should.eql(['a']); - }); - - it('should emit `set` when a key/value pair is set:', function() { - var keys = []; - app.store.on('set', function(key) { - keys.push(key); - }); - - app.store.set('a', 'b'); - keys.should.eql(['a']); - }); - - it('should emit `set` when an object value is set:', function() { - var keys = []; - app.store.on('set', function(key) { - keys.push(key); - }); - - app.store.set('a', {b: 'c'}); - keys.should.eql(['a']); - }); - - it('should emit `set` when an array of objects is passed:', function() { - var keys = []; - app.store.on('set', function(key) { - keys.push(key); - }); - - app.store.set([{a: 'b'}, {c: 'd'}]); - keys.should.eql(['a', 'c']); - }); - - it('should emit `del` when a value is deleted:', function(cb) { - app.store.on('del', function(keys) { - keys.should.equal('a'); - assert(typeof app.store.get('a') === 'undefined'); - cb(); - }); - - app.store.set('a', {b: 'c'}); - app.store.get('a').should.eql({b: 'c'}); - app.store.del('a'); - }); -}); diff --git a/test/support/spy.js b/test/support/spy.js index cb9ad861..b473c6e1 100644 --- a/test/support/spy.js +++ b/test/support/spy.js @@ -10,8 +10,7 @@ function maybeCallAsync(module, func) { return sinon.stub(module, func, function() { var args = Array.prototype.slice.call(arguments); args.unshift(module, func); - var err = typeof errorfn === 'function' && - errorfn.apply(this, args); + var err = typeof errorfn === 'function' && errorfn.apply(this, args); if (!err) { original.apply(this, arguments); } else { diff --git a/test/view.content.js b/test/view.content.js deleted file mode 100644 index 856aa9da..00000000 --- a/test/view.content.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('view.content', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - app.engine('tmpl', require('engine-base')); - }); - - it('should normalize the `content` property on a view to a string:', function(cb) { - app.page('abc', {path: 'test/fixtures/templates/a.tmpl'}) - .set('read', function() { - this.contents = fs.readFileSync(this.path); - return this; - }); - - app.views.pages.abc.read(); - - assert('content' in app.views.pages.abc); - assert(typeof app.views.pages.abc.content === 'string'); - cb(); - }); -}); diff --git a/test/view.events.js b/test/view.events.js deleted file mode 100644 index fa53cb7e..00000000 --- a/test/view.events.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('view.events', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - }); - - it('should emit events:', function() { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); - var page = app.pages.getView('a.tmpl'); - var events = []; - - page.on('option', function(key) { - events.push(key); - }); - - page.option('a', 'b'); - page.option('c', 'd'); - page.option('e', 'f'); - page.option({g: 'h'}); - - events.should.eql(['a', 'c', 'e', 'g']); - }); -}); diff --git a/test/view.isType.js b/test/view.isType.js deleted file mode 100644 index f56e903e..00000000 --- a/test/view.isType.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -require('mocha'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var View = App.View; -var view; - -describe('view.isType', function() { - beforeEach(function() { - view = new View(); - }); - - it('should expose thie "isType" method', function() { - assert.equal(typeof view.isType, 'function'); - }); - - it('should return true if a view is the given "type"', function() { - assert(view.isType('renderable')); - }); - - it('should return false if a view is not the given "type"', function() { - assert(!view.isType('partial')); - }); -}); diff --git a/test/view.js b/test/view.js deleted file mode 100644 index 668ec8b3..00000000 --- a/test/view.js +++ /dev/null @@ -1,1159 +0,0 @@ -'use strict'; - -require('mocha'); -var should = require('should'); -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); -var Stream = require('stream'); -var es = require('event-stream'); -var support = require('./support'); -var App = support.resolve(); -var View = App.View; -var view; - -describe('View', function() { - describe('instance', function() { - it('should create an instance of View:', function() { - view = new View(); - assert(view instanceof View); - }); - }); - - describe('static methods', function() { - it('should expose `extend`:', function() { - assert.equal(typeof View.extend, 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - view = new View(); - }); - - it('should expose `set`:', function() { - assert.equal(typeof view.set, 'function'); - }); - it('should expose `get`:', function() { - assert.equal(typeof view.get, 'function'); - }); - it('should expose `del`:', function() { - assert.equal(typeof view.del, 'function'); - }); - it('should expose `define`:', function() { - assert.equal(typeof view.define, 'function'); - }); - it('should expose `visit`:', function() { - assert.equal(typeof view.visit, 'function'); - }); - it('should expose `compile`:', function() { - assert.equal(typeof view.compile, 'function'); - }); - it('should expose `render`:', function() { - assert.equal(typeof view.render, 'function'); - }); - it('should expose `isType`:', function() { - assert.equal(typeof view.isType, 'function'); - }); - }); - - describe('properties', function() { - it('should expose an `options` property', function() { - view = new View({}); - assert.deepEqual(view.options, {}); - assert(view.hasOwnProperty('options')); - }); - - it('should add `options` when passed on the constructor', function() { - view = new View({options: {foo: 'bar'}}); - assert.equal(view.options.foo, 'bar'); - }); - - it('should expose a `data` property', function() { - view = new View({app: {}}); - assert.deepEqual(view.data, {}); - assert(view.hasOwnProperty('data')); - }); - - it('should add `data` when passed on the constructor', function() { - view = new View({data: {foo: 'bar'}}); - assert.equal(view.data.foo, 'bar'); - }); - - it('should add `locals` when passed on the constructor', function() { - view = new View({locals: {foo: 'bar'}}); - assert.equal(view.locals.foo, 'bar'); - }); - }); - - describe('set', function() { - it('should set properties on the object', function() { - view = new View(); - view.set('foo', 'bar'); - assert.equal(view.foo, 'bar'); - }); - }); - - describe('get', function() { - it('should get properties from the object', function() { - view = new View(); - view.set('foo', 'bar'); - assert.equal(view.get('foo'), 'bar'); - }); - }); - - describe('cwd', function() { - it('should get properties from the object', function() { - view = new View({cwd: 'test/fixtures'}); - assert.equal(view.cwd, 'test/fixtures'); - }); - }); - - describe('clone', function() { - it('should clone the view:', function() { - view = new View({content: 'foo'}); - view.set({path: 'foo/bar'}); - view.set('options.one', 'two'); - var clone = view.clone(); - assert(clone.contents); - clone.set('baz', 'quux'); - clone.set('options.three', 'four'); - assert.equal(clone.get('foo'), view.get('foo')); - assert.equal(clone.get('baz'), 'quux'); - assert.equal(typeof view.get('baz'), 'undefined'); - // not deep cloned - assert.equal(clone.get('options.three'), 'four'); - assert.equal(view.get('options.three'), 'four'); - }); - - it('should deep clone the entire object', function() { - view = new View({content: 'foo'}); - view.set({path: 'foo/bar'}); - view.set('options.one', 'two'); - var clone = view.clone({deep: true}); - clone.set('options.three', 'four'); - assert.equal(view.get('options.one'), 'two'); - assert.equal(clone.get('options.one'), 'two'); - assert.equal(clone.get('options.three'), 'four'); - assert.equal(typeof view.get('options.three'), 'undefined'); - }); - }); - - describe('visit', function() { - it('should visit all properties on an object and call the specified method', function() { - view = new View(); - var obj = { - foo: 'bar', - bar: 'baz', - baz: 'bang' - }; - view.visit('set', obj); - assert.equal(view.get('foo'), 'bar'); - assert.equal(view.get('bar'), 'baz'); - assert.equal(view.get('baz'), 'bang'); - }); - - it('should visit all properties on all objects in an array and call the specified method', function() { - view = new View(); - var arr = [{foo: 'bar', bar: 'baz', baz: 'bang'}]; - view.visit('set', arr); - assert.equal(view.get('foo'), 'bar'); - assert.equal(view.get('bar'), 'baz'); - assert.equal(view.get('baz'), 'bang'); - }); - }); - - describe('compile', function() { - it('should get view.layout from view.data.layout', function() { - view = new View({path: 'foo', contents: 'a b c', data: {layout: 'default'}}); - assert.equal(view.layout, 'default'); - }); - it('should get view.layout from view.options.layout', function() { - view = new View({path: 'foo', contents: 'a b c', options: {layout: 'default'}}); - assert.equal(view.layout, 'default'); - }); - it('should get view.layout from view.locals.layout', function() { - view = new View({path: 'foo', contents: 'a b c', locals: {layout: 'default'}}); - assert.equal(view.layout, 'default'); - }); - it('should get view.layout from the view', function() { - view = new View({path: 'foo', contents: 'a b c', layout: 'default'}); - assert.equal(view.layout, 'default'); - }); - - it('should add a compiled function to `view.fn`', function() { - view = new View({path: 'foo', contents: 'a <%= name %> z'}); - view.compile(); - assert.equal(typeof view.fn, 'function'); - }); - - it('should render a compiled template', function(cb) { - view = new View({path: 'foo', contents: 'a <%= name %> z'}); - view.compile(); - view.render({name: 'Halle'}, function(err, res) { - if (err) return cb(err); - assert.equal(res.contents.toString(), 'a Halle z'); - cb(); - }); - }); - - it('should render `fn` using data passed on the constructor', function(cb) { - view = new View({ - path: 'foo', - contents: 'a <%= name %> z', - data: { - name: 'Brooke' - } - }); - - view.compile(); - view.render(function(err, res) { - if (err) return cb(err); - assert.equal(res.contents.toString(), 'a Brooke z'); - cb(); - }); - }); - }); - - describe('render', function() { - it('should render a template', function(cb) { - view = new View({path: 'foo', contents: 'a <%= name %> z'}); - view.render({name: 'Halle'}, function(err, res) { - if (err) return cb(err); - assert.equal(res.contents.toString(), 'a Halle z'); - cb(); - }); - }); - - it('should render fn using data passed on the constructor', function(cb) { - view = new View({ - path: 'foo', - contents: 'a <%= name %> z', - data: { - name: 'Brooke' - } - }); - - view.render(function(err, res) { - if (err) return cb(err); - assert.equal(res.contents.toString(), 'a Brooke z'); - cb(); - }); - }); - - it('should pass errors in the callback.', function(cb) { - view = new View({ - path: 'foo', - contents: 'a <%= name %> z' - }); - - view.render(function(err) { - assert.equal(err.message, 'name is not defined'); - cb(); - }); - }); - }); -}); - -/** - * The following unit tests are from Vinyl - * Since we inherit vinyl in View, we need - * to ensure that these still pass. - */ - -describe('View', function() { - describe('isVinyl()', function() { - it('should return true on a vinyl object', function(cb) { - var view = new View(); - assert.equal(View.isVinyl(view), true); - cb(); - }); - it('should return false on a normal object', function(cb) { - assert.equal(View.isVinyl({}), false); - cb(); - }); - it('should return false on a null object', function(cb) { - assert.equal(View.isVinyl({}), false); - cb(); - }); - }); - - describe('constructor()', function() { - it('should default cwd to process.cwd', function(cb) { - var view = new View(); - view.cwd.should.equal(process.cwd()); - cb(); - }); - - it('should default base to cwd', function(cb) { - var cwd = '/'; - var view = new View({cwd: cwd}); - view.base.should.equal(cwd); - cb(); - }); - - it('should default base to cwd even when none is given', function(cb) { - var view = new View(); - view.base.should.equal(process.cwd()); - cb(); - }); - - it('should default path to null', function(cb) { - var view = new View(); - should.not.exist(view.path); - cb(); - }); - - it('should default history to []', function(cb) { - var view = new View(); - view.history.should.eql([]); - cb(); - }); - - it('should default stat to null', function(cb) { - var view = new View(); - should.not.exist(view.stat); - cb(); - }); - - it('should default contents to null', function(cb) { - var view = new View(); - should.not.exist(view.contents); - cb(); - }); - - it('should set base to given value', function(cb) { - var val = '/'; - var view = new View({base: val}); - view.base.should.equal(val); - cb(); - }); - - it('should set cwd to given value', function(cb) { - var val = '/'; - var view = new View({cwd: val}); - view.cwd.should.equal(val); - cb(); - }); - - it('should set path to given value', function(cb) { - var val = '/test.coffee'; - var view = new View({path: val}); - view.path.should.equal(val); - view.history.should.eql([val]); - cb(); - }); - - it('should set history to given value', function(cb) { - var val = '/test.coffee'; - var view = new View({history: [val]}); - view.path.should.equal(val); - view.history.should.eql([val]); - cb(); - }); - - it('should set stat to given value', function(cb) { - var val = {}; - var view = new View({stat: val}); - view.stat.should.equal(val); - cb(); - }); - - it('should set contents to given value', function(cb) { - var val = new Buffer('test'); - var view = new View({contents: val}); - view.contents.should.equal(val); - cb(); - }); - }); - - describe('isBuffer()', function() { - it('should return true when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var view = new View({contents: val}); - view.isBuffer().should.equal(true); - cb(); - }); - - it('should return false when the contents are a Stream', function(cb) { - var val = new Stream(); - var view = new View({contents: val}); - assert(!view.isBuffer()); - cb(); - }); - - it('should return false when the contents are a null', function(cb) { - var view = new View({contents: null}); - assert(!view.isBuffer()); - cb(); - }); - }); - - describe('isStream()', function() { - it('should return false when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var view = new View({contents: val}); - assert(!view.isStream()); - cb(); - }); - - it('should return true when the contents are a Stream', function(cb) { - var val = new Stream(); - var view = new View({contents: val}); - view.isStream().should.equal(true); - cb(); - }); - - it('should return false when the contents are a null', function(cb) { - var view = new View({contents: null}); - assert(!view.isStream()); - cb(); - }); - }); - - describe('isNull()', function() { - it('should return false when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var view = new View({contents: val}); - assert(!view.isNull()); - cb(); - }); - - it('should return false when the contents are a Stream', function(cb) { - var val = new Stream(); - var view = new View({contents: val}); - assert(!view.isNull()); - cb(); - }); - - it('should return true when the contents are a null', function(cb) { - var view = new View({contents: null}); - view.isNull().should.equal(true); - cb(); - }); - }); - - describe('isDirectory()', function() { - var fakeStat = { - isDirectory: function() { - return true; - } - }; - - it('should return false when the contents are a Buffer', function(cb) { - var val = new Buffer('test'); - var view = new View({contents: val, stat: fakeStat}); - assert(!view.isDirectory()); - cb(); - }); - - it('should return false when the contents are a Stream', function(cb) { - var val = new Stream(); - var view = new View({contents: val, stat: fakeStat}); - assert(!view.isDirectory()); - cb(); - }); - - it('should return true when the contents are a null', function(cb) { - var view = new View({contents: null, stat: fakeStat}); - view.isDirectory().should.equal(true); - cb(); - }); - }); - - describe('clone()', function() { - it('should copy all attributes over with Buffer', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Buffer('test') - }; - var view = new View(options); - var view2 = view.clone(); - - view2.should.not.equal(view, 'refs should be different'); - view2.cwd.should.equal(view.cwd); - view2.base.should.equal(view.base); - view2.path.should.equal(view.path); - view2.contents.should.not.equal(view.contents, 'buffer ref should be different'); - view2.contents.toString('utf8').should.equal(view.contents.toString('utf8')); - cb(); - }); - - it('should copy buffer\'s reference with option contents: false', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.js', - contents: new Buffer('test') - }; - - var view = new View(options); - - var copy1 = view.clone({ contents: false }); - copy1.contents.should.equal(view.contents); - - var copy2 = view.clone({}); - copy2.contents.should.not.equal(view.contents); - - var copy3 = view.clone({ contents: 'any string' }); - copy3.contents.should.not.equal(view.contents); - - cb(); - }); - - it('should copy all attributes over with Stream', function(cb) { - var stream = new Stream.PassThrough(); - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: stream - }; - - var view = new View(options); - var view2 = view.clone(); - - stream.write(new Buffer('wa')); - - process.nextTick(function() { - stream.write(new Buffer('dup')); - stream.end(); - }); - - view2.should.not.equal(view, 'refs should be different'); - view2.cwd.should.equal(view.cwd); - view2.base.should.equal(view.base); - view2.path.should.equal(view.path); - view2.contents.should.not.equal(view.contents, 'stream ref should not be the same'); - view.contents.pipe(es.wait(function(err, data) { - if (err) return cb(err); - - view2.contents.pipe(es.wait(function(err, data2) { - if (err) return cb(err); - - data2.should.not.equal(data, 'stream contents ref should not be the same'); - data2.should.eql(data, 'stream contents should be the same'); - })); - })); - - stream.on('end', cb); - }); - - it('should copy all attributes over with null', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - var view = new View(options); - var view2 = view.clone(); - - view2.should.not.equal(view, 'refs should be different'); - view2.cwd.should.equal(view.cwd); - view2.base.should.equal(view.base); - view2.path.should.equal(view.path); - should.not.exist(view2.contents); - cb(); - }); - - it('should properly clone the `stat` property', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.js', - contents: new Buffer('test'), - stat: fs.statSync(__filename) - }; - - var view = new View(options); - var copy = view.clone(); - - assert(copy.stat.isFile()); - assert(!copy.stat.isDirectory()); - assert(copy.stat instanceof fs.Stats); - - assert(view.stat.hasOwnProperty('birthtime')); - assert(copy.stat.hasOwnProperty('birthtime')); - assert.deepEqual(view.stat, copy.stat); - cb(); - }); - - it('should properly clone the `history` property', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.js', - contents: new Buffer('test'), - stat: fs.statSync(__filename) - }; - - var view = new View(options); - var copy = view.clone(); - - copy.history[0].should.equal(options.path); - copy.path = 'lol'; - view.path.should.not.equal(copy.path); - cb(); - }); - - it('should copy custom properties', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - - var view = new View(options); - view.custom = { a: 'custom property' }; - var view2 = view.clone(); - - view2.should.not.equal(view, 'refs should be different'); - view2.cwd.should.equal(view.cwd); - view2.base.should.equal(view.base); - view2.path.should.equal(view.path); - view2.custom.should.equal(view.custom); - view2.custom.a.should.equal(view.custom.a); - - cb(); - }); - - it('should copy history', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - - var view = new View(options); - view.path = '/test/test.js'; - view.path = '/test/test-938di2s.js'; - var view2 = view.clone(); - - view2.history.should.eql([ - '/test/test.coffee', - '/test/test.js', - '/test/test-938di2s.js' - ]); - view2.history.should.not.equal([ - '/test/test.coffee', - '/test/test.js', - '/test/test-938di2s.js' - ]); - view2.path.should.eql('/test/test-938di2s.js'); - - cb(); - }); - - it('should copy all attributes deeply', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - - var view = new View(options); - view.custom = { a: 'custom property' }; - - var view2 = view.clone(true); - view2.custom.should.eql(view.custom); - view2.custom.should.not.equal(view.custom); - view2.custom.a.should.equal(view.custom.a); - - var view3 = view.clone({ deep: true }); - view3.custom.should.eql(view.custom); - view3.custom.should.not.equal(view.custom); - view3.custom.a.should.equal(view.custom.a); - - var view4 = view.clone(false); - view4.custom.should.eql(view.custom); - view4.custom.should.equal(view.custom); - view4.custom.a.should.equal(view.custom.a); - - var view5 = view.clone({ deep: false }); - view5.custom.should.eql(view.custom); - view5.custom.should.equal(view.custom); - view5.custom.a.should.equal(view.custom.a); - - cb(); - }); - }); - - describe('pipe()', function() { - it('should write to stream with Buffer', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Buffer('test') - }; - var view = new View(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(options.contents.toString('utf8')); - }); - stream.on('end', function() { - cb(); - }); - var ret = view.pipe(stream); - ret.should.equal(stream, 'should return the stream'); - }); - - it('should pipe to stream with Stream', function(cb) { - var testChunk = new Buffer('test'); - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Stream.PassThrough() - }; - var view = new View(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - cb(); - }); - var ret = view.pipe(stream); - ret.should.equal(stream, 'should return the stream'); - - view.contents.write(testChunk); - }); - - it('should do nothing with null', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - var view = new View(options); - var stream = new Stream.PassThrough(); - stream.on('data', function() { - throw new Error('should not write'); - }); - stream.on('end', function() { - cb(); - }); - var ret = view.pipe(stream); - ret.should.equal(stream, 'should return the stream'); - }); - - it('should write to stream with Buffer', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Buffer('test') - }; - var view = new View(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(options.contents.toString('utf8')); - cb(); - }); - stream.on('end', function() { - throw new Error('should not end'); - }); - var ret = view.pipe(stream, {end: false}); - ret.should.equal(stream, 'should return the stream'); - }); - - it('should pipe to stream with Stream', function(cb) { - var testChunk = new Buffer('test'); - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Stream.PassThrough() - }; - var view = new View(options); - var stream = new Stream.PassThrough(); - stream.on('data', function(chunk) { - should.exist(chunk); - (chunk instanceof Buffer).should.equal(true, 'should write as a buffer'); - chunk.toString('utf8').should.equal(testChunk.toString('utf8')); - cb(); - }); - stream.on('end', function() { - throw new Error('should not end'); - }); - var ret = view.pipe(stream, {end: false}); - ret.should.equal(stream, 'should return the stream'); - - view.contents.write(testChunk); - }); - - it('should do nothing with null', function(cb) { - var options = { - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }; - var view = new View(options); - var stream = new Stream.PassThrough(); - stream.on('data', function() { - throw new Error('should not write'); - }); - stream.on('end', function() { - throw new Error('should not end'); - }); - var ret = view.pipe(stream, {end: false}); - ret.should.equal(stream, 'should return the stream'); - process.nextTick(cb); - }); - }); - - describe('inspect()', function() { - it('should return correct format when no contents and no path', function(cb) { - var view = new View(); - view.inspect().should.equal(''); - cb(); - }); - - it('should return correct format when Buffer and no path', function(cb) { - var val = new Buffer('test'); - var view = new View({ - contents: val - }); - view.inspect().should.equal('>'); - cb(); - }); - - it('should return correct format when Buffer and relative path', function(cb) { - var val = new Buffer('test'); - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: val - }); - view.inspect().should.equal('>'); - cb(); - }); - - it('should return correct format when Buffer and only path and no base', function(cb) { - var val = new Buffer('test'); - var view = new View({ - cwd: '/', - path: '/test/test.coffee', - contents: val - }); - delete view.base; - view.inspect().should.equal('>'); - cb(); - }); - - it('should return correct format when Stream and relative path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: new Stream.PassThrough() - }); - view.inspect().should.equal('>'); - cb(); - }); - - it('should return correct format when null and relative path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee', - contents: null - }); - view.inspect().should.equal(''); - cb(); - }); - }); - - describe('contents get/set', function() { - it('should work with Buffer', function(cb) { - var val = new Buffer('test'); - var view = new View(); - view.contents = val; - view.contents.should.equal(val); - cb(); - }); - - it('should work with Stream', function(cb) { - var val = new Stream.PassThrough(); - var view = new View(); - view.contents = val; - view.contents.should.equal(val); - cb(); - }); - - it('should work with null', function(cb) { - var val = null; - var view = new View(); - view.contents = val; - assert.equal(view.contents, null); - cb(); - }); - - it('should work with string', function(cb) { - var val = 'test'; - var view = new View(); - view.contents = val; - assert.deepEqual(view.contents, new Buffer(val)); - cb(); - }); - }); - - describe('relative get/set', function() { - it('should error on set', function(cb) { - var view = new View(); - try { - view.relative = 'test'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should error on get when no base', function(cb) { - var view = new View(); - delete view.base; - try { - view.relative; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should error on get when no path', function(cb) { - var view = new View(); - try { - view.relative; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return a relative path from base', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.relative.should.equal('test.coffee'); - cb(); - }); - - it('should return a relative path from cwd', function(cb) { - var view = new View({ - cwd: '/', - path: '/test/test.coffee' - }); - view.relative.should.equal(path.join('test', 'test.coffee')); - cb(); - }); - }); - - describe('dirname get/set', function() { - it('should error on get when no path', function(cb) { - var view = new View(); - try { - view.dirname; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return the dirname of the path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.dirname.should.equal('/test'); - cb(); - }); - - it('should error on set when no path', function(cb) { - var view = new View(); - try { - view.dirname = '/test'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the dirname of the path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.dirname = '/test/foo'; - view.path.should.equal('/test/foo/test.coffee'); - cb(); - }); - }); - - describe('basename get/set', function() { - it('should error on get when no path', function(cb) { - var view = new View(); - try { - view.basename; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return the basename of the path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.basename.should.equal('test.coffee'); - cb(); - }); - - it('should error on set when no path', function(cb) { - var view = new View(); - try { - view.basename = 'test.coffee'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the basename of the path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.basename = 'foo.png'; - view.path.should.equal('/test/foo.png'); - cb(); - }); - }); - - describe('extname get/set', function() { - it('should error on get when no path', function(cb) { - var view = new View(); - try { - view.extname; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should return the extname of the path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.extname.should.equal('.coffee'); - cb(); - }); - - it('should error on set when no path', function(cb) { - var view = new View(); - try { - view.extname = '.coffee'; - } catch (err) { - should.exist(err); - cb(); - } - }); - - it('should set the extname of the path', function(cb) { - var view = new View({ - cwd: '/', - base: '/test/', - path: '/test/test.coffee' - }); - view.extname = '.png'; - view.path.should.equal('/test/test.png'); - cb(); - }); - }); - - describe('path get/set', function() { - it('should record history when instantiation', function() { - var view = new View({ - cwd: '/', - path: '/test/test.coffee' - }); - - view.path.should.eql('/test/test.coffee'); - view.history.should.eql(['/test/test.coffee']); - }); - - it('should record history when path change', function() { - var view = new View({ - cwd: '/', - path: '/test/test.coffee' - }); - - view.path = '/test/test.js'; - view.path.should.eql('/test/test.js'); - view.history.should.eql(['/test/test.coffee', '/test/test.js']); - - view.path = '/test/test.coffee'; - view.path.should.eql('/test/test.coffee'); - view.history.should.eql(['/test/test.coffee', '/test/test.js', '/test/test.coffee']); - }); - - it('should not record history when set the same path', function() { - var view = new View({ - cwd: '/', - path: '/test/test.coffee' - }); - - view.path = '/test/test.coffee'; - view.path = '/test/test.coffee'; - view.path.should.eql('/test/test.coffee'); - view.history.should.eql(['/test/test.coffee']); - - // ignore when set empty string - view.path = ''; - view.path.should.eql('/test/test.coffee'); - view.history.should.eql(['/test/test.coffee']); - }); - - it('should throw when set path null in constructor', function() { - (function() { - view = new View({ - cwd: '/', - path: null - }); - }).should.throw('path should be string'); - }); - - it('should throw when set path null', function() { - view = new View({ - cwd: '/', - path: 'foo' - }); - - (function() { - view.path = null; - }).should.throw('path should be string'); - }); - }); -}); diff --git a/test/view.methods.js b/test/view.methods.js deleted file mode 100644 index 27ed99ad..00000000 --- a/test/view.methods.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('view.methods', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - app.create('page'); - }); - - describe('.use', function() { - it('should expose `.use` for running plugins on a view:', function() { - app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) - .use(function() { - this.options.foo = 'bar'; - }) - .use(function() { - this.options.bar = 'baz'; - }); - - var page = app.pages.getView('a.tmpl'); - page.options.should.have.property('foo'); - page.options.should.have.property('bar'); - }); - }); - - describe('.render:', function() { - it('should expose `.render` for rendering a view:', function(cb) { - app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>', locals: {a: 'bbb'}}) - .render({}, function(err, res) { - if (err) return cb(err); - res.contents.toString().should.equal('bbb'); - cb(); - }); - }); - }); -}); diff --git a/test/view.option.js b/test/view.option.js deleted file mode 100644 index 64b28903..00000000 --- a/test/view.option.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -require('should'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('view.option', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - }); - - it('should set an option:', function() { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); - var page = app.pages.getView('a.tmpl'); - - page.options.should.not.have.property('foo'); - page.option('foo', 'bar'); - page.options.should.have.property('foo'); - }); - - it('should extend options:', function() { - app.pages('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}); - var page = app.pages.getView('a.tmpl'); - page.option('a', 'b'); - page.option('c', 'd'); - page.option('e', 'f'); - page.options.should.have.properties(['a', 'c', 'e']); - }); -}); diff --git a/test/view.render.js b/test/view.render.js deleted file mode 100644 index 95b0efb6..00000000 --- a/test/view.render.js +++ /dev/null @@ -1,68 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var support = require('./support'); -var App = support.resolve(); -var View = App.View; -var view, app; - -describe('view.render', function() { - describe('views', function() { - it('should expose `.render` for rendering a view:', function(cb) { - view = new View({path: 'a.tmpl', content: '<%= a %>'}); - - view.render({a: 'bbb'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('bbb'); - cb(); - }); - }); - }); - - describe('views created by collection and app', function() { - beforeEach(function() { - app = new App(); - view = new View(); - app.engine('tmpl', require('engine-base')); - app.create('layouts', {viewType: 'layout'}); - app.create('pages'); - }); - - it('should expose `.render` for rendering a view:', function(cb) { - app.page('a.tmpl', {path: 'a.tmpl', content: '<%= a %>'}) - .render({a: 'bbb'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('bbb'); - cb(); - }); - }); - - it('should render a view with a layout', function(cb) { - app.layout('default.tmpl', {content: 'a {% body %} b'}); - app.page('a.tmpl', {content: '<%= title %>', layout: 'default.tmpl'}) - .render({title: 'zzz'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('a zzz b'); - cb(); - }); - }); - - it('should render a view with a layout', function(cb) { - app.layout('foo.tmpl', {content: 'a {% body %} a'}); - app.layout('bar.tmpl', {content: 'b {% body %} b'}); - app.pages('a.tmpl', {content: '<%= title %>'}); - - app.pages.getView('a.tmpl') - .option('resolveLayout', function() { - return 'bar.tmpl'; - }) - .render({title: 'zzz'}, function(err, res) { - if (err) return cb(err); - res.content.should.equal('b zzz b'); - cb(); - }); - }); - }); -}); - diff --git a/test/view.set.js b/test/view.set.js deleted file mode 100644 index 9654bca4..00000000 --- a/test/view.set.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var fs = require('fs'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('view.set', function() { - beforeEach(function() { - app = new App(); - app.create('page'); - app.engine('tmpl', require('engine-base')); - }); - - it('should set a property on a view:', function(cb) { - app.page('abc', {path: 'test/fixtures/templates/a.tmpl'}) - .set('read', function() { - this.contents = fs.readFileSync(this.path); - return this; - }); - - assert('read' in app.views.pages.abc); - app.views.pages.abc - .read() - .set('data.name', 'Brooke') - .render(function(err, res) { - if (err) return cb(err); - assert(res.content === 'Brooke'); - cb(); - }); - }); -}); diff --git a/test/view.use.js b/test/view.use.js deleted file mode 100644 index f20593bd..00000000 --- a/test/view.use.js +++ /dev/null @@ -1,106 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var View = App.View; -var view, app; - -describe('view.use', function() { - beforeEach(function() { - view = new View(); - }); - - it('should expose the instance to `use`:', function(cb) { - view.use(function(inst) { - assert(inst instanceof View); - cb(); - }); - }); - - it('should be chainable:', function(cb) { - view.use(function(inst) { - assert(inst instanceof View); - }) - .use(function(inst) { - assert(inst instanceof View); - }) - .use(function(inst) { - assert(inst instanceof View); - cb(); - }); - }); - - it('should expose the view to a plugin:', function() { - view.use(function(view) { - assert(view instanceof View); - view.foo = function(str) { - return str + ' ' + 'bar'; - }; - }); - assert(view.foo('foo') === 'foo bar'); - }); - - it('should be chainable:', function() { - view - .use(function(view) { - view.a = 'aaa'; - }) - .use(function(view) { - view.b = 'bbb'; - }) - .use(function(view) { - view.c = 'ccc'; - }); - - assert(view.a === 'aaa'); - assert(view.b === 'bbb'); - assert(view.c === 'ccc'); - }); -}); - -describe('collection > view .use', function() { - beforeEach(function() { - app = new App(); - }); - - it('should pass plugins down to views', function(cb) { - var count = 0; - app.create('pages'); - app.pages.use(function(inst) { - return function(view) { - count++; - view.count = count; - }; - }); - app.pages.addView('foo', {content: 'this is content'}); - var view = app.pages.getView('foo'); - assert.equal(view.count, 1); - cb(); - }); - - it('should pass plugins down to views after a view is created', function(cb) { - var count = 0; - - app.create('pages'); - app.pages.addView('foo', {content: 'this is content'}); - app.pages.addView('bar', {content: 'this is content'}); - app.pages.addView('baz', {content: 'this is content'}); - - // add plugin after adding views... - app.pages.use(function(inst) { - return function(view) { - count++; - view.count = count; - }; - }); - - assert.equal(app.pages.getView('foo').count, 1); - assert.equal(app.pages.getView('bar').count, 2); - assert.equal(app.pages.getView('baz').count, 3); - assert.equal(count, 3); - cb(); - }); -}); diff --git a/test/viewTypes.js b/test/viewTypes.js deleted file mode 100644 index 8b246958..00000000 --- a/test/viewTypes.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var app; - -describe('viewTypes', function() { - describe('view types', function() { - beforeEach(function() { - app = new App(); - app.engine('tmpl', require('engine-base')); - }); - - it('should add collection (plural) to the `viewTypes` object', function() { - app.viewTypes = []; // reset - app.create('foo', {viewType: 'layout'}); - app.create('bar', {viewType: 'layout'}); - assert.deepEqual(app.viewTypes.layout, [ 'foos', 'bars' ]); - - app.create('baz', {viewType: 'renderable'}); - assert.deepEqual(app.viewTypes.renderable, [ 'bazs' ]); - }); - - it('should add collection to the given viewType', function() { - app.create('layout', {viewType: 'layout'}); - assert(app.layouts.options.viewType[0] === 'layout'); - }); - - it('should add a collection to multiple viewTypes', function() { - app.create('foo', {viewType: ['layout', 'renderable']}); - assert.deepEqual(app.foos.options.viewType, ['layout', 'renderable']); - }); - }); -}); diff --git a/test/views.getView.js b/test/views.getView.js deleted file mode 100644 index 0b742140..00000000 --- a/test/views.getView.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Views = App.Views; -var views; - -describe('views.getView', function() { - beforeEach(function() { - views = new Views(); - views.option('renameKey', function(key, view) { - return '123/' + (view ? view.basename : path.basename(key)); - }); - - views.addView('one', {content: 'this is one'}); - views.addView('a/b/c/one.txt', {content: 'this is a/b/c/one.txt'}); - views.addView('a/b/c/two.txt', {content: '...'}); - views.addView('a/b/c/three.txt', {content: 'this is three', base: 'a/b/c'}); - }); - - it('should throw an error when a string is not passed', function(cb) { - try { - views.getView(null); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'expected a string'); - cb(); - } - }); - - it('should get a view by `key`', function() { - assert(views.getView('a/b/c/one.txt')); - assert.equal(views.getView('123/a/b/c/one.txt').content, 'this is a/b/c/one.txt'); - }); - - it('should get a view by `path', function() { - assert(views.getView('a/b/c/one.txt')); - assert.equal(views.getView('a/b/c/one.txt').content, 'this is a/b/c/one.txt'); - }); - - it('should get a view by `relative', function() { - assert(views.getView('123/one.txt')); - assert.equal(views.getView('123/one.txt').content, 'this is a/b/c/one.txt'); - }); - - it('should get a view by `basename`', function() { - assert(views.getView('one.txt')); - assert.equal(views.getView('one.txt').content, 'this is a/b/c/one.txt'); - }); - - it('should get a view by `filename`', function() { - assert(views.getView('one')); - assert.equal(views.getView('one').content, 'this is one'); - - assert(views.getView('three')); - assert.equal(views.getView('three').content, 'this is three'); - }); -}); diff --git a/test/views.js b/test/views.js deleted file mode 100644 index 4737e03e..00000000 --- a/test/views.js +++ /dev/null @@ -1,558 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var path = require('path'); -var assert = require('assert'); -var typeOf = require('kind-of'); -var isBuffer = require('is-buffer'); -var support = require('./support'); -var App = support.resolve(); -var List = App.List; -var View = App.View; -var Views = App.Views; -var collection; - -describe('views', function() { - describe('constructor', function() { - it('should create an instance of Views:', function() { - var collection = new Views(); - assert(collection instanceof Views); - }); - - it('should instantiate without `new`:', function() { - var collection = Views(); - assert(collection instanceof Views); - }); - }); - - describe('static methods', function() { - it('should expose `extend`:', function() { - assert(typeof Views.extend === 'function'); - }); - }); - - describe('prototype methods', function() { - beforeEach(function() { - collection = new Views(); - }); - - var methods = [ - 'use', - 'setView', - 'addView', - 'addViews', - 'addList', - 'getView', - 'constructor', - 'set', - 'get', - 'del', - 'define', - 'visit', - 'on', - 'once', - 'off', - 'emit', - 'listeners', - 'hasListeners' - ]; - - methods.forEach(function(method) { - it('should expose ' + method + ' method', function() { - assert(typeof collection[method] === 'function'); - }); - }); - - it('should expose isCollection property', function() { - assert(typeof collection.isCollection === 'boolean'); - }); - - it('should expose queue property', function() { - assert(Array.isArray(collection.queue)); - }); - - it('should expose views property', function() { - assert(typeOf(collection.views) === 'object'); - }); - - it('should expose options property', function() { - assert(typeOf(collection.options) === 'object'); - }); - }); - - describe('instance', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should set a value on the instance:', function() { - collection.set('a', 'b'); - assert(collection.a === 'b'); - }); - - it('should get a value from the instance:', function() { - collection.set('a', 'b'); - assert(collection.get('a') === 'b'); - }); - }); - - describe('option', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should set a key/value pair on options:', function() { - collection.option('a', 'b'); - assert(collection.options.a === 'b'); - }); - - it('should set an object on options:', function() { - collection.option({c: 'd'}); - assert(collection.options.c === 'd'); - }); - - it('should get an option:', function() { - collection.option({c: 'd'}); - var c = collection.option('c'); - assert(c === 'd'); - }); - }); - - describe('addView', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should throw an error when args are invalid:', function() { - (function() { - collection.addView(function() {}); - }).should.throw('expected value to be an object.'); - }); - - it('should add a view to `views`:', function() { - collection.addView('foo'); - collection.views.should.have.property('foo'); - - collection.addView('one', {content: '...'}); - assert(typeof collection.views.one === 'object'); - assert(isBuffer(collection.views.one.contents)); - }); - - it('should create an instance of `View`:', function() { - collection.addView('one', {content: '...'}); - assert(collection.views.one instanceof collection.View); - }); - - it('should allow an `View` constructor to be passed:', function() { - View.prototype.foo = function(key, value) { - this[key] = value; - }; - collection = new Views({View: View}); - collection.addView('one', {content: '...'}); - collection.views.one.foo('bar', 'baz'); - assert(collection.views.one.bar === 'baz'); - }); - - it('should allow an instance of `View` to be passed:', function() { - var collection = new Views({View: View}); - var view = new View({content: '...'}); - collection.addView('one', view); - view.set('abc', 'xyz'); - assert(collection.views.one instanceof collection.View); - assert(isBuffer(collection.views.one.contents)); - assert(collection.views.one.abc === 'xyz'); - }); - - it('should expose the `isType` method on items', function() { - var collection = new Views({View: View}); - var view = new View({content: '...'}); - collection.setView('one', view); - - var one = collection.getView('one'); - assert(one.isType('renderable')); - }); - - it('should set viewTypes on a collection', function() { - var collection = new Views({View: View}); - collection.viewType(['partial']); - - var view = new View({content: '...'}); - collection.setView('one', view); - - var one = collection.getView('one'); - assert(!one.isType('renderable')); - assert(one.isType('partial')); - }); - }); - - describe('deleteView', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should delete an view from `views` by view instance', function() { - collection.addView('foo'); - assert(collection.views.hasOwnProperty('foo')); - - collection.addView('one', {content: '...'}); - assert(collection.views.hasOwnProperty('one')); - assert.equal(Object.keys(collection.views).length, 2); - - var foo = collection.getView('foo'); - collection.deleteView(foo); - assert.equal(Object.keys(collection.views).length, 1); - }); - - it('should delete an view from `views` by view `key`', function() { - collection.addView('foo'); - assert(collection.views.hasOwnProperty('foo')); - - collection.addView('one', {content: '...'}); - assert(collection.views.hasOwnProperty('one')); - assert.equal(Object.keys(collection.views).length, 2); - - collection.deleteView('foo'); - assert.equal(Object.keys(collection.views).length, 1); - }); - }); - - describe('addViews', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should emit an error if a string glob pattern is passed', function(cb) { - try { - collection.addViews('*.js'); - cb(new Error('expected an error')); - } catch (err) { - assert(err); - assert(err.message); - assert(/glob/.test(err.message)); - cb(); - } - }); - - it('should emit an error if an array glob pattern is passed', function(cb) { - try { - collection.addViews(['*.js']); - cb(new Error('expected an error')); - } catch (err) { - assert(err); - assert(err.message); - assert(/glob/.test(err.message)); - cb(); - } - }); - - it('should add multiple views:', function() { - collection.addViews({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - assert(isBuffer(collection.views.one.contents)); - assert(isBuffer(collection.views.two.contents)); - }); - - it('should return the collection instance for chaining:', function() { - var views = collection.addViews({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - - var view = views.getView('one'); - assert(view); - assert(view.content); - assert(view.content === 'foo'); - }); - - it('should create views from an instance of Views', function() { - collection.addViews({ - one: {content: 'foo'}, - two: {content: 'bar'} - }); - var pages = new Views(collection); - assert(isBuffer(pages.views.one.contents)); - assert(isBuffer(pages.views.two.contents)); - }); - - it('should add an array of views:', function() { - collection.addViews([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - assert(isBuffer(collection.views.one.contents)); - assert(isBuffer(collection.views.two.contents)); - }); - }); - - describe('view', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should return a single collection view from a key-value pair', function() { - var one = collection.view('one', {content: 'foo'}); - var two = collection.view('two', {content: 'bar'}); - - assert(one.isView); - assert(one.path === 'one'); - assert(two.isView); - assert(two.path === 'two'); - }); - - it('should return a single collection view from an object', function() { - var one = collection.view({path: 'one', content: 'foo'}); - var two = collection.view({path: 'two', content: 'bar'}); - - assert(one.isView); - assert(one.path === 'one'); - assert(two.isView); - assert(two.path === 'two'); - }); - }); - - describe('addList', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should emit an error if a string glob pattern is passed', function(cb) { - try { - collection.addList('*.js'); - cb(new Error('expected an error')); - } catch (err) { - assert(err); - assert(err.message); - assert(/glob/.test(err.message)); - cb(); - } - }); - - it('should emit an error if an array glob pattern is passed', function(cb) { - try { - collection.addList(['*.js']); - cb(new Error('expected an error')); - } catch (err) { - assert(err); - assert(err.message); - assert(/glob/.test(err.message)); - cb(); - } - }); - - it('should add a list of views:', function() { - collection.addList([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - assert(isBuffer(collection.views.one.contents)); - assert(isBuffer(collection.views.two.contents)); - }); - - it('should add a list from the constructor:', function() { - var list = new List([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - - collection = new Views(list); - assert(isBuffer(collection.views.one.contents)); - assert(isBuffer(collection.views.two.contents)); - }); - - it('should add list items from the constructor:', function() { - var list = new List([ - {path: 'one', content: 'foo'}, - {path: 'two', content: 'bar'} - ]); - - collection = new Views(list.items); - assert(isBuffer(collection.views.one.contents)); - assert(isBuffer(collection.views.two.contents)); - }); - - it('should throw an error when list is not an array:', function() { - var views = new Views(); - (function() { - views.addList(); - }).should.throw('expected list to be an array.'); - - (function() { - views.addList({}); - }).should.throw('expected list to be an array.'); - - (function() { - views.addList('foo'); - }).should.throw('expected list to be an array.'); - }); - - it('should load an array of items from an event:', function() { - var pages = new Views(); - - pages.on('addList', function(list) { - while (list.length) { - pages.addView({path: list.pop()}); - } - this.loaded = true; - }); - - pages.addList(['a.txt', 'b.txt', 'c.txt']); - assert(pages.views.hasOwnProperty('a.txt')); - assert(pages.views['a.txt'].path === 'a.txt'); - }); - - it('should load an array of items from the addList callback:', function() { - var collection = new Views(); - - collection.addList(['a.txt', 'b.txt', 'c.txt'], function(fp) { - return {path: fp}; - }); - assert(collection.views.hasOwnProperty('a.txt')); - assert(collection.views['a.txt'].path === 'a.txt'); - }); - - it('should load an object of views from an event:', function() { - var collection = new Views(); - - collection.on('addViews', function(views) { - for (var key in views) { - collection.addView('foo/' + key, views[key]); - delete views[key]; - } - }); - - collection.addViews({ - a: {path: 'a.txt'}, - b: {path: 'b.txt'}, - c: {path: 'c.txt'} - }); - - assert(collection.views.hasOwnProperty('foo/a')); - assert(collection.views['foo/a'].path === 'a.txt'); - }); - - it('should signal `loaded` when finished:', function() { - var collection = new Views(); - - collection.on('addViews', function(views) { - for (var key in views) { - if (key === 'c') break; - collection.addView('foo/' + key, views[key]); - } - }); - - collection.addViews({ - a: {path: 'a.txt'}, - b: {path: 'b.txt'}, - c: {path: 'c.txt'} - }); - - assert(collection.views.hasOwnProperty('foo/a')); - assert(!collection.views.hasOwnProperty('foo/c')); - assert(collection.views['foo/a'].path === 'a.txt'); - }); - }); - - describe('getView', function() { - beforeEach(function() { - collection = new Views(); - }); - it('should get a view from `views`:', function() { - collection.addView('one', {content: 'aaa'}); - collection.addView('two', {content: 'zzz'}); - assert(isBuffer(collection.views.one.contents)); - assert(isBuffer(collection.getView('one').contents)); - assert(collection.getView('one').contents.toString() === 'aaa'); - assert(collection.getView('two').contents.toString() === 'zzz'); - }); - }); - - describe('count', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should get the number of views:', function() { - collection.addView('one', {content: 'aaa'}); - collection.addView('two', {content: 'zzz'}); - assert(Object.keys(collection.views).length === 2); - }); - }); -}); - -describe('options', function() { - describe('options.renameKey', function() { - beforeEach(function() { - collection = new Views({ - renameKey: function(key) { - return path.basename(key); - } - }); - }); - - it('should use a custom rename key function on view keys', function() { - collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); - assert(collection.views['d.hbs'].contents.toString() === 'foo bar baz'); - }); - - it('should get a view with the renamed key:', function() { - collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); - assert(collection.getView('d.hbs').contents.toString() === 'foo bar baz'); - }); - - it('should get a view with the original key:', function() { - collection.addView('a/b/c/d.hbs', {content: 'foo bar baz'}); - assert(collection.getView('a/b/c/d.hbs').contents.toString() === 'foo bar baz'); - }); - }); -}); - -describe('queue', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should emit arguments on addView', function(cb) { - collection.on('addView', function(args) { - assert(args[0] === 'a'); - assert(args[1] === 'b'); - assert(args[2] === 'c'); - assert(args[3] === 'd'); - assert(args[4] === 'e'); - cb(); - }); - - collection.addView('a', 'b', 'c', 'd', 'e'); - }); - - it('should expose the `queue` property for loading views', function() { - collection.queue.push(collection.view('b', {path: 'b'})); - - collection.addView('a', {path: 'a'}); - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - }); - - it('should load all views on the queue when addView is called', function() { - collection.on('addView', function(args) { - var len = args.length; - var last = args[len - 1]; - if (typeof last === 'string') { - args[len - 1] = { content: last }; - } - }); - - collection.addView('a.html', 'aaa'); - collection.addView('b.html', 'bbb'); - collection.addView('c.html', 'ccc'); - - assert(collection.views.hasOwnProperty('a.html')); - assert(collection.getView('a.html').content === 'aaa'); - assert(collection.views.hasOwnProperty('b.html')); - assert(collection.getView('b.html').content === 'bbb'); - assert(collection.views.hasOwnProperty('c.html')); - assert(collection.getView('c.html').content === 'ccc'); - }); -}); diff --git a/test/views.use.js b/test/views.use.js deleted file mode 100644 index 838e7f3c..00000000 --- a/test/views.use.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -require('mocha'); -require('should'); -var assert = require('assert'); -var support = require('./support'); -var App = support.resolve(); -var Views = App.Views; -var View = App.View; -var collection; - -describe('views.use', function() { - beforeEach(function() { - collection = new Views(); - }); - - it('should expose the instance to `use`:', function(cb) { - collection.use(function(inst) { - assert(inst instanceof Views); - cb(); - }); - }); - - it('should be chainable:', function(cb) { - collection.use(function(inst) { - assert(inst instanceof Views); - }) - .use(function(inst) { - assert(inst instanceof Views); - }) - .use(function(inst) { - assert(inst instanceof Views); - cb(); - }); - }); - - it('should expose the collection to a plugin:', function() { - collection.use(function(views) { - assert(views instanceof Views); - views.foo = views.addView.bind(views); - }); - - collection.foo('a', {content: '...'}); - assert(collection.views.hasOwnProperty('a')); - }); - - it('should expose collection when chained:', function() { - collection - .use(function(views) { - assert(views instanceof Views); - views.foo = views.addView.bind(views); - }) - .use(function(views) { - assert(views instanceof Views); - views.bar = views.addView.bind(views); - }) - .use(function(views) { - assert(views instanceof Views); - views.baz = views.addView.bind(views); - }); - - var pages = collection; - - pages.foo({path: 'a', content: '...'}); - pages.bar({path: 'b', content: '...'}); - pages.baz({path: 'c', content: '...'}); - - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); - }); - - it('should work when a custom `View` constructor is passed:', function() { - collection = new Views({View: require('vinyl')}); - collection - .use(function(views) { - assert(views instanceof Views); - views.foo = views.addView.bind(views); - }) - .use(function(views) { - assert(views instanceof Views); - views.bar = views.addView.bind(views); - }) - .use(function(views) { - assert(views instanceof Views); - views.baz = views.addView.bind(views); - }); - - var pages = collection; - - pages.foo({path: 'a', content: '...'}); - pages.bar({path: 'b', content: '...'}); - pages.baz({path: 'c', content: '...'}); - - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); - }); - - it('should pass to view `use` if a function is returned:', function() { - collection.use(function(views) { - assert(views instanceof Views); - - return function(view) { - view.foo = views.addView.bind(views); - assert(view instanceof View); - }; - }); - - collection.addView('a', {content: '...'}) - .foo({path: 'b', content: '...'}) - .foo({path: 'c', content: '...'}) - .foo({path: 'd', content: '...'}); - - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); - assert(collection.views.hasOwnProperty('d')); - }); - - it('should be chainable when a view function is returned:', function() { - collection - .use(function(views) { - assert(views instanceof Views); - - return function(view) { - view.foo = views.addView.bind(views); - assert(view instanceof View); - }; - }) - .use(function(views) { - assert(views instanceof Views); - - return function(view) { - view.bar = views.addView.bind(views); - assert(view instanceof View); - }; - }) - .use(function(views) { - assert(views instanceof Views); - - return function(view) { - view.baz = views.addView.bind(views); - assert(view instanceof View); - }; - }); - - collection.addView('a', {content: '...'}) - .foo({path: 'b', content: '...'}) - .bar({path: 'c', content: '...'}) - .baz({path: 'd', content: '...'}); - - assert(collection.views.hasOwnProperty('a')); - assert(collection.views.hasOwnProperty('b')); - assert(collection.views.hasOwnProperty('c')); - assert(collection.views.hasOwnProperty('d')); - }); -}); diff --git a/test/vinyl.js b/test/vinyl.js deleted file mode 100644 index f4d64df0..00000000 --- a/test/vinyl.js +++ /dev/null @@ -1,13 +0,0 @@ -var fs = require('fs'); -var path = require('path'); - -/** - * Placeholder for when assemble-core is updated with the latest vinyl-fs - */ - -// var cwd = path.resolve(__dirname, 'vinyl'); -// fs.readdirSync(cwd).forEach(function(file) { -// if (/\.js$/.test(file)) { -// require(path.resolve(cwd, file)); -// } -// }); diff --git a/test/vinyl/LICENSE b/test/vinyl/LICENSE deleted file mode 100644 index 3ebc3089..00000000 --- a/test/vinyl/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013-2016 Fractal - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/vinyl/dest-modes.js b/test/vinyl/dest-modes.js deleted file mode 100644 index cdcc3cf5..00000000 --- a/test/vinyl/dest-modes.js +++ /dev/null @@ -1,483 +0,0 @@ -'use strict'; - -var os = require('os'); -var path = require('path'); - -var fs = require('graceful-fs'); -var del = require('delete'); -var File = require('vinyl'); -var expect = require('expect'); -var through = require('through2'); - -var vfs = require('vinyl-fs'); - -function wipeOut(cb) { - this.timeout(20000); - - expect.restoreSpies(); - - // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 - del(path.join(__dirname, './fixtures/highwatermark'), function(err) { - if (err) return cb(err); - del(path.join(__dirname, './out-fixtures/'), cb); - }); -} - -var MASK_MODE = parseInt('7777', 8); - -function masked(mode) { - return mode & MASK_MODE; -} - -var isWindows = (os.platform() === 'win32'); -var isDarwin = (os.platform() === 'darwin'); - -describe('.dest() with custom modes', function() { - beforeEach(wipeOut); - afterEach(wipeOut); - - it('should set the mode of a written buffer file if set on the vinyl object', function(done) { - if (isWindows) { - console.log('Changing the mode of a file is not supported by node.js in Windows.'); - console.log('Windows is treated as though it does not have permission to make this operation.'); - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - - - it('should set the sticky bit on the mode of a written stream file if set on the vinyl object', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('1655', 8); - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - stat: { - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - setTimeout(function() { - contentStream.write(expectedContents); - contentStream.end(); - }, 100); - stream.end(); - }); - - it('should set the mode of a written stream file if set on the vinyl object', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('655', 8); - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - stat: { - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - setTimeout(function() { - contentStream.write(expectedContents); - contentStream.end(); - }, 100); - stream.end(); - }); - - it('should set the mode of a written directory if set on the vinyl object', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test'); - var expectedMode = parseInt('655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should set sticky bit on the mode of a written directory if set on the vinyl object', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test'); - var expectedMode = parseInt('1655', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should write new files with the mode specified in options', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('744', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, mode: expectedMode }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should update file mode to match the vinyl mode', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var startMode = parseInt('0655', 8); - var expectedMode = parseInt('0722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, startMode); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should update directory mode to match the vinyl mode', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputBase = path.join(__dirname, './fixtures/'); - var inputPath = path.join(__dirname, './fixtures/wow'); - var expectedPath = path.join(__dirname, './out-fixtures/wow'); - var expectedBase = path.join(__dirname, './out-fixtures'); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: expectedPath, - stat: fs.statSync(inputPath), - }); - var startMode = firstFile.stat.mode; - var expectedMode = parseInt('727', 8); - - var expectedFile = new File(firstFile); - expectedFile.stat.mode = (startMode & ~parseInt('7777', 8)) | expectedMode; - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(firstFile); - stream.write(expectedFile); - stream.end(); - }); - - it('should use different modes for files and directories', function(done) { - if (isWindows) { - this.skip(); - return; - } - - - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - var expectedBase = path.join(__dirname, './out-fixtures/wow'); - var expectedPath = path.join(__dirname, './out-fixtures/wow/suchempty'); - // NOTE: Darwin does not set setgid - var expectedDirMode = isDarwin ? parseInt('755', 8) : parseInt('2755', 8); - var expectedFileMode = parseInt('655', 8); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: fs.readFileSync(inputPath), - stat: fs.statSync(inputPath), - }); - - var onEnd = function() { - expect(masked(fs.lstatSync(expectedBase).mode)).toEqual(expectedDirMode); - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedFileMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { - cwd: __dirname, - mode: expectedFileMode, - dirMode: expectedDirMode, - }); - stream.on('end', onEnd); - stream.write(firstFile); - stream.end(); - }); - - it('should not fchmod a matching file', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('711', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - var onEnd = function() { - expect(fchmodSpy.calls.length).toEqual(0); - expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should see a file with special chmod (setuid/setgid/sticky) as distinct', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('3722', 8); - var normalMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: normalMode, - }, - }); - - var onEnd = function() { - expect(fchmodSpy.calls.length).toEqual(1); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should report fchmod errors', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('722', 8); - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function() { - var callback = arguments[arguments.length - 1]; - callback(new Error('mocked error')); - }); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('error', function(err) { - expect(err).toExist(); - expect(fchmodSpy.calls.length).toEqual(1); - done(); - }); - stream.write(expectedFile); - }); -}); diff --git a/test/vinyl/dest-times.js b/test/vinyl/dest-times.js deleted file mode 100644 index 0af64735..00000000 --- a/test/vinyl/dest-times.js +++ /dev/null @@ -1,223 +0,0 @@ -'use strict'; - -var os = require('os'); -var path = require('path'); - -var fs = require('graceful-fs'); -var del = require('delete'); -var File = require('vinyl'); -var expect = require('expect'); - -var vfs = require('vinyl-fs'); - -function wipeOut() { - this.timeout(20000); - - expect.restoreSpies(); - - // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 - return del(path.join(__dirname, './out-fixtures/')); -} - -var isWindows = (os.platform() === 'win32'); - -describe('.dest() with custom times', function() { - beforeEach(wipeOut); - afterEach(wipeOut); - - it('should not call futimes when no mtime is provided on the vinyl stat', function(done) { - if (isWindows) { - console.log('Changing the time of a directory errors in Windows.'); - console.log('Windows is treated as though it does not have permission to make this operation.'); - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var earlier = Date.now() - 1000; - - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: {}, - }); - - var onEnd = function() { - var stats = fs.lstatSync(expectedPath); - - expect(futimesSpy.calls.length).toEqual(0); - expect(stats.atime.getTime()).toBeGreaterThan(earlier); - expect(stats.mtime.getTime()).toBeGreaterThan(earlier); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should call futimes when an mtime is provided on the vinyl stat', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMtime = fs.lstatSync(inputPath).mtime; - - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mtime: expectedMtime, - }, - }); - - var onEnd = function() { - var stats = fs.lstatSync(expectedPath); - - expect(futimesSpy.calls.length).toEqual(1); - expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); - expect(expectedFile.stat.mtime).toEqual(expectedMtime); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should not call futimes when provided mtime on the vinyl stat is invalid', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var earlier = Date.now() - 1000; - - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mtime: new Date(undefined), - }, - }); - - var onEnd = function() { - var stats = fs.lstatSync(expectedPath); - - expect(futimesSpy.calls.length).toEqual(0); - expect(stats.mtime.getTime()).toBeGreaterThan(earlier); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should call futimes when provided mtime on the vinyl stat is valid but provided atime is invalid', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMtime = fs.lstatSync(inputPath).mtime; - var invalidAtime = new Date(undefined); - - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - atime: invalidAtime, - mtime: expectedMtime, - }, - }); - - var onEnd = function() { - var stats = fs.lstatSync(expectedPath); - - expect(futimesSpy.calls.length).toEqual(1); - expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); - - it('should write file atime and mtime using the vinyl stat', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedAtime = fs.lstatSync(inputPath).atime; - var expectedMtime = fs.lstatSync(inputPath).mtime; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - atime: expectedAtime, - mtime: expectedMtime, - }, - }); - - var onEnd = function() { - var stats = fs.lstatSync(expectedPath); - - expect(stats.atime.getTime()).toEqual(expectedAtime.getTime()); - expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); - expect(expectedFile.stat.mtime).toEqual(expectedMtime); - expect(expectedFile.stat.atime).toEqual(expectedAtime); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('end', onEnd); - stream.write(expectedFile); - stream.end(); - }); -}); diff --git a/test/vinyl/dest.js b/test/vinyl/dest.js deleted file mode 100644 index 551f8ea7..00000000 --- a/test/vinyl/dest.js +++ /dev/null @@ -1,1229 +0,0 @@ -'use strict'; - -var spies = require('./spy'); -var chmodSpy = spies.chmodSpy; -var fchmodSpy = spies.fchmodSpy; -var futimesSpy = spies.futimesSpy; -var fstatSpy = spies.fstatSpy; - -var vfs = require('vinyl-fs'); - -var os = require('os'); -var path = require('path'); -var fs = require('graceful-fs'); -var del = require('delete'); -var Writeable = require('readable-stream/writable'); -var expect = require('expect'); - -var bufEqual = require('buffer-equal'); -var through = require('through2'); -var File = require('vinyl'); - -var should = require('should'); -require('mocha'); - -function wipeOut(cb) { - this.timeout(20000); - spies.setError('false'); - fstatSpy.reset(); - chmodSpy.reset(); - fchmodSpy.reset(); - futimesSpy.reset(); - expect.restoreSpies(); - - // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 - del(path.join(__dirname, './fixtures/highwatermark'), function(err) { - if (err) return cb(err); - del(path.join(__dirname, './out-fixtures/'), cb); - }); -}; - -var dataWrap = function(fn) { - return function(data, enc, cb) { - fn(data); - cb(); - }; -}; - -var realMode = function(n) { - return n & parseInt('777', 8); -}; - -function noop() {} - -describe('dest stream', function() { - beforeEach(wipeOut); - afterEach(wipeOut); - - it.skip('should explode on invalid folder (empty)', function(done) { - var stream; - try { - stream = vfs.dest(); - } catch (err) { - should.exist(err); - should.not.exist(stream); - done(); - } - }); - - it.skip('should explode on invalid folder (empty string)', function(done) { - var stream; - try { - stream = vfs.dest(''); - } catch (err) { - should.exist(err); - should.not.exist(stream); - done(); - } - }); - - it('should not explode if the sourcemap option is true', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - var stream = vfs.dest(path.join(__dirname, './out-fixtures/'), { sourcemaps: true }); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not explode if the sourcemap option is string', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - var stream = vfs.dest(path.join(__dirname, './out-fixtures/'), { sourcemaps: '.' }); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not explode if sourcemap option is an object', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var options = { - sourcemaps: { - addComment: false, - }, - }; - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - var stream = vfs.dest(path.join(__dirname, './out-fixtures/'), options); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should pass through writes with cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should pass through writes with default cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - done(); - }; - - var stream = vfs.dest(path.join(__dirname, './out-fixtures/')); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not write null files', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(false); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with relative cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: path.relative(process.cwd(), __dirname) }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder with function and relative cwd', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedContents = fs.readFileSync(inputPath); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); - }; - - var stream = vfs.dest(function(file) { - should.exist(file); - file.should.equal(expectedFile); - return './out-fixtures'; - }, { cwd: path.relative(process.cwd(), __dirname) }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write buffer files to the right folder', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write streaming files to the right folder', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered))); - bufferStream.on('finish', onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - setTimeout(function() { - contentStream.write(expectedContents); - contentStream.end(); - }, 100); - stream.end(); - }); - - it('should write directories to the right folder', function(done) { - var inputPath = path.join(__dirname, './fixtures/test'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - }, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).isDirectory().should.equal(true); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should allow piping multiple dests in streaming mode', function(done) { - var inputPath1 = path.join(__dirname, './out-fixtures/multiple-first'); - var inputPath2 = path.join(__dirname, './out-fixtures/multiple-second'); - var inputBase = path.join(__dirname, './out-fixtures/'); - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var stream1 = vfs.dest('./out-fixtures/', { cwd: __dirname }); - var stream2 = vfs.dest('./out-fixtures/', { cwd: __dirname }); - var content = fs.readFileSync(srcPath); - var rename = through.obj(function(file, _, next) { - file.path = inputPath2; - this.push(file); - next(); - }); - - stream1.on('data', function(file) { - file.path.should.equal(inputPath1); - }); - - stream1.pipe(rename).pipe(stream2); - stream2.on('data', function(file) { - file.path.should.equal(inputPath2); - }).once('end', function() { - fs.readFileSync(inputPath1, 'utf8').should.equal(content.toString()); - fs.readFileSync(inputPath2, 'utf8').should.equal(content.toString()); - done(); - }); - - var file = new File({ - base: inputBase, - path: inputPath1, - cwd: __dirname, - contents: content, - }); - - stream1.write(file); - stream1.end(); - }); - - it('should write new files with the default user mode', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('666', 8) & (~process.umask()); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - chmodSpy.reset(); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should change to the specified base as string', function(done) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - - var firstFile = new File({ - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath), - }); - - var buffered = []; - - var onEnd = function() { - buffered[0].base.should.equal(inputBase); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { - cwd: __dirname, - base: inputBase, - }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should change to the specified base as function', function(done) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - - var firstFile = new File({ - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath), - }); - - var buffered = []; - - var onEnd = function() { - buffered[0].base.should.equal(inputBase); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { - cwd: __dirname, - base: function(file) { - should.exist(file); - file.path.should.equal(inputPath); - return inputBase; - }, - }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should report IO errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, 0); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('error', function(err) { - expect(err).toExist(); - done(); - }); - stream.write(expectedFile); - }); - - it('should report stat errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - spies.setError(function(mod, fn) { - if (fn === 'fstat' && typeof arguments[2] === 'number') { - return new Error('stat error'); - } - }); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('error', function(err) { - err.message.should.equal('stat error'); - done(); - }); - stream.write(expectedFile); - }); - - it('should not overwrite files with overwrite option set to false', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, './out-fixtures'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); - done(); - }; - - // Write expected file which should not be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: false }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should overwrite files with overwrite option set to true', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, './out-fixtures'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); - done(); - }; - - // This should be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: true }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should not overwrite files with overwrite option set to a function that returns false', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, './out-fixtures'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(existingContents)).should.equal(true); - done(); - }; - - // Write expected file which should not be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: function() { - return false; - }, }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should overwrite files with overwrite option set to a function that returns true', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var inputContents = fs.readFileSync(inputPath); - - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedBase = path.join(__dirname, './out-fixtures'); - var existingContents = 'Lorem Ipsum'; - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: inputContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - bufEqual(fs.readFileSync(expectedPath), new Buffer(inputContents)).should.equal(true); - done(); - }; - - // This should be overwritten - fs.mkdirSync(expectedBase); - fs.writeFileSync(expectedPath, existingContents); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, overwrite: function() { - return true; - }, }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should create symlinks when the `symlink` attribute is set on the file', function(done) { - var inputPath = path.join(__dirname, './fixtures/test-create-dir-symlink'); - var inputBase = path.join(__dirname, './fixtures/'); - var inputRelativeSymlinkPath = 'wow'; - - var expectedPath = path.join(__dirname, './out-fixtures/test-create-dir-symlink'); - - var inputFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: null, // '' - }); - - // `src()` adds this side-effect with `keepSymlinks` option set to false - inputFile.symlink = inputRelativeSymlinkPath; - - var buffered = []; - - var onEnd = function() { - fs.readlink(buffered[0].path, function() { - buffered[0].symlink.should.equal(inputFile.symlink); - buffered[0].path.should.equal(expectedPath); - done(); - }); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.on('error', done); - stream.pipe(bufferStream); - stream.write(inputFile); - stream.end(); - }); - - it('should emit finish event', function(done) { - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - stream.once('finish', function() { - done(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: new Buffer('1234567890'), - }); - - stream.write(file); - stream.end(); - }); - - it('does not get clogged by highWaterMark', function(done) { - fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); - for (var idx = 0; idx < 17; idx++) { - fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); - } - - var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); - var srcStream = vfs.src(srcPath); - var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var fileCount = 0; - var countFiles = through.obj(function(file, enc, cb) { - fileCount++; - - cb(null, file); - }); - - destStream.once('finish', function() { - fileCount.should.equal(17); - done(); - }); - - srcStream.pipe(countFiles).pipe(destStream); - }); - - it('allows backpressure when piped to another, slower stream', function(done) { - this.timeout(20000); - - fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); - for (var idx = 0; idx < 24; idx++) { - fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); - } - - var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); - var srcStream = vfs.src(srcPath); - var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var fileCount = 0; - var countFiles = through.obj(function(file, enc, cb) { - fileCount++; - - cb(null, file); - }); - - var slowFileCount = 0; - var slowCountFiles = new Writeable({ - objectMode: true, - write: function(file, enc, cb) { - slowFileCount++; - - setTimeout(function() { - cb(null, file); - }, 250); - }, - }); - - slowCountFiles.once('finish', function() { - fileCount.should.equal(24); - slowFileCount.should.equal(24); - done(); - }); - - srcStream - .pipe(countFiles) - .pipe(destStream) - .pipe(slowCountFiles); - }); - - it('should respect readable listeners on destination stream', function(done) { - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var srcStream = vfs.src(srcPath); - var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - srcStream - .pipe(destStream); - - var readables = 0; - destStream.on('readable', function() { - var data = destStream.read(); - - if (data != null) { - readables++; - } - }); - - destStream.on('error', done); - - destStream.on('finish', function() { - readables.should.equal(1); - done(); - }); - }); - - it('should respect data listeners on destination stream', function(done) { - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var srcStream = vfs.src(srcPath); - var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - srcStream - .pipe(destStream); - - var datas = 0; - destStream.on('data', function() { - datas++; - }); - - destStream.on('error', done); - - destStream.on('finish', function() { - datas.should.equal(1); - done(); - }); - }); - - it('sinks the stream if all the readable event handlers are removed', function(done) { - fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); - for (var idx = 0; idx < 17; idx++) { - fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); - } - - var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); - var srcStream = vfs.src(srcPath); - var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var fileCount = 0; - var countFiles = through.obj(function(file, enc, cb) { - fileCount++; - - cb(null, file); - }); - - destStream.on('readable', noop); - - destStream.once('finish', function() { - fileCount.should.equal(17); - done(); - }); - - srcStream.pipe(countFiles).pipe(destStream); - - process.nextTick(function() { - destStream.removeListener('readable', noop); - }); - }); - - it('sinks the stream if all the data event handlers are removed', function(done) { - - this.timeout(10000); - - fs.mkdirSync(path.join(__dirname, './fixtures/highwatermark')); - for (var idx = 0; idx < 17; idx++) { - fs.writeFileSync(path.join(__dirname, './fixtures/highwatermark/', 'file' + idx + '.txt')); - } - - var srcPath = path.join(__dirname, './fixtures/highwatermark/*.txt'); - var srcStream = vfs.src(srcPath); - var destStream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var fileCount = 0; - function onData() { - fileCount++; - } - - var countFiles = through.obj(function(file, enc, cb) { - onData(); - - cb(null, file); - }); - - destStream.on('data', onData); - - destStream.once('finish', function() { - fileCount.should.equal(17); - done(); - }); - - srcStream.pipe(countFiles).pipe(destStream); - - process.nextTick(function() { - destStream.removeListener('data', onData); - }); - }); - - it('should pass options to through2', function(done) { - var srcPath = path.join(__dirname, './fixtures/test.coffee'); - var content = fs.readFileSync(srcPath); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, objectMode: false }); - - stream.on('error', function(err) { - err.should.match(/Invalid non-string\/buffer chunk/); - done(); - }); - - var file = new File({ - path: srcPath, - cwd: __dirname, - contents: content, - }); - - stream.write(file); - stream.end(); - }); - - it('should successfully process unbuffered items', function(done) { - var srcPath = path.join(__dirname, './fixtures/*'); - var srcStream = vfs.src(srcPath, { buffer: false }); - var destStream = vfs.dest('./out-fixtures', { cwd: __dirname }); - - srcStream - .pipe(destStream) - .once('finish', done); - }); - - it('should not exhaust available file descriptors when streaming thousands of files', function(done) { - // This can be a very slow test on boxes with slow disk i/o - this.timeout(0); - - // Make a ton of files. Changed from hard links due to Windows failures - var numFiles = 6000; - fs.mkdirSync(path.join(__dirname, './out-fixtures')); - fs.mkdirSync(path.join(__dirname, './out-fixtures/in/')); - - for (var idx = 0; idx < numFiles; idx++) { - fs.writeFileSync(path.join(__dirname, './out-fixtures/in/test' + idx + '.coffee'), ''); - } - - var srcStream = vfs.src(path.join(__dirname, './out-fixtures/in/*.coffee'), { buffer: false }); - var destStream = vfs.dest('./out-fixtures/out/', { cwd: __dirname }); - - var fileCount = 0; - - srcStream - .pipe(through.obj(function(file, enc, cb) { - fileCount++; - - cb(null, file); - })) - .pipe(destStream) - .once('finish', function() { - fileCount.should.equal(numFiles); - done(); - }); - }); - - it('errors if we cannot mkdirp', function(done) { - var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { - var callback = arguments[arguments.length - 1]; - callback(new Error('mocked error')); - }); - - var outputDir = path.join(__dirname, './out-fixtures/'); - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - }); - - var stream = vfs.dest(outputDir); - stream.write(expectedFile); - stream.on('error', function(err) { - expect(err).toExist(); - expect(mkdirSpy.calls.length).toEqual(1); - done(); - }); - }); - - it('errors if vinyl object is a directory and we cannot mkdirp', function(done) { - var ogMkdir = fs.mkdir; - - var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { - if (mkdirSpy.calls.length > 1) { - var callback = arguments[arguments.length - 1]; - callback(new Error('mocked error')); - } else { - ogMkdir.apply(null, arguments); - } - }); - - var outputDir = path.join(__dirname, './out-fixtures/'); - var inputPath = path.join(__dirname, './other-dir/'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - }, - }); - - var stream = vfs.dest(outputDir); - stream.write(expectedFile); - stream.on('error', function(err) { - expect(err).toExist(); - expect(mkdirSpy.calls.length).toEqual(2); - done(); - }); - }); - - // TODO: is this correct behavior? had to adjust it - it('does not error if vinyl object is a directory and we cannot open it', function(done) { - var outputDir = path.join(__dirname, './out-fixtures/'); - var inputPath = path.join(__dirname, './other-dir/'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - mode: parseInt('000', 8), - }, - }); - - var stream = vfs.dest(outputDir); - stream.write(expectedFile); - stream.on('error', function(err) { - expect(err).toNotExist(); - done(err); - }); - stream.end(function() { - var exists = fs.existsSync(path.join(outputDir, './other-dir/')); - expect(exists).toEqual(true); - done(); - }); - }); - - it('errors if vinyl object is a directory and open errors', function(done) { - var openSpy = expect.spyOn(fs, 'open').andCall(function(writePath, flag, cb) { - cb(new Error('mocked error')); - }); - - var outputDir = path.join(__dirname, './out-fixtures/'); - var inputPath = path.join(__dirname, './other-dir/'); - - var expectedFile = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - isDirectory: function() { - return true; - }, - }, - }); - - var stream = vfs.dest(outputDir); - stream.write(expectedFile); - stream.on('error', function(err) { - expect(err).toExist(); - expect(openSpy.calls.length).toEqual(1); - done(); - }); - }); - - it('error if content stream errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - - var contentStream = through.obj(); - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: contentStream, - }); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.write(expectedFile); - setTimeout(function() { - contentStream.emit('error', new Error('mocked error')); - }, 100); - stream.on('error', function(err) { - expect(err).toExist(); - done(); - }); - }); -}); diff --git a/test/vinyl/file-operations.js b/test/vinyl/file-operations.js deleted file mode 100644 index 748e1a0c..00000000 --- a/test/vinyl/file-operations.js +++ /dev/null @@ -1,917 +0,0 @@ -'use strict'; - -var expect = require('expect'); - -var os = require('os'); -var fs = require('graceful-fs'); -var del = require('delete'); -var path = require('path'); -var File = require('vinyl'); -var buffer = require('buffer'); -var defaultResolution = require('default-resolution'); - -var fo = require('vinyl-fs/lib/file-operations'); - -var closeFd = fo.closeFd; -var isOwner = fo.isOwner; -var writeFile = fo.writeFile; -var getModeDiff = fo.getModeDiff; -var getTimesDiff = fo.getTimesDiff; -var updateMetadata = fo.updateMetadata; - -var resolution = defaultResolution(); - -var MASK_MODE = parseInt('7777', 8); - -function masked(mode) { - return mode & MASK_MODE; -} - -function noop() {} - -var isWindows = (os.platform() === 'win32'); - -describe('isOwner', function() { - - var ownerStat = { - uid: 9001, - }; - - var nonOwnerStat = { - uid: 9002, - }; - - var getuidSpy; - var geteuidSpy; - - beforeEach(function(done) { - if (typeof process.geteuid !== 'function') { - process.geteuid = noop; - } - - // Windows :( - if (typeof process.getuid !== 'function') { - process.getuid = noop; - } - - getuidSpy = expect.spyOn(process, 'getuid').andReturn(ownerStat.uid); - geteuidSpy = expect.spyOn(process, 'geteuid').andReturn(ownerStat.uid); - - done(); - }); - - afterEach(function(done) { - expect.restoreSpies(); - - if (process.geteuid === noop) { - delete process.geteuid; - } - - // Windows :( - if (process.getuid === noop) { - delete process.getuid; - } - - done(); - }); - - // TODO: test for having neither - - it('uses process.geteuid() when available', function(done) { - - isOwner(ownerStat); - - expect(getuidSpy.calls.length).toEqual(0); - expect(geteuidSpy.calls.length).toEqual(1); - - done(); - }); - - it('uses process.getuid() when geteuid() is not available', function(done) { - delete process.geteuid; - - isOwner(ownerStat); - - expect(getuidSpy.calls.length).toEqual(1); - - done(); - }); - - it('returns false when non-root and non-owner', function(done) { - var result = isOwner(nonOwnerStat); - - expect(result).toEqual(false); - - done(); - }); - - it('returns true when owner and non-root', function(done) { - var result = isOwner(ownerStat); - - expect(result).toEqual(true); - - done(); - }); - - it('returns true when non-owner but root', function(done) { - expect.spyOn(process, 'geteuid').andReturn(0); // 0 is root uid - - var result = isOwner(nonOwnerStat); - - expect(result).toEqual(true); - - done(); - }); -}); - -describe('getModeDiff', function() { - - it('returns 0 if both modes are the same', function(done) { - var fsMode = parseInt('777', 8); - var vfsMode = parseInt('777', 8); - - var result = getModeDiff(fsMode, vfsMode); - - expect(result).toEqual(0); - - done(); - }); - - it('returns 0 if vinyl mode is not a number', function(done) { - var fsMode = parseInt('777', 8); - var vfsMode = undefined; - - var result = getModeDiff(fsMode, vfsMode); - - expect(result).toEqual(0); - - done(); - }); - - it('returns a value greater than 0 if modes are different', function(done) { - var fsMode = parseInt('777', 8); - var vfsMode = parseInt('744', 8); - - var result = getModeDiff(fsMode, vfsMode); - - expect(result).toEqual(27); - - done(); - }); - - it('does not matter the order of diffing', function(done) { - var fsMode = parseInt('655', 8); - var vfsMode = parseInt('777', 8); - - var result = getModeDiff(fsMode, vfsMode); - - expect(result).toEqual(82); - - done(); - }); - - it('includes the sticky/setuid/setgid bits', function(done) { - var fsMode = parseInt('1777', 8); - var vfsMode = parseInt('4777', 8); - - var result = getModeDiff(fsMode, vfsMode); - - expect(result).toEqual(fsMode ^ vfsMode); - - done(); - }); -}); - -describe('getTimesDiff', function() { - - it('returns undefined if vinyl mtime is not a valid date', function(done) { - var fsStat = { - mtime: new Date(), - }; - var vfsStat = { - mtime: new Date(undefined), - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(undefined); - - done(); - }); - - it('returns undefined if vinyl mtime & atime are both equal to counterparts', function(done) { - var now = Date.now(); - var fsStat = { - mtime: new Date(now), - atime: new Date(now), - }; - var vfsStat = { - mtime: new Date(now), - atime: new Date(now), - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(undefined); - - done(); - }); - - // TODO: is this proper/expected? - it('returns undefined if vinyl mtimes equals the counterpart and atimes are null', function(done) { - var now = Date.now(); - var fsStat = { - mtime: new Date(now), - atime: null, - }; - var vfsStat = { - mtime: new Date(now), - atime: null, - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(undefined); - - done(); - }); - - it('returns a diff object if mtimes do not match', function(done) { - var now = Date.now(); - var then = now - 1000; - var fsStat = { - mtime: new Date(now), - }; - var vfsStat = { - mtime: new Date(then), - }; - var expected = { - mtime: new Date(then), - atime: undefined, - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(expected); - - done(); - }); - - it('returns a diff object if atimes do not match', function(done) { - var now = Date.now(); - var then = now - 1000; - var fsStat = { - mtime: new Date(now), - atime: new Date(now), - }; - var vfsStat = { - mtime: new Date(now), - atime: new Date(then), - }; - var expected = { - mtime: new Date(now), - atime: new Date(then), - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(expected); - - done(); - }); - - it('returns the fs atime if the vinyl atime is invalid', function(done) { - var now = Date.now(); - var fsStat = { - mtime: new Date(now), - atime: new Date(now), - }; - var vfsStat = { - mtime: new Date(now), - atime: new Date(undefined), - }; - var expected = { - mtime: new Date(now), - atime: new Date(now), - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(expected); - - done(); - }); - - // TODO: is this proper/expected? - it('makes atime diff undefined if fs and vinyl atime are invalid', function(done) { - var now = Date.now(); - var fsStat = { - mtime: new Date(now), - atime: new Date(undefined), - }; - var vfsStat = { - mtime: new Date(now), - atime: new Date(undefined), - }; - var expected = { - mtime: new Date(now), - atime: undefined, - }; - - var result = getTimesDiff(fsStat, vfsStat); - - expect(result).toEqual(expected); - - done(); - }); -}); - -describe('closeFd', function() { - - it('calls the callback with propagated error if fd is not a number', function(done) { - var propagatedError = new Error(); - - closeFd(propagatedError, null, function(err) { - expect(err).toEqual(propagatedError); - - done(); - }); - }); - - it('calls the callback with close error if no error to propagate', function(done) { - closeFd(null, -1, function(err) { - expect(err).toExist(); - - done(); - }); - }); - - it('calls the callback with propagated error if close errors', function(done) { - var propagatedError = new Error(); - - closeFd(propagatedError, -1, function(err) { - expect(err).toEqual(propagatedError); - - done(); - }); - }); - - it('calls the callback with propagated error if close succeeds', function(done) { - var propagatedError = new Error(); - - var fd = fs.openSync(path.join(__dirname, './fixtures/test.coffee'), 'r'); - - var spy = expect.spyOn(fs, 'close').andCallThrough(); - - closeFd(propagatedError, fd, function(err) { - spy.restore(); - - expect(spy.calls.length).toEqual(1); - expect(err).toEqual(propagatedError); - - done(); - }); - }); - - it('calls the callback with no error if close succeeds & no propagated error', function(done) { - var fd = fs.openSync(path.join(__dirname, './fixtures/test.coffee'), 'r'); - - var spy = expect.spyOn(fs, 'close').andCallThrough(); - - closeFd(null, fd, function(err) { - spy.restore(); - - expect(spy.calls.length).toEqual(1); - expect(err).toEqual(undefined); - - done(); - }); - }); -}); - -describe('writeFile', function() { - - var filepath; - - beforeEach(function(done) { - filepath = path.join(__dirname, './fixtures/writeFile.txt'); - - done(); - }); - - afterEach(function() { - // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 - return del(filepath); - }); - - it('writes a file to the filesystem, does not close and returns the fd', function(done) { - var expected = 'test'; - var content = new Buffer(expected); - - writeFile(filepath, content, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, function() { - var written = fs.readFileSync(filepath, 'utf-8'); - - expect(written).toEqual(expected); - - done(); - }); - }); - }); - - it('defaults to writing files with 0666 mode', function(done) { - var expected = parseInt('0666', 8) & (~process.umask()); - var content = new Buffer('test'); - - writeFile(filepath, content, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, function() { - var stats = fs.lstatSync(filepath); - - expect(masked(stats.mode)).toEqual(expected); - - done(); - }); - }); - }); - - it('accepts a different mode in options', function(done) { - if (isWindows) { - console.log('Changing the mode of a file is not supported by node.js in Windows.'); - this.skip(); - return; - } - - var expected = parseInt('0777', 8) & (~process.umask()); - var content = new Buffer('test'); - var options = { - mode: parseInt('0777', 8), - }; - - writeFile(filepath, content, options, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, function() { - var stats = fs.lstatSync(filepath); - - expect(masked(stats.mode)).toEqual(expected); - - done(); - }); - }); - }); - - it('defaults to opening files with write flag', function(done) { - var content = new Buffer('test'); - - writeFile(filepath, content, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.read(fd, new Buffer(4), 0, 4, 0, function(readErr) { - expect(readErr).toExist(); - - fs.close(fd, done); - }); - }); - }); - - it('accepts a different flag in options', function(done) { - var expected = 'test'; - var content = new Buffer(expected); - var options = { - flag: 'w+', - }; - - writeFile(filepath, content, options, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.read(fd, new Buffer(4), 0, 4, 0, function(readErr, _, written) { - expect(readErr).toNotExist(); - - expect(written.toString()).toEqual(expected); - - fs.close(fd, done); - }); - }); - }); - - it('appends to a file if append flag is given', function(done) { - var initial = 'test'; - var toWrite = '-a-thing'; - - fs.writeFileSync(filepath, initial, 'utf-8'); - - var expected = initial + toWrite; - - var content = new Buffer(toWrite); - var options = { - flag: 'a', - }; - - writeFile(filepath, content, options, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, function() { - var written = fs.readFileSync(filepath, 'utf-8'); - - expect(written).toEqual(expected); - - done(); - }); - }); - }); - - it('does not pass a file descriptor if open call errors', function(done) { - filepath = path.join(__dirname, './not-exist-dir/writeFile.txt'); - var content = new Buffer('test'); - - writeFile(filepath, content, function(err, fd) { - expect(err).toExist(); - expect(typeof fd === 'number').toEqual(false); - - done(); - }); - }); - - it('passes a file descriptor if write call errors', function(done) { - var existsFilepath = path.join(__dirname, './fixtures/test.coffee'); // File must exist - var content = new Buffer('test'); - var options = { - flag: 'r', - }; - - writeFile(existsFilepath, content, options, function(err, fd) { - expect(err).toExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, done); - }); - }); - - it('passes an error if called with string as data', function(done) { - writeFile(filepath, 'test', function(err) { - expect(err).toExist(); - - done(); - }); - }); - - it('does not error on SlowBuffer', function(done) { - if (!buffer.SlowBuffer) { - this.skip(); - return; - } - - var expected = 'test'; - var buf = new Buffer(expected); - var content = new buffer.SlowBuffer(4); - buf.copy(content, 0, 0, 4); - - writeFile(filepath, content, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, function() { - var written = fs.readFileSync(filepath, 'utf-8'); - - expect(written).toEqual(expected); - - done(); - }); - }); - }); - - it('does not error if options is falsey', function(done) { - var content = new Buffer('test'); - writeFile(filepath, content, null, function(err, fd) { - expect(err).toNotExist(); - expect(typeof fd === 'number').toEqual(true); - - fs.close(fd, done); - }); - }); -}); - -describe('updateMetadata', function() { - - var inputPath = path.join(__dirname, './fixtures/stats.txt'); - var file; - - beforeEach(function(done) { - file = new File({ - base: __dirname, - cwd: __dirname, - path: inputPath, - contents: null, - stat: { - - }, - }); - - done(); - }); - - afterEach(function(done) { - expect.restoreSpies(); - - del.sync(inputPath); - - if (process.geteuid === noop) { - delete process.geteuid; - } - - done(); - }); - - it('passes the error and file descriptor if fstat fails', function(done) { - if (isWindows) { - console.log('Changing the time of a directory errors in Windows.'); - console.log('Changing the mode of a file is not supported by node.js in Windows.'); - console.log('Windows is treated as though it does not have permission to make these operations.'); - this.skip(); - return; - } - - var fd = 9001; - - updateMetadata(fd, file, function(err, fd2) { - expect(err).toExist(); - expect(typeof fd === 'number').toEqual(true); - expect(fd2).toEqual(fd); - - done(); - }); - }); - - it('updates the vinyl object with fs stats', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fd = fs.openSync(inputPath, 'w+'); - var stats = fs.fstatSync(fd); - - updateMetadata(fd, file, function(err, fd2) { - // Not sure why .toEqual doesn't match these - Object.keys(file.stat).forEach(function(key) { - expect(file.stat[key]).toEqual(stats[key]); - }); - - fs.close(fd2, done); - }); - }); - - it('does not touch the fs if nothing to update', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(fchmodSpy.calls.length).toEqual(0); - expect(futimesSpy.calls.length).toEqual(0); - - fs.close(fd2, done); - }); - }); - - it('does not touch the fs if process is not owner of the file', function(done) { - if (isWindows) { - this.skip(); - return; - } - - if (typeof process.geteuid !== 'function') { - process.geteuid = noop; - } - - expect.spyOn(process, 'geteuid').andReturn(9002); - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - file.stat.mtime = new Date(Date.now() - 1000); - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(fchmodSpy.calls.length).toEqual(0); - expect(futimesSpy.calls.length).toEqual(0); - - fs.close(fd2, done); - }); - }); - - it('updates times on fs and vinyl object if there is a diff', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var now = Date.now(); - var then = now - 1000; - file.stat.mtime = new Date(then); - file.stat.atime = new Date(then); - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(futimesSpy.calls.length).toEqual(1); - var stats = fs.fstatSync(fd); - var mtimeMs = Date.parse(file.stat.mtime); - var mtime = resolution ? mtimeMs - (mtimeMs % resolution) : mtimeMs; - var atimeMs = Date.parse(file.stat.atime); - var atime = resolution ? atimeMs - (atimeMs % resolution) : atimeMs; - expect(file.stat.mtime).toEqual(new Date(then)); - expect(mtime).toEqual(Date.parse(stats.mtime)); - expect(file.stat.atime).toEqual(new Date(then)); - expect(atime).toEqual(Date.parse(stats.atime)); - - fs.close(fd2, done); - }); - }); - - it('forwards futimes error and descriptor upon error', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var futimesSpy = expect.spyOn(fs, 'futimes').andCall(function(fd, atime, mtime, cb) { - cb(new Error('mocked error')); - }); - - var now = Date.now(); - var then = now - 1000; - file.stat.mtime = new Date(then); - file.stat.atime = new Date(then); - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(err).toExist(); - expect(futimesSpy.calls.length).toEqual(1); - expect(typeof fd2 === 'number').toEqual(true); - - fs.close(fd2, done); - }); - }); - - it('updates the mode on fs and vinyl object if there is a diff', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - - var mode = parseInt('777', 8); - file.stat.mode = mode; - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(fchmodSpy.calls.length).toEqual(1); - var stats = fs.fstatSync(fd); - expect(file.stat.mode).toEqual(stats.mode); - - fs.close(fd2, done); - }); - }); - - - it('updates the sticky bit on mode on fs and vinyl object if there is a diff', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - - var mode = parseInt('1777', 8); - file.stat.mode = mode; - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(fchmodSpy.calls.length).toEqual(1); - var stats = fs.fstatSync(fd); - expect(file.stat.mode).toEqual(stats.mode); - - fs.close(fd2, done); - }); - }); - - it('forwards fchmod error and descriptor upon error', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var mode = parseInt('777', 8); - file.stat.mode = mode; - - var fd = fs.openSync(inputPath, 'w+'); - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { - cb(new Error('mocked error')); - }); - - updateMetadata(fd, file, function(err, fd2) { - expect(err).toExist(); - expect(fchmodSpy.calls.length).toEqual(1); - expect(typeof fd2 === 'number').toEqual(true); - - fs.close(fd2, done); - }); - }); - - it('updates the mode & times on fs and vinyl object if there is a diff', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var mode = parseInt('777', 8); - file.stat.mode = mode; - - var now = Date.now(); - var then = now - 1000; - file.stat.mtime = new Date(then); - file.stat.atime = new Date(then); - - var fd = fs.openSync(inputPath, 'w+'); - - updateMetadata(fd, file, function(err, fd2) { - expect(fchmodSpy.calls.length).toEqual(1); - expect(futimesSpy.calls.length).toEqual(1); - - var stats = fs.fstatSync(fd); - var mtimeMs = Date.parse(file.stat.mtime); - var mtime = resolution ? mtimeMs - (mtimeMs % resolution) : mtimeMs; - var atimeMs = Date.parse(file.stat.atime); - var atime = resolution ? atimeMs - (atimeMs % resolution) : atimeMs; - - expect(file.stat.mtime).toEqual(new Date(then)); - expect(mtime).toEqual(Date.parse(stats.mtime)); - expect(file.stat.atime).toEqual(new Date(then)); - expect(atime).toEqual(Date.parse(stats.atime)); - expect(file.stat.mode).toEqual(stats.mode); - - fs.close(fd2, done); - }); - }); - - it('forwards fchmod error and descriptor through futimes if there is a time diff', function(done) { - if (isWindows) { - this.skip(); - return; - } - - var expectedErr = new Error('mocked error'); - - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { - cb(expectedErr); - }); - var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); - - var mode = parseInt('777', 8); - file.stat.mode = mode; - - var now = Date.now(); - var then = now - 1000; - file.stat.mtime = new Date(then); - file.stat.atime = new Date(then); - - var fd = fs.openSync(inputPath, 'w'); - - updateMetadata(fd, file, function(err, fd2) { - expect(err).toExist(); - expect(err).toEqual(expectedErr); - expect(fchmodSpy.calls.length).toEqual(1); - expect(futimesSpy.calls.length).toEqual(1); - expect(typeof fd2 === 'number').toEqual(true); - - fs.close(fd2, done); - }); - }); -}); diff --git a/test/vinyl/fixtures/bom-utf16be.txt b/test/vinyl/fixtures/bom-utf16be.txt deleted file mode 100644 index b9dce78a5d31af4803acd1a0f0dfc14f064a5de1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 diff --git a/test/vinyl/fixtures/bom-utf16le.txt b/test/vinyl/fixtures/bom-utf16le.txt deleted file mode 100644 index 07cc600c98675d221bb56d10af38e650538734c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15l Date: Tue, 28 Jun 2016 14:55:12 -0400 Subject: [PATCH 253/282] run update --- .travis.yml | 1 + package.json | 53 ++++++++++++++++++++-------------------------------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb1d261d..e9f3361b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,5 +8,6 @@ node_js: matrix: fast_finish: true allow_failures: + - node_js: '4' - node_js: '0.10' - node_js: '0.12' diff --git a/package.json b/package.json index ae0dad76..5ec66460 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "files": [ "bin", "index.js", - "lib", - "utils.js" + "lib" ], "main": "index.js", "preferGlobal": true, @@ -46,20 +45,16 @@ "set-blocking": "^2.0.0", "set-value": "^0.3.3", "through2": "^2.0.1", - "vinyl": "^1.1.1", "yargs-parser": "^2.4.0" }, "devDependencies": { - "async-each": "^1.0.0", + "base-config-process": "^0.1.7", + "base-generators": "^0.4.1", + "base-questions": "^0.6.6", "base-store": "^0.4.4", - "buffer-equal": "^1.0.0", - "consolidate": "^0.14.1", - "default-resolution": "^2.0.0", - "define-property": "^0.2.5", - "delete": "^0.3.2", - "engine-handlebars": "^0.8.0", - "event-stream": "^3.3.3", - "expect": "^1.20.1", + "base-test-runner": "^0.2.0", + "base-test-suite": "^0.1.11", + "cross-spawn": "^4.0.0", "generate-foo": "^0.1.5", "generator-util": "^0.2.9", "global-modules": "^0.2.2", @@ -70,22 +65,14 @@ "gulp-mocha": "^2.2.0", "gulp-reflinks": "^0.1.0", "gulp-unused": "^0.1.2", - "helper-example": "^0.1.0", - "is-buffer": "^1.1.3", - "kind-of": "^3.0.3", + "is-absolute": "^0.2.5", "load-pkg": "^3.0.1", "mocha": "^2.5.3", - "parser-front-matter": "^1.3.0", - "readable-stream": "^2.1.4", - "resolve-glob": "^0.1.8", - "rimraf": "^2.5.2", + "npm-install-global": "^0.1.2", + "resolve": "^1.1.7", "should": "^9.0.2", "sinon": "^1.17.4", - "spawn-commands": "^0.3.1", - "swig": "^1.4.2", - "templates": "^0.22.5", - "verb-readme-generator": "^0.1.14", - "vinyl-fs": "^2.4.3" + "verb-readme-generator": "^0.1.14" }, "keywords": [ "verb" @@ -110,21 +97,21 @@ ] }, "reflinks": [ - "gulp", + "assemble", "bach", - "generate", "base", - "assemble", - "update", + "consolidate", + "generate", + "gulp", "handlebars", + "handlebars-helpers", "lodash", "swig", - "consolidate", - "templates", "template-helpers", - "handlebars-helpers", - "verb-readme-generator", - "verb" + "templates", + "update", + "verb", + "verb-readme-generator" ], "tasks": [ "readme" From 31c8947d5db03858d901abca0d5628b47435ca30 Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Wed, 29 Jun 2016 15:04:25 -0400 Subject: [PATCH 254/282] deps --- package.json | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5ec66460..8a5e1ca1 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "test": "mocha" }, "dependencies": { - "base-runner": "^0.8.0", - "common-middleware": "^0.2.9", + "base-runner": "^0.8.1", + "common-middleware": "^0.2.10", "config-file": "^0.3.2", "data-store": "^0.16.0", "debug": "^2.2.0", @@ -36,7 +36,7 @@ "export-files": "^2.1.1", "extend-shallow": "^2.0.1", "fs-exists-sync": "^0.1.0", - "generate": "^0.7.3", + "generate": "^0.8.0", "get-value": "^2.0.6", "gulp-format-md": "^0.1.9", "lazy-cache": "^2.0.1", @@ -63,7 +63,6 @@ "gulp-eslint": "^2.0.0", "gulp-istanbul": "^1.0.0", "gulp-mocha": "^2.2.0", - "gulp-reflinks": "^0.1.0", "gulp-unused": "^0.1.2", "is-absolute": "^0.2.5", "load-pkg": "^3.0.1", @@ -71,8 +70,7 @@ "npm-install-global": "^0.1.2", "resolve": "^1.1.7", "should": "^9.0.2", - "sinon": "^1.17.4", - "verb-readme-generator": "^0.1.14" + "sinon": "^1.17.4" }, "keywords": [ "verb" From b43995b4314a64893a237e543fa0d09972e6d3e9 Mon Sep 17 00:00:00 2001 From: Brian Woodward Date: Wed, 29 Jun 2016 15:04:37 -0400 Subject: [PATCH 255/282] typo --- test/app.docs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/app.docs.js b/test/app.docs.js index 9500eab9..cdf89551 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -19,7 +19,7 @@ describe('app', function() { 'b.hbs': {path: 'b.hbs', content: 'b'}, 'c.hbs': {path: 'c.hbs', content: 'c'}, }); - assert.each(Object.keys(app.views.docs).length, 3); + assert.equal(Object.keys(app.views.docs).length, 3); }); }); }); From 3ac1aca08b9f94c9b7aa88d4039e922d003f4605 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 9 Jul 2016 00:34:41 -0400 Subject: [PATCH 256/282] adds version command --- bin/verb.js | 30 +++++++++++++++++++----------- lib/commands/version.js | 10 ++++++++++ lib/generator.js | 8 ++++---- lib/utils.js | 37 ++++++++++++++++++++++++++++++++++++- 4 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 lib/commands/version.js diff --git a/bin/verb.js b/bin/verb.js index 9745821b..5aad5945 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,7 +1,9 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; -require('set-blocking')(true); +process.on('exit', function() { + require('set-blocking')(true); +}); var util = require('util'); var Verb = require('..'); @@ -9,11 +11,7 @@ var commands = require('../lib/commands'); var tasks = require('../lib/tasks'); var utils = require('../lib/utils'); var args = process.argv.slice(2); -var argv = require('yargs-parser')(args); -if (argv.v) { - console.log('generate v' + Verb.pkg.version); - process.exit(); -} +var argv = utils.parseArgs(args); /** * Listen for errors on all instances @@ -24,6 +22,20 @@ Verb.on('generate.preInit', function(app) { console.log(err.stack); process.exit(1); }); + + app.on('build', function(event, build) { + if (build && typeof event === 'string' && !build.isSilent) { + var time = (build && build.time) ? app.log.red(build.time) : ''; + var result = event === 'finished' ? time + ' ' + app.log.green(app.log.check) : time; + app.log(event, build.key, result); + } + }); + + app.on('task', function(event, task) { + if (task && (event === 'finished' || event === 'starting') && !task.isSilent) { + app.log(event, task.key, app.log.red(task.time)); + } + }); }); /** @@ -71,7 +83,6 @@ utils.runner(Verb, options, argv, function(err, app, runnerContext) { var config = app.get('cache.config') || {}; ctx.argv.tasks = []; - app.config.process(config, function(err, config) { if (err) return handleErr(app, err); @@ -83,10 +94,7 @@ utils.runner(Verb, options, argv, function(err, app, runnerContext) { var arr = tasks(app, ctx, argv); app.log.success('running tasks:', arr); app.generate(arr, function(err) { - if (err) return handleErr(app, err); - - app.emit('done'); - process.exit(); + if (err) handleErr(app, err); }); }); }); diff --git a/lib/commands/version.js b/lib/commands/version.js new file mode 100644 index 00000000..6e98689c --- /dev/null +++ b/lib/commands/version.js @@ -0,0 +1,10 @@ +'use strict'; + +var pkg = require('../../package'); + +module.exports = function(app) { + return function(val, key, config, next) { + console.log(app.log.cyan(`${app._name} v${pkg.version}`)); + process.exit(); + }; +}; diff --git a/lib/generator.js b/lib/generator.js index 5d91cf97..4b391b1a 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,15 +1,14 @@ 'use strict'; var path = require('path'); -var opts = {alias: {diff: 'diffOnly'}, boolean: ['diff']}; -var argv = require('yargs-parser')(process.argv.slice(2), opts); var cwd = path.resolve.bind(path, __dirname, 'templates'); var middleware = require('common-middleware'); var extend = require('extend-shallow'); var DataStore = require('data-store'); var log = require('log-utils'); -var reflinks = require('./reflinks'); var utils = require('./utils'); +var argv = utils.parseArgs(process.argv.slice(2)); +var reflinks = require('./reflinks'); var diff = require('./diff')(argv); /** @@ -321,7 +320,8 @@ module.exports = function(verb, base) { */ verb.task('help', { silent: true }, function(cb) { - base.cli.process({ help: true }, cb); + verb.enable('silent'); + verb.cli.process({ help: true }, cb); }); /** diff --git a/lib/utils.js b/lib/utils.js index 2747097e..94a56023 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -16,14 +16,49 @@ require('extend-shallow', 'extend'); require('fs-exists-sync', 'exists'); require('generate'); require('get-value', 'get'); +require('global-modules', 'gm'); require('gulp-format-md', 'format'); require('log-utils', 'log'); require('reflinks'); require('set-value', 'set'); require('through2', 'through'); -require('yargs-parser', 'yargs'); +require('yargs-parser', 'parse'); require = fn; +/** + * argv options + */ + +utils.opts = { + boolean: ['diff'], + alias: { + add: 'a', + config: 'c', + configfile: 'f', + diff: 'diffOnly', + global: 'g', + help: 'h', + init: 'i', + silent: 'S', + verbose: 'v', + version: 'V', + remove: 'r' + } +}; + +utils.parseArgs = function(argv) { + var obj = utils.parse(argv, utils.opts); + if (obj.init) { + obj._.push('init'); + delete obj.init; + } + if (obj.help) { + obj._.push('help'); + delete obj.help; + } + return obj; +}; + utils.getConfig = function(app, name) { var runtimeConfig = path.resolve(app.cwd, name); var config = utils.configFile('.verbrc.json') || {}; From 6dffc9a833ecdc6d02cd67f8aaad56f05890daf4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 9 Jul 2016 00:35:00 -0400 Subject: [PATCH 257/282] change lookup pattern to `verb-generate-*` --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 83554506..2c167f8f 100644 --- a/index.js +++ b/index.js @@ -144,8 +144,8 @@ Object.defineProperty(Verb.prototype, 'log', { Verb.lookup = function(app) { return function(key) { var patterns = [key]; - if (!/^verb-([^-]+)-generator/.test(key)) { - patterns.unshift(`verb-${key}-generator`); + if (!/^verb-generate-([^-]+)/.test(key)) { + patterns.unshift(`verb-generate-${key}`); } if (app.enabled('generate')) { patterns.push(`generate-${key}`); @@ -160,7 +160,7 @@ Verb.lookup = function(app) { */ Verb.toAlias = function(name) { - return name.replace(/^(?:verb-([^-]+)-generator$)|(?:generate-)/, '$1'); + return name.replace(/^(?:verb-generate-([^-]+)$)|(?:generate-)/, '$1'); }; /** From 7da124e0d32404537faff77bbd3228f8287ed5d2 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 9 Jul 2016 00:37:12 -0400 Subject: [PATCH 258/282] adds logo --- docs/logo.png | Bin 0 -> 79157 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 docs/logo.png diff --git a/docs/logo.png b/docs/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..011b46ab23934508600f4b5ab2524de9d8c11545 GIT binary patch literal 79157 zcmeFad0b8F_XqxzS;!DVWVnV1T~kr2i_A)rqUj_Gsg9ya!*I(zTuSJ^(1Qb@-C_3sPc>>JtQ1wY2@H!$CC;$pww!`5{t`fZ2H_MH>gIN3Vv z+_cknhu6MmJ9UuUg3>iBe%tI>-cb2+U*+nGTmMs0=$ltLSV!4g_;;_u!?8Q$)D%{4 zi@e_FZY%Re?rE>n{mei8_4M$X{eM~P@!fM$ujR0O{z*^y8_UWouhonkBM;O5_xVo) z|9{c|-@W^BZ?a(FlEvA#d}6A;x!6VYQy{*;#tx{I!w1dpMfCpiR?)vkPp|W56Vr9L zwdSh-SBLJl>Yt~16Yv?#)Zxj!@IUzbTmwvw=w^Rz%-irYptxYijXyfV8^r`%Q_^05u^|IF1nxEg^7FeF1 z_g7JIURP~Rh550Khugk{w5fTkE*o6kW>1&1uy+$45p`}cj8~|(wvWi%?q2nN zvCrs?99_N3bg+nF;=vT>#y;?PXYs;s=g8B%8EyVBPQTc6vQ`c8Irl6L_YEvd6VBe{ za{uo!onumuhkjn#>a}11o`n4&R>+o5X`{zuU*M&X;Wy9FEILqPeJC>4iSy9@-<3n!p4x+S%Go-%Ttu<>YED0XY`ev;nTKO zr1a3EW^8oaWIN9RRT)h~@DThr;#H5H8Ksr+qAElbD+qCKuTo0sZb=pGBIhu9lc+(G zC(bp7L5yM(l13e>_MX$W>UO;7!D2iP`m{knPS$W+oULZxyxjMGMneDe3T2SR|HNCO z;ZJMCr!22FXndU%-(RIt`3cqegDG@|u#@5MO*<-k8*7J`bH^sqV_JWP-tNj;cBh_3LZe;fG3lt85C$0fbw|^5UCE9H5ZSUB>dV5<2mG86>ot!^;arTCe z@7JC-vOC{|tOH%%A|q%`5Nm2!jZ)O;au=9qTp*i0E^otGkzK<#u_hjiT|~tkZ2KlE z(sy7*th#*Q!eG&qRxbyf4H5HLY&ew@rRBB>tu^kCqUj=4r_qJ9qu7LAddk0>QMFWw zH76XhID1jiZQl{;B||zr>{9feH>TGSoeI6>DJC>?)!eTgYb-56*1Jd7MK>K*BeMHR zMi{p^`^(W*wOv`WO1Jg)jH-1#6JMZhLeA+77i-8S{YYcdwal_A^WIv2#8;};B*qsU zq+0Y5TcQ0o!}!5139Y9VGE*+SZ?(D|n|g-gNxZbq*->Rje{2&aEM!Jh937jgcbNnm z^Z8VVao%%nMc=m{bQmmnam-aQzJSWfEEH=mI>e;%$dB!!kcG^}F~1iQM>d}%zIF+{ z)^RZJ(=jZ+*ghrt)PDDl#f?wq+1;L-+Pt2iO8p{Tn7NLvcHBg}6eDgrNjwXjy0_n` zz5QDZ+8!xkU9jhoUOe%v5tWrwGqS$%Ieq0jeQ?V;)C}#ryFll&RyDnvx zBTCO3y63ZQ7!e?B78Ov#5#wldg=PuTI$@Pk0Oq9-a1)!?UM;FDxWFmpvg;`WG-OXe zg{oadGhO71lT?`y(%(-jGOMs~L@LF~b&gG9D|`}L;l)abkqh@W-;?8ST7LVQ=m7yD z<0dw+L#WB#<%5X?(N=J{iY{QWc!3X|sV%B;vuL_yX*Z178uq?5r>s^iW0crO@+U`m zTIt$6PbfiQH{&a#%4)^7yCWXs{Bt$yqhk|2N>FltEWs|S!!og!*L;*1Va}_>%H<8; z#3ICtFaI-7ttzx&L(0>!XtYy6MO3b@BOO3lJiz}xD#b4MO~^>-%U%zv(aqLXq;X=1 zIEblO8vZl0C@$jx9A6qDQW5D;7-;nt!$f0o_OzlnU%SDKsn?98Q@&NyN80;~j}Kj( zUGX(#R;hU}UdXLvU+1jv^aZCQC!#au_*iz%>Q#Q zQGxkfIzJ@X_`wVJW+nDT^Wzj2?ysgAE?Gq*#jFsMz0YeCE3Yg>VHee${eyhPu6Qqy z3boKJ&@i|e|2K#PkZg!>>|fLmd3|sos0^tv@5Lx2 z&TbnDt0S(iex8m$N?pd!%o#Q3h{mxQ>>#t z;L4DZ077i^(%_1xy5d0cjruVIBBsxcfJg)5zm$b}nAnIfz>k*DNW&fzH28NggzVny2&xRJhMI$My5ED>FX&fLqnjg+rA+Av!lv1AKwqh&p{#$D zxIp43hIwo-fq5(#DE)uSQ=BBhT-Zz%AOzZT%g34KRFwW&Daj}{Ma&NL#5%UgRdWU_ zVI2$fuPz__R_thc)RzU*IY$$$cE@X^IM)|@QZo&V!CJ$dR{=BLd0Bpf(WSKzg{23+1~k@}N4MTNeUm0Va!jL1&rSPjz$I3b{rhBEWv z;>*u-jt*=}3Nm&$MUi<>_;xd0n>$diA;QY2OJXoU&qGmE526 zGd4{}8p~KqDneL7_;wRrj~Zt%pwJdX$dFjr?HA(6yhN}tHX&Gm{cY*-v8j|Z@C>Pl zFTjF8nzH_a!^TU2HlHEknm0sz7&w(Uow3gv@|Iq}(ex$ep6p^OfmRxpAgQJaLJ@zF zOvct4O^x6{h&AyvW-In&igNxQ8i{~Zl>uk6+1d+2l{A)WjigskhlB~N4f~nKK@sNO z6oCRIDq#2u!+5ob3RlgVv4IF#2FoOD{(3sY={Zr!Y7uF!5Dd|ra%svw{ToFQuqy27 zkWEnFUtBC@mQ&uy=MxJ6XM#WlAj^@^LjDXo{E%rf85M1@#nF5^fq~Fie8yy&(}A*} z=mo(dB{45!+UOL*Nm4gM*)q&9J%l7}#)C^}NO)K#WqT@6CAol`h!YxWKPtma(~r>s z%<|N%k3+PBNQ0`RNt=zOe$aj&DqIS5q${{aXc=KOequ+MVZX}cpM+w%OLTt+%tYNDZApVGN zF9Wd>%6sFsLwUa^;yor!=tV9U#&}i~BeQ}z$_S(ihmi$j_$5@hEx$yS9V4tA36}N1 zc-&rMKxSkvt_{qZumLS^<80|cyu^GC(Leiljgn@~x0{xL5i|-qFN<3{-jM1dk5)J4 zx5KxO-c#nc6CcN{l#)JH%&0~Ifa?N6n^&;0ZDBmk_Es8#yOOokftT&WcKi z_@T>*hwy)snLquxd6P-QH$wcG4jL#ePvFWd4(bww(n%U2$(EN%^*=w9%&!gRV~;!K zEqx*;WEy%@y%(S3b{Xbt5TZ8h69MI>bm)@-nIH2hX)Z;pzH#{CO%Trj)hW*A7~fhK zqc$#Y`yW|AElrhShlu{GAjY8lPf~m{-)I=dY)UO+Zp|Xd1Elu*A|YgkG~M&3N%odyCX=AQG?AiFj%H165<(*8Q(BSX+btl~9l+O{%9gi2 zvuu9_jV`jViB08wl=m#&3w^f=Hd zpsY75Lm)CiXu6cLJ`q}F>q&g?^ z_faN(NI19pSprh?8#I#x$VP#NMiD|pPoNzb^Kh;K1KVmzkSb~Ir+$cD4{Qpe9I>$Z zS*Uv?nKG1eZp(B$zRhzd9Ea|enPZHSIL54_ZaQb16yvDyq)8|7)E3rqs5 z&?k2zj&r1i!#hnCxEBgf+JC{3N+yztTU2<{LE>ac>yk86Gbz-I9{>U+7>B}7y-lsv ziv$wBGlkG7*ySkF&2GdtLiyV{jEB?qfL63LkD{=n$4O<0QvqW_LEu0kPC)P?U#7U$ zno2Op-z2515UHd^M-;`l;j}0%gGQ6eDq!>wsdP9(c$%hK6)VH|Sr92>unqbDlPU1U zY)WN=G?%d3gK*VCbL9})4~7;M;cL(%!}~vzB#~6irUa4FREFXPrb!|>yNXZ&yt4uv zu@S05xcZ80Z7m^Fs^>Vuh~m8MaI4XXWWy?&%+lA%2q5Uk)f`L`H4$*B$|GZO_X89# z8WU351&pN?jWzBvQ?h^r&?DnYYqpt!v|5_XkkHWXc5I3s>29=ZQViNa&GI#6wg=Qi z{b7oJ8b>6p>UQde?RR9njNiKg_o}G~{F zBPNx89Lo7Cxjv=&baMGUbFTM+kj510^lU0wAn&L&m7%aZYR=s%qI6PJ-aAgh0H#x* zKo^V<_NeC&h!Qx62)o~;9scn2>^-MH$3*lo|1^ABZf{R?k^M_QxXga6VaZ7F2DxR7M__)M2;3|!->0avqnyBYw6hH5(k+M_*8b|>(WY6 z6)^{hhS}#Uo>}=$sB^-Jf_*-|zVWLvDZN?B7=-YC0rrK#Ghg4#5|Zpu=LM5JP?;~njOGRH!P(DT+f?`9ej5*~ z(9*_hV{iRZq9FTsVcS0Yk;Z|K4gsmp zxfx#P%gw|JXlTF|av<{M(ChmJo;zNYDORvOZKjjnLAx?>!}f_pfJ~^05mJA?E<>dA zlu$SjtoCNjs@L(A)L+P(3B@}k;z*S-UVq$~zWx+m|!3GC$wnSM1%VxDR z|B!Uq{wq15b49Svc?rlp`$c)Tc?D4;b(6Z6^^kEhWXb*t6J%pI5~@=9FVFA_D340U zO-4vIw#PvQWKW0g_jHe%st8_86(BqWp;$71%oQAnnCQ_{+W>^b0*ulAyKPg?>?3@w zbS>snY%#`|PEiHsQz-1Loy3bR#}V<()Ye|T-m2zIG5(?`vue(neZ)sf2_IukPs%u% zxRp-2pvirS{4vXdD@$t~Gc1UL6l4UT-8Wt__VNmEMeOBhi+}^UBPptR39>$R??p_N zu6TaO$+)${J8=Euq=dLGT=YWO2X&M0gb$=FqkNIwv*MXAm>;)25HsuRIq)H}eA;{C z{UbYjOqrWx`Rb(Uz!(Ic)4T(FO+@*usyhi`m};1hCF%)O0(|?%`;s8ac54+^)ciW+ z798i%O-1#!!Ie>?Xc|DmGaS0&C>I+wi3AuA060#A>}8cr3U5iG=H(Orcwrr!zNRs{ zkpxlX_a>#vBm)+>7jGART1^bN*WlT53BDN*x4Rw@IiAEobW@S%F5_WL;9`iPS$J1c zOvW^38Gp6&ZK}f+5rBFq2d{Qo_1jNcn|Z&Wltyu+TM2x!=2z04V;u;7g?Fxhyj=8m z>mzIc6wwr14qbE{g;@teC4_1+{>8kJ&VPp3SMb>2*_oD<=u%vjB3?;ri#sFwQ;vi-jK#!l40XT0VHUSA?!YZO+ z%N4;ogzHfOLZkwC&Lsj=nFi|=)mmg!kp)I;W;cQdlWlWPMS6k(u`va3@)9nF3AM=t zofmKrU7kS@h+?WZh;sr-ulX25g4tw%^sTZ2bh4lU8#g^-!%}L3 zJ;ts!5wR_}{Rw)XB~8^rBPzn5G>(%XfZ7ePkx3YBA4Wvb8#&cw6Ho=%CV-FMYgv^2 zBxMWhK~Y8+Ir4OfD$4xiM>u;oVYIA`jGLk4I6It504+*yeUZonBlvHm_AxJfGuH^#`)0`hEWX=vIIq6K!t}_5D8-?M8G&# zI6ijo*FQOZq;D2#-mJO*3hZdH%28=hzZtaV5*}+#J42k_X#|g20lowBT(ehwE7wB z0=#RoISh5%aa@qMm`JZMVruoR*5XzQaeVKCuJ6k(97P$ro2V|E#8B18sgR^_k`oiD z7nWe%+G|y$Bt=4tA=rrt!03%d8g|BC$vztenCEC&GDzE78q1BpZj5kg@g^E%4Z=5x z#7yBEDLKN<&PXwuy;qe`Eu{J$rwJU#*;+yrUg-CbKy8S`%YDerL+tcG8Lo)fs3oNb zpWBi90t9XZpt@^holIkvG*A1Gy*d`Sl!yy+8jje)gbVr%!Y;)2!%;>;)zGM#KR`Yl zh1s2isOahkr{94v3VWp(pl79dn#m(v&2nR~4p+nZh&y^AM(uG*>-W8M~TAIAzc%$eNawLLwqTq}`Vw#ACRuizm+{1rsT#I;n9kAW7#) zc!Qb4sf23;LKLt2mS9c8 z3}Xhn2%uhEZ_u4Y<_#{0Bp^qfe}#e$M8@Y2&bPwA3^vwbs8 z0?YbC^<^!DbeCO zVujJerdF2#KKz^F1eaHJV}s2nF`EpRkuG-W9x684g$ zYliZv*ilnGxMYrmYen8^P*Y+Q>2>IhfO%p8XHp3SPCwgyI|`~Vg46juAB`u7fMCjX z9uQOgeN!`(EjV8Zmtsc|q)}2%;|ywUSS$|bf-E9n)(Om6Z?uNS!)#!aHEE=*#UiZ0 zq`%Wbmb4;9ah|k&gM%@vlqjtVft7O!JJ}jfpS)eB@q(C(T19|EL(m{okHY{ni-z)p zV)5qhk7?UX+DD+=7!pWh*30HI6qdrFEQajX#Q>|8rZeU!ru`(6*W)HL?oHujN^3$C z$3Kp%VjKo3or>Eeo6S(%WY}yC&hf@*RAONoGD4$}NQMN3GxNsVWFbx-M zpRkl3eHTE@I=DeApb~HTGaWcou|cwp57=)E){0+XNb-j2$s=fRS;@Qk57{b;er1dj-&YB83-<>mP;v z5Jz*q7Z$M{^++b{WUS;fSVe?Vv3;PsCn<~}0UebDQ{hjn0KOc?*l}+$2H859P)bP^ z<1uQg5#DO^prMojQUtM52g9t2ctKSU(unw`lDdPbCVo`iBb^EEN@6KvLk`(QXE4{T*WmhSsUyK zm{4);7x0cg9MB1$@(+;pbELXUQh6|r*bxPF1|*?F`vLE@!ABQ1B&%yl3q^1VPmsLF z`0Fib0@Jzxq4`2d2d5ObKUh;rTWMX1*g=vuIJ*#2PQIy3^8=tS$2D+hcM9_nK&kYYw9 zg*n<%XW4v*w6Gv~k=w&~AR^bs2Mt&rl7v!bA@-xVflwRbCbKxySYG35Ys#qtN6FNN z2(8AX6hp*(GEV2fsAO^(>dfX!YnJRMnuquBbJSTV38f6VJ1b7Phm)c7a!G^o1yK($ zDrs2+sdujgVGN;E8Wugg4B`~o4#bsp7}*bgpwz?1kf#TSFpcS0f%a3^PN@5K-*J#lJ^3+RmwJz>2F^VGyWfgNqWFEu?(mA+uz-Cj3i#aQbTP-cF z=iuE$*1({Li}C)k2F8WMm|!^eaonGhW>V;Pa>cc$5!Mg1r*Ma{n)Gi0f5rJNS4q|5 z2JvA69w9yOU)cnieZc(_MLdTGcCvP-o=#?YAJ-iiau`Wx*9UQg|m#6J9vXvQPJQi>nTj+t+^mCPLZ3YSP4=!Q0cVjI9 zrIKmngYJIA!5nuAaplDuSRrjP!of8h%wj?T1%d#uvXrx;7r04xmONZhijB;_+0)6{ z7vXij&qxc0Z-r+!1HK@Ga%A6>QU!fpsPLJ3Jb&_VS;hu8NO84?xhA+d`y8W8yjKSr zW`Bo9`1gfvxJ_At({0@L9vv8nOYg-!9Hs>a%6rg$kTy9nqmr~K>v8+Vyhry13Okw7 zHV*RzOsS;X2#|tj?Z2CpXQu*wO11-;qYy^%Mh$7CgM?;H;tmcaRHJ4Rgpuu1j1d2Q zn36$vfwvW_2O3nkE|fSKqssMfXa`H7XC8wnbqA{tBb4j~nlmyxWmrFRC4Fdmch-E^ zMUnDFfDf4-5PEG&te-8?Hwv_&}~V|5ql1hjTcD z>z>9K?IjL0Vi!ZBBpZrs4H-c{*vACA$bBi}L+oU(dtynhhrK>-qYRd4p04Jc1#L($ zF@Fq(fP(B^9h`9Q`8shFA)A51{2sjzfMBCIk}_==W1iD{Fjka2lPh|%@N*-=%RT2$ z-p294EX zn%p_YPNs=m%2iQ&lDL1w+{7_75SRqsMG-R<1W`&-S6{&WA@!9#TpW3ZNVls=e+sv! z(x5w;B<>G&+cjIqRIL7MVbIf%K8u4Ut&beAOJR7U+zz=l3jMcDER_HI@OJZ!LVaQ6 zuR6=a_*1m^M~l`@J*lWOoUL-a67tRE$fJUZh)vsTa`2_^)ILbVzHbv^ZyP)d{E>3M z^bTd*aKn6k(Xl2+P}8sT0D~)G!}n(E9?=T27X*6Buz;$^x8VjtPoDtDltXZf)K;`v za{Tbvr;SD_XO1c%)?69X@aIrqoP#TB_kfb|N2PdCLTRN9TyOXiuGdPL;bp9s)Ut$h z!r{1~{gv`U-zj|%e}JCRgYX2#E1o^R?W>&RArUbA%7H!0 z+wOz>lg5Dj{YFkTs;^C{;uP>8>0VoysBDcKQu!Tw(NN>BzsM>ea9&Ww@w1{$0}-!! z5O_%j-ck3u?Ge?NtRHegMTsa}WWG)gH3VWe&@}$4u}Af3BPXKShnrQtO?I>TAfYi9 z@wmaW*FQQoHLsNDIJ(8$!%ri)k%OGM?zvYth~;V{x2d zG|P&iI)CBY@DW;E*2SCdV=M+IL+Bh-cwd=JZGghdmLyygtsIDw4MD@0k9qBhO*ASJ zALi}Ya%b>ck4=a@@GHL`Gi$_DBa?B7M#YlDPmb*qnKao=>Vqt?f-xO4)3d32DLFw9 zgq^I*0LJ&h z0-iu0C&S0B9Ly~v!Nft8p&;Q-ETIB~Vp%Wa_epHgt<^2T$H9aQFrvA2wTade5F#_; zW&p8pO&DLx&G@T2AYi)03HUV=>Sw#Df}aU=zzf)m0q3T-e6w`nEYP`A87gz#Eigd` z7GVAeXdQgp*lZ}lVEdg7;6@2bh_4GK5(NF*&DiL4z+m-SIrIS|zyT0Y0*-uA;-|tn zXId_#oVlWeG^{~|8o*d(-3+zIIc4Nj=Byovl&8ZAl&?Wm1N|G$joB(O!kB$nzO!&& zim^z;xmjWY=J2+g;`wvGa7WXA0J=wFN~u<;|-lYXP%Wurwaguh8W|mdk%6Y2zoUWFlaasX<-vbgwo<0sw&-{CO2N!W9RXGMeq_R3# z#|WH6;>2jqa|y&6a1dz##<>BGn~I$X%_-4vR$bWyk(B2T96wFXSMWgCWun z5@J_naoYV-HRyL_ty#8@(m(Lwd zP6O>sj(1*V4p^Z5AV5qAq`Zfdu~!FuXnnV^Ekp?kr+|st4Pm`C~TWIJP{4i_^^6tx+V7vk9O8LEXQ73{6Rd`+snvU=tt;2;k(V6y|yP46Lq!=B6Kj!knnA^tN^VX`@nbBZlZ5)49|635XwXF&m@ z)z(f}+4wj)gEK+0zBpYc{{kwo44&b&O|PJ38n?jF&dN!8pL8$41ZsGK{>EQ#_+=Jd zmaq|IdC@7OAmt3ik#KELLH;%x%KJ&&l$n);oh(G2af3k0z6m&2dncp3io0GIx)X;9 zuyOt=kkDaz&r}8-%fTO7Z9irP!vNdy04InB95@NnpWnjC7;Ie2vg4WmVNRA3-4-cH zMQoZljD<6Ar~`n_Y)4})e^di*b@?fS*qgWK&NCg8R9(= z0}Qh%W)|+=xS2KoCZQ52KNn}pEd!Z6$P<`MVY0?$dc;2_P1SQCm$7jFQ`DUdnOw#w z#EAoG=reY_K;Gcl`J!WuZjvAZ{Hh+O@>5DE6zHtr?-f03BDiJW;#TW*$kj|9gh~D+ zaN;hV(#z5XjvVu}@o}!NS|7Fp0y(BrJ;N9~vZ4q`f7xm|ekPbO-~8V`By@SauIX_g zu59vw^0H$B@Dj@X{*=HNb@G&x6k}7eU3eo6cU$fAJ z;Yt-0RI&srQt)q;Gq78KdIlzc;fsTc<9L`TFvONM(h=qLZ}jjGZa zUX%K;@345N?o1hIz34K>GSFy;D|Cuhn1WYu{o}iY*4mH7oLQ20koW>bs4N1eXfO)a ziH*>c(V%5&j97zR|9}Rs7OCES9Np+PRU(3*?jG(5Oz*?yKmhiE=I@;dy>n)q0tJ^K zN&0`(U9<_MSA&VF=B~Y3+(SpDG0|-c*JhrV7j%IMgoZ80KVSEgY6)dN5EBvMB0vV* z8>Z;LyF=Cp!Vz%a2B_&K@>~irK=TCb7LQAkh7i>GRQS6{!xg|KTms1Rw@hWE(sfGpq@Y*OLqsTMLG(&``022W|+7370v@1NX^`6f~0Y(w- zkUD-=n=ODK3ODLwE!4f`oE$YgBaC>Q8uI(TMxIeMfX+de<2-uf3D#w6G}gjZ5E1Ai z)9fR9*WOa0xJII8X1V9-bED^>PXF#Ha;(3P=uvwl+@?i8{9mGR2!b>Q3i3aBE~4Hc zMtX1uXh*}fALLi%V~v3RtuhD`tT=X7R9l&|a-iT+S1f9H`*L4}`SoMAxeG;bXw*QMX?KJzoZ4(y!@9iFo3kJi{Ki_mr zXC&L(x0t4^_O^zX0UlZtxbyb)*1C`GQjg^CDjv`jHQ9sr-1;I4ySv2M!2GT#7MhVmFV6i!c2MtWMy7$XrbuV359` zm&Q~>e!po`FT8V_Ce`k-catyt*4%r2z=+C2&u(XZwle2Ts~lFb-|E=pm<@74unig3 zTPmssU-8S5ktx0GV1s$(>lsc36G!nkL0t=qYS#2c4Gsau_nky3d$Y-c`2M|A#>u>`>zfR4@h3rr*Kv5dh|U^DskjiuhgcxUaRH2R|$rE-}K|_ z{wp$TEZ>;0b+SifXzwQd=AwO~qa1qBwr{IGI`@rqfg`a`=J~vk8Ua~_mG(O)|NZIN z0Zisr(pxCC^?7ghkfno+=5&92K(BvHG+_+Es`)qP zF2$H0AORdxb$5tH#-d(@3qj#`%#Q``PmAM()^EO2-0zb7{!h8R_03+5zEW4zT5v&f zB~lK~+xTSMd}IaoyK-XZZEG{P9*gQ*OBNOBB<~STpofREc zV-{u1lPf$_a8YVfUhdt)hOza%jO4Zfz_x{CzOam{_yhIJKTibx+VCt2v zT(skT{S-f_)E>3&+ab?0`x2O|^R{U=r!96$=(Q7?_Q)MVi2LxojTwfw0xI03`Vi(W zv6_DE#4Hn3;k{Fyx2o82hwFS!C}Bt)DlVW`CyvS!E_$0UH3aMN!fuT2CAqQ^a3iN1 zb5(ly1Dea95a8+9S8x*`ADBIFbL^=*?^|WykUP4qg)ku>DKZ+JjE zudiS+WDe{1OGZdTU0g)HdEeP%g>d7(@ZOIE4TW{woN-ZJsWC5*PHk>)whf%rW}1v5 z^$!~^9~NqluDAy`~Gjk_w+Ho|UZBJeiapHfpP6R5FRhpPq3Mv` z+PA4cp8U2+szLq0{N=-3ULqa1m*nR)zs5Y@R9C5K3a=kXJx!ecCqR`oi;;mUYK$JT z1H4)o2I(*B$vpK}jrmgd+5cHb>7SB8`fyWj=cEhQyoWdGx_NUs8NJ;WRj;MA&uO3_ zWB^lMApmby@D#7Z`Ktm%~|061vuX`p#u8 z^kcW!mw4M?A+I-QWEx1FN#DlwR7JuVo;MsCF(NN+b&|Fe0vd>}@eBWV?ezt#MsRD? z7^;e`%zPP|{P@TX#f~J|$bfpCjjF#thPMRZ_BM3QHcW}v-w+pOjX3zWsdRw29 zBcpCs!|F1+^$1oj7e5FB^8+q0qh9XBdz3Y&CqG_bd6AQYIV9R8uMF=S39|f`R#>(s zeo*sE^uc|HymIXK-*nd{&HZvod~Y6H&kQpcn_YQ244RzrQYIk=ttx)vyP#K4=!i<& z8{5Zx-Je!~Z>Q0BJsN*3N5<=g^YFA_#6c%sbF38c9QBS)j(Le53wnDfkBX9p zSJu7i5<2!6AU!dDP?oGc%2pom7GVV`f_?emgfyp}NzA?_j!XQNS-%Di{7&$U44E=!Hz?LfEP*f>1eSpq| zcYhBmgf~Eaj?$Fo8)nPm?D5{8b9)!gjUVKd>KiW&8V{QwtncjmS#f&u@@~s8PGGcT z-%k0@qhacTang{cKRqAhw`YD1Oz4DQ1lk$_U4<%4|=d)^j4}XJ|CFGtoiIaK<|*| zzoi-}R2V&tfwzA@e&=;ly0-6!Zz&Fedz||zG(Mj4j;*!GGv5+iSeihlim;UDjHa_4Pb&X&;tZoK zQOXmT%8a94uJ`#57^mj{{8DX>bdXVvs{7#%?D1GL0()tUXKe3`(~s;0h(2w`HO&yK zu^U~R0$=9KSWaWLA^-Skz(=q6LErYv1buXPLEujFFuC&S{Nv7^t_s|&$L?#$J%pE$ z>2mu~aF>l++3fuQnZg+JV>)j$r9A@Fx0(3Lt~21rz6w&WU}tD$kKZ)8k~Jl(C={VPI-@A#+cX1E_2_BokLTW2Lj|{WS2BEb^~f0mb^S|le%Av1tpDTKQ2h0$c0MNqvm11O33UIdb1(NL3j1a7 zqy-Q)Ucx}!XWDLO`CagKNgpk#Dn5sFZJP(Lg<_3fuBQbuR$lXZ__AIwz#t7b)x)E4{oqUodzs6JIZbMSW|&A;^`1Ly<;$S==ME8UfOPSKabuU0uQg@# zEbM)1QUe65v^*JL>P!NKK=(n{hZBjh2Q9w^a_Z47lMaPPA~v8jNDWP{QV1iXu#3nY zmjCvp?3lmV_1A_})TCo2jkh`m^vfYVtyKNz!XtPtP2K%c0ct-^V;VvEn%3i`>(`mC zDYqB=FzuoY$~{*)%a@7Y=T5I@tf6pmmh}YBVf(o!4I z-4@;bbCQ+ge0WG?;^ux?FEW1=SnK`x_V)@rw7v-`dm8Qr6q$Yb5z){b*)>VYI2maB zhrfKDb;-BUCKW8YCaV8&)dGV>IeO!Yj5&KB*(jIc2YH#Z+iun`vg#gekiHN`E4;bHQ!j7P zBhf_zIG6bq;OO3Y^bqJ#4k@2CO*~oduVCP#fzRv*CAec+bZ$oOlO{Nq?GGT+{`oh1 z0k3;d<;)QYV4hx++_8*LfqPW^GrBKPJulMvl__*M@p~QT*7}^nveS4+D#hB`4!(7(m z^NhRv`_HX6c^h5WHQ8!@9vnuj5ZZ_EW0(4am*=~qa2dlJ_{t^1G_~rc-h`<7<*Dfz z1M!J{$zyREK6wqzalX63WV}7ET&^8Rt#VjKszPP*Vl0+Fd%mqUjAwHMD*$PbCmNY| zUb#ma;n(Vx6`$c!(8UqZGvaC8J>PHc@?l2gpkAx#XFo^s`hd^mm7lDJ&)QN*(x=qm zip2AN>!68!^{3~hm=J6T6q)sNC#Z|DKA5{GDv>;A&C_eCh&v7Kizt!!9DgW>z%WMj z`!f^_bVyTv!K|))?+E9dFWcyYJ3q+N)rER_`%P^{_^A{0a7x!FfWU&Tf2H^iUs8?sZpniSC9{{5no}D3ZJvjCq_o$l%_bf57hgiZ`kq^7d=z zFLEg-&boxuo?1aOcj>{#&97L5tj&s(qUwL?`sY09W=C2OA(~91(1hcYmkb^XO3HcF zoaphzeK!g8pYhWKDAGc+SdA&(%@8TGe!R3O-reMv1291P{fLLY?HC&H`J*1lc<4?w zd*G5&RW3{iiXrbkDm(?9>o$A%U3uDB&C|Q9Z|m^% zG)c|>bMq>^LFMR(C&S?!ex#*A=vaV7I&-r+0Po*jz(;X4pD={2<7{`K}U!@y2~g+A%UIg3>z@C;)AAHBO4wO-3jT7O;qs)BAIcpR>2~d0c6IjM0cK5Gp(9mz*YJq>XHuo z(mfF!@;)D7dfvtr)9Yc4g<;>dznP@Yrj3VclDmAWH_UkdY7kucQ1FVZT{s&XihT_| zfAX9^v*^g=m{*O5r@}c%K;NuubZ|6mZsGZz)?{f{M^!whasqFHOOq~;COrVZSLif+ z6$5<=m_M^6AJ4FVlG475&PjCDgUJw zzsEplKN7J>|3qK0NYUD#>dk~WKA-fMyY`)j1_hi9TbDT3o6ugE_n&@9WJcuCFvW1- z)zM+9H!E%z-NzA}{RNNC+@Zbdj7@qceCKH_7B%i2GLt%wEyUVICglBcw{pK=S0A)&#`~GDSqlHum_(Mg zXA5jGVO{g|*AiN&F_L~C?hV>xs{evRsJoylLnX&)>PF?28@!sWw`=CIR}L*X+%%|Y zjQI$;g~Q@hEXz>x#P`D| zPIx`{Hf2S`ETM68Fgf>ra$Vc&6s#Kac)IlwNnW5BIkFbRl~+@Vofzqp2ha6qt;B1N zv+wdtRgYaqOumu4!ly8R)Oi9QY%%nPh^H_o)B;xllazc@*cijk4Q0cJC~N5^vm1USrMJtkRQ;(e{RmhgWM&r;o0Ls zN{{dZSQEVQM1;ml+<|2t%)zJKBYFS!+X%d{$Z4>KnX$9z8pt!r8?}?l;aWjDS;H*+ za2ncEJGT?Q<5%GnB>oG4rWU#tSSJY`uSt+7A{Z`eAoP0k^TBw{XfuBPlYHtw1n!i6 zQ`C@vF?!oLD?+T<5G*P8SwXC_P@9x7`5E~8IPx+x?u^MKnZd8TD=`H8`s77S`UyX} z_B&%SXSNb;q9UkrxT}!9_|IU8zZv#_%nFBK=k5|dgCTF_X0}=3s+W2GuP-m+Q}9Rf zM+WF`hzc+M1q~%HIg|WG=ieB8pZGogmcAsdXpbGvLNk+qw!eXR$wT@0U*tnn$XrT!2Kr$x4VF12(w?+Y zH^x7rrX?yYYN#fVS^2t5WUPrtFeUjyJjYG(94m1kh_pY97s&ra0MkOPNn~N>leXhM z@(PLDQ-sS7N6T}A>{ECCo5bf_6}&DM3z}h zmN~l&+nX7?-mW+UVz)oZ1{l!EHwOipbluIhlZ4ZdEInF!Pm6+a_1O!S6 z3okuh&j9AJjc9B=j?<%$(doaQiqR+%P|05c}cS`^% z*>k$$j6Ga3vCM>|o z(oqS-1R39ouMR1te3Vk_ht5eyaHjj?Jy9xpNZu4X5x-P-;*+ma8m>%*f1zC@GPARr z;NKsGQ}*KTE)kNDJZ&QUTVR}$fwK?7i$~2Vm@qIopK?m^cet$xl?59oUYDsZ zNg_K;_&HW2m;(ALp^Mpe2N4g@zMMix!;fib+3B>6aprI3nrt)1SNW zsZIa8L*@}QlLI#HOHO4JJOMHIwa>l2d3iZe?&wdD*#6gIXTAAYMvvWNMT__9(aoDq z^6D!=J)jZxPXGhszuJ)-+S^Id!6seVV5djOUd7DXyxN+C1Y&R>Qeg-x7G0@mfvb{l z?r!j?Ik26G(@tKg#1C8uj%~oh3>a9v4olVWSy@`T#B!}(~-v`5JoJ0 zce3-RY}b0+-tY;n3FJ{4$tGcBAm^vkYv4z_b6zNGNSp~(L+cP0?3T8>p@7I=8vNix zVaM48FaWBAEf26-j!IO#-X&~;0yT%HZ8k{!f~8FI=RX8FS7t|C!raI|d`#X0LfKJe z5V_LbdcECTa~Mg`{e1Dh(-mU&f0?YIGT$a5+(vh)B6!k==N-bWUgN34^@um{4=(qz zK8eLH-;#ec=j#%xxlS!&-?wG%lQDGcXYUhTB)Ap06DxYG!VIc#BE2`Lay&hIIHq_D zZiIJ#aueL4r#q80+jJFIa*=X1l^$UQ{x$o4Vhc0cgd--ak|_d0zE#DiTkj`j6-qWC zkQGgFg(crE9G(Rz&`MS4nhu{iir9dQ(Fi zVHQG-Pva_UFVjD}V;|-z06!My_xFK$px)C)?=<0OHU$$f) zYg;&DxhI-0PIJnuxAG!yRtJY6498O?XLmz&umfo;T&=0Ma7nF?-3Mv~ZzC@q6MB9c zhiY@lAH!q@)dVGO%}lMyv%n_^`xBj>am*YbEMCxBumLU;CO?0(_vfUq(O*^WlTpr) z`uvY~JI0DS7STWWsu&xdb2X&&a?=k-G7dY1M8uC_Qvx*CZHBAZUSa;bEU$Kyw3;~L ziP#SW5yBXf0A5`67QGbfjb$%%>MP4(6ok(D;U1KJx|5gn2D|QqUfogEMrPCCtK^HWx0dWL)fB&e zm9ccjgwXtqombWDq`{DjgyWx&*<*3ltme#IvBIAaDevXo(t*fR0IyQPu`|LffBkZ= z{O78mWcq?ibLe!#nwAW%Sv(>YMCyFI%2t%t^kW@%1$dh^L0*Z|=Oaz<>)-D9%iB+k zrr{rd7g`-}nqJdY{WZOz?R5xbF{V7ktuQg&uW>~qK5=6oJhr~ov9B*QM_n}}D=KM@ zTK52sC2Y;y-ttgKo2XQODY4yw^5EJxo$Kwo|4Q%v-1eHB=eQ+bbGe7FiAW_;aTK;8 z^Mi;~=MfgMyCL?usI6&JPT&FLWlIPo^(9t^A*V?l*5D<$<_JdpRK+gzIGrSxE}$g{^es|!}AMO~|pDW@uGrrCtFd3jlTID3E{n%=ms6P<5bvbTC0iI^lA2;(<^*zY^T{f4L8B)~_nFn6n=EntJWA4wZPTtN#Aq zYfl}(7cedan0%Aw55bQ`{_#heL-XcW|M=w{@q8 z_~S(RoA9Nq6H!L;tmSQ!+!y;a`I|5E#NWe*DfmJ9qx3g;cTp|CD#AQ!++tCa|iVQ zMmyV84|?ogHv;t!p!xuIOEI*~cuod}bRGoS4lZg9rOqN5{nKOCu z`D4>#--){1t((E41oZBu$~~uwOT)V2u0-v-bfyIN!-d^JuYJZ=*)$yNX!ox=llE?S zs@~vGp|1lj@+;qOK&?%mv`&wE_kkap*H$wuY5c0V&ArDO6mIT)YjT{D+@1cb4I+(u zZ(kL;ulKDf!gS3_Yv;ZSo|i^VJv&_QQ?J1~MkDX0lz*-95C1uG8*T-%R<7xp>uU=- zRcyB5`B}xIx^uFACTSh1FYMC$;AXD|UuKeiu%P^|MZd<5W37ROIkV2g*C{IBcm56`zUk=)$P znurejnS)}3SG~h?@tm(ZH5=|*y=R2S$1>lIgJIB_B!GOp_FBV(bcbx|R7klTkcR7w28x=@4h)xbvf~2FzqR zk`T@7>JzU4KUzkgp^N9a-_spuyrh)2135oJ8rM zw=_2Rva4^%Eu5X^6_89``8SUWSCSQuNcqyOH!kwCVT6%WYD4J;-4DmdH19ln!Gxf% z44TK`k=<|8d`S-hx>)3SEthQ7jU*+FG9#KsUO}1;1j7j`AO242HKt?c;^ZXvVAOs( z{^=%-GfSpmh@m+e0Gpy(y^Yq*%;u;kJ*l2I9%seNXj;0&p7syN*c*&)#-EKwX9*re zQ(J8`=*&}H4_7$Gx#KU#GkGMpYE(Jl%%JL^iSRUv*%2GuI_EGce0b6h!0LA2p&=BO zv@M4#e7`{wSa_VEC%9>&&vHoefy&%poHuWeHZE|8_+(K5v(z0e;ZM+*6Ta$Ky{SI| z^+!g5R6mUhCx88N1jHS#czYgy6pH`1o{*gH-#V|9tQVo?{y*Lu%#*xTQC08-j!%If zo4LfYqT_&G*Yy~J`MmjPsdE1G=4#SY;Sm7%bc+= zgfd|uzfW{`5fk6b*X}~z+9fc853RAZRUR<@v6B@0M1NxTzIFq&Bm0W1+~by#uMw@g z2>xgdI=*TnyqJcMaYsT|*pZKaE>`cWC(N$IDqTqWKbZ0}0miUb9^WjxQJmNGRn!Km znVm$2tjdp8*!?%DwF#EOJ}$r{?UYKb+-K~7%W?2lSaIG44$b8I%WQa9Y;bSP9th1k zwta`k3*c6kY9ifCTBH^9t_~ktg-a$v`e~?5gQ_?EMaJ7*V|0cf{}}-xI+ONl3zk$* zs?X8})u~75l{jgRI$0q|+@!BN9+Lv3kbGZ*nPxDS4Au75U@>5y)Rm8M<;t>&sknD0 zkw{F%VQgH`$=lmQ!9X?P2~9teUJ`^$JQ{7Q$VwzDH~@?{@&I#AyRyHf9`I*)-DH0Nr0Zw!rJfDp-FMpSWDS zK=*GL6fs(@JeZz)cpR;V@vNkykWQ(XapF!#Ri9O#NZ(fmn|4P?m7qF6T3GU zOXNdNH-sJo$ddzYaTCl%A|jh_(^f!JdAoP;QmSeJg;aS@5F|93$f~81A=VBOE6c=CQ6NH=Yx4{hLOgpYpxKU$&;^rO8iI|j< z-#ps;A_ixa%oI-*eKYUz`iGKZ1HR|rN(t3s(Whv@mS!4t8G zNG5X8^-b|#=zkxUbS3~@b4L{>!lR|!z;s(mjMWs3=>PX&uNdy@PHYw$*jPRuP(`$* z<#jWT-()WS0^hXAQ{>9U;%vfE6do52o;fn`OlzY>B}Q>L)*4*1+>2O_n5~~Nqf%m? zT6-7|%3QiTs^t~FOg^c4QGEHO?8P<1?i3Q2#&jOp@!?E)*MPQbSsup-<2w6Za{Dt@ zyv1#M;)@-5lym0wB?};ZKc%kR0($P~rBJs6_jAP1?xEcQ)l1X~a0!u-co|d(|62B) z@XIvOT8ut8Vg{7PUOWvM*sRZ6&H&Jn26l5lv1p^cl4lCZ< zT^ILDA?6U7Ycu&;+veQkblBg#aYG@eF9tev;wO>yyWgQ9A%W_e`Hz0U{i??dE>nJBB>;EPrexko$_G3w086gsME*i1Qe4G#1^0U z+pq_A;^5x6Y^)%-?x3aH2_|@R>~MfHm24omaj#$`ev7lS^3W#)9MK?J1Ri0c^2 zFBpXfiCI`ZNV8VPJWUHSGn@?86i5|AI&^6-(T&0-qsjA{gzPRDOwKd{Ce0`&^e3Z}celd?`0y9__v6v(esC}c{(Ya+_qE|{vPBd2b^`9--yDFKiIBW# zEtuG1;u$h)u?vov+3wzeQkL*L@8QFR_&L_N^$55!F+@|^xd#!P3kJF3_UIPEuA);z zPv9pRUs3s3c!9QMtj@~yr4Bc5)ti16vq&dXJ0Z1boWVGPu37y9_{TW8uO zb4O)ZC}*1hH7kfY;~N-g5+H^S4AA@N^9Kwqv>O1ZMRC4ejL)@ue_^BLO8c0y zLJWIEEB3_8J*(aK^{zdIxa)>byV-*?DGQ5KcJ#Xb)e!eMvCYI|^82LM_jM_d4XzA$ z8#J|VA^acLtKT-`kXZ!ovId{_;+Jkm$hK_S|IhbVukKetTg@xVJ$KwYgx6e8{bc_c zu0Kg977`lY>pi7C4mNO#XiHeYso>}V1jv@-!&2KOV5)Q{Uh;_gv3+0Pr-0bM>G$Y~ z6W&S91P}%{dekQ2Ge_W2i{R&&YdvYxQ1#m)MgPNQBCmBARH1rxu37_LyvUGXEidvy z(;~VnA4?H7@23SJAj>>&EPANiLmLNih^f2TKUE#?V&cIzIk<2Of=iQo zG<;>}-W~P!Vn>n%Sq$Rci~aQ+{qm^Y}d!ga(g?#uK;$4!?jm8k5<&i zk=t&VmxF`P;82biPlpe8Jm@>b@fcwv`9o%g3u}FUG!+iPGt+Q2`nuu1;MV=8wD1tT zpKtngc6M``nfDC-DpSU`f%O}Hp((HB4WUF?h~d6!*WsycR{-a{U@}LW!N+IE+u+IZ}dNkgO z2kf&kBe}!3_8BJTIeI&6ySvFDvzDzHtBFnaj4|Bjv$oB#ZW#W$ON@(2B)rD^ePf+Z zpjq0&A_MS)Lhwjz^O5lRQ5W1d_kp3d@c+ihxH0TP2#i(`y<$41C-RxtcU{<{_^+Fc z2`~Ic1jf9iDtg6HGsA(nPYFIWtCo_z%)c}AQ3clHB_xc9xH>E)dgmOn;33T`%lurx zr)27g>t!BY3>1XUQy{luIgsuH>72Wmx9ff9&(;eLe(9Tt!gE#$;GcxKYt>0kD`o;& zNX+6q$hT>iG(i^b4oEmys~0UbcdB>#E!ngYPr~KtrWcLx`&xVUAjtA%PwkDLvp&9k z>$2c1cWYRv=>S68ZTC~xWlWnnmzSa@q! z>Dg$KL?ax*Ca<2Elvi>j^_K3e+W-@%tFsGnD-eted#J6k>4756d;1IVH(p1T+=sEc~*f6(lH@P?VK zyPJn)CEY85lpcALCu^wh^^#H;o^eZTCKjOy&n zzSUT;G;^|VeH*8c+CK@%E!aii+P$vx%+H4VMi>(KnSLi}O8N)9L3}3hUrg5~1I?~A zn*VMB!~KVZ|Byh$0*Q;ycwrd;goGxtw{koc&e#F3u$Jy7lhoEa}>AxvW|3mD5?1TOj67-)c_)is} z|5U;MSE}F+DPOQhl}ggJYm@4#w#E5(YVY#erU7=E@p|ApQ#1IVq|T?Wcg|0Wt8J-L zPLzjqgA+-5q$TOEQCBTL*wIv#P%!j5HteNZySy@3o^SY1gXS&iX^KL}Nmuh=FAfrs z?RUtvsV1+$q~Jx`UnKodUh%ba^G!Lw^s>svPUjy(;XzlDRuyh_RQSBr;5trjh-^#x zhz6Cky406%$`x)EwX9ys5Z%VUY+lJ(~ zW<~{-*4)N>zK~J)c2`f62fjnbcwECdh<}Jr@>$Ja(=N2Bsa#=L|AZ_pvxlJM!`GUW?^V{}3hv~& zO$h~#l^$2zqQh!hKi;{5a~3N}{-tz-X6=orpt=S4xCC@@@rTBnM$gihReKAnbHCd( zcQ1gev^~i(AU3L`v?i1!&oJs! z`Ekp*95>@I6G!b7M}w~$%mBQ>B#B;FBIuWbamt$P!@%q!Ybz{h`KKa*_AI%6U-~}l zU&!aO8N};*=d|)sP+cCuicr=1y`NR5&+11nah9n`w0!{lXJ4vTt?!4%m$aqHB%z?X zE%|x+vb-8En@4}Gga_WZ;k1^iU3k#8vQ8_yfigJcX2yizK{Y=ALOR6M4(BtNh-Wp2 z*WFSthgT49fmOI}ftO>akNqPa&M0{jI$iTgPwv9@PmYRc5@^4p(B!7JkNl8a3p!qm5exId;9Cf zboL{sM>msGcf^fDl%*McH^=ZiQiB*lY7ipB?eXQ9^$~OLf=*I?bt%qQDpO*nh18L1 zIBe*%3O5PMBM1K|Q#3|A-ANDRWm7A|s|$khTO<-m%U=4S@z7v0WaN1r$FRI|HyylF zmJi1#!ZdAz{g3V0hO>7_I6a{tXZ=vTH<_ zvtuNoDStesn$tE+RXD&9Uaxq#=f^0LF&1W3hE*3FgAqIkI4$X40^+&KlJfDJpN5YC zybdSm@~a=a%S#W7F%nrO6b!2QLP{Q~gv~1I*Z+7869AOgED8n};)k3FjOtABWvVb~LKdkUX+`8<42gx*gW`t5hf z?gJ86lV%GZdF#5~`bcp1R}`re zlSY!wr?}}Xt-=Eu({PEc7(Ud4|Be1Z*yNns3|LLEHo15Spyj%j9-r4qDsUn%jHWLb zjKmZ`$Grl8;#0}VHa9k7B|t9s`h-uyH4Vb(G7YMrEk*mnZW?LN#aI5O*V{D-Rto>X zLfyD*GI9}&7{O2?& zCaiG-wsp_K*IAeII>F()J#hmIS95>63>y!Grrf~wYjJ6vJ56;;X(Zg*OenEA_t;G% zx}b>IWGp&1Si3MbY4kXYhzoU%M~`5%tJ3T3xSI$V>E1K!!@w!JfXp@5Xq8;gN#_e( zJWVeO7TIT!6WNfS(^d=)J&I(KEuI~Yv$|gQdoUUKC6+rzwPh#KotA$SFu03pPr+J( z4ZrP(r=!Q8ID4ED!+en`fDAqJfsF97^)>(l56}7VZ}S68Qh zD`tMBw4v$gV*5(mhYNEY^N0p)Bx4Sf{QmE;FC2q}q7X=Il?<#pltQx5B={b>> z+Z&`Xc;g&xqtbICO|;y_^_*ro>K1)8znaBLk?U~m3oiafJT%%!>B+TJo}rR4m>em@ zbx$s-q=Fd{QW2liXoxMHSpDTxj5p~)B*Og-?yiZ^qUzYVhEz`r4|xE;(*tu&w^rnlG}H}p^=4|Pv~hgDSlZyA>3Sd zyGOWlg;6Cp!dmYVwb}2uM>q^~oeWcOG=5nJd3xWmwy$xNPFyomJ%=?@(%3tdoEdbZ z-hi|L!|7sgjVBd2;iF4qHjriQa;m7uh8ryKUS&*sYL@x5Ur1C)MiG$4Nb$9HdX4xo zKCkJ^!qvo}2m^`vFPKDB5$nE#^d*r4SNOTy#Sk}gk(6PXiB9nCuby~%;csUm@qfq& z!kg6<@nfdo>!>ZP?dw&#L_TQ?6V2up;~m;I)o*ztIc4f9^`78+)H`W=2`~6w&~(zT z!>3K*8#k5oSxiQp720+~JJ@Ln{NY1e-mmL(#~dMn0y!ZtjTi@E-qHBTljNP7p_e~dckND88+Q22xO?a%KZ-8$u%qR6k_u(^(F zy@paRe6*PKo!b3*WAs?!x{-k>vgvN0}Q!?;T#%bh? zKlarm_h|UC6^1k|K;N}_*Fty=&hExea#27SH%dI98?xhVsZdQ}ik#^HfF0&L!qz2BD zQ(!syCW~Mef$tSLAPJdvCs9=!X=5pyO-ETlzJwy6(Mv}^9Ib%ypbrvhD2vuT-Zs=4 zgUPnn82j3hSnE6KWQAz)ZD-!j%b@PTt+P&F=2X@R=e)K@XUHRA{a;f;UX47Oqj~zs!uk_c3PwsB z*l=~*g~E2v-e_3ZUO*AMqe7`1Tp>XA;6?h{jqPD3uIGUb_@0Vb0`r>Imo-v7XR4K2 zTRpufc`95!YIYl9b79(atpjbZ*OR`Xh>I#Y6V%lk6Uo~~^zBtTck2UsPsYRZ)>|n> z##L3g;>{-PaV^YQQTP6>TT(xCIlj;CJg?^3xO{#(9=ONVxp0YZWuSGSwzXaB#+LD* zM&VnsM^thqt7lK^7^(&!8ulj#09tGhshyrHq}@vvBXjv@g4^gQZ&k3g#Kqc0y^2@! zQ-OKmyw?SR>yF2}Y}37d3};#D2pr}GmsN5;i)Q|bpAec_DSeEqdg(W*4C?Ck@sYAdgjRr{|B`N#c^jqyg-9m1g<+392w{DJsSQRu#xm18J#?0rEaCfVWs-K znyQzd>yIm+x8gy}qY2S`v>PkVtL4vcPBz5j2&?)Oj9z@LV?UZ8LcH9qVr#34LYZHaF)z+Y6e)BD#tlzfnnS?`7^k_NYH+bn;^&uBLxjLPAmEdi?_v3M%=>0tnp52w8_~Isf)De9R&cL&;qBocJb5uEFitmJ+WO<)wp+z< zsf2_=H7lh8pTs7k?I&N#Awz4$dds}--9s@)rSZLrwwLpO|NS& z>;%11+s~PBvfeZ2h>yBIcW7pN3L1<8nPNZgOksgZWo_ncH@4Y`z(4`E&yctst^l7E{l523?OI~KZTe@ zL|C}LnVhmi=!0gOSteeQ_!le~C|62^Mtp_$JAs1xQ>eFlG&8ekF8XGr6n#Pb3}g+) z-dMD1(1aJ5oy0$OtY%t{p~GNcW$tHmet=x5H9ufS3T9I7H=-EeAlMOK(fduYUhgP& zTDc!C-0flr~;$y_mtA>%7#1J1c06aSohRd$)m?_mXJ521UD^nJpl z@ClY}4mqoHnD_Ur!<(poL`D%T8{bV0@O!U3P&_QO%>6S!?(jeG9XHuEe=317m-7R@ zR0hdBS^ZBh-Y|I&wWI-3@(32tG20uZ*UsAfZ}XVt^_&009yUbR%yiva-c9p;uMgiX zLh_kM%weJB;A8y{9yVRyHZG<9y!KWkvVd2KM5|@#Q~`X~&2n-4D;+ z?Z1n;cjw-zUcwry!JWsXYZj&V3puKjajbVeKWZ^nKJ>?Kn$Vvu&`QhMF+_$vq90G% zHwLJ>qjREk1mTpM`}sZOJq#Z?$iBa`_v>DY_55Mk{iQ23UKNBK>dkxbqgEU+IWL|I zZ)7t6kiOe7~dTg?;lb&l8Uby{;2X(%ywCHO8TfMCl3ued`2l_{A4s-=O!@4Js@$*!^B%>)UDx|jW&M+a z{Ug$ZeLHrLqsb|N&lUyr+XE_jgoOlPcE`?MvEvo%i=%(aFrQ6bNUs=mxT@~xD1nxp zS=VpQvPW}K$D$1_W>O#oI-@ks*u6j0yS_L{hQfjgp}k^EU<}=CY_92#+0{C>+xy_X zvpg3{t|8r8L!GanjE$reW5O@PJizHA_D6ika9$*SAF&fYnVBC^BLip4&Zg+y-!xmL zBGzA)ZkJ=G^qLI{p|f=e3E{{v&tiX<`}z@(mc8OYRnyCbQchF7+*d#I_wHW{K$zxZ z6UqH-Z!GRnYyubWjh8Yjd#+CL4pM>7`-MtbiuZUzf773Uqnw2sT4pC587m$F@pgRZ zS29uG^rsy^;Pt1ulItjF#UXSc7CxZ}n2}ty9XtFFwS({t!gv{O(SBmlLr)37Sa>ku z(dDonlBc!vjqjEolp~`w?pKvwUox(n8Zfxh#}FA1moM(m@-c{uyOOcj(t~v->zUWU zH93HN$uGzf0K5Pfo2dOFH*9;JF;$Czp|_8hfqLKK9(vjUpzSXJ7QXB({;fw(5SIAY zfnaU9U*TGS57N z2u7|;m$6_?F-;2N%oo?`Ex&ZYF*3ig+Tpk5aO;3I{9?J692oX znh*2V48oy2{pn3w+n@CWMz=GOLAl1fROR_QHM*&-nvk>I>&y}<7tCK35aDo0?p`%~ z2UV6kBK9edW_D<${T(n$JTxlaaYJ=JpkPdI{ij}^>EV*Uhehu{-M$NR-^FvwF8lA7 z`y-t7*&A?gYU^m}`VQc^_W;>SPUefe9fmHFOmEsi^a}eG#I~eHM!1yB7AR{hzCAo{ ztBfsuZ*Tqn{p~-26X(K2%9fI0-oMhpDkKgZ<+#r>TFx|S1WXFjKR|W;rN&D#E6d!R zl=^%Dv!S|J)rc~4yNb&A@N})e%1c>r6QoV}9N%~{mlV9CB zqsAKngwp7{s$w=zBAh}m<6$#BStWQUqh+4dfEn4Zb@5l}UKp@*x;Tyd?U!`#(M)v>WfV6dE(5+&w60-^p?rOlUn!kwh|y*DV^@vBhOad zn7#8ea|#SxYvTrz!yuP^P8}UycoM)kIZ-OdqIp+Sa}L7@8LfAu9MNf!+IOQlfKVs{ zLQCWV0Jjpr?erm^kD)l3_gH1-EcO@p!?q&jxk;f zC2t5+cVVSv+gIMh_E^rK&B{{gc7=FxGClQ4o{Me}H812FD>Mg5gt z+oWoTy<@Zw%im-`$sP4KvSKdiw{3j=awUPCV!ePDpj&Dz^&6P|Y0COD+7)|)A}e_A zmtYelnTa`7PFQ#Y;Bx;gpM>utrL48F-l2!U2Zrkwp!;ls6m5Fe<^8P3^|UnxsFfYB zO2*P#u=K2gK06cexftt>*)l6V)!J|UrCjupuhGpGe8Hs-*)=bu_+|!QG&4`Si$1>I zJ1cRa-~H6=E$QJ>_hHJ8iQQ}m!EC4cl}t}sOoCK`UGs|RcUK&edp7U(@MjNX*eotl zYv(H-M8oU$&oOVy7$MaP?A+Oer+xsCduG2&$dyouy}M#m+yVd}P`h||mdmYBDF6f9 zb-UT>0szOKu^_UL7 zo__bO=|s_ZxTD)D|5{%bM^X2k4krBGT?BH^n!I}VWt=#UM#$`PlhHxBmZ-h6Lt{Wwr_dWj)oA5}oFp%M z8G+bTwm-sdyTaMKfUy}b2OXBN)JDtC)Y>Sq*08!|yIW=#X3C6tuDoAJ&LuVk?iHPI zoSM=_G(A8xC+MRhaFsLWdgo@UO4JE*T)sb|@{pXT4ovr=Z+4K^B&n2TndT{Z>cfqB zz^dnN(T%EP`O)6KhUXPUE4SpsRb|Erh0u}FGd)$&75%etA!G) z519UEnBSO^u!;@~iIc#~eA)NKR(+HQiK(Ni>z#Ges-&I~FZpU;1#7vZ5P^M#ua3Ah zy_6!Gx2s+`z)J4uZjEj+%Mz5b-|ZSDSp&1O>_CKTF8Z1)?-8?ol~+40cwmrttuHTs zZeEXmfc`7Kwc^0{y=_TS!rLZ(@AmjhzPq?G)%cRD3Hz~O34ii(mn}L}0 zJnm@M1>XY0X2OR3fWS&xp|783=t#v}(5t*($rUvw7Mu>H&l8r7k&MW^D6dT))Ln1v zpo+h4UzmJj5M(^B%Y55jJ2}9@aeu@xBl(^+duQpTwa=-(JX?%hrqy}L=T-TiSuNo3 z@y8uqQoLqMxr(o(ebXz~>l~mS63G_Zib)N%AUJg?ll=zEO65bz2&p9esvu5#K}R7T)Yv5Jfe> z!QpYCuSX|2N^OPS;Loq0sGFH5Ycl$PhQk`!DA}>KbwA`z>5rXyqvrd>n%z8e;INC0 zLB%PSw3dNyN9--nd2Pi!hK2q!>tsc$lxS*8*Sc06l2eNW^!(PEdolA(mQ+=;4_>MF z4efRqavbc*^OM_8Jij27AB*+ghL(zYzv-lPw{DtQ}xvm)t9Q6818w?{IDA!K?vtdgBw647_|# z_7LA@J+xB0Ux$QQ;n5S42$Nofu(R-l%*9psNpNWk!(=XIYr;h^;Dx7UE;^np|LcV{ zUt#022apS=KhyN#wh&D4`b(F^3>J>m zpbJuZXdic?`ctMnFF;J={V8wx8uUAr)qgJ*JJF3i2C?kmE6O=r>GNpc#9-~q8 z5j-_TCL7>t30!gDW5&y^^)ezb`^BJFHQzHeQRmXz!-Yew2I~Y~ed{O1uyEnZ$M&w< z=gNsTK*6l)`2PfM&`ScGXdB#L2BDvE(yT>d;-R9Q1ku7a$e^Dc75Z8uDdl~ZRM(1z zFFkv?Ux|YXS_WbvGEYz%WE|c*i#={N_-fOSh)rW89AtaN_beC+ zfIt`CFaCSQfu;<Z~Yz>c~%cr-!m+qf983fj0R&}J_pr1w*mAyt$*}DIr+LbJ~te6s7a6t z!SzxHmuxFn1+}w~k{N$8pCA6*TS6aJd+I!oWZ&_MObIN&@Y;$4K^eyH(vL}OrMXRL z?XH6f{pE3bsr%uPq3c4Pmm7IV7qDnc@6X`6Em)4CAWOy`1O^JKKi!rtVP4)&aA{sT z1a;#L0~CyzW#{f6T`LtDz0=>F+h*yb!%)cJjRx}Ix23oFlRrQGPU%w(92{{hXIgX0 zTT=L>Jf;?P`7;^-%%k_mRfz^-W9+smL}8%g+%GB5_lHj z73D!P!p3Ho&RgYDb*LN90gpc+^tDk^v)5HAEBkxhZ&F+DJXa2f=x9oKKK~SuyHHm+!$Xz%dY< zD%hs2H~`WaR?#3~IM#fc7X4NlPYUv zKp0>%(EggQt|IddgP1@hTGl}uU^NKt0|ehdAKC)X0t623*;1`CW^1EQFa68`^6VZ! zQ?cqR-N38w{cy#Y{^koFHbE>pPfp+pXl@7Fck_K~oMT(OB>m=B?UkBBex;C7>bmcX?C=+|7pI(DD~3e9S^> zjqlzVD36w5K(Akz998?xnD#%AmA34qK&T)iNE{(u&>t9x-Vz{=eGUXtOj1M+kTYTa zxiGmx#s0CcL_0DoUjnKz40%9`m_Z3j+tNF9iq=ZrPn>VPld#+SzhV4Lz=Q71t@Y0( z3~PHs48T|LdYM1YPqusaO}|L09aJ~0+PSKH>vB|VAn$>ChGO+pE~DTI*PN-v0e&|o z7O2^Tl7BRj+Gzk`d`Le3EXYi_1cZqFDW#QRSb_5uf!a}a6LK~@9{8$A z!jF23R)Phv1|(QOW|4V(esV^^$E7kXuMrEx_8zB*un*2&v>N=t?~OeV4-A3_q*m_lON|u=BAk~ay@3Ft zIe1iESb6!@dMONsYeH(x4%VH~L=h7Jks9EEOioIb zkvh@na?ovHM;R@UI^J}v!LC>+bi_izj86!>S_Y~Gx#LwP{x3}LxPrV@_QHG&leV<1h#V2?(+VZ{vAR@)Q4p_b;FLmEAPz zM-@S~rN2Dd3RwVl!iC9psK>7yf>2;vje`9G9f&wnT0 zy!<&?6TaLB%8_05E4wpiKLrsC5RVXLjS7vOl$!dyLQ1A#v07C5iFUyXL_M$_T(vV> z0)fap*lWZb41us5=qwzJt-36)31?k0O2r{R5>3S;fZG?DQQw#WN?x4N5-IO`Jg|J? za>O=)+_SweOm0`7)$BHG#?_RqDm%3Ak2=sk``B+^naQIHx@}lJ@5Izay{HqP0h4qfuBnK5U8nH1D|;T`O2%jo4b~auo)- z1#Y_->%CPh4rPg2&e3naKL!xJppVWlBsN?F_vBCJJzrL)W}mrE%>vPQOhlM ztU7CUF;*Pb2^`EWuJhimhz#)uu4xr5N%!8$&CXsrT;t#r9&Ij%@b)sMcfM9i z?16fZXWV@z7y36x+%kgGq;$-n9JlJ2bw$9Cn+=I#uQ#Y9U`R@KB;3aPPF9_;j(YRm zEPa7w{LxCW2R?6)L(E5LVP!*WR3aPOHJ{@%zsPUn7VYLCx7zzN zPS!3+eOM!yD%sA$MyvNvi$kZ zZ@XB0Hg43$^q(q^T=4F{>DuvS2Zr3NTFvnXGdSYhcP&@^;mdN|4I5Y7VF}2SOwaEw znZ6vRXHDL!#HaZNRw#>?CtQ4hBA=ZL)NlU-i&wClguH3Tu2r~yY6R(J{c&HhzZRL_ zK=S%w3rt*CKI#ky_*yPRy@@pnuGuYlp!W|L-vc^dP4K3`P8*9If#vb{JIDthFa!CO z^m8uj+^C3z4Zxm^a)DN5Xzc|%iT>!kXInpQcPr4JT>uQoRE1e*D9cC~>u9+D$8)S- z*aqBW9vx=wGoc9RD^B}qiUEkmExTD`;|+5^m&g#fEOAt+pyMIDzX}6z{k~S^{vkJ0 z#S17qe63<@y3?n|&3%y&AIHhAu2E^O@zQ=SmN4E^i<+}e;UHnC7VpM-r1L*`%;tjf zG?xY-QWXANKw88vJZ77LK>!^hw89hQ)~J+&1W9B(XiAv(B+TRw<&fP8JizIwH(9`i z5-i*9v%cuRP2C*sV=hm-6!k_WRees!KVmJ~R0>)SxC~vzLS8Y~;@WTDw%1FTJmSc; zisrH!;BsayMxsHewF^j*L`(9mi-?Tl`?%Tr`Y&3Pzfvq^e0-1_Z@OEcb%md1Gd^n% z|Ck_e4grODqglimEaU1mYs69#^-9gT%ny{Do&kP6h1w7&@!Quu#WG%qKUENs`3sY^ zoJ_U+`4aC3j=*~<1--D4MUQU&Tg(H-E}G4gcBdQ2BS8;5!E(32rCxp~w_f3_fXLb@ zy|%0d)HE{=uP^ANb%rjO8Hp0}*PHj*y22Q&h%Nr$v|#g$-1tVLljUMLEKAF(NG+?m z4P0b}xiq&x%VI~{SA*tE4g{vpkU4$Iy@5xT;GK*%W8ysrftD7lu`)&?XKOGQK7F87 z31&(nXEx4(FF|lY4G1*w0+N5x;)36O(;Kc8GysL4;VsCCYp@_YrAwxl9}3e~0fQvs zy$WUfx@u8d5c92{8ZrIJzP!j2#7au0-$-l-*m$%S3(@rPzj8Qps?H3?2M%{Rg2zw9 z1P*tTFxW<3uW2s+Qvm#x82lze;5M<0GDFo0GVke_oa%dh>^%0n#n0wpzQ~7ETlQ-%y=orjiY%~xD`HkwTL|gVsMV!gTCtS8L zzi^meiB@TL3fM5zg12e03N^pzQR5hn!A1J8K&I-XwM%}}KN2-2)a9%wYR9LKvg4_U zi}{N+5nB<|;)~zc&DR={-()K>l4XghZdFvrknds(m9T_$TDgi1McGNtiXlhLUxxog zy|I^Z!)%lFsfJ;iFn@y^^+nbTOMK)`RGcDWn(#glJ4=f%2}a5i1;gZe_XalxtQhs8 z`0I#-b-v3=?MWqE`-wM4f&wzYB8zL2fd9lgOwda#VCX7#!}uF!lwKkrP=9KVo&(ddd=q9yIaAMpeAgKBhrbG`H3B!mXl&3L+|iLD?vYkgSJPUrzBbz zkF^-+7~)8w-ozKpb5r27B&+)kJTWZdw)nFpY`0&0(N3|9v5go3s?=2w%P_AR9JOl& z%kb;iQ)Lz3swjgQlQ@DycXFO83M0_{{)-YVJ7^3n^a@t@5#z`9R977?urk9c6jTU$ zhS4Li0za@Z&#*5pko%R%dSho9 zTQOS`yn+L7RT}2>d9{|qkDfb33fNIZK zgw|Tt$5uSz@==BCTC7Gf2r1Z53UR z2-X-7?ctSW-Rs-P*;*$-XIy&(! zE6xIjIVRD5l7KxOa2Kg7#azCRE%g$KrBwX~6uCdB5%e6hx$sigfM?@Y&XA$!SjL4K zZh_cR9pTLUMkjGNh+OgEeeBY%(GC+ilk-+l1>U>JME>e1T)iruKNVXd>`HBeh%uSt zz_c{ZTSdf^VqSEOccNa|)+bl!@AjGsZItfV8|8($1>R~LmOmSu6 z8z;q=?fW71RgAB2wv3I!=F%3Ng|%SJJvb0r1e~{u!mu@F&vgxX+Dm0XSb`;yx1*~& z$e7H)NC0OEX=87J$)&2iqmt8vuQM@?ARCP9 z=NoYm8RGT9ERwhw#~kVf!#PcqShkjPmf6!-QQBVUs>vocie+pXHe~|m8M)J?84CeV zEMmlyVwT?TVySuS&JraD3@9MGtf!B`0f+TlKS56-;{qfxBW2_x2azGx8562 z)CaVDCJ0Cw_B2ssXqR}WkhQI!vAcCMG&H8*-Gl7At_&ueA9v9=upGN;0{YqhG`j@Z#|aD*7PQfSKY zE=ePHWrlcul9D36l#Dl;c2MlA>gi(eA`-f=m|>9EibjwRL>Ah0aYmVC-r{HwHQ~** zjQoAA>DZIujAVP5I2wf79R?2^DICpmegdnTDMmleM-e|tMuiKNr3_B|C|>NZ41Px? zdD)IOEP}S=bBx+{G@SOPHb$^qBH_Fjn12P}O35UR*!h^hu!Qr5liQQK=&wXTKPTa~ zL&HEE=JRTqL?FGzWzM4XdB)2S=WC5@Q< zgY$iZ?foDaLy6?*sp(_mJvG5u=fbgwbcvtJn?(TB@9GLhB#>+axYDQ`n;7N@=xizJ9Oiw*9!G0P4=)e>4C~?| zFue(PV++n^WBtvTK(H*6GkTP$3&COqp1;Kr^OrEbi;-?QC$?Y0O$30dd@bY9TJQp? z`Du@nhJ#2*PC#M{RApN2HK!>llyBL7Cz$=K8U~cXA$@%?6i(1r~5xcy;F!8Vzq}CbVTD% zU!=+V1B)S7jV^AW!SnDoRgw@uY2@fCzPqdrW1otRD6YqtF{NpOnC&@sRUq!#0$;KnNVv z#Zl;o8O#8@0JMZ0K%lDI#Sb1jRU%}9on(CjyVz)#`LLxskS+OWnqUu(wIH=lr_h#4 z*fVmVnag|vK{hR1fE&fRDZGgUf_GiK&?!>^{R9A#k3wbVy97EBhh#*Y7eoe#>!L-4 z2Z#@Wl_>S27*TL>CX{x#`T6r2aN-*Wqli!kyHPT!_d!#|@+DHo_+@?)`btVWX-qAt z+JF{A{y36AEr+@o=}jV4TDAcvhMWw;X& zfTE#YGDgBd5{nZ@Zt%_aJryHUmgU zvujq8cU)r0ksUqvQ4yH@qQ5(rvQe#BfgK%e$f+ZR?3T-N`SoXFJ0{$NSp*qC+FPna z^(Kl98M;ajh8crbXtM9e0w6dbG)0neL1`o$XODE@v9v=HJIY^6nB){CF^#mC%kR0I zDy{4x<`fO~xZ6Q$TuK*FH;NgYk3(6fbei@Sx=1T?a8d*=FT&YtSjJ59X*9{`&IpBQ z9jDZg9<_|3Hrg^w&g>VLEs(&0fFD|*RiE#!*$m}N zvcpexBNvL&@*d=rj^o6yEti*7eUnatanK^{bTa;QuwzibOC^2)FHT}anQcUgyR5L- zA(1iWlv+NdJnONL36_%i`}{J$>!0HCAYxN+tbrotG)uK88@g2NE7EU_v)MG(m_>Xs z{yM3MwGdwu`P0llt^Ba3#AcN9q)?}otS`zAK8!10#7t0M3bk9wmX?Gel4ul(CgK$! z<1QH^A4^Fnh+4=ukx}+IS#PxEkT~DLc~U5QQkQ`G=LSi}1sUViGGgO#(o9^W=QT-! zK@?3)p|C)vyu>^d7cQXwM-XTjDI4tw_DF=>kYZ?4sBEy4_(WWu6nj!kkZc9f_J~Br z*>Qve(k{wGJ525eoRK4Dg!)m)Zl0{CV0&2XtfH$pp@i6{U0u=Z(`AH=O{P=a#2K4B zu8g~}mTLfjh;kdD0XS3A#3bKcY)O$TPTZ25mB-KMGq+~Aj;VX1cUpsYYDt9Yfc(GD z{dP=#pZ3Tta)0#hF(6_D+vBul!7c3pS&5G#&eo0aJ)znE&yJ`8JH|9O{5X_Ybgj}p zlNQ;HD~_!x^P3JrfTGC>W32Yv2W_Q(?zGK8m}B&8R*ms|?5yZ15CAvA*5(gQD;j88 zaaEI5!`6ByY>R153%s#&Z_S)zxzu^XyJ^XSj`g4g&^vPeF6%Py0n{H8JxFbUF_|2v zni7qrh$gh4fT-ye{c?Lfl|s+!d3)k3@~{~|?}({l*Ux=m55rS6==Wh9*t&m`0p>qS=pbbeC_WNbTCin#<2FD)Nm#MtZ^HVr zA)^epi7zkkTHbioIB$=~H(DDqE(Z4MB;+S<1Y33Pt*>wWN1E@mdtuu!qxlOCRK!ZG zC0sLo$)ufB$JyPU0o29$GKfTp0uW{&JfWPhpL4_;gePEhXYx8pCZTrIlNpzb^pi=p zN$8>UZG^k}Ch~qTeSZ*e_Yb3|w@5fF3c9za=$l1)Ff{0ZujEb`$n$RkK{l~+qVBb& zex!JhP&VN|ls!s@!P?csOYeLS!UPo3gWp7v=iY*&@r;%QTq-5vMbllVJyz4pF?##A znOMuS?+$-E`|*sMI&|p32t|a2gl8y+6BG-6)BTq9&bTL8LmS26te_LY-_nz4btv)4 zR_AqXxtIk+S;Q0K%P((KNnLil?J#GiV!w+;pg?d+jrYvKx_3F@FeaEBEzKyaj)N48btZ9jc)RZbg02$; zRZ|j#%za=0B&4saT3{04ND2wv>GUD<9;Av%%*LllBn(9pPZLe)KhC7I_G%?b35Z4= z5EFnmf-GNe%<{%82(&NO+vOVMyB+i*VkaX1qXN z4qpEt!XEz;{PN^M#GIk_iR(s8A6S2_F>xblZ4fa74-pMUQa%DV!rxf`qK~*90Jj5W zxND;OqDe0P)3A0YbwPw|CM-Tn+Th6W1Jz*hM8Bes#DFuQK_{BOwb~>R;CoMf=6%&8 z-}mr47=YPD(KBC$y|nM9g94`J!v(j;Kt@bsvGKFoEK1(O(s5$*QKpK$OypvTR}VS+ z8$IE#DU(3~5%WQNtYE=GC$@umqVq>@PHeVoIN7H55HGNtK#2RWrizA# zCuGh0N^C$Fk49TOJ;ARJ$%mtsSj|>Tun;7dAc}ObY9lRhAim@?qmU6h_txI`=r@2t z8<>XG-LrI(OEv|89H)we;b5c0CYn)~LQqN4T_|Jq$%+fAH%TZWbR0qS3ua&@m8fv= z(iR(ojiDHh^hjtP*Q`&+w`XmcF~^tmLXLVW_SRl3(#6D!Paq61v-H?Ha#pqS`z^>*_(+i2Zz4FDdn6RS`zqlFUZ zpUg-qvd&zKt>i=lEQN+RX^dx!f&$&3iwrLV5dTSUj2NcbbE$RZB@TKZSNI!CbDm5|o>kLQ_ zAmcyygYQG%45ENI9|~n_L5$(k}1(fYu0t|c^t3XsqK8}Ed!(~*c%~sHG{)GS8HmV}+{*b6p=Bb+0gmNwv zGcoXlGMI4Uf@!~$c&W&a>Ud#3aRTGM<0emmb~yI(iId1>qmbwi84laH9p*P7>@BP& zJ`ggtCG%I=f)UxcN!Eyou2C+C9xHW2uk$U$JcgpFk0CmQA;?bhz-H3tG)4gN5yeiF zUU%#zRejh6Jjf_dkyue$6oeKm>Lg@>k0-n&l`lBel(4uS5a1d(S-8uU@%Sc<0oiuC zfQ_S)5%r_zXwj4*7Z)fW{lX$ zbbM6}sR9$;k{VI=U+%svj|sOY!34xfLfX^>@ot}c8wqh7R$(q>5#kt_r9Kq087T>~ zm?JZuf(%dzx8-DH@!SWuehmBg>M3sAU_x(6om9aH%7USXzz<4K^1!VH6Q_#9KqN5c zZc8<2B5T{A-@F4C)euX+pM-35B>Iz`BV#~q4T$<6&Q4~7C&k!m9KkZ%nS5(&(K8hM2QDZ{Y3f5{H6=5NXRR^u3 z!4Ml2FbkCtl^G?$RjE2c6{0+@4}1hMCRNb{Lr5F76?G;)U=$`}ThuTZT4Tk>fTPKX z5gZ;y0eSU1XP=oNe?xMAaV_rk+;jHh?C#vo@Zq@V%_Q7Db0i zRxe8LfNbb!;m7e*e%h_dXJ~V%BNM>KcT=t8RWMoYSBw*Su+D$NkFMV}X*;0xM zYlmDDUsgxpz2ay<>IhDgfG`jS+cfd7xAxDP-xvM;v{v z!d3ulJn(rns$;asoR0)tkqD|4dZeGGuk{h|N+~PnmvUa0&XFTDg{2Bx;=_ zL9yb_#HtC5*lT%t#q7_0LHWx=YrCvYy3tsW554~+tWZB6LKYkwWSl6TTWYC%>JI_= zf&rjj-y*;s6O4CkfHzD1R)fUAohiA%lQCjtoFRq#iv$T}l_=t;y{UB;2sS$Il!>Gb zQ$jgQ;_=@(C0U93r6i-f(HI;2vPewx@taQ=ZK<`5;y_A#cMJncylr3kDxu>s8uE00K8>SG= zN`Loa!uv*RlE6Fm56KG(Rlz0a8%=%%$eHLjJ>~l4IU%OZ5g7qyA0Za~uuf~$%)^KL`79tRchDkS0feQDQUrMNe4tM{} zRUSlrkpPWSN5!2e+#Z0s0W`>IUeGrM93-~F*P?aI3<=i{Xc7VtK5Cgx@3%yV)N_;* zR5gpww2WsfMg1QG1dE8lije&G%Z(6y9n%|piK&Vg#E9}3V{5>QhYj-;-WiJ8mPmW! z$S8*g>D{%H&RE;U#o7cBjwpa4c35YxrU^t^Qqm+Fh0dkctn zugP)MC*jeAI9NECHp8ie8n-3@_R}Wvl1wGagQy^A!W&McU=u?D4Zo2fTTU=;IHjf7 ziajVVA=p3zK|ZObfkrFEGFd@Id~^2>=_h%d%w9wb4Du4Y7^mH<4t9xUGOfjeUJ5P= zYpE@*L@4ROcCQ@q@R;E0B7m#q2ov1fsIsL7I;6x1dn{FO5dD{O8?{$*4#F5Rp%n5m z-Tj33cyAK|h5bqwFIOn71u~Y$#OhbJauthxo>}SCt@Qi~tbg%+{Myx@@%tWQ1V~WiMVZb-3pD5@69lX1$ zqOX#G?3*Q*FX=kdUHGIl6_HMyj950MewhEB$f9w%?{gg;#Z788GR*$g4hb%t3 z9M0J65hTE(Z8XQ^ecXxbLQ?-boTYhqK6rC9*^oWV4GERH2cA&Qk_Ax(56&UlJ+Q*q zZ5+!GWOU=H`am~WI0mB4uBB#9w!{AuMP%$pm2R-0EV#KH`=}^BF}wV@mF2>Bs9?6t zN367jc5yOa-u#D4Rp>&5_Rlz%kRln|EeeF#ZNku303|@jn$umpT#2QFCz~Zon0^Uh z1nNHUgi@vwMRtHP5Ec)osP~~i&d{6Fj6`moxu6sq1FBIVOb}WwyE+e8MFtXlX3)v! zXIX^KjMD1E921`k>Ke+J>NyG(`bomgViJ);w&!<=Wh%k>#2nVU!}S<93jlD5P&+Gg z!v)J*9ygQaK_Iv>ZZlUM{Sjof*a%N3i#QV@aq;T-{>ql*aXcD|0wW8GA}i2cr!q0_ zYa2bW?7&o70BZ15O~il@f2wku}a zbIW-0nHlEG2>?-r0dig+1{y=gFu4!0{en-1EiJ~yRmq>Lg${UkBQAX(se_Ui&UyJ? zm1F8Rx&AunoY#;)fx1KRmKDA(ZGO4KyFf5MlnkHE^nqGUH>sI+KO*I^U%{o2sNr`A7z?8J4Qf3w$tEHy&?D&B0 zzLzylqiv>W+{H6WCjoWV3^wyw#Mq^ayqZI2npzVg{<)s7OMZ(9Fe3zl#e0rVRH8PF zYZ^*aQh4n;k|A4zARLlqJ94Ag*tOT-_9i|z`php27c3zdQ7=UKl%16)1G=x6y!BahYaM{beLe@K5;Pk4_(22;OKRWB zBX)=(r)=oTYaL@(^294UAbs4jYe(hX2kKdvM$1?9OR0~g=^yYjPW9b&R0u*hQp&Yo z{fp-d@_N-p4c?SHEF|kzI#|k+#E7np3>%E-x^>w3q&%~8!2HO|9UDzC*M#5=bEU*G za1Q#Q)%!eRP^Mv2tjWA&T$49|wCH^yJ~pN}1VHQ;<|1cR^wxgAD=t&_eDsa~qhekl z#YselO8e&GB4@{)yo=7L!OJ#no2UI_=WmifC*9qnXw4RmI#SqY;-7~UO;{0WsoFmN zv_0!0A9Mt7*go^)?oZ9O&K~1W z^OOav?@zABI>%ySbE|$e|rpX07X4I-f>%xw9`+atN-y7TO8((ex;nV@KB4s8O4Vd1W1H1?A ziOuTbj;iRmx3*=se*3r_P~J#2?fx zz84~H?F(zHj;|R%y|bZlc+Bbz8SOVaarVZwU%G)M6j)Q+eyhl~LOZJ=_fXBg=8mc9 zPcIGZ_!>W1@IX71Kdi{(DbBpjqYtF_du(AHE68OnUz}nDxO*QyaRj=w~zV^F1T literal 0 HcmV?d00001 From 54b4daafa97e54eb6d716e4e7ec0a901c39dce55 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 9 Jul 2016 00:41:26 -0400 Subject: [PATCH 259/282] update verbfile --- verbfile.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/verbfile.js b/verbfile.js index f15380b4..c11bb58a 100644 --- a/verbfile.js +++ b/verbfile.js @@ -1,16 +1,28 @@ 'use strict'; var through = require('through2'); +var reflinks = require('gulp-reflinks'); +var format = require('gulp-format-md'); +var utils = require('./lib/utils'); -module.exports = function(verb) { - verb.extendWith('verb-readme-generator'); +module.exports = function(app) { + app.use(require('verb-generate-readme')); - verb.task('default', ['readme'], function() { - return verb.src('README.md') + app.task('docs', ['setup'], function(cb) { + app.layouts('docs/src/templates/layouts/*.md'); + return app.src('docs/src/*.md', {layout: 'default'}) + .pipe(app.renderFile('*')) + .pipe(reflinks()) + .pipe(format()) + .pipe(app.dest('docs')) + }); + + app.task('default', ['readme'], function() { + return app.src('README.md') .pipe(through.obj(function(file, enc, next) { file.content = file.content.replace(/^(#{2,}\s*\[)\./gm, '$1'); next(null, file); })) - .pipe(verb.dest('.')); + .pipe(app.dest('.')); }); }; From 63a6397754f757a40fc2c071c4ee86ef22c63643 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Sat, 9 Jul 2016 00:47:24 -0400 Subject: [PATCH 260/282] update deps --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a5e1ca1..c10b6f7f 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "gulp-eslint": "^2.0.0", "gulp-istanbul": "^1.0.0", "gulp-mocha": "^2.2.0", + "gulp-reflinks": "^0.1.4", "gulp-unused": "^0.1.2", "is-absolute": "^0.2.5", "load-pkg": "^3.0.1", @@ -70,7 +71,8 @@ "npm-install-global": "^0.1.2", "resolve": "^1.1.7", "should": "^9.0.2", - "sinon": "^1.17.4" + "sinon": "^1.17.4", + "verb-generate-readme": "^0.1.21" }, "keywords": [ "verb" From 47cead39fad03fd47ed146acfd9854f2405d4bdc Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Wed, 13 Jul 2016 08:31:05 -0400 Subject: [PATCH 261/282] update unit tests --- test/_suite.js | 1 + test/app.docs.js | 2 +- test/app.generator.js | 8 +- test/app.includes.js | 4 +- test/app.layouts.js | 2 +- test/app.pages.js | 2 +- test/app.partials.js | 2 +- test/app.questions.js | 359 +++++++++--------- test/app.register.js | 2 +- test/app.store.js | 5 +- test/fixtures/bom-utf16be.txt | Bin 244 -> 0 bytes test/fixtures/bom-utf16le.txt | Bin 244 -> 0 bytes test/fixtures/bom-utf8.txt | 1 - test/fixtures/copy/a.txt | 1 - test/fixtures/copy/b.txt | 1 - test/fixtures/copy/c.txt | 1 - test/fixtures/copy/example.txt | 1 - test/fixtures/data/a.json | 3 - test/fixtures/data/alert.json | 7 - test/fixtures/data/b.json | 3 - test/fixtures/data/c.json | 3 - test/fixtures/data/data.json | 3 - test/fixtures/data/test.json | 4 - test/fixtures/example.txt | 1 - .../front-matter/autodetect-no-lang.md | 5 - test/fixtures/front-matter/autodetect-yaml.md | 5 - test/fixtures/front-matter/lang-yaml.md | 5 - test/fixtures/generators/b/.gitignore | 15 - test/fixtures/generators/b/index.js | 12 - test/fixtures/generators/b/package.json | 7 - test/fixtures/generators/c/.gitignore | 15 - test/fixtures/generators/c/generator.js | 39 -- test/fixtures/generators/c/package.json | 7 - test/fixtures/generators/c/verbfile.js | 39 -- test/fixtures/generators/e/.gitignore | 15 - test/fixtures/generators/e/generator.js | 43 --- test/fixtures/generators/e/package.json | 7 - test/fixtures/generators/e/templates/post.hbs | 5 - test/fixtures/generators/e/verbfile.js | 43 --- test/fixtures/generators2.js | 41 -- test/fixtures/generic/run.dmc | 1 - test/fixtures/generic/test.dmc | 1 - test/fixtures/helpers/a.js | 3 - test/fixtures/helpers/b.js | 3 - test/fixtures/helpers/c.js | 3 - test/fixtures/helpers/obj.js | 9 - test/fixtures/noext/license | 21 - test/fixtures/one/index.js | 1 + test/fixtures/one/package.json | 2 +- test/fixtures/posts/a.hbs | 4 - test/fixtures/posts/a.txt | 4 - test/fixtures/posts/b.hbs | 4 - test/fixtures/posts/b.txt | 4 - test/fixtures/posts/c.hbs | 4 - test/fixtures/posts/c.txt | 4 - test/fixtures/templates/a.tmpl | 1 - test/fixtures/templates/b.tmpl | 1 - test/fixtures/templates/c.tmpl | 1 - test/fixtures/test-symlink | 1 - test/fixtures/test-symlink-dir/suchempty | 1 - test/fixtures/test.coffee | 1 - test/fixtures/three/four/five/generator.js | 14 - test/fixtures/three/four/five/package.json | 30 -- test/fixtures/three/four/five/templates/a.txt | 0 test/fixtures/three/four/five/templates/b.txt | 0 test/fixtures/three/four/five/templates/c.txt | 0 test/fixtures/three/four/five/verbfile.js | 14 - test/fixtures/three/four/generator.js | 14 - test/fixtures/three/four/package.json | 30 -- test/fixtures/three/four/templates/a.txt | 0 test/fixtures/three/four/templates/b.txt | 0 test/fixtures/three/four/templates/c.txt | 0 test/fixtures/three/four/verbfile.js | 14 - test/fixtures/three/generator.js | 14 - test/fixtures/three/package.json | 30 -- test/fixtures/three/templates/a.txt | 0 test/fixtures/three/templates/b.txt | 0 test/fixtures/three/templates/c.txt | 0 test/fixtures/three/verbfile.js | 14 - test/fixtures/two/package.json | 2 +- test/fixtures/updaters/a.txt | 1 - test/fixtures/updaters/b.txt | 1 - test/fixtures/updaters/c.txt | 1 - test/fixtures/verbfile.js | 49 --- test/fixtures/vinyl/bom-utf16be.txt | Bin 244 -> 0 bytes test/fixtures/vinyl/bom-utf16le.txt | Bin 244 -> 0 bytes test/fixtures/vinyl/bom-utf8.txt | 1 - test/fixtures/vinyl/test-symlink | 1 - .../fixtures/vinyl/test-symlink-dir/suchempty | 1 - test/fixtures/vinyl/test.coffee | 1 - test/fixtures/vinyl/wow/suchempty | 1 - test/fixtures/watch/test.txt | 1 - test/fixtures/wow/suchempty | 1 - test/generators.events.js | 2 - test/runner.js | 1 - 95 files changed, 195 insertions(+), 836 deletions(-) delete mode 100644 test/fixtures/bom-utf16be.txt delete mode 100644 test/fixtures/bom-utf16le.txt delete mode 100644 test/fixtures/bom-utf8.txt delete mode 100644 test/fixtures/copy/a.txt delete mode 100644 test/fixtures/copy/b.txt delete mode 100644 test/fixtures/copy/c.txt delete mode 100644 test/fixtures/copy/example.txt delete mode 100644 test/fixtures/data/a.json delete mode 100644 test/fixtures/data/alert.json delete mode 100644 test/fixtures/data/b.json delete mode 100644 test/fixtures/data/c.json delete mode 100644 test/fixtures/data/data.json delete mode 100644 test/fixtures/data/test.json delete mode 100644 test/fixtures/example.txt delete mode 100644 test/fixtures/front-matter/autodetect-no-lang.md delete mode 100644 test/fixtures/front-matter/autodetect-yaml.md delete mode 100644 test/fixtures/front-matter/lang-yaml.md delete mode 100644 test/fixtures/generators/b/.gitignore delete mode 100644 test/fixtures/generators/b/index.js delete mode 100644 test/fixtures/generators/b/package.json delete mode 100644 test/fixtures/generators/c/.gitignore delete mode 100644 test/fixtures/generators/c/generator.js delete mode 100644 test/fixtures/generators/c/package.json delete mode 100644 test/fixtures/generators/c/verbfile.js delete mode 100644 test/fixtures/generators/e/.gitignore delete mode 100644 test/fixtures/generators/e/generator.js delete mode 100644 test/fixtures/generators/e/package.json delete mode 100644 test/fixtures/generators/e/templates/post.hbs delete mode 100644 test/fixtures/generators/e/verbfile.js delete mode 100644 test/fixtures/generators2.js delete mode 100644 test/fixtures/generic/run.dmc delete mode 100644 test/fixtures/generic/test.dmc delete mode 100644 test/fixtures/helpers/a.js delete mode 100644 test/fixtures/helpers/b.js delete mode 100644 test/fixtures/helpers/c.js delete mode 100644 test/fixtures/helpers/obj.js delete mode 100644 test/fixtures/noext/license create mode 100644 test/fixtures/one/index.js delete mode 100644 test/fixtures/posts/a.hbs delete mode 100644 test/fixtures/posts/a.txt delete mode 100644 test/fixtures/posts/b.hbs delete mode 100644 test/fixtures/posts/b.txt delete mode 100644 test/fixtures/posts/c.hbs delete mode 100644 test/fixtures/posts/c.txt delete mode 100644 test/fixtures/templates/a.tmpl delete mode 100644 test/fixtures/templates/b.tmpl delete mode 100644 test/fixtures/templates/c.tmpl delete mode 120000 test/fixtures/test-symlink delete mode 100644 test/fixtures/test-symlink-dir/suchempty delete mode 100644 test/fixtures/test.coffee delete mode 100644 test/fixtures/three/four/five/generator.js delete mode 100644 test/fixtures/three/four/five/package.json delete mode 100644 test/fixtures/three/four/five/templates/a.txt delete mode 100644 test/fixtures/three/four/five/templates/b.txt delete mode 100644 test/fixtures/three/four/five/templates/c.txt delete mode 100644 test/fixtures/three/four/five/verbfile.js delete mode 100644 test/fixtures/three/four/generator.js delete mode 100644 test/fixtures/three/four/package.json delete mode 100644 test/fixtures/three/four/templates/a.txt delete mode 100644 test/fixtures/three/four/templates/b.txt delete mode 100644 test/fixtures/three/four/templates/c.txt delete mode 100644 test/fixtures/three/four/verbfile.js delete mode 100644 test/fixtures/three/generator.js delete mode 100644 test/fixtures/three/package.json delete mode 100644 test/fixtures/three/templates/a.txt delete mode 100644 test/fixtures/three/templates/b.txt delete mode 100644 test/fixtures/three/templates/c.txt delete mode 100644 test/fixtures/three/verbfile.js delete mode 100644 test/fixtures/updaters/a.txt delete mode 100644 test/fixtures/updaters/b.txt delete mode 100644 test/fixtures/updaters/c.txt delete mode 100644 test/fixtures/verbfile.js delete mode 100644 test/fixtures/vinyl/bom-utf16be.txt delete mode 100644 test/fixtures/vinyl/bom-utf16le.txt delete mode 100644 test/fixtures/vinyl/bom-utf8.txt delete mode 120000 test/fixtures/vinyl/test-symlink delete mode 100644 test/fixtures/vinyl/test-symlink-dir/suchempty delete mode 100644 test/fixtures/vinyl/test.coffee delete mode 100644 test/fixtures/vinyl/wow/suchempty delete mode 100644 test/fixtures/watch/test.txt delete mode 100644 test/fixtures/wow/suchempty diff --git a/test/_suite.js b/test/_suite.js index 8a136aaf..e8b7d440 100644 --- a/test/_suite.js +++ b/test/_suite.js @@ -1,5 +1,6 @@ 'use strict'; +process.env.GENERATE_TEST = true; var generate = require('..'); var runner = require('base-test-runner')(); var suite = require('base-test-suite'); diff --git a/test/app.docs.js b/test/app.docs.js index cdf89551..cbab80e6 100644 --- a/test/app.docs.js +++ b/test/app.docs.js @@ -17,7 +17,7 @@ describe('app', function() { app.docs({ 'a.hbs': {path: 'a.hbs', content: 'a'}, 'b.hbs': {path: 'b.hbs', content: 'b'}, - 'c.hbs': {path: 'c.hbs', content: 'c'}, + 'c.hbs': {path: 'c.hbs', content: 'c'} }); assert.equal(Object.keys(app.views.docs).length, 3); }); diff --git a/test/app.generator.js b/test/app.generator.js index 9b0a51b1..88a5f66f 100644 --- a/test/app.generator.js +++ b/test/app.generator.js @@ -191,15 +191,15 @@ describe('.generator', function() { it('should register a generator function from a file path', function() { var one = base.generator('one', fixtures('one/generator.js')); assert(base.generators.hasOwnProperty('one')); - assert(typeof base.generators.one === 'object'); + assert.equal(typeof base.generators.one, 'object'); assert.deepEqual(base.generators.one, one); }); it('should get a registered generator by name', function() { var one = base.generator('one', fixtures('one/generator.js')); var two = base.generator('two', fixtures('two/generator.js')); - assert.deepEqual(base.generator('one'), one); - assert.deepEqual(base.generator('two'), two); + assert(base.generator('one')); + assert(base.generator('two')); }); }); @@ -268,8 +268,6 @@ describe('.generator', function() { }); it('should expose namespace on `this`', function(cb) { - var name = base._name; - base.generator('generate-foo', function(app, first) { assert.equal(this.namespace, base._name + '.foo'); diff --git a/test/app.includes.js b/test/app.includes.js index 390660d6..2f3898b3 100644 --- a/test/app.includes.js +++ b/test/app.includes.js @@ -13,14 +13,14 @@ describe('app', function() { app.create('include'); } len = Object.keys(app.views.includes).length; - }) + }); describe('add includes', function() { it('should add includes to `app.views.includes`:', function() { app.includes({ 'a.hbs': {path: 'a.hbs', content: 'a'}, 'b.hbs': {path: 'b.hbs', content: 'b'}, - 'c.hbs': {path: 'c.hbs', content: 'c'}, + 'c.hbs': {path: 'c.hbs', content: 'c'} }); assert((Object.keys(app.views.includes).length - len) === 3); }); diff --git a/test/app.layouts.js b/test/app.layouts.js index d731a0b4..74d972b5 100644 --- a/test/app.layouts.js +++ b/test/app.layouts.js @@ -18,7 +18,7 @@ describe('.layouts()', function() { app.layouts({ 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, - 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')} }); assert(Object.keys(app.views.layouts).length === 3); }); diff --git a/test/app.pages.js b/test/app.pages.js index 1d324ac0..6be23244 100644 --- a/test/app.pages.js +++ b/test/app.pages.js @@ -18,7 +18,7 @@ describe('.pages()', function() { app.pages({ 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, - 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')} }); assert.equal(Object.keys(app.views.pages).length, 3); }); diff --git a/test/app.partials.js b/test/app.partials.js index 9622fcfe..f8d06564 100644 --- a/test/app.partials.js +++ b/test/app.partials.js @@ -18,7 +18,7 @@ describe('.partials()', function() { app.partials({ 'a.hbs': {path: 'a.hbs', contents: new Buffer('a')}, 'b.hbs': {path: 'b.hbs', contents: new Buffer('b')}, - 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')}, + 'c.hbs': {path: 'c.hbs', contents: new Buffer('c')} }); assert(Object.keys(app.views.partials).length === 3); diff --git a/test/app.questions.js b/test/app.questions.js index eb9d6708..f1cc4078 100644 --- a/test/app.questions.js +++ b/test/app.questions.js @@ -3,7 +3,6 @@ process.env.NODE_ENV = 'test'; require('mocha'); -var fs = require('fs'); var assert = require('assert'); var questions = require('base-questions'); var config = require('base-config-process'); @@ -49,245 +48,247 @@ describe('app.questions', function() { if (process.env.TRAVIS) { return; + } - describe('app.ask', function() { - beforeEach(function() { - app = new App(); - app.use(config()); - app.use(questions()); - app.use(store('base-questions-tests/ask')); - }); + describe('app.ask', function() { + beforeEach(function() { + app = new App(); + app.use(config()); + app.use(questions()); + app.use(store('base-questions-tests/ask')); + }); - afterEach(function() { - app.store.del({force: true}); - app.questions.clear(); - app.cache.data = {}; - }); + afterEach(function() { + app.store.del({force: true}); + app.questions.clear(); + app.cache.data = {}; + }); - it.skip('should force all questions to be asked', function(cb) { - app.questions.option('init', 'author'); - app.ask({force: true}, function(err, answers) { - console.log(answers) - cb(); - }); + it.skip('should force all questions to be asked', function(cb) { + app.questions.option('init', 'author'); + app.ask({force: true}, function(err, answers) { + if (err) return cb(err); + console.log(answers); + cb(); }); + }); - it('should store a question:', function() { - app.question('a', 'b'); - assert(app.questions); - assert(app.questions.cache); - assert(app.questions.cache.a); - assert.equal(app.questions.cache.a.name, 'a'); - assert.equal(app.questions.cache.a.message, 'b'); - }); + it('should store a question:', function() { + app.question('a', 'b'); + assert(app.questions); + assert(app.questions.cache); + assert(app.questions.cache.a); + assert.equal(app.questions.cache.a.name, 'a'); + assert.equal(app.questions.cache.a.message, 'b'); + }); - it.skip('should re-init a specific question:', function(cb) { - this.timeout(20000); - app.question('a', 'b'); - app.question('c', 'd'); - app.question('e', 'f'); - app.data({a: 'b'}); + it.skip('should re-init a specific question:', function(cb) { + this.timeout(20000); + app.question('a', 'b'); + app.question('c', 'd'); + app.question('e', 'f'); + app.data({a: 'b'}); - app.questions.get('e') - .force() + app.questions.get('e') + .force(); - app.ask(function(err, answers) { - console.log(answers); - cb(); - }); + app.ask(function(err, answers) { + if (err) return cb(err); + console.log(answers); + cb(); }); + }); - it('should ask a question defined on `ask`', function(cb) { - app.data('name', 'Brian Woodward'); + it('should ask a question defined on `ask`', function(cb) { + app.data('name', 'Brian Woodward'); - app.ask('name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.name, 'Brian Woodward'); - cb(); - }); + app.ask('name', function(err, answers) { + if (err) return cb(err); + assert.equal(answers.name, 'Brian Woodward'); + cb(); }); + }); - it('should ask a question and use a `cache.data` value to answer:', function(cb) { - app.question('a', 'this is a question'); - app.data('a', 'b'); + it('should ask a question and use a `cache.data` value to answer:', function(cb) { + app.question('a', 'this is a question'); + app.data('a', 'b'); - app.ask('a', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.a, 'b'); - - app.data('a', 'zzz'); - app.ask('a', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.a, 'zzz'); - cb(); - }) - }); - }); + app.ask('a', function(err, answers) { + if (err) return cb(err); + assert.equal(answers.a, 'b'); - it('should ask a question and use a `store.data` value to answer:', function(cb) { - app.question('a', 'this is another question'); - app.store.set('a', 'c'); + app.data('a', 'zzz'); app.ask('a', function(err, answers) { if (err) return cb(err); - assert(answers); - assert.equal(answers.a, 'c'); + assert.equal(answers.a, 'zzz'); cb(); - }) + }); }); + }); - it('should ask a question and use a config value to answer:', function(cb) { - app.question('a', 'b'); - app.config.process({data: {a: 'foo'}}, function(err) { - if (err) return cb(err); - - app.store.set('a', 'c'); - - app.ask('a', function(err, answer) { - if (err) return cb(err); - assert(answer); - assert.equal(answer.a, 'foo'); - cb(); - }); - }); + it('should ask a question and use a `store.data` value to answer:', function(cb) { + app.question('a', 'this is another question'); + app.store.set('a', 'c'); + app.ask('a', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.a, 'c'); + cb(); }); + }); + + it('should ask a question and use a config value to answer:', function(cb) { + app.question('a', 'b'); + app.config.process({data: {a: 'foo'}}, function(err) { + if (err) return cb(err); - it('should prefer `cache.data` to `store.data`', function(cb) { - app.question('a', 'b'); - app.data('a', 'b'); app.store.set('a', 'c'); app.ask('a', function(err, answer) { if (err) return cb(err); assert(answer); - assert.equal(answer.a, 'b'); + assert.equal(answer.a, 'foo'); cb(); - }) + }); }); + }); - it('should update data with data loaded by config', function(cb) { - app.question('a', 'this is a question'); - app.data('a', 'b'); + it('should prefer `cache.data` to `store.data`', function(cb) { + app.question('a', 'b'); + app.data('a', 'b'); + app.store.set('a', 'c'); - app.config.process({data: {a: 'foo'}}, function(err) { - if (err) return cb(err); + app.ask('a', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.a, 'b'); + cb(); + }); + }); - app.ask('a', function(err, answer) { - if (err) return cb(err); + it('should update data with data loaded by config', function(cb) { + app.question('a', 'this is a question'); + app.data('a', 'b'); - assert(answer); - assert.equal(answer.a, 'foo'); - cb(); - }); + app.config.process({data: {a: 'foo'}}, function(err) { + if (err) return cb(err); + + app.ask('a', function(err, answer) { + if (err) return cb(err); + + assert(answer); + assert.equal(answer.a, 'foo'); + cb(); }); }); }); + }); - describe('session data', function() { - before(function() { - site = new App(); - site.use(config()); - site.use(questions()); - site.use(store('base-questions-tests/site')); - - app = new App(); - app.use(config()); - app.use(questions()); - app.use(store('base-questions-tests/ask')); - }); + describe('session data', function() { + before(function() { + site = new App(); + site.use(config()); + site.use(questions()); + site.use(store('base-questions-tests/site')); - after(function() { - site.store.del({force: true}); - site.questions.clear(); + app = new App(); + app.use(config()); + app.use(questions()); + app.use(store('base-questions-tests/ask')); + }); - app.store.del({force: true}); - app.questions.clear(); - }); + after(function() { + site.store.del({force: true}); + site.questions.clear(); + + app.store.del({force: true}); + app.questions.clear(); + }); - it('[app] should ask a question and use a `cache.data` value to answer:', function(cb) { - app.question('package.name', 'this is a question'); - app.data('package.name', 'base-questions'); + it('[app] should ask a question and use a `cache.data` value to answer:', function(cb) { + app.question('package.name', 'this is a question'); + app.data('package.name', 'base-questions'); + app.ask('package.name', function(err, answers) { + if (err) return cb(err); + assert.equal(answers.package.name, 'base-questions'); + + app.data('package.name', 'foo-bar-baz'); app.ask('package.name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.package.name, 'base-questions'); - - app.data('package.name', 'foo-bar-baz'); - app.ask('package.name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.package.name, 'foo-bar-baz'); - cb(); - }) + if (err) return cb(err); + assert.equal(answers.package.name, 'foo-bar-baz'); + cb(); }); }); + }); - it('[site] should ask a question and use a `cache.data` value to answer:', function(cb) { - site.question('package.name', 'this is a question'); - site.data('package.name', 'base-questions'); + it('[site] should ask a question and use a `cache.data` value to answer:', function(cb) { + site.question('package.name', 'this is a question'); + site.data('package.name', 'base-questions'); + site.ask('package.name', function(err, answers) { + if (err) return cb(err); + assert.equal(answers.package.name, 'base-questions'); + + site.data('package.name', 'foo-bar-baz'); site.ask('package.name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.package.name, 'base-questions'); - - site.data('package.name', 'foo-bar-baz'); - site.ask('package.name', function(err, answers) { - if(err) return cb(err) - assert.equal(answers.package.name, 'foo-bar-baz'); - cb(); - }) + if (err) return cb(err); + assert.equal(answers.package.name, 'foo-bar-baz'); + cb(); }); }); + }); - it('[app] should ask a question and use a `store.data` value to answer:', function(cb) { - app.question('author.name', 'this is another question'); - app.store.set('author.name', 'Brian Woodward'); - app.ask('author.name', function(err, answers) { - if (err) return cb(err); - assert(answers); - assert.equal(answers.author.name, 'Brian Woodward'); - cb(); - }) + it('[app] should ask a question and use a `store.data` value to answer:', function(cb) { + app.question('author.name', 'this is another question'); + app.store.set('author.name', 'Brian Woodward'); + app.ask('author.name', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.author.name, 'Brian Woodward'); + cb(); }); + }); - it('[site] should ask a question and use a `store.data` value to answer:', function(cb) { - site.question('author.name', 'this is another question'); - site.store.set('author.name', 'Jon Schlinkert'); - site.ask('author.name', function(err, answers) { - if (err) return cb(err); - assert(answers); - assert.equal(answers.author.name, 'Brian Woodward'); - cb(); - }) + it('[site] should ask a question and use a `store.data` value to answer:', function(cb) { + site.question('author.name', 'this is another question'); + site.store.set('author.name', 'Jon Schlinkert'); + site.ask('author.name', function(err, answers) { + if (err) return cb(err); + assert(answers); + assert.equal(answers.author.name, 'Brian Woodward'); + cb(); }); + }); - it('[app] should ask a question and use a config value to answer:', function(cb) { - app.question('foo', 'Username?'); - app.config.process({data: {foo: 'jonschlinkert'}}, function(err) { - if (err) return cb(err); + it('[app] should ask a question and use a config value to answer:', function(cb) { + app.question('foo', 'Username?'); + app.config.process({data: {foo: 'jonschlinkert'}}, function(err) { + if (err) return cb(err); - app.store.set('foo', 'doowb'); + app.store.set('foo', 'doowb'); - app.ask('foo', function(err, answer) { - if (err) return cb(err); - assert(answer); - assert.equal(answer.foo, 'jonschlinkert'); - cb(); - }); + app.ask('foo', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.foo, 'jonschlinkert'); + cb(); }); }); + }); - it('[site] should ask a question and use a config value to answer:', function(cb) { - site.question('foo', 'Username?'); - site.config.process({data: {foo: 'doowb'}}, function(err) { - if (err) return cb(err); + it('[site] should ask a question and use a config value to answer:', function(cb) { + site.question('foo', 'Username?'); + site.config.process({data: {foo: 'doowb'}}, function(err) { + if (err) return cb(err); - site.ask('foo', function(err, answer) { - if (err) return cb(err); - assert(answer); - assert.equal(answer.foo, 'doowb'); - cb(); - }); + site.ask('foo', function(err, answer) { + if (err) return cb(err); + assert(answer); + assert.equal(answer.foo, 'doowb'); + cb(); }); }); }); - } + }); }); diff --git a/test/app.register.js b/test/app.register.js index 90e5b3bf..c5a5cd4b 100644 --- a/test/app.register.js +++ b/test/app.register.js @@ -167,7 +167,7 @@ describe('.register', function() { base.register('not-exposed', require(fixtures('not-exposed.js'))); cb(new Error('expected an error')); } catch (err) { - assert.equal(err.message, `cannot resolve: 'not-exposed'`); + assert.equal(err.message, 'cannot resolve: \'not-exposed\''); cb(); } }); diff --git a/test/app.store.js b/test/app.store.js index aa745e09..ca0137f6 100644 --- a/test/app.store.js +++ b/test/app.store.js @@ -1,7 +1,6 @@ 'use strict'; require('mocha'); -var fs = require('fs'); var path = require('path'); var assert = require('assert'); var store = require('base-store'); @@ -90,7 +89,7 @@ describe('store', function() { assert(!app.store.has('a.b.g.j.z')); }); - it('should return true if a key exists `.hasOwn()` on the store', function() { + it('should return true if a key exists `.hasOwn()` on the store', function() { app.store.set('foo', 'bar'); app.store.set('baz', null); app.store.set('qux', undefined); @@ -279,8 +278,6 @@ describe('events', function() { }); it('should emit `has`:', function(cb) { - var keys = []; - app.store.on('has', function(val) { assert(val); cb(); diff --git a/test/fixtures/bom-utf16be.txt b/test/fixtures/bom-utf16be.txt deleted file mode 100644 index b9dce78a5d31af4803acd1a0f0dfc14f064a5de1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 diff --git a/test/fixtures/bom-utf16le.txt b/test/fixtures/bom-utf16le.txt deleted file mode 100644 index 07cc600c98675d221bb56d10af38e650538734c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15l - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -module.exports = function() { - // do stuff -}; diff --git a/test/fixtures/generators/b/package.json b/test/fixtures/generators/b/package.json deleted file mode 100644 index 95afba0d..00000000 --- a/test/fixtures/generators/b/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "bbb", - "private": true, - "version": "0.1.0", - "files": ["index.js"], - "main": "generator.js" -} diff --git a/test/fixtures/generators/c/.gitignore b/test/fixtures/generators/c/.gitignore deleted file mode 100644 index 80a228ca..00000000 --- a/test/fixtures/generators/c/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.DS_Store -*.sublime-* -_gh_pages -bower_components -node_modules -npm-debug.log -actual -test/actual -temp -tmp -TODO.md -vendor -.idea -benchmark -coverage diff --git a/test/fixtures/generators/c/generator.js b/test/fixtures/generators/c/generator.js deleted file mode 100644 index 2c448ee6..00000000 --- a/test/fixtures/generators/c/generator.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.task('default', function(cb) { - console.log('c > default'); - cb(); - }); - app.task('c_a', function(cb) { - console.log('c > a'); - cb(); - }); - app.task('c_b', function(cb) { - console.log('c > b'); - cb(); - }); - app.task('c_c', function(cb) { - console.log('c > c'); - cb(); - }); - - app.register('docs', function(docs, base) { - docs.task('default', function(cb) { - console.log('c > x'); - cb(); - }); - docs.task('c_x', function(cb) { - console.log('c > x'); - cb(); - }); - docs.task('c_y', function(cb) { - console.log('c > y'); - cb(); - }); - docs.task('c_z', function(cb) { - console.log('c > z'); - cb(); - }); - }); -}; diff --git a/test/fixtures/generators/c/package.json b/test/fixtures/generators/c/package.json deleted file mode 100644 index 753957e6..00000000 --- a/test/fixtures/generators/c/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "a", - "private": true, - "version": "0.1.0", - "files": ["index.js"], - "main": "generator.js" -} diff --git a/test/fixtures/generators/c/verbfile.js b/test/fixtures/generators/c/verbfile.js deleted file mode 100644 index 2c448ee6..00000000 --- a/test/fixtures/generators/c/verbfile.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.task('default', function(cb) { - console.log('c > default'); - cb(); - }); - app.task('c_a', function(cb) { - console.log('c > a'); - cb(); - }); - app.task('c_b', function(cb) { - console.log('c > b'); - cb(); - }); - app.task('c_c', function(cb) { - console.log('c > c'); - cb(); - }); - - app.register('docs', function(docs, base) { - docs.task('default', function(cb) { - console.log('c > x'); - cb(); - }); - docs.task('c_x', function(cb) { - console.log('c > x'); - cb(); - }); - docs.task('c_y', function(cb) { - console.log('c > y'); - cb(); - }); - docs.task('c_z', function(cb) { - console.log('c > z'); - cb(); - }); - }); -}; diff --git a/test/fixtures/generators/e/.gitignore b/test/fixtures/generators/e/.gitignore deleted file mode 100644 index 80a228ca..00000000 --- a/test/fixtures/generators/e/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.DS_Store -*.sublime-* -_gh_pages -bower_components -node_modules -npm-debug.log -actual -test/actual -temp -tmp -TODO.md -vendor -.idea -benchmark -coverage diff --git a/test/fixtures/generators/e/generator.js b/test/fixtures/generators/e/generator.js deleted file mode 100644 index fc333ec2..00000000 --- a/test/fixtures/generators/e/generator.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -/** - * testing... - */ - -module.exports = function(app, base, env) { - app.task('default', function(cb) { - console.log('e > default'); - cb(); - }); - app.task('one', function(cb) { - console.log('e > one'); - cb(); - }); - app.task('two', function(cb) { - console.log('e > two'); - cb(); - }); - - // console.log(app) - app.task('three', function(cb) { - base.data({title: 'three'}); - base.build('readme', cb); - }); - - app.register('eee', '../c'); - - app.register('e_docs', function(docs) { - docs.task('e_x', function(cb) { - console.log('e > x'); - cb(); - }); - docs.task('e_y', function(cb) { - console.log('e > y'); - cb(); - }); - docs.task('e_z', function(cb) { - console.log('e > z'); - cb(); - }); - }); -}; diff --git a/test/fixtures/generators/e/package.json b/test/fixtures/generators/e/package.json deleted file mode 100644 index 48c57d85..00000000 --- a/test/fixtures/generators/e/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "generator-e", - "private": true, - "version": "0.1.0", - "files": ["index.js"], - "main": "generator.js" -} diff --git a/test/fixtures/generators/e/templates/post.hbs b/test/fixtures/generators/e/templates/post.hbs deleted file mode 100644 index 16e08aeb..00000000 --- a/test/fixtures/generators/e/templates/post.hbs +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Post ---- - -This is three \ No newline at end of file diff --git a/test/fixtures/generators/e/verbfile.js b/test/fixtures/generators/e/verbfile.js deleted file mode 100644 index fc333ec2..00000000 --- a/test/fixtures/generators/e/verbfile.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -/** - * testing... - */ - -module.exports = function(app, base, env) { - app.task('default', function(cb) { - console.log('e > default'); - cb(); - }); - app.task('one', function(cb) { - console.log('e > one'); - cb(); - }); - app.task('two', function(cb) { - console.log('e > two'); - cb(); - }); - - // console.log(app) - app.task('three', function(cb) { - base.data({title: 'three'}); - base.build('readme', cb); - }); - - app.register('eee', '../c'); - - app.register('e_docs', function(docs) { - docs.task('e_x', function(cb) { - console.log('e > x'); - cb(); - }); - docs.task('e_y', function(cb) { - console.log('e > y'); - cb(); - }); - docs.task('e_z', function(cb) { - console.log('e > z'); - cb(); - }); - }); -}; diff --git a/test/fixtures/generators2.js b/test/fixtures/generators2.js deleted file mode 100644 index e2ddbb04..00000000 --- a/test/fixtures/generators2.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -var Generate = require('./'); -var app = new Generate({foo: 'bar'}); - -app.register('a', function(a, base) { - a.register('a1', function(aa) { - aa.register('aaa', require('./generators/f/generator.js')); - aa.task('default', function(cb) { - console.log('aa > default'); - cb(); - }); - }); - a.register('a2', function(aa) { - }); -}); - -app.register('b', new Generate()); -app.generators.b.register('bb', function(bb) { - bb.register('bbb', function() {}); - bb.task('default', function(cb) { - console.log('bb > default'); - cb(); - }); -}); - -app.register('c', function(c, base) { - c.register('cc', function(cc, basecc) { - cc.task('default', function(cb) { - console.log('cc > default'); - cb(); - }); - - cc.register('base', require('./generator')); - }); -}); - -app.generate('a.a1.aaa', ['default'], function(err) { - if (err) throw err; - console.log('DONE!!!'); -}); diff --git a/test/fixtures/generic/run.dmc b/test/fixtures/generic/run.dmc deleted file mode 100644 index f5925c06..00000000 --- a/test/fixtures/generic/run.dmc +++ /dev/null @@ -1 +0,0 @@ -# run.dmc \ No newline at end of file diff --git a/test/fixtures/generic/test.dmc b/test/fixtures/generic/test.dmc deleted file mode 100644 index 2286d5d4..00000000 --- a/test/fixtures/generic/test.dmc +++ /dev/null @@ -1 +0,0 @@ -# test.dmc \ No newline at end of file diff --git a/test/fixtures/helpers/a.js b/test/fixtures/helpers/a.js deleted file mode 100644 index 4e4b7183..00000000 --- a/test/fixtures/helpers/a.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function aaa() { - -}; diff --git a/test/fixtures/helpers/b.js b/test/fixtures/helpers/b.js deleted file mode 100644 index b02f87bc..00000000 --- a/test/fixtures/helpers/b.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function bbb() { - -}; diff --git a/test/fixtures/helpers/c.js b/test/fixtures/helpers/c.js deleted file mode 100644 index f5735ca7..00000000 --- a/test/fixtures/helpers/c.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function ccc() { - -}; diff --git a/test/fixtures/helpers/obj.js b/test/fixtures/helpers/obj.js deleted file mode 100644 index 07a122a4..00000000 --- a/test/fixtures/helpers/obj.js +++ /dev/null @@ -1,9 +0,0 @@ -exports.one = function one() { - -}; -exports.two = function two() { - -}; -exports.three = function three() { - -}; diff --git a/test/fixtures/noext/license b/test/fixtures/noext/license deleted file mode 100644 index 65f90aca..00000000 --- a/test/fixtures/noext/license +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015, Jon Schlinkert. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/test/fixtures/one/index.js b/test/fixtures/one/index.js new file mode 100644 index 00000000..f2eca0ed --- /dev/null +++ b/test/fixtures/one/index.js @@ -0,0 +1 @@ +module.exports = require('./generator'); \ No newline at end of file diff --git a/test/fixtures/one/package.json b/test/fixtures/one/package.json index d1d46764..abdc1f4f 100644 --- a/test/fixtures/one/package.json +++ b/test/fixtures/one/package.json @@ -12,7 +12,7 @@ "files": [ "index.js" ], - "main": "index.js", + "main": "generator.js", "engines": { "node": ">=0.10.0" }, diff --git a/test/fixtures/posts/a.hbs b/test/fixtures/posts/a.hbs deleted file mode 100644 index 556558e4..00000000 --- a/test/fixtures/posts/a.hbs +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: AAA ---- -This is {{title}} \ No newline at end of file diff --git a/test/fixtures/posts/a.txt b/test/fixtures/posts/a.txt deleted file mode 100644 index bca29ee6..00000000 --- a/test/fixtures/posts/a.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: AAA ---- -This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/b.hbs b/test/fixtures/posts/b.hbs deleted file mode 100644 index 8b19d24d..00000000 --- a/test/fixtures/posts/b.hbs +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: BBB ---- -This is {{title}} \ No newline at end of file diff --git a/test/fixtures/posts/b.txt b/test/fixtures/posts/b.txt deleted file mode 100644 index 1e128c70..00000000 --- a/test/fixtures/posts/b.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: BBB ---- -This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/posts/c.hbs b/test/fixtures/posts/c.hbs deleted file mode 100644 index d5a7421e..00000000 --- a/test/fixtures/posts/c.hbs +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: CCC ---- -This is {{title}} \ No newline at end of file diff --git a/test/fixtures/posts/c.txt b/test/fixtures/posts/c.txt deleted file mode 100644 index 32f91870..00000000 --- a/test/fixtures/posts/c.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: CCC ---- -This is <%= title %> \ No newline at end of file diff --git a/test/fixtures/templates/a.tmpl b/test/fixtures/templates/a.tmpl deleted file mode 100644 index 36f1f1b5..00000000 --- a/test/fixtures/templates/a.tmpl +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/b.tmpl b/test/fixtures/templates/b.tmpl deleted file mode 100644 index 36f1f1b5..00000000 --- a/test/fixtures/templates/b.tmpl +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/test/fixtures/templates/c.tmpl b/test/fixtures/templates/c.tmpl deleted file mode 100644 index 36f1f1b5..00000000 --- a/test/fixtures/templates/c.tmpl +++ /dev/null @@ -1 +0,0 @@ -<%= name %> \ No newline at end of file diff --git a/test/fixtures/test-symlink b/test/fixtures/test-symlink deleted file mode 120000 index 3fcfe6c7..00000000 --- a/test/fixtures/test-symlink +++ /dev/null @@ -1 +0,0 @@ -test.coffee \ No newline at end of file diff --git a/test/fixtures/test-symlink-dir/suchempty b/test/fixtures/test-symlink-dir/suchempty deleted file mode 100644 index 65bbcaab..00000000 --- a/test/fixtures/test-symlink-dir/suchempty +++ /dev/null @@ -1 +0,0 @@ -suchempty \ No newline at end of file diff --git a/test/fixtures/test.coffee b/test/fixtures/test.coffee deleted file mode 100644 index 6769dd60..00000000 --- a/test/fixtures/test.coffee +++ /dev/null @@ -1 +0,0 @@ -Hello world! \ No newline at end of file diff --git a/test/fixtures/three/four/five/generator.js b/test/fixtures/three/four/five/generator.js deleted file mode 100644 index 19b86e81..00000000 --- a/test/fixtures/three/four/five/generator.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = function(generate, base) { - generate.task('default', function() {}); - generate.task('a', function() {}); - generate.task('b', function() {}); - generate.task('c', function() {}); - - generate.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); -}; diff --git a/test/fixtures/three/four/five/package.json b/test/fixtures/three/four/five/package.json deleted file mode 100644 index 984f0f6b..00000000 --- a/test/fixtures/three/four/five/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "five", - "description": "The most interesting project in the world > Verb", - "version": "0.1.0", - "homepage": "https://github.com/jonschlinkert/five", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "repository": "jonschlinkert/five", - "bugs": { - "url": "https://github.com/jonschlinkert/five/issues" - }, - "license": "MIT", - "files": [ - "index.js" - ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, - "dependencies": { - "resolve-modules": "^0.1.2" - }, - "devDependencies": { - "mocha": "*", - "should": "*" - }, - "keywords": [] -} diff --git a/test/fixtures/three/four/five/templates/a.txt b/test/fixtures/three/four/five/templates/a.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/four/five/templates/b.txt b/test/fixtures/three/four/five/templates/b.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/four/five/templates/c.txt b/test/fixtures/three/four/five/templates/c.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/four/five/verbfile.js b/test/fixtures/three/four/five/verbfile.js deleted file mode 100644 index 61f8bf67..00000000 --- a/test/fixtures/three/four/five/verbfile.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = function(generate, base) { - generate.task('default', function() {}); - generate.task('a', function() {}); - generate.task('b', function() {}); - generate.task('c', function() {}); - - generate.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); -}; \ No newline at end of file diff --git a/test/fixtures/three/four/generator.js b/test/fixtures/three/four/generator.js deleted file mode 100644 index 19b86e81..00000000 --- a/test/fixtures/three/four/generator.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = function(generate, base) { - generate.task('default', function() {}); - generate.task('a', function() {}); - generate.task('b', function() {}); - generate.task('c', function() {}); - - generate.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); -}; diff --git a/test/fixtures/three/four/package.json b/test/fixtures/three/four/package.json deleted file mode 100644 index 3f25912f..00000000 --- a/test/fixtures/three/four/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "four", - "description": "The most interesting project in the world > Verb", - "version": "0.1.0", - "homepage": "https://github.com/jonschlinkert/four", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "repository": "jonschlinkert/four", - "bugs": { - "url": "https://github.com/jonschlinkert/four/issues" - }, - "license": "MIT", - "files": [ - "index.js" - ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, - "dependencies": { - "resolve-modules": "^0.1.2" - }, - "devDependencies": { - "mocha": "*", - "should": "*" - }, - "keywords": [] -} diff --git a/test/fixtures/three/four/templates/a.txt b/test/fixtures/three/four/templates/a.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/four/templates/b.txt b/test/fixtures/three/four/templates/b.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/four/templates/c.txt b/test/fixtures/three/four/templates/c.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/four/verbfile.js b/test/fixtures/three/four/verbfile.js deleted file mode 100644 index 61f8bf67..00000000 --- a/test/fixtures/three/four/verbfile.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = function(generate, base) { - generate.task('default', function() {}); - generate.task('a', function() {}); - generate.task('b', function() {}); - generate.task('c', function() {}); - - generate.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); -}; \ No newline at end of file diff --git a/test/fixtures/three/generator.js b/test/fixtures/three/generator.js deleted file mode 100644 index 19b86e81..00000000 --- a/test/fixtures/three/generator.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = function(generate, base) { - generate.task('default', function() {}); - generate.task('a', function() {}); - generate.task('b', function() {}); - generate.task('c', function() {}); - - generate.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); -}; diff --git a/test/fixtures/three/package.json b/test/fixtures/three/package.json deleted file mode 100644 index 12b5fac3..00000000 --- a/test/fixtures/three/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "three", - "description": "The most interesting project in the world > Verb", - "version": "0.1.0", - "homepage": "https://github.com/jonschlinkert/three", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "repository": "jonschlinkert/three", - "bugs": { - "url": "https://github.com/jonschlinkert/three/issues" - }, - "license": "MIT", - "files": [ - "index.js" - ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, - "dependencies": { - "resolve-modules": "^0.1.2" - }, - "devDependencies": { - "mocha": "*", - "should": "*" - }, - "keywords": [] -} diff --git a/test/fixtures/three/templates/a.txt b/test/fixtures/three/templates/a.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/templates/b.txt b/test/fixtures/three/templates/b.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/templates/c.txt b/test/fixtures/three/templates/c.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/three/verbfile.js b/test/fixtures/three/verbfile.js deleted file mode 100644 index 61f8bf67..00000000 --- a/test/fixtures/three/verbfile.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -module.exports = function(generate, base) { - generate.task('default', function() {}); - generate.task('a', function() {}); - generate.task('b', function() {}); - generate.task('c', function() {}); - - generate.register('foo', function(app) { - app.task('x', function() {}); - app.task('y', function() {}); - app.task('z', function() {}); - }); -}; \ No newline at end of file diff --git a/test/fixtures/two/package.json b/test/fixtures/two/package.json index 8a02df01..0f9fde1f 100644 --- a/test/fixtures/two/package.json +++ b/test/fixtures/two/package.json @@ -12,7 +12,7 @@ "files": [ "index.js" ], - "main": "index.js", + "main": "generator.js", "engines": { "node": ">=0.10.0" }, diff --git a/test/fixtures/updaters/a.txt b/test/fixtures/updaters/a.txt deleted file mode 100644 index 7c4a013e..00000000 --- a/test/fixtures/updaters/a.txt +++ /dev/null @@ -1 +0,0 @@ -aaa \ No newline at end of file diff --git a/test/fixtures/updaters/b.txt b/test/fixtures/updaters/b.txt deleted file mode 100644 index 01f02e32..00000000 --- a/test/fixtures/updaters/b.txt +++ /dev/null @@ -1 +0,0 @@ -bbb \ No newline at end of file diff --git a/test/fixtures/updaters/c.txt b/test/fixtures/updaters/c.txt deleted file mode 100644 index 2383bd58..00000000 --- a/test/fixtures/updaters/c.txt +++ /dev/null @@ -1 +0,0 @@ -ccc \ No newline at end of file diff --git a/test/fixtures/verbfile.js b/test/fixtures/verbfile.js deleted file mode 100644 index 3dbcc451..00000000 --- a/test/fixtures/verbfile.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.register('generate-aaa', function(app) { - app.task('default', function(cb) { - console.log('generate > default'); - cb(); - }); - - app.register('sub', function(sub) { - sub.task('default', function(cb) { - console.log('aaa > sub > default'); - cb(); - }); - - sub.register('bbb', function(bbb) { - bbb.task('default', function(cb) { - console.log('aaa > sub > bbb > default'); - cb(); - }); - }); - }); - }); - - app.register('generate-abc', 'test/fixtures/generators/a/generator.js'); - - app.register('generate-bbb', function(app) { - app.task('default', function(cb) { - app.generate('aaa.sub.bbb', 'default', cb); - }); - }); - - app.register('generate-ccc', function(app) { - app.task('default', function(cb) { - app.generate('abc', 'default', cb); - }); - }); - - app.register('generate-ddd', function(app) { - app.task('default', function(cb) { - app.generate('abc.docs', 'x', cb); - }); - }); - - app.generate('aaa.sub', ['default'], function(err) { - if (err) throw err; - console.log('done'); - }); -}; diff --git a/test/fixtures/vinyl/bom-utf16be.txt b/test/fixtures/vinyl/bom-utf16be.txt deleted file mode 100644 index b9dce78a5d31af4803acd1a0f0dfc14f064a5de1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+I}XAy5Jacu26Qf=0Eq@sM*@i=qJaY#5&}waq&RRn3Xa4Tc-{cbe#Sc=zn?E@ zuZymVayru+l}y7P<@I1MK)hWXxZY@{g_hJzYt4Dvs;8dRDlmE2!LB37&GahJPDg5G zyEjIUb8?Hu>i*d9+Q4pAn^J>j{bf4+Qmn{O;+32Wrj#?&PC0#o+uan?UxFJmPf0ua E03tFf0ssI2 diff --git a/test/fixtures/vinyl/bom-utf16le.txt b/test/fixtures/vinyl/bom-utf16le.txt deleted file mode 100644 index 07cc600c98675d221bb56d10af38e650538734c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 244 zcmX|+%?`mp6otRFH?W%}3lbZ#mXJt@4G%E1O4?47PI);CkK`4cxy9!GoVn*`-p>~Y zuH1+?F6tGzrhboj9@;Y@-Y$;1UNd3FTy@Kesopkps%IL4CNFld>nNl)y+UZqNwu)u z8>5qRa*M`l|5*Q8iQQ0|QYFpu%XIuwER-RaS8~oYrJPIl?9@kcyPIPAOJL|a#!5Tj E15l Date: Fri, 29 Jul 2016 13:58:18 -0400 Subject: [PATCH 262/282] docs --- {docs => support}/logo.png | Bin support/templates/config.md | 168 ++++++++++++++++++++ support/templates/features.md | 41 +++++ support/templates/front-matter.md | 5 + support/templates/layouts.md | 5 + support/templates/layouts/default.md | 3 + support/templates/middleware.md | 5 + support/templates/old-readme.md | 221 ++++++++++++++++++++++++++ support/templates/pipeline-plugins.md | 5 + support/templates/tasks.md | 186 ++++++++++++++++++++++ 10 files changed, 639 insertions(+) rename {docs => support}/logo.png (100%) create mode 100644 support/templates/config.md create mode 100644 support/templates/features.md create mode 100644 support/templates/front-matter.md create mode 100644 support/templates/layouts.md create mode 100644 support/templates/layouts/default.md create mode 100644 support/templates/middleware.md create mode 100644 support/templates/old-readme.md create mode 100644 support/templates/pipeline-plugins.md create mode 100644 support/templates/tasks.md diff --git a/docs/logo.png b/support/logo.png similarity index 100% rename from docs/logo.png rename to support/logo.png diff --git a/support/templates/config.md b/support/templates/config.md new file mode 100644 index 00000000..562e1b8e --- /dev/null +++ b/support/templates/config.md @@ -0,0 +1,168 @@ +--- +title: Verb config +--- + +The `verb` object in package.json may be used to define configuration settings for the current project. + +## Overriding values + +Most of the configuration options defined in package.json will be used as "project-wide" settings. + +For example, the `layout` defined in package.json will be applied to all templates in the project. This is not ideal in many cases, so verb gives you several ways to override these settings in cases where you need to be more granular. + +- **[front-matter](front-matter.md)**: For most options, values defined in the [front matter](front-matter.md) of individual templats will override the values defined in package.json. +- **[middleware](middleware.md)**: +- **[plugin](pipeline-plugins.md)**: + +## Supported properties + +- [layout](#layout) +- [lint](#lint) +- [middleware](#middleware) +- [plugins](#plugins) +- [reflinks](#reflinks) +- [related](#related) +- [run](#run) +- [tasks](#tasks) +- [toc](#toc) + + +### layout + +Define the "default" [layout](layouts.md) to use for all templates in the project. + +**Type**: `String` + +**Default**: `undefined` + +**Example** + +```js +{ + "layout": "default" +} +``` + +### lint + +**Type**: `Boolean` + +**Default**: `undefined` + +**Example** + +```js +{ + "lint": true +} +``` + +### middleware + +**Type**: `Object` + +**Default**: `undefined` + +**Example** + +```js +{ + "middleware": {} +} +``` + +### plugins + +**Type**: `Object` + +**Default**: `undefined` + +**Example** + +```js +{ + "plugins": {} +} +``` + +### reflinks + +**Type**: `Object` + +**Default**: `undefined` + +**Example** + +```js +{ + "reflinks": {} +} +``` + +### related + +**Type**: `Object` + +**Default**: `undefined` + +**Example** + +```js +{ + "related": {} +} +``` + +### run + +**Type**: `Boolean` + +**Default**: `undefined` + +**Example** + +```js +{ + "run": {} +} +``` + +### tasks + +**Type**: `Array` + +**Default**: `undefined` + +**Example** + +```js +{ + "tasks": {} +} +``` + +### toc + +**Type**: `Boolean|Object` + +**Default**: `undefined` + +**Example** + +```js +{ + // converted to "{render: true}" + "toc": true +} +// or +{ + "toc": { + "render": true, + "method": "postRender" + } +} +``` + +**Options** + +- `render`: actually add the Table of Contents to the file. \ No newline at end of file diff --git a/support/templates/features.md b/support/templates/features.md new file mode 100644 index 00000000..04624c98 --- /dev/null +++ b/support/templates/features.md @@ -0,0 +1,41 @@ +--- +title: Features +--- + +* Create your own documentation generator +* Render templates +* [Generate a README.md][verb-readme-generator] from a template + ++ **Tasks** - run [gulp][]-like tasks. Verb's task system is powered by [bach][], the same library used in [gulp][] v4.0. ++ **Generators** - powerful flow control, with support for [generate][] generators (verb is built on top of generate) ++ **"Smart" plugins** - highly pluggable, with 75+ [smart plugins]() available for creating custom verb applications. Additionaly, most [base][], [generate][], [assemble][], and [update][] plugins can be used with verb. ++ **Streams** - methods for working with the file system and streams, with full support for [assemble][] and [gulp][] plugins ++ **Markdown** - Create markdown documentation from templates ++ **Templates** - Render templates using any template engine, such as [handlebars][], [lodash][] or [swig][]. Or any engine supported by [consolidate][]. ++ **Layouts and partials** - Support for layouts, partials, and custom template collections ++ **Collections** - First class template collection support, powered by the [templates][] library ++ **Helpers** - Async and sync helper support. Use any helpers from [template-helpers][], [handlebars-helpers][], or any helper from the [helpers org](https://github.com/helpers). + +## Bonus features + +### Plugin ecosystem + +Verb is build on [base][] and shares a common plugin ecosystem with the following applications: + +- [generate][]: generat projects +- [assemble][]: build projects +- [update][]: maintain projects + +### Easy to extend + +TODO + +```js +module.exports = function(verb) { + verb.use(require('verb-readme-generator')); + + verb.task('default', ['readme'], function(cb) { + cb(); + }); +}; +``` diff --git a/support/templates/front-matter.md b/support/templates/front-matter.md new file mode 100644 index 00000000..84b173eb --- /dev/null +++ b/support/templates/front-matter.md @@ -0,0 +1,5 @@ +--- +title: Front-matter +--- + +TODO \ No newline at end of file diff --git a/support/templates/layouts.md b/support/templates/layouts.md new file mode 100644 index 00000000..4b88e80f --- /dev/null +++ b/support/templates/layouts.md @@ -0,0 +1,5 @@ +--- +title: Layouts +--- + +TODO \ No newline at end of file diff --git a/support/templates/layouts/default.md b/support/templates/layouts/default.md new file mode 100644 index 00000000..00bb92af --- /dev/null +++ b/support/templates/layouts/default.md @@ -0,0 +1,3 @@ +# {%= title %} + +{% body %} \ No newline at end of file diff --git a/support/templates/middleware.md b/support/templates/middleware.md new file mode 100644 index 00000000..2eba88b3 --- /dev/null +++ b/support/templates/middleware.md @@ -0,0 +1,5 @@ +--- +title: Middleware +--- + +TODO \ No newline at end of file diff --git a/support/templates/old-readme.md b/support/templates/old-readme.md new file mode 100644 index 00000000..79be96f4 --- /dev/null +++ b/support/templates/old-readme.md @@ -0,0 +1,221 @@ +A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. + +**I just want to generate a readme, can verb do this?** + +Yes! Just run [verb-readme-generator][]. + + +## What is verb? + +Verb is a documentation build system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. + +Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. + +## Features + +* Create markdown docs from templates +* Use any template engine, such as [handlebars][], [lodash][] or [swig][] +* Use templates, partials, or layouts +* Helper support (sync and async!) +* First class template collection support +* Instance plugins and pipeline plugins +* Middleware + +**Plugins** + +Verb has first-class plugin support, along with native support for [gulp][] plugins, so you can do things like: + +* [Ignore files][gulp-drafts] marked as "drafts" +* CSS minification or reduction +* Spin up a dev server +* SASS or LESS compiling and minification +* JavaScript minification, reduction or concatenation +* Asset copying or renaming +* Image compression +* HTML minification and linting +* RSS feeds + +**Developers** + +* Use custom code to your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`) +* Use globally or locally installed verb "generators" +* Support for sub-generators +* Generators can be composed of multiple single-responsibility generators + +## Quickstart + +**Install readme generator** + +The `verb-readme-generator` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: + +```sh +$ npm i -g verb-readme-generator +``` + +**.verb.md** + +You should now be able to generate your project's readme from a `.verb.md` template with the following command: + +```sh +$ verb readme +``` + +Or you can go a step further by adding a `verbfile.js` to your project, allowing you to customize verb with generators, plugins, middleware, helpers, templates and more! + +**verbfile.js** + +Next, create a `verbfile.js` with the following code: + +```js +module.exports = function(app) { + app.extendWith('verb-readme-generator'); + + // call the `readme` task from verb-readme-generator + app.task('default', ['readme']); +}; +``` + +## verbfile + +Your project's `verbfile.js` should either export an instance of Verb or a function. + +**Instance** + +```js +var verb = require('verb'); +var app = verb(); + +module.exports = app; +``` + +**Function** + +When a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). + +```js +module.exports = function(verb) { + // "private" verb instance is created for this verbfile +}; +``` + +## CLI + +**Installing the CLI** + +To run verb from the command line, you'll need to install verb globally first. You can that now with the following command: + +```sh +$ npm i -g verb +``` + +This adds the `verb` command to your system path, allowing it to be run from any directory in a project. + +**How the Verb CLI works** + +When the `verb` command is run, the globally installed verb looks for a locally installed verb module using node's `require()` system. + +If a locally installed verb is found, the CLI loads the local installation of the verb library. If a local verb is not found, the global verb will be used. + +Next, verb applies the configuration from your `verbfile.js` and/or `.verb.json`, then executes any generators or tasks you've specified for verb to run. + +### Command prefixes + +The following flags can be used as "prefixes" to other commands for setting options from the command line: + +- `--option`: set options in memory +- `--data`: set data to be passed to templates in memory +- `--config`: persist options to package.json +- `--save`: persist options to a global config store + +### Commands + +{%= apidocs("lib/commands/*.js") %} + +### silent + +Typically, when running generators and tasks you'll see something like this in the terminal. + +![running-generators](https://cloud.githubusercontent.com/assets/383994/14978816/7449a5c6-10ec-11e6-9bac-07e482e915f2.gif) + +Since it's common for tasks to be composed of other smaller tasks, or for generators to be composed of other generators or sub-generators, sometimes it's necessary to prevent these sub-tasks or sub-generators from displaying in the terminal. + +To silence a specific task, + +### config + +Persist configuration settings to the `verb` object in `package.json`. + +```sh +$ verb --config +``` + +Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. + + +### save + +Persist options values to the global data store for `app` ([verb][], [assemble][], [generate][], [update][], etc) + +```sh +$ verb --save +``` + +Most of the above CLI commands can be prefixed with `--save` to persist the value to the global config store. + +### file + +Specify the file to use instead of `verbfile.js`. + +```sh +$ verb --file +``` + +**Example** + +```sh +$ verb --file foo.js +``` + +### cwd + +Specify the cwd to use + +```sh +$ verb --cwd= +``` + +**Example** + +```sh +$ verb --cwd="foo/bar" +``` + +Display the currently defined cwd: + +```sh +$ verb --cwd +``` + +#### tasks + +Set the default tasks to run for a project: + +```sh +$ verb --config=tasks: +``` + +**Example** + +```sh +# single task +$ verb --config=tasks:foo + +# array of tasks +$ verb --config=tasks:foo,bar,baz +``` + +## API +{%= apidocs("index.js") %} + +## Upgrading +{%= include("upgrading") %} diff --git a/support/templates/pipeline-plugins.md b/support/templates/pipeline-plugins.md new file mode 100644 index 00000000..487061bf --- /dev/null +++ b/support/templates/pipeline-plugins.md @@ -0,0 +1,5 @@ +--- +title: Pipeline plugins +--- + +TODO \ No newline at end of file diff --git a/support/templates/tasks.md b/support/templates/tasks.md new file mode 100644 index 00000000..648f5f50 --- /dev/null +++ b/support/templates/tasks.md @@ -0,0 +1,186 @@ +--- +title: Tasks +related: + doc: ['generators#running-generators', 'generators', 'generator-js'] +--- + +Tasks are used for wrapping code that should be executed at a later point, either when specified by command line or explicitly run when using the API. + + + +## Creating tasks + +Tasks are asynchronous functions that are registered by name using the `.task` method, and can be run using the `.build` method. + +## Running tasks + +Tasks can be run by command line or API. + +### Command line + +Pass the names of the tasks to run after the `generate` command. + +**Examples** + +Run task `foo`: + +```sh +generate foo +``` + +Run tasks `foo`, `bar` and `baz`: + +```sh +generate foo bar baz +``` + +**Conflict resolution** + +You might notice that [generators](generators.md) can also be run from the command line using the same syntax. Generate can usually determine whether you meant to call tasks or generators. Visit the [running generators](generators.md#running-generators) documentation for more information. + +## Task API + +### .task + +Create a task: + +```js +app.task(name, fn); +``` + +**Params** + +* `name` **{String}**: name of the task to register +* `fn` **{Function}**: asynchronous callback function, or es6 generator function + +**Example** + +```js +app.task('default', function(cb) { + // do task stuff (be sure to call the callback) + cb(); +}); +``` + +**Stream or callback** + +When using generate's file system API (`.src`/`.dest` etc), you can optionally return a stream instead of calling a callback. Either a callback must be called, or a stream must be returned, otherwise generate has no way of knowing when a task is complete. + +### .build + +Run one more tasks. + +**Params** + +* `names` **{String|Array|Function}**: names of one or more tasks to run, or callback function if you only want to run the [default task](#default-task) +* `callback`: callback function, invoked after all tasks have finished executing. The callback function exposes `err` as the only argument, with any errors that occurred during the execution of any tasks. + +**Example** + +```js +app.task('foo', function(cb) { + // do task stuff + cb(); +}); +app.task('foo', function(cb) { + // do task stuff + cb(); +}); + +app.build(['foo', 'bar'], function(err) { + if (err) return console.log(err); + console.log('done'); +}); +``` + +### .generate + +The `.generate` may also be used to run tasks. However, `.generate` can be used to run _tasks and generators_, thus it will also look for generators to run when a task is not found. + +_To ensure that only tasks are run, use the `.build` method._ + +See the [generators documentation](#generators) for more details. + + +## Task composition + +### Task dependencies + +When a task has "dependencies", this means that one or more other tasks need to finish before the task is executed. + +Dependencies can be passed as the second argument to the `.task` method. + +**Example** + +In the following example, task `foo` has dependencies `bar` and `baz`: + +```js +app.task('foo', ['bar', 'baz'], function(cb) { + // do task stuff + cb(); +}); +``` + +Task `foo` will not execute until tasks `bar` and `baz` have completed. + +### Alias tasks + +An "alias" task is a task with one or more dependencies and _no callback_. + +**Example** + +In this example, task `foo` is an alias for tasks `bar` and `baz`: + +```js +app.task('foo', ['bar', 'baz']); +``` + +In this example, task `foo` is an alias for task `baz` + +```js +app.task('foo', ['baz']); +``` + +## Task options + +An `options` object may be passed as the second argument to the `.task` method. + +**Example** + +```js +app.task('default', {foo: 'bar'}, function(cb) { + // do task stuff + cb(); +}); +``` + +### options.silent + +Silence logging output for a specific task. + + +**Example** + +```js +app.task('default', {silent: true}, function(cb) { + // do task stuff + cb(); +}); +``` + +## default task + +The `default` task is run automatically when a callback is passed as the only argument: + +```js +app.task('default', function(cb) { + // do task stuff + cb(); +}); + +// no need to specify "default", but you can if you want +app.build(function(err) { + if (err) return console.log(err); + console.log('done'); +}); +``` From 7b296f39f0f25097c6c38393548dd6e241c1c64d Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 13:58:54 -0400 Subject: [PATCH 263/282] run update --- .editorconfig | 13 ++----------- .gitignore | 2 ++ .travis.yml | 1 + 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.editorconfig b/.editorconfig index 408d8707..818e0724 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,3 @@ -# http://editorconfig.org root = true [*] @@ -9,14 +8,6 @@ indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true -[*.md] +[{**/{actual,fixtures,expected,templates}/**,*.md}] trim_trailing_whitespace = false -insert_final_newline = false - -[**/{actual,fixtures,expected}/**] -trim_trailing_whitespace = false -insert_final_newline = false - -[**/templates/**] -trim_trailing_whitespace = false -insert_final_newline = false +insert_final_newline = false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 37bcd361..f033cb7a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ bower_components vendor temp tmp +_draft +_drafts diff --git a/.travis.yml b/.travis.yml index e9f3361b..3932eaa2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ sudo: false language: node_js node_js: + - '6' - '5' - '4' - '0.12' From a71fc92330b8e652b9d457c582fbf8eecd7e0341 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 13:59:24 -0400 Subject: [PATCH 264/282] add format plugin --- verbfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/verbfile.js b/verbfile.js index c11bb58a..f32a4b60 100644 --- a/verbfile.js +++ b/verbfile.js @@ -23,6 +23,7 @@ module.exports = function(app) { file.content = file.content.replace(/^(#{2,}\s*\[)\./gm, '$1'); next(null, file); })) + .pipe(format()) .pipe(app.dest('.')); }); }; From 1e65b7b3b36b22ba196d9174486b3c44e7271069 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 13:59:57 -0400 Subject: [PATCH 265/282] improve template --- lib/templates/verbfile.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/templates/verbfile.js b/lib/templates/verbfile.js index 2dad7982..7818a6a8 100644 --- a/lib/templates/verbfile.js +++ b/lib/templates/verbfile.js @@ -4,8 +4,9 @@ layout: false 'use strict'; module.exports = function(verb) { - verb.task('default', function(cb) { - - cb(); + verb use(require('verb-generate-readme')); + verb.helper('foo', function(str) { + return str; }); -}; + verb.task('default', ['readme']); +}; \ No newline at end of file From 904642118d16040ed8df462b1c65eb60030c5301 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 14:00:26 -0400 Subject: [PATCH 266/282] add list task to built-in generator --- lib/generator.js | 50 +++++++++++++++++++++++++------------ lib/list.js | 38 ++++++++++++++++++++++++++++ lib/reflinks.js | 64 ------------------------------------------------ lib/tasks.js | 6 ++++- lib/utils.js | 7 ++++-- 5 files changed, 82 insertions(+), 83 deletions(-) create mode 100644 lib/list.js delete mode 100644 lib/reflinks.js diff --git a/lib/generator.js b/lib/generator.js index 4b391b1a..6995eeb5 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -2,13 +2,9 @@ var path = require('path'); var cwd = path.resolve.bind(path, __dirname, 'templates'); -var middleware = require('common-middleware'); -var extend = require('extend-shallow'); -var DataStore = require('data-store'); -var log = require('log-utils'); var utils = require('./utils'); +var list = require('./list'); var argv = utils.parseArgs(process.argv.slice(2)); -var reflinks = require('./reflinks'); var diff = require('./diff')(argv); /** @@ -16,8 +12,11 @@ var diff = require('./diff')(argv); */ module.exports = function(verb, base) { - var common = new DataStore('common-config'); - verb.use(middleware()); + var common = new utils.DataStore('common-config'); + var gm = path.resolve.bind(path, utils.gm); + var cwd = path.resolve.bind(path, verb.cwd); + + verb.use(utils.middleware()); /** * Listen for errors @@ -44,7 +43,7 @@ module.exports = function(verb, base) { var dest = verb.option('dest'); verb.src(src) - .pipe(reflinks(verb)) + .pipe(utils.reflinks(verb)) .pipe(diff('before')) .pipe(utils.format()) .pipe(diff('after', 'before')) @@ -195,6 +194,25 @@ module.exports = function(verb, base) { }); }); + /** + * Display a list of installed verb generators. + * + * ```sh + * $ verb list + * ``` + * @name list + * @api public + */ + + verb.task('list', { silent: true }, function() { + return verb.src([gm('verb-generate-*'), cwd('node_modules/verb-generate-*')]) + .pipe(utils.through.obj(function(file, enc, next) { + file.alias = verb.toAlias(file.basename); + next(null, file); + })) + .pipe(list(verb)); + }); + /** * Display a help menu of available commands and flags. * @@ -243,7 +261,7 @@ module.exports = function(verb, base) { gen.task('del', function(cb) { var keys = ['name', 'username', 'twitter', 'email']; keys.forEach(function(key) { - console.log(log.red(' Deleted:'), key, common.get(key)); + console.log(verb.log.red(' Deleted:'), key, common.get(key)); common.del(keys); }); cb(); @@ -253,7 +271,7 @@ module.exports = function(verb, base) { var keys = ['name', 'username', 'twitter', 'email']; console.log(); keys.forEach(function(key) { - console.log(key + ': ' + log.cyan(common.get(key))); + console.log(key + ': ' + verb.log.cyan(common.get(key))); }); console.log(); cb(); @@ -261,9 +279,9 @@ module.exports = function(verb, base) { gen.task('me', function(cb) { console.log(); - console.log(' Answers to the following questions will be stored in:', log.bold('~/.common-config.json')); + console.log(' Answers to the following questions will be stored in:', verb.log.bold('~/.common-config.json')); console.log(' The stored values will be used later in (your) templates.'); - console.log(` To skip a question, just hit ${log.bold('')}`); + console.log(` To skip a question, just hit ${verb.log.bold('')}`); console.log(); gen.question('common.name', 'What is your name?'); @@ -285,7 +303,7 @@ module.exports = function(verb, base) { if (answers.common.hasOwnProperty(key)) { var val = answers.common[key]; common.set(key, val); - vals.push(log.green(key + ': ' + val)); + vals.push(verb.log.green(key + ': ' + val)); } } @@ -296,11 +314,11 @@ module.exports = function(verb, base) { console.log(); console.log(' To delete these values, run:'); console.log(); - console.log(log.bold(' $ gen store:del')); + console.log(verb.log.bold(' $ gen store:del')); console.log(); console.log(' To update these values, run:'); console.log(); - console.log(log.bold(' $ gen store:me')); + console.log(verb.log.bold(' $ gen store:me')); console.log(); cb(); }); @@ -343,7 +361,7 @@ module.exports = function(verb, base) { function file(verb, src, options, cb) { var defaults = { cwd: cwd(), dest: verb.cwd }; - var opts = extend({}, defaults, options); + var opts = utils.extend({}, defaults, options); var dest = path.resolve(opts.dest); verb.engine('*', require('engine-base')); diff --git a/lib/list.js b/lib/list.js new file mode 100644 index 00000000..f988b269 --- /dev/null +++ b/lib/list.js @@ -0,0 +1,38 @@ +'use strict'; + +var path = require('path'); +var utils = require('./utils'); + +module.exports = function(app) { + function bold(str) { + return app.log.underline(app.log.bold(str)); + } + + var list = [[bold('version'), bold('name'), bold('alias')]]; + var cache = {}; + return utils.through.obj(function(file, enc, next) { + if (cache[file.stem]) { + next(); + return; + } + + cache[file.stem] = true; + var pkgPath = path.resolve(file.path, 'package.json'); + var pkg = require(pkgPath); + list.push([app.log.gray(pkg.version), file.basename, app.log.cyan(file.alias)]); + next(); + }, function(cb) { + console.log(); + console.log(utils.table(list, { + stringLength: function(str) { + return utils.strip(str).length; + } + })); + + console.log(); + console.log(app.log.magenta(list.length + ' verb generators installed')); + console.log(); + cb(); + }); +}; + diff --git a/lib/reflinks.js b/lib/reflinks.js deleted file mode 100644 index aaf488da..00000000 --- a/lib/reflinks.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -var utils = require('./utils'); - -/** - * Lint readme.md for missing reflinks, and add them to - * package.json if enabled by the user. - */ - -module.exports = function(app, options) { - options = options || {}; - - var arr = []; - if (Array.isArray(options.reflinks)) { - arr = options.reflinks; - } - - var re = /(\[[-\w._]+?\]\[\])/g; - var count = 0; - - return utils.through.obj(function(file, enc, next) { - var existing = app.option('reflinks') || {}; - var matches = file.content.match(re); - - if (matches && matches.length) { - matches.forEach(function(match) { - var idx = match.indexOf(']'); - var name = match.slice(1, idx).trim().toLowerCase(); - if (arr.indexOf(name) === -1) { - arr.push(name); - count++; - } - }); - } - - if (count === 0) { - next(null, file); - return; - } - - if (options.save === true) { - save(app, arr); - } - - utils.reflinks(arr, options, function(err, links) { - if (err) return next(err); - file.content += '\n\n'; - file.content += links.join('\n'); - next(null, file); - }); - }); -}; - -/** - * Save reflinks to package.json config - */ - -function save(app, arr) { - if (app && app.pkg && typeof app.pkg.set !== 'function') { - app.pkg.set('verb.reflinks', arr); - app.pkg.logInfo('updated package.json with:', { reflinks: arr }); - app.pkg.save(); - } -} diff --git a/lib/tasks.js b/lib/tasks.js index 35c07bbd..7a87923f 100644 --- a/lib/tasks.js +++ b/lib/tasks.js @@ -25,6 +25,10 @@ module.exports = function(app, ctx, argv) { isDefaults = true; task = 'defaults.' + task; + } else if (task === 'list') { + isDefaults = true; + task = 'defaults:list'; + } else if (task === 'init') { isDefaults = true; task = 'defaults:init'; @@ -62,7 +66,7 @@ module.exports = function(app, ctx, argv) { var verbmd = utils.exists('.verb.md'); // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default if (verbmd && !configExists) { - return ['verb-readme-generator']; + return ['verb-generate-readme']; } // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` diff --git a/lib/utils.js b/lib/utils.js index 94a56023..9f8cda7e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,15 +12,19 @@ require = utils; require('base-runner', 'runner'); require('config-file'); +require('common-middleware', 'middleware'); require('extend-shallow', 'extend'); +require('data-store', 'DataStore'); require('fs-exists-sync', 'exists'); require('generate'); require('get-value', 'get'); require('global-modules', 'gm'); require('gulp-format-md', 'format'); require('log-utils', 'log'); -require('reflinks'); +require('gulp-reflinks', 'reflinks'); require('set-value', 'set'); +require('strip-color', 'strip'); +require('text-table', 'table'); require('through2', 'through'); require('yargs-parser', 'parse'); require = fn; @@ -38,7 +42,6 @@ utils.opts = { diff: 'diffOnly', global: 'g', help: 'h', - init: 'i', silent: 'S', verbose: 'v', version: 'V', From 38a654e88d8e7a0241cfa8b2f83c1f0cbcf8a2d9 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 14:03:15 -0400 Subject: [PATCH 267/282] make defaults smarter --- lib/questions.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/questions.js b/lib/questions.js index 9dfbfb50..466bcaff 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -12,13 +12,13 @@ module.exports = function(app, options) { }) .set('config.toc', 'Add Table of Contents to README.md?', { type: 'confirm', - default: false + default: app.pkg.get('verb.toc') || false }) .set('config.plugins', 'Plugins to use (comma-separated):', { - default: ['gulp-format-md'] + default: app.pkg.get('verb.plugins') || ['gulp-format-md'] }) .set('config.tasks', 'Tasks or generators to run (comma-separated)', { - default: ['readme'] + default: app.pkg.get('verb.tasks') || ['readme'] }) .set('config.lint.reflinks', 'Do you want to lint for missing reflinks and add them to verb config?', { type: 'confirm' @@ -59,7 +59,6 @@ module.exports = function(app, options) { cb(null, answers); return; } - var choices = answer.map(function(val) { return 'config.' + val; }); @@ -73,17 +72,22 @@ function defaultLayout(app) { if (typeof layout === 'string' && layout.trim() !== 'default') { return layout; } - var name = app.pkg.get('name'); if (/^generate-/.test(name)) { return 'generator'; } - if (/^update-/.test(name)) { + if (/^updater-/.test(name)) { return 'updater'; } if (/^helper-/.test(name)) { return 'helper'; } + if (/^assemble-/.test(name)) { + return 'assemble'; + } + if (/^verb-/.test(name)) { + return 'verb'; + } return 'default'; } From a99f850d5613036da9f6cab9d136f51fbcba6b53 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 14:03:25 -0400 Subject: [PATCH 268/282] use yargs-parser directly --- bin/verb.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/bin/verb.js b/bin/verb.js index 5aad5945..cd5297b7 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -11,7 +11,7 @@ var commands = require('../lib/commands'); var tasks = require('../lib/tasks'); var utils = require('../lib/utils'); var args = process.argv.slice(2); -var argv = utils.parseArgs(args); +var argv = require('yargs-parser')(args); /** * Listen for errors on all instances @@ -22,20 +22,6 @@ Verb.on('generate.preInit', function(app) { console.log(err.stack); process.exit(1); }); - - app.on('build', function(event, build) { - if (build && typeof event === 'string' && !build.isSilent) { - var time = (build && build.time) ? app.log.red(build.time) : ''; - var result = event === 'finished' ? time + ' ' + app.log.green(app.log.check) : time; - app.log(event, build.key, result); - } - }); - - app.on('task', function(event, task) { - if (task && (event === 'finished' || event === 'starting') && !task.isSilent) { - app.log(event, task.key, app.log.red(task.time)); - } - }); }); /** From 5aed8c708dc39256c05299df9986f787c2405e14 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 14:03:30 -0400 Subject: [PATCH 269/282] update deps --- package.json | 50 +++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index c10b6f7f..b0e59373 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "files": [ "bin", "index.js", - "lib" + "lib", + "LICENSE", + "README.md" ], "main": "index.js", "preferGlobal": true, @@ -26,53 +28,54 @@ "test": "mocha" }, "dependencies": { - "base-runner": "^0.8.1", - "common-middleware": "^0.2.10", + "base-runner": "^0.8.2", + "common-middleware": "^0.3.0", "config-file": "^0.3.2", - "data-store": "^0.16.0", + "data-store": "^0.16.1", "debug": "^2.2.0", "diff": "^2.2.3", "engine-base": "^0.1.2", "export-files": "^2.1.1", "extend-shallow": "^2.0.1", "fs-exists-sync": "^0.1.0", - "generate": "^0.8.0", + "generate": "^0.9.8", "get-value": "^2.0.6", + "global-modules": "^0.2.3", "gulp-format-md": "^0.1.9", + "gulp-reflinks": "^0.2.0", "lazy-cache": "^2.0.1", - "log-utils": "^0.1.4", - "reflinks": "^0.2.7", + "log-utils": "^0.2.1", "set-blocking": "^2.0.0", "set-value": "^0.3.3", + "strip-color": "^0.1.0", + "text-table": "^0.2.0", "through2": "^2.0.1", - "yargs-parser": "^2.4.0" + "yargs-parser": "^2.4.1" }, "devDependencies": { - "base-config-process": "^0.1.7", - "base-generators": "^0.4.1", - "base-questions": "^0.6.6", + "base-config-process": "^0.1.9", + "base-generators": "^0.4.5", + "base-questions": "^0.7.3", "base-store": "^0.4.4", "base-test-runner": "^0.2.0", - "base-test-suite": "^0.1.11", + "base-test-suite": "^0.2.3", "cross-spawn": "^4.0.0", "generate-foo": "^0.1.5", "generator-util": "^0.2.9", - "global-modules": "^0.2.2", - "graceful-fs": "^4.1.4", + "graceful-fs": "^4.1.5", "gulp": "^3.9.1", - "gulp-eslint": "^2.0.0", + "gulp-eslint": "^3.0.1", "gulp-istanbul": "^1.0.0", "gulp-mocha": "^2.2.0", - "gulp-reflinks": "^0.1.4", "gulp-unused": "^0.1.2", "is-absolute": "^0.2.5", "load-pkg": "^3.0.1", "mocha": "^2.5.3", "npm-install-global": "^0.1.2", "resolve": "^1.1.7", - "should": "^9.0.2", - "sinon": "^1.17.4", - "verb-generate-readme": "^0.1.21" + "should": "^10.0.0", + "sinon": "^1.17.5", + "verb-generate-readme": "^0.1.28" }, "keywords": [ "verb" @@ -80,12 +83,13 @@ "lintDeps": { "ignore": [ "coverage", - "docs" + "docs", + "lib/templates" ] }, "verb": { - "toc": false, - "layout": "minimal", + "toc": true, + "layout": "common-minimal", "plugins": [ "gulp-format-md" ], @@ -121,4 +125,4 @@ "reflinks": true } } -} +} \ No newline at end of file From 5ed87dcc0d7f7d47ce75ff913d58f65127dc6263 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 14:03:36 -0400 Subject: [PATCH 270/282] generate docs --- .verb.md | 83 ++++++++++++++++++-------- CHANGELOG.md | 22 ++++++- readme.md | 160 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 183 insertions(+), 82 deletions(-) diff --git a/.verb.md b/.verb.md index 7f88311b..37325f7b 100644 --- a/.verb.md +++ b/.verb.md @@ -96,7 +96,35 @@ module.exports = function(verb) { }; ``` -## CLI Commands +## Config + +Options can be defined in `package.json` on the `verb` object. See the [config docs](docs/config.md) for more details. + +## CLI + +### Tasks + +Verb only has a few built-in [tasks](docs/tasks.md), but these will externalized to [generators](docs/generators.md) in the near future. + +#### list + +List currently installed verb [generators](docs/generators.md). + +```js +$ verb new +``` + + +#### new + +Generate a new [verbfile.js](docs/verbfile.js) in the current working directory. + +```js +$ verb new +``` + + +### Options There are several generic flags for setting options from the command line: `option`, `data`, `config` and `save`. Beyond these there are also a number of specialized flags described below. @@ -105,7 +133,7 @@ There are several generic flags for setting options from the command line: `opti - `--config`: persist options to package.json - `--save`: persist options to a global config store -### config +#### --config Persist configuration settings to the `verb` object in `package.json`. @@ -116,7 +144,34 @@ $ verb --config Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. -### save +#### --config=tasks + +Set the default tasks to run for a project: + +```sh +$ verb --config=tasks: +``` + +**Example** + +```sh +# single task +$ verb --config=tasks:foo + +# array of tasks +$ verb --config=tasks:foo,bar,baz +``` + +#### --init + +Initialize a `verb` config object in the package.json of the current project. + +```sh +$ verb --init +``` + + +#### --save Persist options values to the global data store for `app` ([verb][], [assemble][], [generate][], [update][], etc) @@ -126,7 +181,7 @@ $ verb --save Most of the above CLI commands can be prefixed with `--save` to persist the value to the global config store. -### file +#### --file Specify the file to use instead of `verbfile.js`. @@ -140,7 +195,7 @@ $ verb --file $ verb --file foo.js ``` -### cwd +#### --cwd Specify the cwd to use @@ -160,24 +215,6 @@ Display the currently defined cwd: $ verb --cwd ``` -#### tasks - -Set the default tasks to run for a project: - -```sh -$ verb --config=tasks: -``` - -**Example** - -```sh -# single task -$ verb --config=tasks:foo - -# array of tasks -$ verb --config=tasks:foo,bar,baz -``` - ## API {%= apidocs("index.js") %} diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e164f47..5c817ac2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Release history -## v0.9 (pending) +Starting with 0.9.0, verb will follow [keep-a-changelog][] conventions for tracking release history. -- `body` tag delimiters have changed. the layout tag is now `{% body %}` (instead of `<<% body %>>`) +## key + +Changelog entries are classified using the following labels _(from [keep-a-changelog][]_): + +- `added`: for new features +- `changed`: for changes in existing functionality +- `deprecated`: for once-stable features removed in upcoming releases +- `removed`: for deprecated features removed in this release +- `fixed`: for any bug fixes + +## [Unreleased] + +- `body` tag delimiters have changed. the layout tag is now `{%% body %}` (instead of `<<% body %>>`) + + +[Unreleased]: https://github.com/generate/generate/compare/0.9.0...HEAD +[0.9.0]: https://github.com/generate/generate/compare/0.8.0...0.9.0 + +[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog diff --git a/readme.md b/readme.md index 8b604e6e..f4eb9ea5 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,38 @@ # verb [![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) -Documentation build system for GitHub projects. - -## Install - -Install with [npm](https://www.npmjs.com/): - -```sh -$ npm install verb --save -``` +

+ + + +

+Documentation build system for GitHub projects, powered by node.js. Verb has full support for gulp and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! + +* [Features](#features) +* [What is verb?](#what-is-verb) +* [Quickstart](#quickstart) +* [verbfile](#verbfile) +* [Config](#config) +* [CLI](#cli) + - [Tasks](#tasks) + + [list](#list) + + [new](#new) + - [Options](#options) + + [--config](#--config) + + [--config=tasks](#--configtasks) + + [--init](#--init) + + [--save](#--save) + + [--file](#--file) + + [--cwd](#--cwd) +* [API](#api) +* [Upgrading](#upgrading) +* [About](#about) + - [Related projects](#related-projects) + - [Contributing](#contributing) + - [Running tests](#running-tests) + - [Author](#author) + - [License](#license) + +_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. @@ -108,7 +132,33 @@ module.exports = function(verb) { }; ``` -## CLI Commands +## Config + +Options can be defined in `package.json` on the `verb` object. See the [config docs](docs/config.md) for more details. + +## CLI + +### Tasks + +Verb only has a few built-in [tasks](docs/tasks.md), but these will externalized to [generators](docs/generators.md) in the near future. + +#### list + +List currently installed verb [generators](docs/generators.md). + +```js +$ verb new +``` + +#### new + +Generate a new [verbfile.js](docs/verbfile.js) in the current working directory. + +```js +$ verb new +``` + +### Options There are several generic flags for setting options from the command line: `option`, `data`, `config` and `save`. Beyond these there are also a number of specialized flags described below. @@ -117,7 +167,7 @@ There are several generic flags for setting options from the command line: `opti * `--config`: persist options to package.json * `--save`: persist options to a global config store -### config +#### --config Persist configuration settings to the `verb` object in `package.json`. @@ -127,7 +177,33 @@ $ verb --config Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. -### save +#### --config=tasks + +Set the default tasks to run for a project: + +```sh +$ verb --config=tasks: +``` + +**Example** + +```sh +# single task +$ verb --config=tasks:foo + +# array of tasks +$ verb --config=tasks:foo,bar,baz +``` + +#### --init + +Initialize a `verb` config object in the package.json of the current project. + +```sh +$ verb --init +``` + +#### --save Persist options values to the global data store for `app` ([verb](https://github.com/verbose/verb), [assemble](https://github.com/assemble/assemble), [generate](https://github.com/generate/generate), [update](https://github.com/update/update), etc) @@ -137,7 +213,7 @@ $ verb --save Most of the above CLI commands can be prefixed with `--save` to persist the value to the global config store. -### file +#### --file Specify the file to use instead of `verbfile.js`. @@ -151,7 +227,7 @@ $ verb --file $ verb --file foo.js ``` -### cwd +#### --cwd Specify the cwd to use @@ -171,27 +247,9 @@ Display the currently defined cwd: $ verb --cwd ``` -#### tasks - -Set the default tasks to run for a project: - -```sh -$ verb --config=tasks: -``` - -**Example** - -```sh -# single task -$ verb --config=tasks:foo - -# array of tasks -$ verb --config=tasks:foo,bar,baz -``` - ## API -### [Verb](index.js#L23) +### [Verb](index.js#L25) Create a verb application with `options`. @@ -216,33 +274,21 @@ If you're currently running verb v0.8.0 or lower, please do the following to cle $ npm cache clean && npm i -g verb ``` -## Related projects +## About -You might also be interested in these projects: +### Related projects -* [assemble](https://www.npmjs.com/package/assemble): Assemble is a powerful, extendable and easy to use static site generator for node.js. Used… [more](https://www.npmjs.com/package/assemble) | [homepage](https://github.com/assemble/assemble) -* [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://www.npmjs.com/package/base) | [homepage](https://github.com/node-base/base) -* [generate](https://www.npmjs.com/package/generate): Fast, composable, highly extendable project generator with a user-friendly and expressive API. | [homepage](https://github.com/generate/generate) +* [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit") +* [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://github.com/node-base/base) | [homepage](https://github.com/node-base/base "base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting with a handful of common methods, like `set`, `get`, `del` and `use`.") +* [generate](https://www.npmjs.com/package/generate): Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the… [more](https://github.com/generate/generate) | [homepage](https://github.com/generate/generate "Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the robustness and configurability of Yeoman, the expressiveness and simplicity of Slush, and more powerful flow control and composability than either.") -## Contributing +### Contributing -Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/verbose/verb/issues/new). +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). -## Building docs - -Generate readme and API documentation with [verb](https://github.com/verbose/verb): - -```sh -$ npm install verb && npm run docs -``` - -Or, if [verb](https://github.com/verbose/verb) is installed globally: - -```sh -$ verb -``` +Please read the [contributing guide](.github/contributing.md) for avice on opening issues, pull requests, and coding standards. -## Running tests +### Running tests Install dev dependencies: @@ -250,18 +296,18 @@ Install dev dependencies: $ npm install -d && npm test ``` -## Author +### Author **Jon Schlinkert** * [github/jonschlinkert](https://github.com/jonschlinkert) * [twitter/jonschlinkert](http://twitter.com/jonschlinkert) -## License +### License Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert). Released under the [MIT license](https://github.com/verbose/verb/blob/master/LICENSE). *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on April 24, 2016._ \ No newline at end of file +_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on July 29, 2016._ \ No newline at end of file From 50aead48ced168890ca09e7425023ab99b28458c Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 15:31:06 -0400 Subject: [PATCH 271/282] add verb stores --- index.js | 36 ++++++------------------------------ lib/utils.js | 46 +++++++++++++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/index.js b/index.js index 2c167f8f..c1a1a62d 100644 --- a/index.js +++ b/index.js @@ -45,6 +45,12 @@ Generate.on('generate.postInit', function(app) { Verb.emit('generate.postInit', app); }); +/** + * Initialize Stores + */ + +utils.stores(Verb.prototype); + /** * Initialize verb data */ @@ -107,36 +113,6 @@ Verb.prototype.initVerb = function(opts) { Verb.emit('verb.postInit', this, this.base); }; -/** - * Expose logging methods - */ - -Object.defineProperty(Verb.prototype, 'log', { - configurable: true, - get: function() { - function log() { - return console.log.bind(console, utils.log.timestamp).apply(console, arguments); - } - log.warn = function(msg) { - return utils.logger('warning', 'yellow').apply(null, arguments); - }; - - log.success = function() { - return utils.logger('success', 'green').apply(null, arguments); - }; - - log.info = function() { - return utils.logger('info', 'cyan').apply(null, arguments); - }; - - log.error = function() { - return utils.logger('error', 'red').apply(null, arguments); - }; - log.__proto__ = utils.log; - return log; - } -}); - /** * Expose custom lookup function for resolving generators */ diff --git a/lib/utils.js b/lib/utils.js index 9f8cda7e..e14b81c5 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -5,23 +5,25 @@ var path = require('path'); var utils = require('lazy-cache')(require); var fn = require; require = utils; +var stores = {}; /** * Lazily required module dependencies */ require('base-runner', 'runner'); -require('config-file'); require('common-middleware', 'middleware'); -require('extend-shallow', 'extend'); +require('config-file'); require('data-store', 'DataStore'); +require('extend-shallow', 'extend'); require('fs-exists-sync', 'exists'); require('generate'); require('get-value', 'get'); require('global-modules', 'gm'); require('gulp-format-md', 'format'); -require('log-utils', 'log'); require('gulp-reflinks', 'reflinks'); +require('log-utils', 'log'); +require('macro-store', 'MacroStore'); require('set-value', 'set'); require('strip-color', 'strip'); require('text-table', 'table'); @@ -29,6 +31,34 @@ require('through2', 'through'); require('yargs-parser', 'parse'); require = fn; +/** + * Initialize stores + */ + +utils.stores = function(proto) { + // create `macros` store + Object.defineProperty(proto, 'macros', { + configurable: true, + set: function(val) { + stores.macros = val; + }, + get: function() { + return stores.macros || (stores.macros = new utils.MacroStore({name: 'verb-macros'})); + } + }); + + // create `app.globals` store + Object.defineProperty(proto, 'globals', { + configurable: true, + set: function(val) { + stores.globals = val; + }, + get: function() { + return stores.globals || (stores.globals = new utils.DataStore('verb-globals')); + } + }); +}; + /** * argv options */ @@ -114,16 +144,6 @@ utils.firstIndex = function(arr, items) { return idx; }; -utils.logger = function(prop, color) { - color = color || 'dim'; - return function(msg) { - var rest = [].slice.call(arguments, 1); - return console.log - .bind(console, utils.log.timestamp, utils.log[prop]) - .apply(console, [utils.log[color](msg), ...rest]); - }; -}; - /** * Expose `utils` modules */ From 62fceec98c58ed75a33623943bccc96f8d8cebf5 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 15:31:29 -0400 Subject: [PATCH 272/282] clean up reflinks in package.json --- package.json | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index b0e59373..3c823dc8 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "gulp-reflinks": "^0.2.0", "lazy-cache": "^2.0.1", "log-utils": "^0.2.1", + "macro-store": "^0.1.0", "set-blocking": "^2.0.0", "set-value": "^0.3.3", "strip-color": "^0.1.0", @@ -90,6 +91,9 @@ "verb": { "toc": true, "layout": "common-minimal", + "tasks": [ + "readme" + ], "plugins": [ "gulp-format-md" ], @@ -97,32 +101,23 @@ "list": [ "assemble", "base", - "generate" + "generate", + "update" ] }, "reflinks": [ "assemble", - "bach", "base", "consolidate", - "generate", "gulp", "handlebars", - "handlebars-helpers", "lodash", - "swig", - "template-helpers", - "templates", - "update", - "verb", - "verb-readme-generator" - ], - "tasks": [ - "readme" + "nunjucks", + "update" ], "sections": false, "lint": { "reflinks": true } } -} \ No newline at end of file +} From 2991d701c9467044422fdec6038b3fe1800c7689 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 16:35:07 -0400 Subject: [PATCH 273/282] lint --- lib/diff.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/diff.js b/lib/diff.js index 07506c6e..3d3ef094 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -35,7 +35,7 @@ module.exports = function(options) { } prev = contents; next(null, file); - }) + }); }; }; From cdd7da56352d31ba41ce0d9f7c1742ddf11a3fd1 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 16:35:20 -0400 Subject: [PATCH 274/282] start scaffolding out `init` task --- lib/commands/defaults.js | 94 ++++++++++++++++++++++++++++++++++++++++ lib/commands/init.js | 87 ++++++++++++++++++++++--------------- lib/generator.js | 53 +++++++++++++--------- lib/utils.js | 30 +++++++++++++ package.json | 16 +++++++ 5 files changed, 225 insertions(+), 55 deletions(-) create mode 100644 lib/commands/defaults.js diff --git a/lib/commands/defaults.js b/lib/commands/defaults.js new file mode 100644 index 00000000..e6d2ca79 --- /dev/null +++ b/lib/commands/defaults.js @@ -0,0 +1,94 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var utils = require('../utils'); +var isProcessed; + +/** + * Persist a value to a namespaced defaults object in package.json. For + * example, if you're using `verb`, the value would be saved to the + * `verb` object. + * + * ```sh + * # display the defaults + * $ app --defaults + * # set a boolean for the current project + * $ app --defaults=toc + * # save the cwd to use for the current project + * $ app --defaults=cwd:foo + * # save the tasks to run for the current project + * $ app --defaults=tasks:readme + * ``` + * + * @name defaults + * @param {Object} app + * @api public + * @cli public + */ + +module.exports = function(app, base, options) { + var ran = false; + return function(val, key, config, next) { + if (ran === true) { + next(); + return; + } + + ran = true; + + // get the keys of properties defined by an `--init` prompt + var keys = app.get('cache.initKeys') || []; + var name = app._name.toLowerCase(); + + if (utils.show(val)) { + var pkgConfig = {}; + pkgConfig[name] = app.pkg.get(name) || {}; + console.error(utils.formatValue(pkgConfig)); + next(); + return; + } + + var pkgPath = path.resolve(app.cwd, 'package.json'); + var pkg = {}; + + if (utils.exists(pkgPath)) { + pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + } + + pkg = merge({}, app.pkg.data, pkg); + app.pkg.del(name); + var orig = pkg[name] || {}; + + // normalize both the old and new values before merging, using + // a schema that is specifically used for normalizing values to + // be written back to package.json + var tmp = app.cli.schema.normalize({config: {}}) || {}; + orig = tmp.config; + + // merge the normalized values + var merged = val; + if (utils.isObject(val) && utils.isObject(orig)) { + merged = merge({}, orig, val); + } + + // show the new value in the console + var show = utils.pick(merged, keys); + app.pkg.logInfo('updated package.json config with', show); + + // update options and `cache.config` + app.set('cache.config', merged); + app.emit('config', merged); + + // update the config property + config[key] = merged; + if (app.pkg.queued === true) { + app.pkg.data = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + } + + // re-set updated config object in `package.json` + app.pkg.set(name, merged); + app.pkg.save(); + app.cli.process(merged, next); + }; +}; diff --git a/lib/commands/init.js b/lib/commands/init.js index d4582d64..166b9bf1 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -17,15 +17,23 @@ var utils = require('../utils'); module.exports = function(app, base, options) { return function(val, key, config, next) { - init(app, {save: false}, function(err, answers) { - if (err) return next(err); - app.set('cache.initKeys', Object.keys(answers.config)); - app.cli.process(answers, next); - }); + prompt(app, next); }; }; -function init(app, options, cb) { +function prompt(app, next) { + ask(app, {save: false}, function(err, answers) { + if (err) { + next(err); + return; + } + var config = (answers && answers.config) || {}; + app.set('cache.initKeys', Object.keys(config)); + app.cli.process(answers, next); + }); +} + +function ask(app, options, cb) { if (typeof app.questions === 'undefined') { cb(new Error('expected base-questions plugin to be defined')); return; @@ -37,54 +45,63 @@ function init(app, options, cb) { } options = utils.extend({}, options, app.options); - questions(app, options); - app.questions.disable('save'); - app.ask('init.choose', function(err, answers) { + app.ask('init.choose', { save: false }, function(err, answers) { if (err) return cb(err); - debug('finished with init.choose "%j"', answers); - - var plugins = arrayify(app, utils.get(answers, 'config.plugins')); - if (plugins.length) { - app.ask('after', { save: false }, function(err, res) { - if (err) return cb(err); - - var answer = utils.get(res, 'after.plugins'); - if (answer === true) { - app.pkg.save(); - app.npm.saveDev(plugins, function(err) { - if (err) return cb(err); - app.pkg.queued = true; - cb(null, answers); - }); - } else { - cb(null, answers); - } - }); - } else { - cb(null, answers); - } + postInit(app, answers, cb); }); }; -function arrayify(app, val) { +function postInit(app, answers, cb) { + var plugins = filter(app, utils.get(answers, 'config.plugins')); + if (!plugins.length) { + cb(null, answers); + + } else { + app.ask('after', { save: false }, function(err, res) { + if (err) return cb(err); + + var answer = utils.get(res, 'after.plugins'); + if (answer === true) { + app.pkg.save(); + app.npm.saveDev(plugins, function(err) { + if (err) return cb(err); + app.pkg.queued = true; + cb(null, answers); + }); + } else { + cb(null, answers); + } + }); + } +} + +function filter(app, val) { if (!val) return []; if (typeof val === 'string') { - return val.split(','); + val = val.split(','); } - var deps = app.pkg.get('devDependencies') || {}; + var devDeps = app.pkg.get('devDependencies') || {}; + var deps = app.pkg.get('dependencies') || {}; var len = val.length; var idx = -1; var res = []; while (++idx < len) { var dep = val[idx]; - if (dep && !deps.hasOwnProperty(dep)) { + if (dep && !devDeps.hasOwnProperty(dep) && !deps.hasOwnProperty(dep)) { res.push(dep); } } return res; } + +/** + * Expose methods + */ + +module.exports.postInit = postInit; +module.exports.prompt = prompt; diff --git a/lib/generator.js b/lib/generator.js index 6995eeb5..3266d812 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -2,6 +2,7 @@ var path = require('path'); var cwd = path.resolve.bind(path, __dirname, 'templates'); +var commands = require('./commands/'); var utils = require('./utils'); var list = require('./list'); var argv = utils.parseArgs(process.argv.slice(2)); @@ -238,16 +239,15 @@ module.exports = function(verb, base) { switch (answers.init) { case 'defaults': - console.log(verb.globals.data); - break; + var defaults = verb.globals.get('defaults'); + commands.init.postInit(verb, { config: defaults }, cb); + return; case 'choose': default: { - console.log('foo'); - break; + commands.init.prompt(verb, cb); + return; } } - - cb(); }); }); @@ -255,10 +255,23 @@ module.exports = function(verb, base) { * Save personal defaults in user home. */ - verb.register('store', function(gen) { - gen.enable('silent'); + verb.register('store', function(app) { + app.enable('silent'); + + app.task('defaults', function(cb) { + var defaults = app.globals.has('defaults.defined'); + var message = defaults + ? 'Would you like to update defaults now?' + : 'No defaults found, would you like to set them now?'; + + app.confirm('defaults', message); + app.ask('defaults', function(err, answers) { + if (err) return cb(err); + + }); + }); - gen.task('del', function(cb) { + app.task('del', function(cb) { var keys = ['name', 'username', 'twitter', 'email']; keys.forEach(function(key) { console.log(verb.log.red(' Deleted:'), key, common.get(key)); @@ -267,7 +280,7 @@ module.exports = function(verb, base) { cb(); }); - gen.task('show', function(cb) { + app.task('show', function(cb) { var keys = ['name', 'username', 'twitter', 'email']; console.log(); keys.forEach(function(key) { @@ -277,20 +290,20 @@ module.exports = function(verb, base) { cb(); }); - gen.task('me', function(cb) { + app.task('me', function(cb) { console.log(); console.log(' Answers to the following questions will be stored in:', verb.log.bold('~/.common-config.json')); console.log(' The stored values will be used later in (your) templates.'); console.log(` To skip a question, just hit ${verb.log.bold('')}`); console.log(); - gen.question('common.name', 'What is your name?'); - gen.question('common.username', 'GitHub username?'); - gen.question('common.url', 'GitHub URL?'); - gen.question('common.twitter', 'Twitter username?'); - gen.question('common.email', 'Email address?'); + app.question('common.name', 'What is your name?'); + app.question('common.username', 'GitHub username?'); + app.question('common.url', 'GitHub URL?'); + app.question('common.twitter', 'Twitter username?'); + app.question('common.email', 'Email address?'); - gen.ask('common', {save: false}, function(err, answers) { + app.ask('common', {save: false}, function(err, answers) { if (err) return cb(err); if (!answers.common) { @@ -314,17 +327,17 @@ module.exports = function(verb, base) { console.log(); console.log(' To delete these values, run:'); console.log(); - console.log(verb.log.bold(' $ gen store:del')); + console.log(verb.log.bold(' $ verb store:del')); console.log(); console.log(' To update these values, run:'); console.log(); - console.log(verb.log.bold(' $ gen store:me')); + console.log(verb.log.bold(' $ verb store:me')); console.log(); cb(); }); }); - gen.task('default', ['me']); + app.task('default', ['me']); }); /** diff --git a/lib/utils.js b/lib/utils.js index e14b81c5..ac542c92 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -22,8 +22,11 @@ require('get-value', 'get'); require('global-modules', 'gm'); require('gulp-format-md', 'format'); require('gulp-reflinks', 'reflinks'); +require('kind-of', 'typeOf'); require('log-utils', 'log'); require('macro-store', 'MacroStore'); +require('merge-deep', 'merge'); +require('object.pick', 'pick'); require('set-value', 'set'); require('strip-color', 'strip'); require('text-table', 'table'); @@ -31,6 +34,33 @@ require('through2', 'through'); require('yargs-parser', 'parse'); require = fn; +/** + * Format a value to be displayed in the command line + */ + +utils.formatValue = function(val) { + return utils.cyan(util.inspect(val, null, 10)); +}; + +/** + * Return true if a value is an object. + */ + +utils.isObject = function(val) { + return utils.typeOf(val) === 'object'; +}; + +/** + * Returns true if `val` is true or is an object with `show: true` + * + * @param {String} `val` + * @return {Boolean} + */ + +utils.show = function(val) { + return utils.isObject(val) && val.show === true; +}; + /** * Initialize stores */ diff --git a/package.json b/package.json index 3c823dc8..a943e5fe 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,12 @@ "global-modules": "^0.2.3", "gulp-format-md": "^0.1.9", "gulp-reflinks": "^0.2.0", + "kind-of": "^3.0.4", "lazy-cache": "^2.0.1", "log-utils": "^0.2.1", "macro-store": "^0.1.0", + "merge-deep": "^3.0.0", + "object.pick": "^1.1.2", "set-blocking": "^2.0.0", "set-value": "^0.3.3", "strip-color": "^0.1.0", @@ -119,5 +122,18 @@ "lint": { "reflinks": true } + }, + "generator": { + "toc": true, + "layout": "default", + "tasks": [ + "readme" + ], + "plugins": [ + "gulp-format-md" + ], + "lint": { + "reflinks": true + } } } From 40e75632beb3511c795afc37b6fd89a86131d65e Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 16:35:51 -0400 Subject: [PATCH 275/282] progress on docs --- .verb.md | 214 ++++++++++++++++++++++++++++++++++++++------------- readme.md | 226 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 328 insertions(+), 112 deletions(-) diff --git a/.verb.md b/.verb.md index 37325f7b..2903ac17 100644 --- a/.verb.md +++ b/.verb.md @@ -1,53 +1,95 @@ A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. +## What is verb? + +**Documentation system** + +Verb is a documentation system that is simple and fast enough to use for [generating a readme][verb-generate-readme] for a GitHub project, yet powerful and smart enough to build the most complex documentation projects around. + **I just want to generate a readme, can verb do this?** -Yes! See [verb-readme-generator][]. +Yes! See [verb-generate-readme][]. + +**Highly pluggable** + +Verb is also highly pluggable, with first-class plugin support. Moreover, verb also supports [gulp][], [base][], [assemble][], [generate][] and [update][] plugins. + +### Why use verb? + +**Why should I use verb, instead of X?** -## Features +Verb is not a _documentation generator_, it's a documentation system that can be used to create documentation generators like [documentation.js](https://github.com/documentationjs/documentation) and [slate](https://github.com/lord/slate). -* Convert markdown templates to markdown files _or_ static HTML -* Use any template engine, such as [handlebars][], [lodash][] or [swig][] -* Use templates, partials, or layouts +For example, [verb-generate-readme][] is a sophisticated readme generator that renders templates using data from the user's environment, like package.json and git config. For API documentation, verb-readme-generator uses template helpers. + +## Highlights + +**Templating** + +* Render templates with any node.js engine, including [handlebars][], [lodash][], [nunjucks][], and all [consolidate][] engines. +* Convert markdown to HTML, or render markdown templates back to markdown (see [.verb.md](#verbmd)) +* Template collections +* Support for "pages", "partials", and "layouts" * Helper support (sync and async!) -* Collections -* Plugins +* Middleware that can be used at any point in the render cycle + +**Helpers** + +* Generate a list of [related links][helpers]{helper-related} to other npm packages +* Resolve [reflinks][helpers]{helper-reflinks} +* More than 150 other helpers available from [template-helpers][] and the [helpers org][github]{helpers} **Plugins** -Verb has first-class plugin support, along with native support for [gulp][] plugins, so you can do things like: +Verb has rich plugin support, along with native support for [gulp plugins][], so if you want to publish a documentation site, you can do things like: * Ignore files marked as "drafts" * CSS minification or reduction -* Spin up a dev server * SASS or LESS compiling and minification * JavaScript minification, reduction or concatenation * Asset copying or renaming * Image compression * HTML minification and linting * RSS feeds +* Anything else you can do with [gulp plugins][] -**Developers** +**Generators** -* Use custom code to your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`) -* Use globally or locally installed verb "generators" -* Support for sub-generators -* Generators can be composed of multiple single-responsibility generators +Verb supports [generate][] generators, which are plugins that are registered by name and can be run by command line or API. Generators can also be published to npm and installed globally or locally. -## What is verb? +See the [generate][] project and [documentation][generate]{docs} for more details. + +## Getting started -Verb is a documentation system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. +### Installing verb -Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. +To use verb's CLI, you will first need to install it globally. You can do that now with the following command: + +```sh +$ npm --global install verb +``` + +This adds the `verb` command to your system path, allowing it to be run from any directory. + +### init + +If you want to test that verb works, while also initializing settings for the current project, run: + +```sh +$ verb init +``` + +### Generators + +All of the "real work" in verb is accomplished using generators. Now that `verb` is installed, you can run any [generator found on npm](https://www.npmjs.com/search?q=generategenerator) -## Quickstart **Install readme generator** -The `verb-readme-generator` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: +The `verb-generate-readme` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: ```sh -$ npm i -g verb-readme-generator +$ npm i -g verb-generate-readme ``` **.verb.md** @@ -66,16 +108,30 @@ Next, create a `verbfile.js` with the following code: ```js module.exports = function(app) { - app.extendWith('verb-readme-generator'); + app.extendWith('verb-generate-readme'); - // call the `readme` task from verb-readme-generator + // call the `readme` task from verb-generate-readme app.task('default', ['readme']); }; ``` -## verbfile +## verbfile.js + +Write any custom code in your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`). + +**Heads up!** + +_If you're using Verb's CLI, `verbfile.js` should export an instance of Verb or a function._ + +**Function** + +When a function is exported, we refer to these as [verb generators](#generators). Generators can be locally or globally installed, and can consist of entirely custom code, or created using other generators and plugins like building blocks. -Your project's `verbfile.js` should either export an instance of Verb or a function. +```js +module.exports = function(app) { + // "app" is an instance of verb created by Verb's CLI for this file +}; +``` **Instance** @@ -86,23 +142,30 @@ var app = verb(); module.exports = app; ``` -**Function** +## Config -When a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). +Options can be defined in `package.json` on the `verb` object. -```js -module.exports = function(verb) { - // "private" verb instance is created for this verbfile -}; -``` +**Example** -## Config +Specify special data to be used in templates for a project: + +```json +{ + "name": "my-project", + "verb": { + "data": { + "twitter": "jonschlinkert" + } + } +} +``` -Options can be defined in `package.json` on the `verb` object. See the [config docs](docs/config.md) for more details. +See the [config docs](docs/config.md) for more details. -## CLI +## Command line -### Tasks +### Built-in tasks Verb only has a few built-in [tasks](docs/tasks.md), but these will externalized to [generators](docs/generators.md) in the near future. @@ -114,7 +177,6 @@ List currently installed verb [generators](docs/generators.md). $ verb new ``` - #### new Generate a new [verbfile.js](docs/verbfile.js) in the current working directory. @@ -123,30 +185,55 @@ Generate a new [verbfile.js](docs/verbfile.js) in the current working directory. $ verb new ``` +### Config flags -### Options +There are a few flags dedicated to setting and persisting configuration settings. -There are several generic flags for setting options from the command line: `option`, `data`, `config` and `save`. Beyond these there are also a number of specialized flags described below. +**Flag** | **Type** | **Scope** | **Description** | **Location** +--- | --- | --- +`--option` | In-memory | Global | Set an option to use in the current session | N/A +`--data` | In-memory | Global | Add data to the context for rendering templates in the current session | N/A +`--config` | Persisted | Project | Persist a configuration setting _to use on the current project_ every time `verb` is run | `~/verb-globals.json` +`--set` | Persisted | Persist a configuration setting _to use on every project_ every time `verb` is run | The `verb` object in package.json -- `--option`: set options in memory -- `--data`: set data to be passed to templates in memory -- `--config`: persist options to package.json -- `--save`: persist options to a global config store -#### --config +### Config examples + +#### Settings Persist configuration settings to the `verb` object in `package.json`. ```sh -$ verb --config +$ verb --config= ``` -Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. +**Related links** + +Persist a list of project names to generate [related links][helpers]{helper-related}: + +```sh +$ verb --config=related.list:generate,assemble,update +``` +Updates the `verb` object in package.json with the following: + +```json +{ + "verb": { + "related": { + "list": [ + "generate", + "assemble", + "update" + ] + } + } +} +``` -#### --config=tasks +#### Tasks -Set the default tasks to run for a project: +Specify tasks to run on a project: ```sh $ verb --config=tasks: @@ -154,23 +241,34 @@ $ verb --config=tasks: **Example** -```sh -# single task -$ verb --config=tasks:foo +Automatically run [verb-generate-readme][] on a project: -# array of tasks +```sh +$ verb --config=tasks:readme +# or, specify an array of tasks $ verb --config=tasks:foo,bar,baz ``` +Updates the `verb` object in package.json with the following: + +```json +{ + "verb": { + "tasks": ["readme"] + } +} +``` + +### Other flags + #### --init -Initialize a `verb` config object in the package.json of the current project. +Initialize a `verb` config object in package.json of the current project. ```sh $ verb --init ``` - #### --save Persist options values to the global data store for `app` ([verb][], [assemble][], [generate][], [update][], etc) @@ -215,8 +313,18 @@ Display the currently defined cwd: $ verb --cwd ``` + ## API {%= apidocs("index.js") %} ## Upgrading {%= include("upgrading") %} + + +[verb-generate-readme]: https://github.com/verbose/verb-generate-readme +[generate]: https://github.com/generate/generate +[verb]: https://github.com/verbose/verb +[verbose]: https://github.com/verbose +[helpers]: https://github.com/helpers/ +[github]: https://github.com/ +[gulp-plugins]: https://www.npmjs.com/browse/keyword/gulpplugin \ No newline at end of file diff --git a/readme.md b/readme.md index f4eb9ea5..d70dc74c 100644 --- a/readme.md +++ b/readme.md @@ -7,18 +7,24 @@

Documentation build system for GitHub projects, powered by node.js. Verb has full support for gulp and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! -* [Features](#features) * [What is verb?](#what-is-verb) -* [Quickstart](#quickstart) -* [verbfile](#verbfile) + - [Why use verb?](#why-use-verb) +* [Highlights](#highlights) +* [Getting started](#getting-started) + - [Installing verb](#installing-verb) + - [init](#init) + - [Generators](#generators) +* [verbfile.js](#verbfilejs) * [Config](#config) -* [CLI](#cli) - - [Tasks](#tasks) +* [Command line](#command-line) + - [Built-in tasks](#built-in-tasks) + [list](#list) + [new](#new) - - [Options](#options) - + [--config](#--config) - + [--config=tasks](#--configtasks) + - [Config flags](#config-flags) + - [Config examples](#config-examples) + + [Settings](#settings) + + [Tasks](#tasks) + - [Other flags](#other-flags) + [--init](#--init) + [--save](#--save) + [--file](#--file) @@ -36,54 +42,95 @@ _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc]( A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. +## What is verb? + +**Documentation system** + +Verb is a documentation system that is simple and fast enough to use for [generating a readme](https://github.com/verbose/verb-generate-readme) for a GitHub project, yet powerful and smart enough to build the most complex documentation projects around. + **I just want to generate a readme, can verb do this?** -Yes! See [verb-readme-generator](https://github.com/verbose/verb-readme-generator). +Yes! See [verb-generate-readme](https://github.com/verbose/verb-generate-readme). + +**Highly pluggable** + +Verb is also highly pluggable, with first-class plugin support. Moreover, verb also supports [gulp](http://gulpjs.com), [base](https://github.com/node-base/base), [assemble](https://github.com/assemble/assemble), [generate](https://github.com/generate/generate) and [update](https://github.com/update/update) plugins. + +### Why use verb? + +**Why should I use verb, instead of X?** -## Features +Verb is not a _documentation generator_, it's a documentation system that can be used to create documentation generators like [documentation.js](https://github.com/documentationjs/documentation) and [slate](https://github.com/lord/slate). -* Convert markdown templates to markdown files _or_ static HTML -* Use any template engine, such as [handlebars](http://www.handlebarsjs.com/), [lodash](https://lodash.com/) or [swig](https://github.com/paularmstrong/swig) -* Use templates, partials, or layouts +For example, [verb-generate-readme](https://github.com/verbose/verb-generate-readme) is a sophisticated readme generator that renders templates using data from the user's environment, like package.json and git config. For API documentation, verb-readme-generator uses template helpers. + +## Highlights + +**Templating** + +* Render templates with any node.js engine, including [handlebars](http://www.handlebarsjs.com/), [lodash](https://lodash.com/), [nunjucks](https://github.com/mozilla/nunjucks), and all [consolidate](https://github.com/visionmedia/consolidate.js) engines. +* Convert markdown to HTML, or render markdown templates back to markdown (see [.verb.md](#verbmd)) +* Template collections +* Support for "pages", "partials", and "layouts" * Helper support (sync and async!) -* Collections -* Plugins +* Middleware that can be used at any point in the render cycle + +**Helpers** + +* Generate a list of [related links](https://github.com/helpers/helper-related) to other npm packages +* Resolve [reflinks](https://github.com/helpers/helper-reflinks) +* More than 150 other helpers available from [template-helpers](https://github.com/jonschlinkert/template-helpers) and the [helpers org](https://github.com/helpers) **Plugins** -Verb has first-class plugin support, along with native support for [gulp](http://gulpjs.com) plugins, so you can do things like: +Verb has rich plugin support, along with native support for [gulp plugins][], so if you want to publish a documentation site, you can do things like: * Ignore files marked as "drafts" * CSS minification or reduction -* Spin up a dev server * SASS or LESS compiling and minification * JavaScript minification, reduction or concatenation * Asset copying or renaming * Image compression * HTML minification and linting * RSS feeds +* Anything else you can do with [gulp plugins][] -**Developers** +**Generators** -* Use custom code to your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`) -* Use globally or locally installed verb "generators" -* Support for sub-generators -* Generators can be composed of multiple single-responsibility generators +Verb supports [generate](https://github.com/generate/generate) generators, which are plugins that are registered by name and can be run by command line or API. Generators can also be published to npm and installed globally or locally. -## What is verb? +See the [generate](https://github.com/generate/generate) project and [documentation](https://github.com/generate/generatedocs) for more details. -Verb is a documentation system that is simple and fast enough to use for generating a readme for a GitHub project, but powerful and smart enough to build the most complex documentation projects around. +## Getting started -Verb is also highly pluggable, with first-class support for instance plugins, pipeline (gulp/vinyl) plugins, routes and middleware, any template engine, helpers, and more. +### Installing verb -## Quickstart +To use verb's CLI, you will first need to install it globally. You can do that now with the following command: + +```sh +$ npm --global install verb +``` + +This adds the `verb` command to your system path, allowing it to be run from any directory. + +### init + +If you want to test that verb works, while also initializing settings for the current project, run: + +```sh +$ verb init +``` + +### Generators + +All of the "real work" in verb is accomplished using generators. Now that `verb` is installed, you can run any [generator found on npm](https://www.npmjs.com/search?q=generategenerator) **Install readme generator** -The `verb-readme-generator` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: +The `verb-generate-readme` is a good example of what's possible with verb, and it's an easy way to get started. You can install the library with the following command: ```sh -$ npm i -g verb-readme-generator +$ npm i -g verb-generate-readme ``` **.verb.md** @@ -102,16 +149,30 @@ Next, create a `verbfile.js` with the following code: ```js module.exports = function(app) { - app.extendWith('verb-readme-generator'); + app.extendWith('verb-generate-readme'); - // call the `readme` task from verb-readme-generator + // call the `readme` task from verb-generate-readme app.task('default', ['readme']); }; ``` -## verbfile +## verbfile.js + +Write any custom code in your project's `verbfile.js` (like `gulpfile.js` or `Gruntfile.js`). -Your project's `verbfile.js` should either export an instance of Verb or a function. +**Heads up!** + +_If you're using Verb's CLI, `verbfile.js` should export an instance of Verb or a function._ + +**Function** + +When a function is exported, we refer to these as [verb generators](#generators). Generators can be locally or globally installed, and can consist of entirely custom code, or created using other generators and plugins like building blocks. + +```js +module.exports = function(app) { + // "app" is an instance of verb created by Verb's CLI for this file +}; +``` **Instance** @@ -122,23 +183,30 @@ var app = verb(); module.exports = app; ``` -**Function** +## Config -When a function is exported, we refer to these as [verb generators](#verb-generators). Verb generators can be locally or globally installed, and can be composed with other verb generators and/or [sub-generators](#sub-generators). +Options can be defined in `package.json` on the `verb` object. -```js -module.exports = function(verb) { - // "private" verb instance is created for this verbfile -}; -``` +**Example** -## Config +Specify special data to be used in templates for a project: + +```json +{ + "name": "my-project", + "verb": { + "data": { + "twitter": "jonschlinkert" + } + } +} +``` -Options can be defined in `package.json` on the `verb` object. See the [config docs](docs/config.md) for more details. +See the [config docs](docs/config.md) for more details. -## CLI +## Command line -### Tasks +### Built-in tasks Verb only has a few built-in [tasks](docs/tasks.md), but these will externalized to [generators](docs/generators.md) in the near future. @@ -158,28 +226,55 @@ Generate a new [verbfile.js](docs/verbfile.js) in the current working directory. $ verb new ``` -### Options +### Config flags -There are several generic flags for setting options from the command line: `option`, `data`, `config` and `save`. Beyond these there are also a number of specialized flags described below. +There are a few flags dedicated to setting and persisting configuration settings. -* `--option`: set options in memory -* `--data`: set data to be passed to templates in memory -* `--config`: persist options to package.json -* `--save`: persist options to a global config store +**Flag** | **Type** | **Scope** | **Description** | **Location** -#### --config +--- | --- | --- +`--option` | In-memory | Global | Set an option to use in the current session | N/A +`--data` | In-memory | Global | Add data to the context for rendering templates in the current session | N/A +`--config` | Persisted | Project | Persist a configuration setting _to use on the current project_ every time `verb` is run | `~/verb-globals.json` +`--set` | Persisted | Persist a configuration setting _to use on every project_ every time `verb` is run | The `verb` object in package.json + +### Config examples + +#### Settings Persist configuration settings to the `verb` object in `package.json`. ```sh -$ verb --config +$ verb --config= +``` + +**Related links** + +Persist a list of project names to generate [related links](https://github.com/helpers/helper-related): + +```sh +$ verb --config=related.list:generate,assemble,update ``` -Most of the above CLI commands can be prefixed with `--config` to persist the value to package.json. +Updates the `verb` object in package.json with the following: + +```json +{ + "verb": { + "related": { + "list": [ + "generate", + "assemble", + "update" + ] + } + } +} +``` -#### --config=tasks +#### Tasks -Set the default tasks to run for a project: +Specify tasks to run on a project: ```sh $ verb --config=tasks: @@ -187,17 +282,29 @@ $ verb --config=tasks: **Example** -```sh -# single task -$ verb --config=tasks:foo +Automatically run [verb-generate-readme](https://github.com/verbose/verb-generate-readme) on a project: -# array of tasks +```sh +$ verb --config=tasks:readme +# or, specify an array of tasks $ verb --config=tasks:foo,bar,baz ``` +Updates the `verb` object in package.json with the following: + +```json +{ + "verb": { + "tasks": ["readme"] + } +} +``` + +### Other flags + #### --init -Initialize a `verb` config object in the package.json of the current project. +Initialize a `verb` config object in package.json of the current project. ```sh $ verb --init @@ -281,6 +388,7 @@ $ npm cache clean && npm i -g verb * [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit") * [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://github.com/node-base/base) | [homepage](https://github.com/node-base/base "base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting with a handful of common methods, like `set`, `get`, `del` and `use`.") * [generate](https://www.npmjs.com/package/generate): Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the… [more](https://github.com/generate/generate) | [homepage](https://github.com/generate/generate "Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the robustness and configurability of Yeoman, the expressiveness and simplicity of Slush, and more powerful flow control and composability than either.") +* [update](https://www.npmjs.com/package/update): Be scalable! Update is a new, open source developer framework and CLI for automating updates… [more](https://github.com/update/update) | [homepage](https://github.com/update/update "Be scalable! Update is a new, open source developer framework and CLI for automating updates of any kind in code projects.") ### Contributing From f7fea8178f092b20d397455590ff9d66ef82436b Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 16:47:40 -0400 Subject: [PATCH 276/282] previewing docs --- .verb.md | 5 ++++ lib/tasks.js | 1 + package.json | 8 ++++-- readme.md | 79 ++++++++++++++++++++++++++++++---------------------- 4 files changed, 57 insertions(+), 36 deletions(-) diff --git a/.verb.md b/.verb.md index 2903ac17..51e73de5 100644 --- a/.verb.md +++ b/.verb.md @@ -59,6 +59,11 @@ Verb supports [generate][] generators, which are plugins that are registered by See the [generate][] project and [documentation][generate]{docs} for more details. + +## Table of Contents + + + ## Getting started ### Installing verb diff --git a/lib/tasks.js b/lib/tasks.js index 7a87923f..c3054c62 100644 --- a/lib/tasks.js +++ b/lib/tasks.js @@ -41,6 +41,7 @@ module.exports = function(app, ctx, argv) { isDefaults = true; task = 'defaults:diff'; } + if (isDefaults === true) { app.enable('silent'); } diff --git a/package.json b/package.json index a943e5fe..89c301fd 100644 --- a/package.json +++ b/package.json @@ -92,8 +92,12 @@ ] }, "verb": { - "toc": true, - "layout": "common-minimal", + "toc": { + "render": true, + "method": "preWrite", + "maxdepth": 3 + }, + "layout": "app", "tasks": [ "readme" ], diff --git a/readme.md b/readme.md index d70dc74c..1821338a 100644 --- a/readme.md +++ b/readme.md @@ -1,44 +1,17 @@ -# verb [![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) -

+

+ Documentation build system for GitHub projects, powered by node.js. Verb has full support for gulp and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! -* [What is verb?](#what-is-verb) - - [Why use verb?](#why-use-verb) -* [Highlights](#highlights) -* [Getting started](#getting-started) - - [Installing verb](#installing-verb) - - [init](#init) - - [Generators](#generators) -* [verbfile.js](#verbfilejs) -* [Config](#config) -* [Command line](#command-line) - - [Built-in tasks](#built-in-tasks) - + [list](#list) - + [new](#new) - - [Config flags](#config-flags) - - [Config examples](#config-examples) - + [Settings](#settings) - + [Tasks](#tasks) - - [Other flags](#other-flags) - + [--init](#--init) - + [--save](#--save) - + [--file](#--file) - + [--cwd](#--cwd) -* [API](#api) -* [Upgrading](#upgrading) -* [About](#about) - - [Related projects](#related-projects) - - [Contributing](#contributing) - - [Running tests](#running-tests) - - [Author](#author) - - [License](#license) +# verb -_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ +[![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) + +![verb demo](https://raw.githubusercontent.com/verbose/verb/master/docs/demo.gif) A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. @@ -101,6 +74,32 @@ Verb supports [generate](https://github.com/generate/generate) generators, which See the [generate](https://github.com/generate/generate) project and [documentation](https://github.com/generate/generatedocs) for more details. +## Table of Contents + +* [Getting started](#getting-started) + - [Installing verb](#installing-verb) + - [init](#init) + - [Generators](#generators) +* [verbfile.js](#verbfilejs) +* [Config](#config) +* [Command line](#command-line) + - [Built-in tasks](#built-in-tasks) + - [Config flags](#config-flags) + - [Config examples](#config-examples) + - [Other flags](#other-flags) +* [API](#api) + - [Verb](#verb) +* [Upgrading](#upgrading) +* [About](#about) + - [Related projects](#related-projects) + - [Community](#community) + - [Contributing](#contributing) + - [Running tests](#running-tests) + - [Author](#author) + - [License](#license) + +_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ + ## Getting started ### Installing verb @@ -390,6 +389,18 @@ $ npm cache clean && npm i -g verb * [generate](https://www.npmjs.com/package/generate): Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the… [more](https://github.com/generate/generate) | [homepage](https://github.com/generate/generate "Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the robustness and configurability of Yeoman, the expressiveness and simplicity of Slush, and more powerful flow control and composability than either.") * [update](https://www.npmjs.com/package/update): Be scalable! Update is a new, open source developer framework and CLI for automating updates… [more](https://github.com/update/update) | [homepage](https://github.com/update/update "Be scalable! Update is a new, open source developer framework and CLI for automating updates of any kind in code projects.") +### Community + +Are you using [Generate](https://github.com/generate/generate) in your project? Have you published a [generator](https://github.com/generate/generate/blob/master/docs/generators.md) and want to share your project with the world? + +Here are some suggestions! + +* If you get like Generate and want to tweet about it, please feel free to mention `@generatejs` or use the `#generatejs` hashtag +* Show your love by starring [Generate](https://github.com/generate/generate) and `verb` +* Get implementation help on [StackOverflow](http://stackoverflow.com/questions/tagged/generate) (please use the `generatejs` tag in questions) +* **Gitter** Discuss Generate with us on [Gitter](https://gitter.im/generate/generate) +* If you publish an generator, thank you! To make your project as discoverable as possible, please add the keyword `generategenerator` to package.json. + ### Contributing Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). @@ -418,4 +429,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on July 29, 2016._ \ No newline at end of file +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.28, on July 29, 2016._ \ No newline at end of file From 20f8a0d36e57a57a3b2faa7a6eea85e3aee4eaa0 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Fri, 29 Jul 2016 16:56:18 -0400 Subject: [PATCH 277/282] fix branch --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 1821338a..226148df 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@

- +

From 01c8181e2c8a0cd35d37dcdded0cdee29a47ad8c Mon Sep 17 00:00:00 2001 From: Ivan Erceg Date: Sun, 8 Jan 2017 02:12:47 -0300 Subject: [PATCH 278/282] Fix bad syntax in verbfile.js template --- lib/templates/verbfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/templates/verbfile.js b/lib/templates/verbfile.js index 7818a6a8..9b3e91b3 100644 --- a/lib/templates/verbfile.js +++ b/lib/templates/verbfile.js @@ -4,9 +4,9 @@ layout: false 'use strict'; module.exports = function(verb) { - verb use(require('verb-generate-readme')); + verb.use(require('verb-generate-readme')); verb.helper('foo', function(str) { return str; }); verb.task('default', ['readme']); -}; \ No newline at end of file +}; From 533b41921dcf59977d81bbc0f5f78cc278655617 Mon Sep 17 00:00:00 2001 From: Aaron K Redshaw Date: Fri, 26 Oct 2018 14:58:43 -0700 Subject: [PATCH 279/282] mostly fixed links and deleted those without a reference I could find --- readme.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index 226148df..5121d4a7 100644 --- a/readme.md +++ b/readme.md @@ -1,21 +1,19 @@

- +

-Documentation build system for GitHub projects, powered by node.js. Verb has full support for gulp and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! +Verb is a documentation build system for GitHub projects powered by node.js. Verb has full support for [gulp](https://gulpjs.com/) and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! # verb [![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) -![verb demo](https://raw.githubusercontent.com/verbose/verb/master/docs/demo.gif) - A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. -## What is verb? +## What can Verb do? **Documentation system** @@ -42,7 +40,7 @@ For example, [verb-generate-readme](https://github.com/verbose/verb-generate-rea **Templating** * Render templates with any node.js engine, including [handlebars](http://www.handlebarsjs.com/), [lodash](https://lodash.com/), [nunjucks](https://github.com/mozilla/nunjucks), and all [consolidate](https://github.com/visionmedia/consolidate.js) engines. -* Convert markdown to HTML, or render markdown templates back to markdown (see [.verb.md](#verbmd)) +* Convert markdown to HTML, or render markdown templates back to markdown * Template collections * Support for "pages", "partials", and "layouts" * Helper support (sync and async!) @@ -56,7 +54,7 @@ For example, [verb-generate-readme](https://github.com/verbose/verb-generate-rea **Plugins** -Verb has rich plugin support, along with native support for [gulp plugins][], so if you want to publish a documentation site, you can do things like: +Verb has rich plugin support, along with native support for [gulp plugins](https://gulpjs.com/plugins/), so if you want to publish a documentation site, you can do things like: * Ignore files marked as "drafts" * CSS minification or reduction @@ -66,13 +64,13 @@ Verb has rich plugin support, along with native support for [gulp plugins][], so * Image compression * HTML minification and linting * RSS feeds -* Anything else you can do with [gulp plugins][] +* Anything else you can do with [gulp plugins](https://gulpjs.com/plugins/) **Generators** Verb supports [generate](https://github.com/generate/generate) generators, which are plugins that are registered by name and can be run by command line or API. Generators can also be published to npm and installed globally or locally. -See the [generate](https://github.com/generate/generate) project and [documentation](https://github.com/generate/generatedocs) for more details. +See the [generate](https://github.com/generate/generate) project for more details. ## Table of Contents @@ -107,7 +105,7 @@ _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc]( To use verb's CLI, you will first need to install it globally. You can do that now with the following command: ```sh -$ npm --global install verb +$ npm --global install verb ``` This adds the `verb` command to your system path, allowing it to be run from any directory. @@ -169,7 +167,7 @@ When a function is exported, we refer to these as [verb generators](#generators) ```js module.exports = function(app) { - // "app" is an instance of verb created by Verb's CLI for this file + // "app" is an instance of verb created by Verb's CLI for this file }; ``` @@ -429,4 +427,4 @@ Released under the [MIT license](https://github.com/verbose/verb/blob/master/LIC *** -_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.28, on July 29, 2016._ \ No newline at end of file +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.28, on July 29, 2016._ From e12a778cd48ae326d2f1088864b95aedb3080cb4 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Thu, 20 Jul 2023 09:05:49 -0400 Subject: [PATCH 280/282] update dotfiles --- .editorconfig | 4 -- .eslintrc.js | 107 +++++++++++++++++++++++++++++++++++++++++++ .eslintrc.json | 122 ------------------------------------------------- .gitignore | 23 +++++----- .travis.yml | 14 ------ gulpfile.js | 14 +++--- package.json | 6 +-- 7 files changed, 129 insertions(+), 161 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json delete mode 100644 .travis.yml diff --git a/.editorconfig b/.editorconfig index 818e0724..62391077 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,3 @@ charset = utf-8 indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true - -[{**/{actual,fixtures,expected,templates}/**,*.md}] -trim_trailing_whitespace = false -insert_final_newline = false \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..ea11918a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,107 @@ +module.exports = { + extends: 'eslint:recommended', + env: { + commonjs: true, + es2021: true, + node: true + }, + parserOptions: { + sourceType: 'script', + ecmaVersion: 'latest' + }, + rules: { + 'eqeqeq': 1, + 'indent': [1, 2], + 'linebreak-style': [1, 'unix'], + 'no-alert': 1, + 'no-async-promise-executor': 0, + 'no-case-declarations': 1, + 'no-console': [1, { 'allow': ['warn', 'error'] }], + 'no-constant-condition': [1, { 'checkLoops': false }], + 'no-dupe-args': 1, + 'no-dupe-keys': 1, + 'no-else-return': 0, + 'no-empty-character-class': 0, + 'no-empty-function': 0, + 'no-empty-pattern': 0, + 'no-empty': [1, { 'allowEmptyCatch': true }], + 'no-eval': 1, + 'no-extra-bind': 1, + 'no-extra-boolean-cast': 1, + 'no-extra-label': 1, + 'no-extra-parens': 1, + 'no-extra-semi': 1, + 'no-fallthrough': 1, + 'no-floating-decimal': 1, + 'no-implicit-coercion': 1, + 'no-implicit-globals': 1, + 'no-implied-eval': 1, + 'no-inner-declarations': 1, + 'no-invalid-this': 1, + 'no-irregular-whitespace': 0, + 'no-iterator': 1, + 'no-label-var': 1, + 'no-labels': 1, + 'no-lone-blocks': 1, + 'no-loop-func': 1, + 'no-mixed-requires': 1, + 'no-multi-assign': 1, + 'no-multi-spaces': 1, + 'no-multi-str': 1, + 'no-multiple-empty-lines': 1, + 'no-native-reassign': 1, + 'no-negated-condition': 1, + 'no-new-func': 1, + 'no-new-object': 1, + 'no-new-require': 1, + 'no-new-symbol': 1, + 'no-new-wrappers': 1, + 'no-new': 1, + 'no-octal-escape': 1, + 'no-octal': 1, + 'no-path-concat': 1, + 'no-proto': 1, + 'no-prototype-builtins': 0, + 'no-redeclare': 1, + 'no-return-assign': 1, + 'no-return-await': 2, + 'no-script-url': 1, + 'no-self-assign': 1, + 'no-self-compare': 1, + 'no-sequences': 1, + 'no-shadow-restricted-names': 1, + 'no-throw-literal': 1, + 'no-undef-init': 1, + 'no-undef': 1, + 'no-unexpected-multiline': 1, + 'no-unreachable': 1, + 'no-unsafe-assignment': 0, + 'no-unsafe-call': 0, + 'no-unsafe-finally': 0, + 'no-unsafe-member-access': 0, + 'no-unsafe-negation': 0, + 'no-unsafe-optional-chaining': 0, + 'no-unsafe-return': 0, + 'no-unused-vars': [1, { 'args': 'none' }], + 'no-use-before-define': 1, + 'no-useless-catch': 0, + 'no-useless-escape': 0, + 'no-void': 1, + 'no-warning-comments': 1, + 'no-with': 1, + 'prefer-promise-reject-errors': 1, + 'quotes': [1, 'single'], + 'radix': 1, + 'rest-spread-spacing': 1, + 'semi-spacing': 1, + 'semi-style': 1, + 'semi': [1, 'always'], + 'space-before-blocks': 1, + 'space-in-parens': 1, + 'space-infix-ops': 1, + 'space-unary-ops': 1, + 'spaced-comment': 1, + 'switch-colon-spacing': 1, + 'unicode-bom': 1, + } +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 948dbdb6..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "ecmaFeatures": { - "modules": true, - "experimentalObjectRestSpread": true - }, - - "env": { - "browser": false, - "es6": true, - "node": true, - "mocha": true - }, - - "globals": { - "document": false, - "navigator": false, - "window": false - }, - - "rules": { - "accessor-pairs": 2, - "arrow-spacing": [2, { "before": true, "after": true }], - "block-spacing": [2, "always"], - "brace-style": [2, "1tbs", { "allowSingleLine": true }], - "comma-dangle": [2, "never"], - "comma-spacing": [2, { "before": false, "after": true }], - "comma-style": [2, "last"], - "constructor-super": 2, - "curly": [2, "multi-line"], - "dot-location": [2, "property"], - "eol-last": 2, - "eqeqeq": [2, "allow-null"], - "generator-star-spacing": [2, { "before": true, "after": true }], - "handle-callback-err": [2, "^(err|error)$" ], - "indent": [2, 2, { "SwitchCase": 1 }], - "key-spacing": [2, { "beforeColon": false, "afterColon": true }], - "keyword-spacing": [2, { "before": true, "after": true }], - "new-cap": [2, { "newIsCap": true, "capIsNew": false }], - "new-parens": 2, - "no-array-constructor": 2, - "no-caller": 2, - "no-class-assign": 2, - "no-cond-assign": 2, - "no-const-assign": 2, - "no-control-regex": 2, - "no-debugger": 2, - "no-delete-var": 2, - "no-dupe-args": 2, - "no-dupe-class-members": 2, - "no-dupe-keys": 2, - "no-duplicate-case": 2, - "no-empty-character-class": 2, - "no-eval": 2, - "no-ex-assign": 2, - "no-extend-native": 2, - "no-extra-bind": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": [2, "functions"], - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-func-assign": 2, - "no-implied-eval": 2, - "no-inner-declarations": [2, "functions"], - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-iterator": 2, - "no-label-var": 2, - "no-labels": 2, - "no-lone-blocks": 2, - "no-mixed-spaces-and-tabs": 2, - "no-multi-spaces": 2, - "no-multi-str": 2, - "no-multiple-empty-lines": [2, { "max": 1 }], - "no-native-reassign": 0, - "no-negated-in-lhs": 2, - "no-new": 2, - "no-new-func": 2, - "no-new-object": 2, - "no-new-require": 2, - "no-new-wrappers": 2, - "no-obj-calls": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-proto": 0, - "no-redeclare": 2, - "no-regex-spaces": 2, - "no-return-assign": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-shadow-restricted-names": 2, - "no-spaced-func": 2, - "no-sparse-arrays": 2, - "no-this-before-super": 2, - "no-throw-literal": 2, - "no-trailing-spaces": 0, - "no-undef": 2, - "no-undef-init": 2, - "no-unexpected-multiline": 2, - "no-unneeded-ternary": [2, { "defaultAssignment": false }], - "no-unreachable": 2, - "no-unused-vars": [2, { "vars": "all", "args": "none" }], - "no-useless-call": 0, - "no-with": 2, - "one-var": [0, { "initialized": "never" }], - "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], - "padded-blocks": [0, "never"], - "quotes": [2, "single", "avoid-escape"], - "radix": 2, - "semi": [2, "always"], - "semi-spacing": [2, { "before": false, "after": true }], - "space-before-blocks": [2, "always"], - "space-before-function-paren": [2, "never"], - "space-in-parens": [2, "never"], - "space-infix-ops": 2, - "space-unary-ops": [2, { "words": true, "nonwords": false }], - "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], - "use-isnan": 2, - "valid-typeof": 2, - "wrap-iife": [2, "any"], - "yoda": [2, "never"] - } -} diff --git a/.gitignore b/.gitignore index f033cb7a..d15d1bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,23 @@ # always ignore files *.DS_Store *.sublime-* +*.code-* +*.log + +# always ignore dirs +temp +tmp +vendor # test related, or directories generated by tests -test/actual -actual +.nyc* coverage -# npm +# package managers node_modules -npm-debug.log +package-lock.json +yarn.lock # misc -_gh_pages -benchmark -bower_components -vendor -temp -tmp _draft -_drafts +TODO.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3932eaa2..00000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -sudo: false -language: node_js -node_js: - - '6' - - '5' - - '4' - - '0.12' - - '0.10' -matrix: - fast_finish: true - allow_failures: - - node_js: '4' - - node_js: '0.10' - - node_js: '0.12' diff --git a/gulpfile.js b/gulpfile.js index 0ea89019..207b3d0f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,12 +1,12 @@ 'use strict'; -var gulp = require('gulp'); -var mocha = require('gulp-mocha'); -var istanbul = require('gulp-istanbul'); -var eslint = require('gulp-eslint'); -var unused = require('gulp-unused'); +const gulp = require('gulp'); +const mocha = require('gulp-mocha'); +const istanbul = require('gulp-istanbul'); +const eslint = require('gulp-eslint'); +const unused = require('gulp-unused'); -var lib = ['index.js', 'lib/**/*.js', 'bin/*.js']; +const lib = ['index.js', 'lib/**/*.js', 'bin/*.js']; gulp.task('coverage', function() { return gulp.src(lib) @@ -28,7 +28,7 @@ gulp.task('lint', function() { gulp.task('unused', function() { return gulp.src(['index.js', 'lib/**/*.js', 'bin/*.js']) - .pipe(unused({keys: Object.keys(require('./lib/utils.js'))})) + .pipe(unused({keys: Object.keys(require('./lib/utils.js'))})); }); gulp.task('default', ['test', 'lint']); diff --git a/package.json b/package.json index 89c301fd..58fa41f5 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "base-runner": "^0.8.2", "common-middleware": "^0.3.0", "config-file": "^0.3.2", - "data-store": "^0.16.1", + "data-store": "^4.0.3", "debug": "^2.2.0", "diff": "^2.2.3", "engine-base": "^0.1.2", @@ -41,7 +41,7 @@ "generate": "^0.9.8", "get-value": "^2.0.6", "global-modules": "^0.2.3", - "gulp-format-md": "^0.1.9", + "gulp-format-md": "^2.0.0", "gulp-reflinks": "^0.2.0", "kind-of": "^3.0.4", "lazy-cache": "^2.0.1", @@ -50,7 +50,7 @@ "merge-deep": "^3.0.0", "object.pick": "^1.1.2", "set-blocking": "^2.0.0", - "set-value": "^0.3.3", + "set-value": "^4.1.0", "strip-color": "^0.1.0", "text-table": "^0.2.0", "through2": "^2.0.1", From 40042fe60a59374dda1e0a0e0e63ccbb73b83b60 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Thu, 20 Jul 2023 10:42:10 -0400 Subject: [PATCH 281/282] lint --- .eslintrc.js | 73 +++++++++++++--- bin/verb.js | 70 +++++++-------- index.js | 153 +------------------------------- lib/Verb.js | 129 +++++++++++++++++++++++++++ lib/commands.js | 6 +- lib/commands/defaults.js | 83 +++++++++--------- lib/commands/dest.js | 16 ++-- lib/commands/init.js | 138 ++++++++++++++--------------- lib/commands/version.js | 10 +-- lib/diff.js | 74 ++++++++-------- lib/format.js | 36 ++++---- lib/generator.js | 184 +++++++++++++++++++++------------------ lib/list.js | 14 +-- lib/questions.js | 93 ++++++++++---------- lib/tasks.js | 28 +++--- lib/utils.js | 149 +++++++++++++++---------------- package.json | 5 +- 17 files changed, 645 insertions(+), 616 deletions(-) create mode 100644 lib/Verb.js diff --git a/.eslintrc.js b/.eslintrc.js index ea11918a..118c4d23 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,7 +2,7 @@ module.exports = { extends: 'eslint:recommended', env: { commonjs: true, - es2021: true, + es2023: true, node: true }, parserOptions: { @@ -10,22 +10,53 @@ module.exports = { ecmaVersion: 'latest' }, rules: { + 'accessor-pairs': 1, + 'array-bracket-newline': [1, 'consistent'], + 'array-bracket-spacing': [1, 'never'], + 'array-callback-return': 1, + 'array-element-newline': [1, 'consistent'], + 'arrow-body-style': 1, + 'arrow-parens': [1, 'as-needed'], + 'arrow-spacing': 1, + 'block-scoped-var': 1, + 'block-spacing': 1, + 'brace-style': 1, + 'callback-return': 0, + 'camelcase': 1, + 'capitalized-comments': 1, + 'class-methods-use-this': 1, + 'comma-dangle': [2, 'never'], + 'comma-spacing': 1, + 'comma-style': 1, + 'complexity': 1, + 'computed-property-spacing': 1, + 'consistent-return': 0, + 'consistent-this': 1, + 'constructor-super': 1, + 'curly': 0, + 'default-case': 1, + 'dot-location': [1, 'property'], + 'dot-notation': 1, + 'eol-last': 1, 'eqeqeq': 1, - 'indent': [1, 2], + 'indent': [1, 2, { SwitchCase: 1 }], 'linebreak-style': [1, 'unix'], 'no-alert': 1, + 'no-array-constructor': 1, 'no-async-promise-executor': 0, + 'no-caller': 1, 'no-case-declarations': 1, - 'no-console': [1, { 'allow': ['warn', 'error'] }], - 'no-constant-condition': [1, { 'checkLoops': false }], + 'no-console': 0, + 'no-constant-condition': [1, { checkLoops: false }], 'no-dupe-args': 1, 'no-dupe-keys': 1, 'no-else-return': 0, 'no-empty-character-class': 0, 'no-empty-function': 0, 'no-empty-pattern': 0, - 'no-empty': [1, { 'allowEmptyCatch': true }], + 'no-empty': [1, { allowEmptyCatch: true }], 'no-eval': 1, + 'no-extend-native': 1, 'no-extra-bind': 1, 'no-extra-boolean-cast': 1, 'no-extra-label': 1, @@ -72,9 +103,11 @@ module.exports = { 'no-shadow-restricted-names': 1, 'no-throw-literal': 1, 'no-undef-init': 1, - 'no-undef': 1, + 'no-undef': 2, 'no-unexpected-multiline': 1, 'no-unreachable': 1, + 'no-unreachable-loop': 1, + 'no-unused-expressions': 1, 'no-unsafe-assignment': 0, 'no-unsafe-call': 0, 'no-unsafe-finally': 0, @@ -82,13 +115,20 @@ module.exports = { 'no-unsafe-negation': 0, 'no-unsafe-optional-chaining': 0, 'no-unsafe-return': 0, - 'no-unused-vars': [1, { 'args': 'none' }], + 'no-unused-vars': [1, { args: 'none' }], 'no-use-before-define': 1, 'no-useless-catch': 0, 'no-useless-escape': 0, + 'no-useless-rename': 1, + 'no-useless-return': 1, + 'no-var': 2, 'no-void': 1, 'no-warning-comments': 1, 'no-with': 1, + 'object-curly-spacing': [1, 'always'], + 'object-shorthand': 1, + 'padded-blocks': [1, { switches: 'never' }], + 'prefer-const': 1, 'prefer-promise-reject-errors': 1, 'quotes': [1, 'single'], 'radix': 1, @@ -100,8 +140,21 @@ module.exports = { 'space-in-parens': 1, 'space-infix-ops': 1, 'space-unary-ops': 1, - 'spaced-comment': 1, + 'spaced-comment': 0, 'switch-colon-spacing': 1, - 'unicode-bom': 1, - } + 'symbol-description': 1, + 'template-tag-spacing': [2, 'never'], + 'template-curly-spacing': [2, 'never'], + 'valid-typeof': 1, + 'wrap-iife': 1, + 'valid-jsdoc': 1, + 'unicode-bom': 1 + }, + + ignorePatterns: [ + 'lib/templates/*', + 'support/*', + 'test/fixtures/*', + 'test/support/*' + ] }; diff --git a/bin/verb.js b/bin/verb.js index cd5297b7..aad86512 100755 --- a/bin/verb.js +++ b/bin/verb.js @@ -1,24 +1,37 @@ #!/usr/bin/env node process.env.GENERATE_CLI = true; -process.on('exit', function() { +process.on('exit', () => { require('set-blocking')(true); }); -var util = require('util'); -var Verb = require('..'); -var commands = require('../lib/commands'); -var tasks = require('../lib/tasks'); -var utils = require('../lib/utils'); -var args = process.argv.slice(2); -var argv = require('yargs-parser')(args); +const util = require('util'); +const Verb = require('..'); +const commands = require('../lib/commands'); +const tasks = require('../lib/tasks'); +const utils = require('../lib/utils'); +const args = process.argv.slice(2); +const argv = require('yargs-parser')(args); + +/** + * Handle errors + */ + +const handleErr = (app, err) => { + if (app && app.base.hasListeners('error')) { + app.base.emit('error', err); + } else { + console.log(err.stack); + process.exit(1); + } +}; /** * Listen for errors on all instances */ -Verb.on('generate.preInit', function(app) { - app.on('error', function(err) { +Verb.on('generate.preInit', app => { + app.on('error', err => { console.log(err.stack); process.exit(1); }); @@ -28,18 +41,18 @@ Verb.on('generate.preInit', function(app) { * Initialize CLI */ -Verb.on('generate.postInit', function(app) { +Verb.on('generate.postInit', app => { if (app.macros.has(args)) { app.macros.set(args); - var macro = {}; + const macro = {}; macro[args[0]] = args.slice(2).join(' '); console.log('saved macro:', util.inspect(macro)); process.exit(); } - var idx = utils.firstIndex(args, ['-D', '--default']); + const idx = utils.firstIndex(args, ['-D', '--default']); if (idx !== -1) { - var del = args.indexOf('--del') !== -1; + const del = args.indexOf('--del') !== -1; if (del) { app.base.store.del('defaultTask'); } else { @@ -53,9 +66,9 @@ Verb.on('generate.postInit', function(app) { * Initialize Runner */ -var options = {name: 'verb'}; +const options = { name: 'verb' }; -utils.runner(Verb, options, argv, function(err, app, runnerContext) { +utils.runner(Verb, options, argv, (err, app, runnerContext) => { if (err) handleErr(app, err); app.set('cache.runnerContext', runnerContext); @@ -65,36 +78,23 @@ utils.runner(Verb, options, argv, function(err, app, runnerContext) { app.register('defaults', require('../lib/generator')); } - var ctx = utils.extend({}, runnerContext); - var config = app.get('cache.config') || {}; + const ctx = utils.extend({}, runnerContext); + const config = app.get('cache.config') || {}; ctx.argv.tasks = []; - app.config.process(config, function(err, config) { + app.config.process(config, (err, config) => { if (err) return handleErr(app, err); app.base.cache.config = config; - app.cli.process(ctx.argv, function(err) { + app.cli.process(ctx.argv, err => { if (err) return handleErr(app, err); - var arr = tasks(app, ctx, argv); + const arr = tasks(app, ctx, argv); app.log.success('running tasks:', arr); - app.generate(arr, function(err) { + app.generate(arr, err => { if (err) handleErr(app, err); }); }); }); }); - -/** - * Handle errors - */ - -function handleErr(app, err) { - if (app && app.base.hasListeners('error')) { - app.base.emit('error', err); - } else { - console.log(err.stack); - process.exit(1); - } -} diff --git a/index.js b/index.js index c1a1a62d..d06fb1cf 100644 --- a/index.js +++ b/index.js @@ -1,152 +1 @@ -/*! - * verb - * - * Copyright (c) 2016, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -var Generate = require('generate'); -var utils = require('./lib/utils'); -var pkg = require('./package'); - -/** - * Create a verb application with `options`. - * - * ```js - * var verb = require('verb'); - * var app = verb(); - * ``` - * @param {Object} `options` Settings to initialize with. - * @api public - */ - -function Verb(options) { - if (!(this instanceof Verb)) { - return new Verb(options); - } - Generate.call(this, options); - this.is('verb'); - this.initVerb(this.options); -} - -/** - * Extend `Verb` - */ - -Generate.extend(Verb); - -Generate.on('generate.preInit', function(app) { - Verb.emit('generate.preInit', app); -}); - -Generate.on('generate.postInit', function(app) { - Verb.emit('generate.postInit', app); -}); - -/** - * Initialize Stores - */ - -utils.stores(Verb.prototype); - -/** - * Initialize verb data - */ - -Verb.prototype.initVerb = function(opts) { - Verb.emit('verb.preInit', this, this.base); - var self = this; - - /** - * Data - */ - - this.data('before', {}); - this.data('after', {}); - this.data('runner', { - name: 'verb', - version: pkg.version, - homepage: pkg.homepage - }); - - /** - * Options - */ - - this.option('lookup', Verb.lookup(this)); - this.option('toAlias', Verb.toAlias); - this.option('help', { - command: 'verb', - configname: 'verbfile', - appname: 'verb' - }); - - /** - * Listeners - */ - - this.on('option', function(key, val) { - if (key === 'dest') self.cwd = val; - }); - - this.on('ask', function(answerVal, answerKey, question) { - if (typeof answerVal === 'undefined') { - var segs = answerKey.split('author.'); - if (segs.length > 1) { - self.questions.answers[answerKey] = self.common.get(segs.pop()); - } - } - }); - - /** - * Middleware - */ - - this.preWrite(/(^|\/)[$_]/, function(file, next) { - file.basename = file.basename.replace(/^_/, '.'); - file.basename = file.basename.replace(/^\$/, ''); - next(); - }); - - Verb.emit('verb.postInit', this, this.base); -}; - -/** - * Expose custom lookup function for resolving generators - */ - -Verb.lookup = function(app) { - return function(key) { - var patterns = [key]; - if (!/^verb-generate-([^-]+)/.test(key)) { - patterns.unshift(`verb-generate-${key}`); - } - if (app.enabled('generate')) { - patterns.push(`generate-${key}`); - } - return patterns; - }; -}; - -/** - * Convert the given `name` to the `alias` to be used in the - * command line. - */ - -Verb.toAlias = function(name) { - return name.replace(/^(?:verb-generate-([^-]+)$)|(?:generate-)/, '$1'); -}; - -/** - * Expose `pkg` as a static property - */ - -Verb.pkg = pkg; - -/** - * Expose `Verb` - */ - -module.exports = Verb; +module.exports = require('./lib/Verb'); diff --git a/lib/Verb.js b/lib/Verb.js new file mode 100644 index 00000000..65f555b5 --- /dev/null +++ b/lib/Verb.js @@ -0,0 +1,129 @@ +/**! + * verb + * Copyright (c) 2016, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +const Generate = require('generate'); +const utils = require('./utils'); +const pkg = require('../package'); + +/** + * Create a verb application with `options`. + * + * ```js + * var verb = require('verb'); + * var app = verb(); + * ``` + * @param {Object} `options` Settings to initialize with. + * @api public + */ + +class Verb extends Generate { + constructor(options) { + super(options); + this.is('verb'); + this.initVerb(this.options); + } + + /** + * Initialize verb data + */ + + initVerb(opts) { + Verb.emit('verb.preInit', this, this.base); + + /** + * Data + */ + + this.data('before', {}); + this.data('after', {}); + this.data('runner', { + name: 'verb', + version: pkg.version, + homepage: pkg.homepage + }); + + /** + * Options + */ + + this.option('lookup', Verb.lookup(this)); + this.option('toAlias', Verb.toAlias); + this.option('help', { + command: 'verb', + configname: 'verbfile', + appname: 'verb' + }); + + /** + * Listeners + */ + + this.on('option', (key, val) => { + if (key === 'dest') { + this.cwd = val; + } + }); + + this.on('ask', (answerVal, answerKey, question) => { + if (typeof answerVal === 'undefined') { + const segs = answerKey.split('author.'); + if (segs.length > 1) { + this.questions.answers[answerKey] = this.common.get(segs.pop()); + } + } + }); + + /** + * Middleware + */ + + this.preWrite(/(^|\/)[$_]/, (file, next) => { + file.basename = file.basename.replace(/^_/, '.'); + file.basename = file.basename.replace(/^\$/, ''); + next(); + }); + + this.constructor.emit('verb.postInit', this, this.base); + } +} + + +/** + * Expose custom lookup function for resolving generators + */ + +Verb.lookup = app => key => { + const patterns = [key]; + if (!/^verb-generate-([^-]+)/.test(key)) { + patterns.unshift(`verb-generate-${key}`); + } + if (app.enabled('generate')) { + patterns.push(`generate-${key}`); + } + return patterns; +}; + +/** + * Convert the given `name` to the `alias` to be used in the + * command line. + */ + +Verb.toAlias = name => name.replace(/^(?:verb-generate-([^-]+)$)|(?:generate-)/, '$1'); + +/** + * Expose `pkg` as a static property + */ + +Verb.pkg = pkg; + +/** + * Expose `Verb` + */ + +utils.stores(Verb.prototype); +module.exports = Verb; diff --git a/lib/commands.js b/lib/commands.js index d53bfef4..1f949b33 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,9 +1,9 @@ 'use strict'; -var commands = require('./commands/'); +const commands = require('./commands/'); -module.exports = function(app, options) { - for (var key in commands) { +module.exports = (app, options) => { + for (const key in commands) { if (commands.hasOwnProperty(key)) { app.cli.map(key, commands[key](app, options)); } diff --git a/lib/commands/defaults.js b/lib/commands/defaults.js index e6d2ca79..514ded3f 100644 --- a/lib/commands/defaults.js +++ b/lib/commands/defaults.js @@ -1,9 +1,9 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var utils = require('../utils'); -var isProcessed; +const fs = require('fs'); +const path = require('path'); +const merge = require('mixin-deep'); +const utils = require('../utils'); /** * Persist a value to a namespaced defaults object in package.json. For @@ -12,24 +12,25 @@ var isProcessed; * * ```sh * # display the defaults - * $ app --defaults + * $ verb --defaults * # set a boolean for the current project - * $ app --defaults=toc + * $ verb --defaults=toc * # save the cwd to use for the current project - * $ app --defaults=cwd:foo + * $ verb --defaults=cwd:foo * # save the tasks to run for the current project - * $ app --defaults=tasks:readme + * $ verb --defaults=tasks:readme * ``` * * @name defaults - * @param {Object} app + * @param {Object} verb * @api public * @cli public */ -module.exports = function(app, base, options) { - var ran = false; - return function(val, key, config, next) { +module.exports = (verb, base, options) => { + let ran = false; + + return (val, key, config, next) => { if (ran === true) { next(); return; @@ -37,58 +38,58 @@ module.exports = function(app, base, options) { ran = true; - // get the keys of properties defined by an `--init` prompt - var keys = app.get('cache.initKeys') || []; - var name = app._name.toLowerCase(); + // Get the keys of properties defined by an `--init` prompt + const keys = verb.get('cache.initKeys') || []; + const name = verb._name.toLowerCase(); if (utils.show(val)) { - var pkgConfig = {}; - pkgConfig[name] = app.pkg.get(name) || {}; + const pkgConfig = {}; + pkgConfig[name] = verb.pkg.get(name) || {}; console.error(utils.formatValue(pkgConfig)); next(); return; } - var pkgPath = path.resolve(app.cwd, 'package.json'); - var pkg = {}; + const pkgPath = path.resolve(verb.cwd, 'package.json'); + let pkg = {}; if (utils.exists(pkgPath)) { pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); } - pkg = merge({}, app.pkg.data, pkg); - app.pkg.del(name); - var orig = pkg[name] || {}; + pkg = merge({}, verb.pkg.data, pkg); + verb.pkg.del(name); + let orig = pkg[name] || {}; - // normalize both the old and new values before merging, using - // a schema that is specifically used for normalizing values to - // be written back to package.json - var tmp = app.cli.schema.normalize({config: {}}) || {}; + // Normalize both the old and new values before merging, using + // A schema that is specifically used for normalizing values to + // Be written back to package.json + const tmp = verb.cli.schema.normalize({ config: {} }) || {}; orig = tmp.config; - // merge the normalized values - var merged = val; + // Merge the normalized values + let merged = val; if (utils.isObject(val) && utils.isObject(orig)) { merged = merge({}, orig, val); } - // show the new value in the console - var show = utils.pick(merged, keys); - app.pkg.logInfo('updated package.json config with', show); + // Show the new value in the console + const show = utils.pick(merged, keys); + verb.pkg.logInfo('updated package.json config with', show); - // update options and `cache.config` - app.set('cache.config', merged); - app.emit('config', merged); + // Update options and `cache.config` + verb.set('cache.config', merged); + verb.emit('config', merged); - // update the config property + // Update the config property config[key] = merged; - if (app.pkg.queued === true) { - app.pkg.data = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + if (verb.pkg.queued === true) { + verb.pkg.data = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); } - // re-set updated config object in `package.json` - app.pkg.set(name, merged); - app.pkg.save(); - app.cli.process(merged, next); + // Re-set updated config object in `package.json` + verb.pkg.set(name, merged); + verb.pkg.save(); + verb.cli.process(merged, next); }; }; diff --git a/lib/commands/dest.js b/lib/commands/dest.js index 0a2f4c02..212cd779 100644 --- a/lib/commands/dest.js +++ b/lib/commands/dest.js @@ -1,14 +1,12 @@ 'use strict'; -var path = require('path'); +const path = require('path'); -module.exports = function(app, base) { - return function(val, key, config, next) { - if (typeof val === 'undefined') { - config[key] = app.cwd; - } else { - config[key] = path.resolve(val); - } - next(); +module.exports = (app, base) => (val, key, config, next) => { + if (typeof val === 'undefined') { + config[key] = app.cwd; + } else { + config[key] = path.resolve(val); } + next(); }; diff --git a/lib/commands/init.js b/lib/commands/init.js index 166b9bf1..1b466dce 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -1,72 +1,45 @@ 'use strict'; -var debug = require('debug')('base:cli:init'); -var questions = require('../questions'); -var utils = require('../utils'); +const debug = require('debug')('base:cli:init'); +const questions = require('../questions'); +const utils = require('../utils'); -/** - * Initialize a prompt session and persist the answers to the `verb` object - * in the package.json in the current working directory. - * - * ```sh - * $ --init - * ``` - * @name --init - * @api public - */ +const filter = (app, val) => { + if (!val) return []; + if (typeof val === 'string') { + val = val.split(','); + } -module.exports = function(app, base, options) { - return function(val, key, config, next) { - prompt(app, next); - }; -}; + const devDeps = app.pkg.get('devDependencies') || {}; + const deps = app.pkg.get('dependencies') || {}; + const len = val.length; + let idx = -1; + const res = []; -function prompt(app, next) { - ask(app, {save: false}, function(err, answers) { - if (err) { - next(err); - return; + while (++idx < len) { + const dep = val[idx]; + if (dep && !devDeps.hasOwnProperty(dep) && !deps.hasOwnProperty(dep)) { + res.push(dep); } - var config = (answers && answers.config) || {}; - app.set('cache.initKeys', Object.keys(config)); - app.cli.process(answers, next); - }); -} - -function ask(app, options, cb) { - if (typeof app.questions === 'undefined') { - cb(new Error('expected base-questions plugin to be defined')); - return; } - - if (typeof options === 'function') { - cb = options; - options = {}; - } - - options = utils.extend({}, options, app.options); - questions(app, options); - - app.ask('init.choose', { save: false }, function(err, answers) { - if (err) return cb(err); - debug('finished with init.choose "%j"', answers); - postInit(app, answers, cb); - }); + return res; }; -function postInit(app, answers, cb) { - var plugins = filter(app, utils.get(answers, 'config.plugins')); +const postInit = (app, answers, cb) => { + const plugins = filter(app, utils.get(answers, 'config.plugins')); + + // eslint-disable-next-line no-negated-condition if (!plugins.length) { cb(null, answers); } else { - app.ask('after', { save: false }, function(err, res) { + app.ask('after', { save: false }, (err, res) => { if (err) return cb(err); - var answer = utils.get(res, 'after.plugins'); + const answer = utils.get(res, 'after.plugins'); if (answer === true) { app.pkg.save(); - app.npm.saveDev(plugins, function(err) { + app.npm.saveDev(plugins, err => { if (err) return cb(err); app.pkg.queued = true; cb(null, answers); @@ -76,28 +49,55 @@ function postInit(app, answers, cb) { } }); } -} +}; + +const ask = (app, options, cb) => { + if (typeof app.questions === 'undefined') { + cb(new Error('expected base-questions plugin to be defined')); + return; + } -function filter(app, val) { - if (!val) return []; - if (typeof val === 'string') { - val = val.split(','); + if (typeof options === 'function') { + cb = options; + options = {}; } - var devDeps = app.pkg.get('devDependencies') || {}; - var deps = app.pkg.get('dependencies') || {}; - var len = val.length; - var idx = -1; - var res = []; + options = utils.extend({}, options, app.options); + questions(app, options); - while (++idx < len) { - var dep = val[idx]; - if (dep && !devDeps.hasOwnProperty(dep) && !deps.hasOwnProperty(dep)) { - res.push(dep); + app.ask('init.choose', { save: false }, function(err, answers) { + if (err) return cb(err); + debug('finished with init.choose "%j"', answers); + postInit(app, answers, cb); + }); +}; + +const prompt = (app, next) => { + ask(app, { save: false }, (err, answers) => { + if (err) { + next(err); + return; } - } - return res; -} + const config = answers && answers.config || {}; + app.set('cache.initKeys', Object.keys(config)); + app.cli.process(answers, next); + }); +}; + +/** + * Initialize a prompt session and persist the answers to the `verb` object + * in the package.json in the current working directory. + * + * ```sh + * $ --init + * ``` + * @name --init + * @api public + */ + +module.exports = (app, base, options) => (val, key, config, next) => { + prompt(app, next); +}; /** * Expose methods diff --git a/lib/commands/version.js b/lib/commands/version.js index 6e98689c..a490a018 100644 --- a/lib/commands/version.js +++ b/lib/commands/version.js @@ -1,10 +1,8 @@ 'use strict'; -var pkg = require('../../package'); +const pkg = require('../../package'); -module.exports = function(app) { - return function(val, key, config, next) { - console.log(app.log.cyan(`${app._name} v${pkg.version}`)); - process.exit(); - }; +module.exports = app => (val, key, config, next) => { + console.log(app.log.cyan(`${app._name} v${pkg.version}`)); + process.exit(); }; diff --git a/lib/diff.js b/lib/diff.js index 3d3ef094..dbc5be34 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -1,8 +1,21 @@ 'use strict'; -var differ = require('diff'); -var through = require('through2'); -var utils = require('./utils'); +const differ = require('diff'); +const through = require('through2'); +const utils = require('./utils'); + +const color = stat => { + if (stat.removed) return 'red'; + if (stat.added) return 'green'; + return 'gray'; +}; + +const diff = (a, b, method) => { + differ[method || 'diffWords'](a, b).forEach(stat => { + process.stderr.write(utils.log[color(stat)](stat.value)); + }); + console.error(); +}; /** * Output to the console a visual representation of the difference between @@ -13,41 +26,26 @@ var utils = require('./utils'); * @api public */ -module.exports = function(options) { - options = options || {}; - var cache = {}; - var prev; - - return function(a, b) { - return through.obj(function(file, enc, next) { - if (options.diff === false) { - next(null, file); - return; - } - var contents = file.contents.toString(); - cache[a] = contents; - var str = b ? (cache[b] || b) : prev; - - if (typeof str !== 'undefined') { - diff(contents, cache[b]); - next(); - return; - } - prev = contents; +module.exports = options => { + const cache = {}; + let prev; + + return (a, b) => through.obj((file, enc, next) => { + if (options?.diff === false) { next(null, file); - }); - }; -}; + return; + } -function diff(a, b, method) { - differ[method || 'diffWords'](a, b).forEach(function(stat) { - process.stderr.write(utils.log[color(stat)](stat.value)); - }); - console.error(); -} + const contents = file.contents.toString(); + cache[a] = contents; + const str = b ? cache[b] || b : prev; -function color(stat) { - if (stat.removed) return 'red'; - if (stat.added) return 'green'; - return 'gray'; -} + if (typeof str !== 'undefined') { + diff(contents, cache[b]); + next(); + return; + } + prev = contents; + next(null, file); + }); +}; diff --git a/lib/format.js b/lib/format.js index 18d9b0d2..53ce0cb4 100644 --- a/lib/format.js +++ b/lib/format.js @@ -1,8 +1,8 @@ 'use strict'; -var path = require('path'); -var reflinks = require('./reflinks'); -var utils = require('./utils'); +const path = require('path'); +const reflinks = require('./reflinks'); +const utils = require('./utils'); /** * Format a markdown file using [pretty-remarkable][]. Optionally @@ -15,21 +15,19 @@ var utils = require('./utils'); * @api public */ -module.exports = function(app, options) { - return function(filepath, key, config, next) { - if (!filepath || typeof filepath !== 'string') { - next(); - return; - } +module.exports = (verb, options) => (filepath, key, config, next) => { + if (!filepath || typeof filepath !== 'string') { + next(); + return; + } - app.src(path.resolve(filepath)) - .pipe(reflinks(app)) - .pipe(utils.format()) - .pipe(app.dest(function(file) { - file.basename = (config.name || path.basename(filepath)); - return (config.dest || path.resolve(path.dirname(filepath))); - })) - .on('error', next) - .on('end', next); - }; + verb.src(path.resolve(filepath)) + .pipe(reflinks(verb)) + .pipe(utils.format()) + .pipe(verb.dest(file => { + file.basename = config.name || path.basename(filepath); + return config.dest || path.resolve(path.dirname(filepath)); + })) + .on('error', next) + .on('end', next); }; diff --git a/lib/generator.js b/lib/generator.js index 3266d812..0380351d 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,21 +1,48 @@ 'use strict'; -var path = require('path'); -var cwd = path.resolve.bind(path, __dirname, 'templates'); -var commands = require('./commands/'); -var utils = require('./utils'); -var list = require('./list'); -var argv = utils.parseArgs(process.argv.slice(2)); -var diff = require('./diff')(argv); +const path = require('path'); +const cwd = path.resolve.bind(path, __dirname, 'templates'); +const commands = require('./commands/'); +const utils = require('./utils'); +const list = require('./list'); +const argv = utils.parseArgs(process.argv.slice(2)); +const diff = require('./diff')(argv); + +/** + * Generate a file + */ + +const file = (verb, src, options, cb) => { + const defaults = { cwd: cwd(), dest: verb.cwd }; + const opts = utils.extend({}, defaults, options); + const dest = path.resolve(opts.dest); + + verb.engine('*', require('engine-base')); + verb + .src(src, { cwd: opts.cwd, layout: null }) + .pipe(verb.renderFile('*')) + .pipe(utils.format()) + .pipe(verb.conflicts(dest)) + .pipe( + verb.dest(file => { + if (opts.name) file.basename = opts.name; + file.basename = file.basename.replace(/^_/, '.'); + file.basename = file.basename.replace(/^\$/, ''); + verb.log.success('created', file.relative); + return dest; + }) + ) + .on('end', cb); +}; /** * Built-in verb tasks */ -module.exports = function(verb, base) { - var common = new utils.DataStore('common-config'); - var gm = path.resolve.bind(path, utils.gm); - var cwd = path.resolve.bind(path, verb.cwd); +module.exports = (verb, base) => { + const common = new utils.DataStore('common-config'); + const gm = path.resolve.bind(path, utils.gm); + const cwd = path.resolve.bind(path, verb.cwd); verb.use(utils.middleware()); @@ -23,7 +50,7 @@ module.exports = function(verb, base) { * Listen for errors */ - verb.on('error', function(err) { + verb.on('error', err => { console.error(err); process.exit(1); }); @@ -39,20 +66,23 @@ module.exports = function(verb, base) { * @api public */ - verb.task('format', function(cb) { - var src = verb.option('src'); - var dest = verb.option('dest'); + verb.task('format', cb => { + const src = verb.option('src'); + const dest = verb.option('dest'); - verb.src(src) + verb + .src(src) .pipe(utils.reflinks(verb)) .pipe(diff('before')) .pipe(utils.format()) .pipe(diff('after', 'before')) - .pipe(verb.dest(function(file) { - if (argv.name) file.basename = argv.name; - return dest || file.dirname; - })) - .on('data', function(file) { + .pipe( + verb.dest(file => { + if (argv.name) file.basename = argv.name; + return dest || file.dirname; + }) + ) + .on('data', file => { console.log('formatted "%s"', file.relative); }) .on('error', cb) @@ -71,12 +101,12 @@ module.exports = function(verb, base) { * @api public */ - verb.task('render', function(cb) { + verb.task('render', cb => { if (!verb.option('src')) { verb.emit('error', new Error('Expected a `--src` filepath')); } else if (!verb.option('dest')) { verb.build(['dest', 'render'], cb); - } else { + } else if (verb.option('dest') && verb.option('src')) { file(verb, verb.option('src'), { dest: verb.cwd }, cb); } }); @@ -90,7 +120,7 @@ module.exports = function(verb, base) { * @api public */ - verb.register('new', function(app) { + verb.register('new', app => { app.option(verb.options); /** @@ -119,7 +149,7 @@ module.exports = function(verb, base) { * @api public */ - app.task('verbfile', function(cb) { + app.task('verbfile', cb => { file(app, 'verbfile.js', null, cb); }); @@ -133,7 +163,7 @@ module.exports = function(verb, base) { * @api public */ - app.task('verbmd', function(cb) { + app.task('verbmd', cb => { file(app, '_verb.md', null, cb); }); @@ -147,7 +177,7 @@ module.exports = function(verb, base) { * @api public */ - app.task('rc', function(cb) { + app.task('rc', cb => { file(app, '_verbrc.json', null, cb); }); @@ -162,7 +192,7 @@ module.exports = function(verb, base) { * @api public */ - app.task('readme', function(cb) { + app.task('readme', cb => { file(app, 'README.md', null, cb); }); @@ -178,9 +208,9 @@ module.exports = function(verb, base) { * @api public */ - app.task('prompt-verbmd', {silent: true}, function(cb) { + app.task('prompt-verbmd', { silent: true }, cb => { app.confirm('verbmd', 'Looks like .verb.md is missing, want to add one?'); - app.ask('verbmd', {save: false}, function(err, answers) { + app.ask('verbmd', { save: false }, (err, answers) => { if (err) { cb(err); return; @@ -205,14 +235,15 @@ module.exports = function(verb, base) { * @api public */ - verb.task('list', { silent: true }, function() { - return verb.src([gm('verb-generate-*'), cwd('node_modules/verb-generate-*')]) - .pipe(utils.through.obj(function(file, enc, next) { + verb.task('list', { silent: true }, () => verb + .src([gm('verb-generate-*'), cwd('node_modules/verb-generate-*')]) + .pipe( + utils.through.obj((file, enc, next) => { file.alias = verb.toAlias(file.basename); next(null, file); - })) - .pipe(list(verb)); - }); + }) + ) + .pipe(list(verb))); /** * Display a help menu of available commands and flags. @@ -224,28 +255,31 @@ module.exports = function(verb, base) { * @api public */ - verb.task('init', { silent: true }, function(cb) { + verb.task('init', { silent: true }, cb => { verb.question('init', 'Would you like to use defaults, or choose settings?', { type: 'list', choices: ['defaults', 'choose'], all: false }); - verb.ask('init', {save: false}, function(err, answers) { + verb.ask('init', { save: false }, (err, answers) => { if (err) { cb(err); return; } + const defaults = verb.globals.get('defaults'); + switch (answers.init) { - case 'defaults': - var defaults = verb.globals.get('defaults'); + case 'defaults': { commands.init.postInit(verb, { config: defaults }, cb); return; + } + case 'choose': default: { commands.init.prompt(verb, cb); - return; + } } }); @@ -255,45 +289,49 @@ module.exports = function(verb, base) { * Save personal defaults in user home. */ - verb.register('store', function(app) { + verb.register('store', app => { app.enable('silent'); - app.task('defaults', function(cb) { - var defaults = app.globals.has('defaults.defined'); - var message = defaults + app.task('defaults', cb => { + const defaults = app.globals.has('defaults.defined'); + const message = defaults ? 'Would you like to update defaults now?' : 'No defaults found, would you like to set them now?'; app.confirm('defaults', message); - app.ask('defaults', function(err, answers) { + app.ask('defaults', (err, answers) => { if (err) return cb(err); - }); }); - app.task('del', function(cb) { - var keys = ['name', 'username', 'twitter', 'email']; - keys.forEach(function(key) { + app.task('del', cb => { + const keys = ['name', 'username', 'twitter', 'email']; + keys.forEach(key => { console.log(verb.log.red(' Deleted:'), key, common.get(key)); common.del(keys); }); cb(); }); - app.task('show', function(cb) { - var keys = ['name', 'username', 'twitter', 'email']; + app.task('show', cb => { + const keys = ['name', 'username', 'twitter', 'email']; console.log(); - keys.forEach(function(key) { + keys.forEach(key => { console.log(key + ': ' + verb.log.cyan(common.get(key))); }); console.log(); cb(); }); - app.task('me', function(cb) { + app.task('me', cb => { console.log(); - console.log(' Answers to the following questions will be stored in:', verb.log.bold('~/.common-config.json')); - console.log(' The stored values will be used later in (your) templates.'); + console.log( + ' Answers to the following questions will be stored in:', + verb.log.bold('~/.common-config.json') + ); + console.log( + ' The stored values will be used later in (your) templates.' + ); console.log(` To skip a question, just hit ${verb.log.bold('')}`); console.log(); @@ -303,7 +341,7 @@ module.exports = function(verb, base) { app.question('common.twitter', 'Twitter username?'); app.question('common.email', 'Email address?'); - app.ask('common', {save: false}, function(err, answers) { + app.ask('common', { save: false }, (err, answers) => { if (err) return cb(err); if (!answers.common) { @@ -311,10 +349,10 @@ module.exports = function(verb, base) { return; } - var vals = []; - for (var key in answers.common) { + const vals = []; + for (const key in answers.common) { if (answers.common.hasOwnProperty(key)) { - var val = answers.common[key]; + const val = answers.common[key]; common.set(key, val); vals.push(verb.log.green(key + ': ' + val)); } @@ -350,7 +388,7 @@ module.exports = function(verb, base) { * @api public */ - verb.task('help', { silent: true }, function(cb) { + verb.task('help', { silent: true }, cb => { verb.enable('silent'); verb.cli.process({ help: true }, cb); }); @@ -367,27 +405,3 @@ module.exports = function(verb, base) { verb.task('default', ['help']); }; - -/** - * Generate a file - */ - -function file(verb, src, options, cb) { - var defaults = { cwd: cwd(), dest: verb.cwd }; - var opts = utils.extend({}, defaults, options); - var dest = path.resolve(opts.dest); - - verb.engine('*', require('engine-base')); - verb.src(src, {cwd: opts.cwd, layout: null}) - .pipe(verb.renderFile('*')) - .pipe(utils.format()) - .pipe(verb.conflicts(dest)) - .pipe(verb.dest(function(file) { - if (opts.name) file.basename = opts.name; - file.basename = file.basename.replace(/^_/, '.'); - file.basename = file.basename.replace(/^\$/, ''); - verb.log.success('created', file.relative); - return dest; - })) - .on('end', cb); -} diff --git a/lib/list.js b/lib/list.js index f988b269..60204f9f 100644 --- a/lib/list.js +++ b/lib/list.js @@ -1,15 +1,15 @@ 'use strict'; -var path = require('path'); -var utils = require('./utils'); +const path = require('path'); +const utils = require('./utils'); module.exports = function(app) { function bold(str) { return app.log.underline(app.log.bold(str)); } - var list = [[bold('version'), bold('name'), bold('alias')]]; - var cache = {}; + const list = [[bold('version'), bold('name'), bold('alias')]]; + const cache = {}; return utils.through.obj(function(file, enc, next) { if (cache[file.stem]) { next(); @@ -17,14 +17,14 @@ module.exports = function(app) { } cache[file.stem] = true; - var pkgPath = path.resolve(file.path, 'package.json'); - var pkg = require(pkgPath); + const pkgPath = path.resolve(file.path, 'package.json'); + const pkg = require(pkgPath); list.push([app.log.gray(pkg.version), file.basename, app.log.cyan(file.alias)]); next(); }, function(cb) { console.log(); console.log(utils.table(list, { - stringLength: function(str) { + stringLength(str) { return utils.strip(str).length; } })); diff --git a/lib/questions.js b/lib/questions.js index 466bcaff..0407ddde 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -1,6 +1,43 @@ 'use strict'; -module.exports = function(app, options) { +const defaultLayout = app => { + const layout = app.pkg.get([app._name, 'layout']); + if (typeof layout === 'string' && layout.trim() !== 'default') { + return layout; + } + const name = app.pkg.get('name'); + if (/^generate-/.test(name)) { + return 'generator'; + } + if (/^updater-/.test(name)) { + return 'updater'; + } + if (/^helper-/.test(name)) { + return 'helper'; + } + if (/^assemble-/.test(name)) { + return 'assemble'; + } + if (/^verb-/.test(name)) { + return 'verb'; + } + return 'default'; +}; + +/** + * Build the list of `config.*` options to prompt the user about + */ + +const buildChoices = app => app.questions.queue.reduce((acc, key) => { + if (key.indexOf('config.') !== 0) { + return acc; + } + + acc.push(key.slice('config.'.length)); + return acc; +}, []); + +module.exports = (app, options) => { /** * Config questions @@ -38,9 +75,10 @@ module.exports = function(app, options) { app.question('init.preferences', 'Would you like to set defaults for this project?', { type: 'confirm', - next: function(answer, question, answers, cb) { - // ensure `init` questions aren't asked again + next(answer, question, answers, cb) { + // Ensure `init` questions aren't asked again delete answers.init; + if (answer === true) { app.ask('init.choose', cb); } else { @@ -52,56 +90,17 @@ module.exports = function(app, options) { app.choices('init.choose', 'Which options would you like to set?', { choices: buildChoices(app), save: false, - next: function(answer, question, answers, cb) { - // ensure `init` questions aren't asked again + next(answer, question, answers, cb) { + // Ensure `init` questions aren't asked again delete answers.init; + if (typeof answer === 'undefined' || answer.length === 0) { cb(null, answers); return; } - var choices = answer.map(function(val) { - return 'config.' + val; - }); + + const choices = answer.map(value => 'config.' + value); app.ask(choices, cb); } }); }; - -function defaultLayout(app) { - var layout = app.pkg.get([app._name, 'layout']); - if (typeof layout === 'string' && layout.trim() !== 'default') { - return layout; - } - var name = app.pkg.get('name'); - if (/^generate-/.test(name)) { - return 'generator'; - } - if (/^updater-/.test(name)) { - return 'updater'; - } - if (/^helper-/.test(name)) { - return 'helper'; - } - if (/^assemble-/.test(name)) { - return 'assemble'; - } - if (/^verb-/.test(name)) { - return 'verb'; - } - return 'default'; -} - -/** - * Build the list of `config.*` options to prompt the user about - */ - -function buildChoices(app) { - return app.questions.queue.reduce(function(acc, key) { - if (key.indexOf('config.') !== 0) { - return acc; - } - key = key.slice('config.'.length); - acc.push(key); - return acc; - }, []); -} diff --git a/lib/tasks.js b/lib/tasks.js index c3054c62..4442df9a 100644 --- a/lib/tasks.js +++ b/lib/tasks.js @@ -1,25 +1,25 @@ 'use strict'; -var utils = require('./utils'); +const utils = require('./utils'); module.exports = function(app, ctx, argv) { if (argv.init === true) { return []; } - var configFile = ctx.env.configFile; - var configExists; - var configTasks; + const configFile = ctx.env.configFile; + let configExists; + let configTasks; - // determine the tasks to run (returns the first value that isn't `["default"]` or `[]`) - var tasks = utils.getTasks(configFile, [ - argv._, // command line - ctx.pkgConfig.tasks, // set in package.json - app.store.get('defaultTasks') // stored user-defined "default" tasks + // Determine the tasks to run (returns the first value that isn't `["default"]` or `[]`) + let tasks = utils.getTasks(configFile, [ + argv._, // Command line + ctx.pkgConfig.tasks, // Set in package.json + app.store.get('defaultTasks') // Stored user-defined "default" tasks ]); tasks = tasks.map(function(task) { - var isDefaults = false; + let isDefaults = false; if (task.indexOf('new') === 0 || task.indexOf('store') === 0) { isDefaults = true; @@ -52,7 +52,7 @@ module.exports = function(app, ctx, argv) { configExists = utils.exists(configFile); configTasks = app.pkg.get('verb.tasks'); - // if a `verbfile.js` or custom configFile exists, return tasks + // If a `verbfile.js` or custom configFile exists, return tasks if (configExists) { if (configTasks && configTasks.length) { app.pkg.logWarning('ignoring tasks defined in package.json:', configTasks); @@ -64,13 +64,13 @@ module.exports = function(app, ctx, argv) { return configTasks; } - var verbmd = utils.exists('.verb.md'); - // if a `.verb.md` exists, but no verbfile.js, set `readme` as the default + const verbmd = utils.exists('.verb.md'); + // If a `.verb.md` exists, but no verbfile.js, set `readme` as the default if (verbmd && !configExists) { return ['verb-generate-readme']; } - // if no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` + // If no verbfile.js, and no `.verb.md`, ask the user if they want a `.verb.md` if (!verbmd) { return ['defaults.new:prompt-verbmd'].concat(tasks); } diff --git a/lib/utils.js b/lib/utils.js index ac542c92..23a08c6a 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,54 +1,53 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var utils = require('lazy-cache')(require); -var fn = require; -require = utils; -var stores = {}; +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const stores = {}; -/** - * Lazily required module dependencies - */ +const defineProperty = (obj, name, fn) => { + Object.defineProperty(obj, name, { + configurable: true, + enumerable: true, + get() { + return fn(); + } + }); +}; -require('base-runner', 'runner'); -require('common-middleware', 'middleware'); -require('config-file'); -require('data-store', 'DataStore'); -require('extend-shallow', 'extend'); -require('fs-exists-sync', 'exists'); -require('generate'); -require('get-value', 'get'); -require('global-modules', 'gm'); -require('gulp-format-md', 'format'); -require('gulp-reflinks', 'reflinks'); -require('kind-of', 'typeOf'); -require('log-utils', 'log'); -require('macro-store', 'MacroStore'); -require('merge-deep', 'merge'); -require('object.pick', 'pick'); -require('set-value', 'set'); -require('strip-color', 'strip'); -require('text-table', 'table'); -require('through2', 'through'); -require('yargs-parser', 'parse'); -require = fn; +defineProperty(exports, 'runner', () => require('base-runner')); +defineProperty(exports, 'middleware', () => require('common-middleware')); +defineProperty(exports, 'config-file', () => require('config-file')); +defineProperty(exports, 'DataStore', () => require('data-store')); +defineProperty(exports, 'extend', () => require('extend-shallow')); +defineProperty(exports, 'exists', () => require('fs-exists-sync')); +defineProperty(exports, 'generate', () => require('generate')); +defineProperty(exports, 'get', () => require('get-value')); +defineProperty(exports, 'gm', () => require('global-modules')); +defineProperty(exports, 'format', () => require('gulp-format-md')); +defineProperty(exports, 'reflinks', () => require('gulp-reflinks')); +defineProperty(exports, 'typeOf', () => require('kind-of')); +defineProperty(exports, 'log', () => require('log-utils')); +defineProperty(exports, 'MacroStore', () => require('macro-store')); +defineProperty(exports, 'merge', () => require('merge-deep')); +defineProperty(exports, 'pick', () => require('object.pick')); +defineProperty(exports, 'set', () => require('set-value')); +defineProperty(exports, 'strip', () => require('strip-color')); +defineProperty(exports, 'table', () => require('text-table')); +defineProperty(exports, 'through', () => require('through2')); +defineProperty(exports, 'parse', () => require('yargs-parser')); /** * Format a value to be displayed in the command line */ -utils.formatValue = function(val) { - return utils.cyan(util.inspect(val, null, 10)); -}; +exports.formatValue = val => exports.cyan(util.inspect(val, null, 10)); /** * Return true if a value is an object. */ -utils.isObject = function(val) { - return utils.typeOf(val) === 'object'; -}; +exports.isObject = val => exports.typeOf(val) === 'object'; /** * Returns true if `val` is true or is an object with `show: true` @@ -57,43 +56,41 @@ utils.isObject = function(val) { * @return {Boolean} */ -utils.show = function(val) { - return utils.isObject(val) && val.show === true; -}; +exports.show = val => exports.isObject(val) && val.show === true; /** * Initialize stores */ -utils.stores = function(proto) { - // create `macros` store +exports.stores = proto => { + // Create `macros` store Object.defineProperty(proto, 'macros', { configurable: true, - set: function(val) { + set(val) { stores.macros = val; }, - get: function() { - return stores.macros || (stores.macros = new utils.MacroStore({name: 'verb-macros'})); + get() { + return stores.macros || (stores.macros = new exports.MacroStore({ name: 'verb-macros' })); } }); - // create `app.globals` store + // Create `app.globals` store Object.defineProperty(proto, 'globals', { configurable: true, - set: function(val) { + set(val) { stores.globals = val; }, - get: function() { - return stores.globals || (stores.globals = new utils.DataStore('verb-globals')); + get() { + return stores.globals || (stores.globals = new exports.DataStore('verb-globals')); } }); }; /** - * argv options + * Argv options */ -utils.opts = { +exports.opts = { boolean: ['diff'], alias: { add: 'a', @@ -109,8 +106,8 @@ utils.opts = { } }; -utils.parseArgs = function(argv) { - var obj = utils.parse(argv, utils.opts); +exports.parseArgs = argv => { + const obj = exports.parse(argv, exports.opts); if (obj.init) { obj._.push('init'); delete obj.init; @@ -122,33 +119,33 @@ utils.parseArgs = function(argv) { return obj; }; -utils.getConfig = function(app, name) { - var runtimeConfig = path.resolve(app.cwd, name); - var config = utils.configFile('.verbrc.json') || {}; +exports.getConfig = (app, name) => { + const runtimeConfig = path.resolve(app.cwd, name); + let config = exports.configFile('.verbrc.json') || {}; - if (utils.exists(runtimeConfig)) { - var rc = JSON.parse(fs.readFileSync(runtimeConfig, 'utf8')); - config = utils.extend({}, config, rc); + if (exports.exists(runtimeConfig)) { + const rc = JSON.parse(fs.readFileSync(runtimeConfig, 'utf8')); + config = exports.extend({}, config, rc); app.base.set('cache.config', config); } }; -utils.getTasks = function(configFile, arrays) { - arrays = utils.arrayify(arrays); - var tasks = []; +exports.getTasks = (configFile, arrays) => { + arrays = exports.arrayify(arrays); + let tasks = []; if (configFile) { - tasks = utils.arrayify(arrays[0]); + tasks = exports.arrayify(arrays[0]); return tasks.length >= 1 ? tasks : ['default']; } - for (var i = 0; i < arrays.length; i++) { - var arr = utils.arrayify(arrays[i]); - // if `default` task is defined, continue + for (let i = 0; i < arrays.length; i++) { + const arr = exports.arrayify(arrays[i]); + // If `default` task is defined, continue if (arr.length === 1 && arr[0] === 'default') { continue; } - // if nothing is defined, continue + // If nothing is defined, continue if (arr.length === 0) { continue; } @@ -158,14 +155,12 @@ utils.getTasks = function(configFile, arrays) { return tasks; }; -utils.arrayify = function(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -}; +exports.arrayify = val => val ? Array.isArray(val) ? val : [val] : []; -utils.firstIndex = function(arr, items) { - items = utils.arrayify(items); - var idx = -1; - for (var i = 0; i < arr.length; i++) { +exports.firstIndex = (arr, items) => { + items = exports.arrayify(items); + let idx = -1; + for (let i = 0; i < arr.length; i++) { if (items.indexOf(arr[i]) !== -1) { idx = i; break; @@ -173,9 +168,3 @@ utils.firstIndex = function(arr, items) { } return idx; }; - -/** - * Expose `utils` modules - */ - -module.exports = utils; diff --git a/package.json b/package.json index 58fa41f5..286e69a4 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "main": "index.js", "preferGlobal": true, "bin": { - "verb": "bin/verb.js" + "v": "bin/verb.js" }, "engines": { "node": ">=0.10.0" @@ -28,6 +28,7 @@ "test": "mocha" }, "dependencies": { + "ansi-colors": "^4.1.3", "base-runner": "^0.8.2", "common-middleware": "^0.3.0", "config-file": "^0.3.2", @@ -48,6 +49,7 @@ "log-utils": "^0.2.1", "macro-store": "^0.1.0", "merge-deep": "^3.0.0", + "mixin-deep": "^2.0.1", "object.pick": "^1.1.2", "set-blocking": "^2.0.0", "set-value": "^4.1.0", @@ -64,6 +66,7 @@ "base-test-runner": "^0.2.0", "base-test-suite": "^0.2.3", "cross-spawn": "^4.0.0", + "eslint": "^8.45.0", "generate-foo": "^0.1.5", "generator-util": "^0.2.9", "graceful-fs": "^4.1.5", From 1a7555b7bd75eb8d5129da0cd2588afbf6cbfdb8 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Thu, 12 Sep 2024 14:44:53 -0400 Subject: [PATCH 282/282] update verb --- .editorconfig | 3 + .eslintrc.js | 203 ++++++++++++++++++++++++-------------- .gitignore | 4 +- LICENSE | 2 +- index.js | 2 + lib/Verb.js | 1 - lib/commands/init.js | 25 +++-- lib/generator.js | 61 +++++++++++- lib/tasks.js | 5 +- lib/templates/_verb.md | 2 +- lib/templates/readme.md | 2 +- lib/templates/verbfile.js | 8 +- package.json | 4 +- readme.md | 76 ++++++-------- verbfile.js | 12 +-- 15 files changed, 254 insertions(+), 156 deletions(-) diff --git a/.editorconfig b/.editorconfig index 62391077..bf1fa99d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,6 @@ charset = utf-8 indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js index 118c4d23..9f14403c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,160 +1,213 @@ +'use strict'; + module.exports = { + root: true, extends: 'eslint:recommended', + env: { commonjs: true, es2023: true, + mocha: true, node: true }, + parserOptions: { + ecmaVersion: 'latest', sourceType: 'script', - ecmaVersion: 'latest' + requireConfigFile: false }, + rules: { - 'accessor-pairs': 1, + 'accessor-pairs': 2, 'array-bracket-newline': [1, 'consistent'], 'array-bracket-spacing': [1, 'never'], 'array-callback-return': 1, 'array-element-newline': [1, 'consistent'], - 'arrow-body-style': 1, + 'arrow-body-style': 0, 'arrow-parens': [1, 'as-needed'], - 'arrow-spacing': 1, + 'arrow-spacing': [1, { before: true, after: true }], 'block-scoped-var': 1, - 'block-spacing': 1, - 'brace-style': 1, + 'block-spacing': [1, 'always'], + 'brace-style': [1, '1tbs', { allowSingleLine: true }], 'callback-return': 0, - 'camelcase': 1, - 'capitalized-comments': 1, - 'class-methods-use-this': 1, - 'comma-dangle': [2, 'never'], - 'comma-spacing': 1, - 'comma-style': 1, + 'camelcase': [0, { allow: [] }], + 'capitalized-comments': 0, + 'class-methods-use-this': 0, + 'comma-dangle': [1, 'never'], + 'comma-spacing': [1, { before: false, after: true }], + 'comma-style': [1, 'last'], 'complexity': 1, 'computed-property-spacing': 1, 'consistent-return': 0, 'consistent-this': 1, - 'constructor-super': 1, - 'curly': 0, + 'constructor-super': 2, + 'curly': [1, 'multi-line', 'consistent'], 'default-case': 1, 'dot-location': [1, 'property'], 'dot-notation': 1, 'eol-last': 1, - 'eqeqeq': 1, + 'eqeqeq': [1, 'allow-null'], + 'for-direction': 1, + 'func-call-spacing': 2, + 'generator-star-spacing': [1, { before: true, after: true }], + 'handle-callback-err': [2, '^(err|error)$'], 'indent': [1, 2, { SwitchCase: 1 }], + 'key-spacing': [1, { beforeColon: false, afterColon: true }], + 'keyword-spacing': [1, { before: true, after: true }], 'linebreak-style': [1, 'unix'], + 'new-cap': [1, { newIsCap: true, capIsNew: false }], + 'new-parens': 2, 'no-alert': 1, 'no-array-constructor': 1, - 'no-async-promise-executor': 0, - 'no-caller': 1, + 'no-async-promise-executor': 1, + 'no-caller': 2, 'no-case-declarations': 1, + 'no-class-assign': 2, + 'no-cond-assign': 2, 'no-console': 0, + 'no-const-assign': 2, 'no-constant-condition': [1, { checkLoops: false }], - 'no-dupe-args': 1, - 'no-dupe-keys': 1, + 'no-control-regex': 2, + 'no-debugger': 2, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-duplicate-imports': 1, 'no-else-return': 0, - 'no-empty-character-class': 0, + 'no-empty-character-class': 2, 'no-empty-function': 0, 'no-empty-pattern': 0, 'no-empty': [1, { allowEmptyCatch: true }], - 'no-eval': 1, - 'no-extend-native': 1, + 'no-eval': 0, + 'no-ex-assign': 2, + 'no-extend-native': 2, 'no-extra-bind': 1, 'no-extra-boolean-cast': 1, 'no-extra-label': 1, - 'no-extra-parens': 1, + 'no-extra-parens': [1, 'all', { conditionalAssign: false, returnAssign: false, nestedBinaryExpressions: false, ignoreJSX: 'multi-line', enforceForArrowConditionals: false }], 'no-extra-semi': 1, - 'no-fallthrough': 1, - 'no-floating-decimal': 1, - 'no-implicit-coercion': 1, + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-global-assign': 2, + 'no-implicit-coercion': 2, 'no-implicit-globals': 1, - 'no-implied-eval': 1, - 'no-inner-declarations': 1, + 'no-implied-eval': 2, + 'no-inner-declarations': [1, 'functions'], + 'no-invalid-regexp': 2, 'no-invalid-this': 1, - 'no-irregular-whitespace': 0, - 'no-iterator': 1, - 'no-label-var': 1, - 'no-labels': 1, - 'no-lone-blocks': 1, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': 2, + 'no-lone-blocks': 2, + 'no-lonely-if': 2, 'no-loop-func': 1, 'no-mixed-requires': 1, + 'no-mixed-spaces-and-tabs': 2, 'no-multi-assign': 1, 'no-multi-spaces': 1, - 'no-multi-str': 1, - 'no-multiple-empty-lines': 1, - 'no-native-reassign': 1, - 'no-negated-condition': 1, - 'no-new-func': 1, - 'no-new-object': 1, - 'no-new-require': 1, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [1, { max: 1 }], + 'no-native-reassign': 2, + 'no-negated-condition': 0, + 'no-negated-in-lhs': 2, + 'no-new-func': 2, + 'no-new-object': 2, + 'no-new-require': 2, 'no-new-symbol': 1, - 'no-new-wrappers': 1, + 'no-new-wrappers': 2, 'no-new': 1, - 'no-octal-escape': 1, - 'no-octal': 1, + 'no-obj-calls': 2, + 'no-octal-escape': 2, + 'no-octal': 2, 'no-path-concat': 1, - 'no-proto': 1, + 'no-proto': 2, 'no-prototype-builtins': 0, - 'no-redeclare': 1, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-restricted-globals': 2, 'no-return-assign': 1, 'no-return-await': 2, 'no-script-url': 1, 'no-self-assign': 1, 'no-self-compare': 1, - 'no-sequences': 1, - 'no-shadow-restricted-names': 1, - 'no-throw-literal': 1, - 'no-undef-init': 1, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-shadow': 0, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-template-curly-in-string': 0, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 1, + 'no-undef-init': 2, 'no-undef': 2, - 'no-unexpected-multiline': 1, - 'no-unreachable': 1, + 'no-unexpected-multiline': 2, + 'no-unneeded-ternary': [1, { defaultAssignment: false }], 'no-unreachable-loop': 1, - 'no-unused-expressions': 1, + 'no-unreachable': 2, 'no-unsafe-assignment': 0, 'no-unsafe-call': 0, - 'no-unsafe-finally': 0, + 'no-unsafe-finally': 2, 'no-unsafe-member-access': 0, - 'no-unsafe-negation': 0, + 'no-unsafe-negation': 2, 'no-unsafe-optional-chaining': 0, 'no-unsafe-return': 0, - 'no-unused-vars': [1, { args: 'none' }], - 'no-use-before-define': 1, + 'no-unused-expressions': 2, + 'no-unused-vars': [1, { vars: 'all', args: 'after-used' }], + 'no-use-before-define': 0, + 'no-useless-call': 2, 'no-useless-catch': 0, 'no-useless-escape': 0, 'no-useless-rename': 1, 'no-useless-return': 1, - 'no-var': 2, + 'no-var': 1, 'no-void': 1, - 'no-warning-comments': 1, - 'no-with': 1, - 'object-curly-spacing': [1, 'always'], + 'no-warning-comments': 0, + 'no-with': 2, + 'object-curly-spacing': [2, 'always', { objectsInObjects: true }], 'object-shorthand': 1, + 'one-var': [1, { initialized: 'never' }], + 'operator-linebreak': [0, 'after', { overrides: { '?': 'before', ':': 'before' } }], 'padded-blocks': [1, { switches: 'never' }], - 'prefer-const': 1, + 'prefer-const': [1, { destructuring: 'all', ignoreReadBeforeAssign: false }], 'prefer-promise-reject-errors': 1, 'quotes': [1, 'single'], - 'radix': 1, + 'radix': 2, 'rest-spread-spacing': 1, - 'semi-spacing': 1, + 'semi-spacing': [1, { before: false, after: true }], 'semi-style': 1, 'semi': [1, 'always'], - 'space-before-blocks': 1, - 'space-in-parens': 1, + 'space-before-blocks': [1, 'always'], + 'space-before-function-paren': [1, { anonymous: 'never', named: 'never', asyncArrow: 'always' }], + 'space-in-parens': [1, 'never'], 'space-infix-ops': 1, - 'space-unary-ops': 1, - 'spaced-comment': 0, + 'space-unary-ops': [1, { words: true, nonwords: false }], + 'spaced-comment': [0, 'always', { markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }], + 'strict': 2, 'switch-colon-spacing': 1, 'symbol-description': 1, - 'template-tag-spacing': [2, 'never'], 'template-curly-spacing': [2, 'never'], - 'valid-typeof': 1, - 'wrap-iife': 1, + 'template-tag-spacing': [2, 'never'], + 'unicode-bom': 1, + 'use-isnan': 2, 'valid-jsdoc': 1, - 'unicode-bom': 1 + 'valid-typeof': 2, + 'wrap-iife': [1, 'any'], + 'yoda': [1, 'never'] }, ignorePatterns: [ - 'lib/templates/*', - 'support/*', - 'test/fixtures/*', - 'test/support/*' + 'node_modules', + 'dist', + 'tmp', + 'temp', + 'lib/templates', + 'support', + 'test/fixtures', + 'test/support' ] }; diff --git a/.gitignore b/.gitignore index d15d1bdf..9d0dd22e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ # always ignore files -*.DS_Store *.sublime-* *.code-* *.log +.DS_Store +.env +.env.* # always ignore dirs temp diff --git a/LICENSE b/LICENSE index 1e49edf8..7cccaf9e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2016, Jon Schlinkert. +Copyright (c) 2015-present, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/index.js b/index.js index d06fb1cf..91d5cdcc 100644 --- a/index.js +++ b/index.js @@ -1 +1,3 @@ +'use strict'; + module.exports = require('./lib/Verb'); diff --git a/lib/Verb.js b/lib/Verb.js index 65f555b5..cf1b5892 100644 --- a/lib/Verb.js +++ b/lib/Verb.js @@ -92,7 +92,6 @@ class Verb extends Generate { } } - /** * Expose custom lookup function for resolving generators */ diff --git a/lib/commands/init.js b/lib/commands/init.js index 1b466dce..a9e460b4 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -4,29 +4,28 @@ const debug = require('debug')('base:cli:init'); const questions = require('../questions'); const utils = require('../utils'); -const filter = (app, val) => { - if (!val) return []; - if (typeof val === 'string') { - val = val.split(','); +const filterDeps = (app, names) => { + if (!names) return []; + + if (typeof names === 'string') { + names = names.split(','); } - const devDeps = app.pkg.get('devDependencies') || {}; - const deps = app.pkg.get('dependencies') || {}; - const len = val.length; - let idx = -1; + const devDeps = app.pkg.get('devDependencies'); + const deps = app.pkg.get('dependencies'); const res = []; - while (++idx < len) { - const dep = val[idx]; - if (dep && !devDeps.hasOwnProperty(dep) && !deps.hasOwnProperty(dep)) { - res.push(dep); + for (const name of names) { + if (name && !devDeps?.[name] && !deps?.[name]) { + res.push(name); } } + return res; }; const postInit = (app, answers, cb) => { - const plugins = filter(app, utils.get(answers, 'config.plugins')); + const plugins = filterDeps(app, utils.get(answers, 'config.plugins')); // eslint-disable-next-line no-negated-condition if (!plugins.length) { diff --git a/lib/generator.js b/lib/generator.js index 0380351d..93abddcb 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -196,6 +196,40 @@ module.exports = (verb, base) => { file(app, 'README.md', null, cb); }); + /** + * Add a `verb` config object to `package.json` in the current working directory. + * + * ```sh + * $ verb new:package-config + * ``` + * @name new:package-config + * @api public + */ + + app.task('package-config', cb => { + const config = app.pkg.get('verb'); + + if (config && app.options.force !== true) { + console.log('verb config already exists in package.json'); + cb(); + return; + } + + app.pkg.set('verb', { + toc: false, + layout: 'default', + tasks: ['readme'], + plugins: ['gulp-format-md'], + reflinks: ['verb'], + lint: { + reflinks: true + } + }); + + app.pkg.save(); + cb(); + }); + /** * Prompts the user to add a `.verb.md` (this task runs automatically when the * `verb` command is given if `verbfile.js` and `.verb.md` are both missing from the @@ -208,8 +242,9 @@ module.exports = (verb, base) => { * @api public */ - app.task('prompt-verbmd', { silent: true }, cb => { + app.task('prompt-verbmd', { silent: true }, async cb => { app.confirm('verbmd', 'Looks like .verb.md is missing, want to add one?'); + app.ask('verbmd', { save: false }, (err, answers) => { if (err) { cb(err); @@ -217,7 +252,29 @@ module.exports = (verb, base) => { } if (answers.verbmd) { - app.build('verbmd', cb); + const config = app.options.pkg ? 'package-config' : 'prompt-package-config'; + app.build(['verbmd', config], cb); + } else { + cb(); + } + }); + }); + + app.task('prompt-package-config', { silent: true }, cb => { + if (app.pkg.get('verb')) { + cb(); + return; + } + + app.confirm('package-config', 'Want to add a verb config to package.json?'); + app.ask('package-config', { save: false }, (err, answers) => { + if (err) { + cb(err); + return; + } + + if (answers['package-config']) { + app.build('package-config', cb); } else { cb(); } diff --git a/lib/tasks.js b/lib/tasks.js index 4442df9a..bbda95fc 100644 --- a/lib/tasks.js +++ b/lib/tasks.js @@ -2,7 +2,7 @@ const utils = require('./utils'); -module.exports = function(app, ctx, argv) { +module.exports = (app, ctx, argv) => { if (argv.init === true) { return []; } @@ -18,7 +18,7 @@ module.exports = function(app, ctx, argv) { app.store.get('defaultTasks') // Stored user-defined "default" tasks ]); - tasks = tasks.map(function(task) { + tasks = tasks.map(task => { let isDefaults = false; if (task.indexOf('new') === 0 || task.indexOf('store') === 0) { @@ -65,6 +65,7 @@ module.exports = function(app, ctx, argv) { } const verbmd = utils.exists('.verb.md'); + // If a `.verb.md` exists, but no verbfile.js, set `readme` as the default if (verbmd && !configExists) { return ['verb-generate-readme']; diff --git a/lib/templates/_verb.md b/lib/templates/_verb.md index 6ce05fd7..ca944cf1 100644 --- a/lib/templates/_verb.md +++ b/lib/templates/_verb.md @@ -1,5 +1,5 @@ ## Usage ```js -var {%= alias %} = require('{%= name %}'); +import {%= alias %} from '{%= name %}'; ``` diff --git a/lib/templates/readme.md b/lib/templates/readme.md index 70b5a431..b4670c07 100644 --- a/lib/templates/readme.md +++ b/lib/templates/readme.md @@ -5,5 +5,5 @@ ## Usage ```js -var {%= varname %} = require('<%= name %>'); +import {%= varname %} from '<%= name %>'; ``` diff --git a/lib/templates/verbfile.js b/lib/templates/verbfile.js index 9b3e91b3..c86585c9 100644 --- a/lib/templates/verbfile.js +++ b/lib/templates/verbfile.js @@ -3,10 +3,12 @@ layout: false --- 'use strict'; -module.exports = function(verb) { +module.exports = verb => { verb.use(require('verb-generate-readme')); - verb.helper('foo', function(str) { - return str; + + verb.helper('foo', input => { + return input; }); + verb.task('default', ['readme']); }; diff --git a/package.json b/package.json index 286e69a4..456d7714 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "main": "index.js", "preferGlobal": true, "bin": { - "v": "bin/verb.js" + "verb": "bin/verb.js" }, "engines": { "node": ">=0.10.0" @@ -70,7 +70,7 @@ "generate-foo": "^0.1.5", "generator-util": "^0.2.9", "graceful-fs": "^4.1.5", - "gulp": "^3.9.1", + "gulp": "^4.0.2", "gulp-eslint": "^3.0.1", "gulp-istanbul": "^1.0.0", "gulp-mocha": "^2.2.0", diff --git a/readme.md b/readme.md index 5121d4a7..6b4a5cc5 100644 --- a/readme.md +++ b/readme.md @@ -1,19 +1,19 @@

- +

-Verb is a documentation build system for GitHub projects powered by node.js. Verb has full support for [gulp](https://gulpjs.com/) and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! +Documentation build system for GitHub projects, powered by node.js. Verb has full support for gulp and assemble plugins and can be used to create documentation generators, themes, documentation websites and much more! # verb -[![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) [![Build Status](https://img.shields.io/travis/verbose/verb.svg?style=flat)](https://travis-ci.org/verbose/verb) +[![NPM version](https://img.shields.io/npm/v/verb.svg?style=flat)](https://www.npmjs.com/package/verb) [![NPM monthly downloads](https://img.shields.io/npm/dm/verb.svg?style=flat)](https://npmjs.org/package/verb) A project without documentation is like a project that doesn't exist. Verb solves this by making it dead simple to generate documentation, using simple markdown templates, with zero configuration required. -## What can Verb do? +## What is verb? **Documentation system** @@ -25,7 +25,7 @@ Yes! See [verb-generate-readme](https://github.com/verbose/verb-generate-readme) **Highly pluggable** -Verb is also highly pluggable, with first-class plugin support. Moreover, verb also supports [gulp](http://gulpjs.com), [base](https://github.com/node-base/base), [assemble](https://github.com/assemble/assemble), [generate](https://github.com/generate/generate) and [update](https://github.com/update/update) plugins. +Verb is also highly pluggable, with first-class plugin support. Moreover, verb also supports [gulp](https://gulpjs.com), [base](https://github.com/node-base/base), [assemble](https://github.com/assemble/assemble), [generate](https://github.com/generate/generate) and [update](https://github.com/update/update) plugins. ### Why use verb? @@ -39,8 +39,8 @@ For example, [verb-generate-readme](https://github.com/verbose/verb-generate-rea **Templating** -* Render templates with any node.js engine, including [handlebars](http://www.handlebarsjs.com/), [lodash](https://lodash.com/), [nunjucks](https://github.com/mozilla/nunjucks), and all [consolidate](https://github.com/visionmedia/consolidate.js) engines. -* Convert markdown to HTML, or render markdown templates back to markdown +* Render templates with any node.js engine, including [handlebars](https://www.handlebarsjs.com/), [lodash](https://lodash.com/), [nunjucks](https://github.com/mozilla/nunjucks), and all [consolidate](https://github.com/ladjs/consolidate) engines. +* Convert markdown to HTML, or render markdown templates back to markdown (see [.verb.md](#verbmd)) * Template collections * Support for "pages", "partials", and "layouts" * Helper support (sync and async!) @@ -54,7 +54,7 @@ For example, [verb-generate-readme](https://github.com/verbose/verb-generate-rea **Plugins** -Verb has rich plugin support, along with native support for [gulp plugins](https://gulpjs.com/plugins/), so if you want to publish a documentation site, you can do things like: +Verb has rich plugin support, along with native support for [gulp plugins][], so if you want to publish a documentation site, you can do things like: * Ignore files marked as "drafts" * CSS minification or reduction @@ -64,13 +64,13 @@ Verb has rich plugin support, along with native support for [gulp plugins](https * Image compression * HTML minification and linting * RSS feeds -* Anything else you can do with [gulp plugins](https://gulpjs.com/plugins/) +* Anything else you can do with [gulp plugins][] **Generators** Verb supports [generate](https://github.com/generate/generate) generators, which are plugins that are registered by name and can be run by command line or API. Generators can also be published to npm and installed globally or locally. -See the [generate](https://github.com/generate/generate) project for more details. +See the [generate](https://github.com/generate/generate) project and [documentation](https://github.com/generate/generatedocs) for more details. ## Table of Contents @@ -85,8 +85,6 @@ See the [generate](https://github.com/generate/generate) project for more detail - [Config flags](#config-flags) - [Config examples](#config-examples) - [Other flags](#other-flags) -* [API](#api) - - [Verb](#verb) * [Upgrading](#upgrading) * [About](#about) - [Related projects](#related-projects) @@ -105,7 +103,7 @@ _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc]( To use verb's CLI, you will first need to install it globally. You can do that now with the following command: ```sh -$ npm --global install verb +$ npm --global install verb ``` This adds the `verb` command to your system path, allowing it to be run from any directory. @@ -167,7 +165,7 @@ When a function is exported, we refer to these as [verb generators](#generators) ```js module.exports = function(app) { - // "app" is an instance of verb created by Verb's CLI for this file + // "app" is an instance of verb created by Verb's CLI for this file }; ``` @@ -351,23 +349,6 @@ Display the currently defined cwd: $ verb --cwd ``` -## API - -### [Verb](index.js#L25) - -Create a verb application with `options`. - -**Params** - -* `options` **{Object}**: Settings to initialize with. - -**Example** - -```js -var verb = require('verb'); -var app = verb(); -``` - ## Upgrading **Clear your cache and re-install** @@ -383,48 +364,47 @@ $ npm cache clean && npm i -g verb ### Related projects * [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit") -* [base](https://www.npmjs.com/package/base): base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting… [more](https://github.com/node-base/base) | [homepage](https://github.com/node-base/base "base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting with a handful of common methods, like `set`, `get`, `del` and `use`.") +* [base](https://www.npmjs.com/package/base): Framework for rapidly creating high quality, server-side node.js applications, using plugins like building blocks | [homepage](https://github.com/node-base/base "Framework for rapidly creating high quality, server-side node.js applications, using plugins like building blocks") * [generate](https://www.npmjs.com/package/generate): Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the… [more](https://github.com/generate/generate) | [homepage](https://github.com/generate/generate "Command line tool and developer framework for scaffolding out new GitHub projects. Generate offers the robustness and configurability of Yeoman, the expressiveness and simplicity of Slush, and more powerful flow control and composability than either.") * [update](https://www.npmjs.com/package/update): Be scalable! Update is a new, open source developer framework and CLI for automating updates… [more](https://github.com/update/update) | [homepage](https://github.com/update/update "Be scalable! Update is a new, open source developer framework and CLI for automating updates of any kind in code projects.") ### Community -Are you using [Generate](https://github.com/generate/generate) in your project? Have you published a [generator](https://github.com/generate/generate/blob/master/docs/generators.md) and want to share your project with the world? - -Here are some suggestions! +Bigger community means more plugins, better support and more progress. Help us make Generate better by spreading the word: -* If you get like Generate and want to tweet about it, please feel free to mention `@generatejs` or use the `#generatejs` hashtag -* Show your love by starring [Generate](https://github.com/generate/generate) and `verb` -* Get implementation help on [StackOverflow](http://stackoverflow.com/questions/tagged/generate) (please use the `generatejs` tag in questions) -* **Gitter** Discuss Generate with us on [Gitter](https://gitter.im/generate/generate) -* If you publish an generator, thank you! To make your project as discoverable as possible, please add the keyword `generategenerator` to package.json. +* Show your love by starring the project +* Tweet about Generate. Mention using `@generatejs`, or use the `#generatejs` hashtag +* Get implementation help on [StackOverflow](http://stackoverflow.com/questions/tagged/generate) with the `generatejs` tag +* Discuss Generate with us on [Gitter](https://gitter.im/generate/generate) +* If you publish a generator, to make your project as discoverable as possible, please add the unique keyword `generategenerator` to your project's package.json. ### Contributing Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). -Please read the [contributing guide](.github/contributing.md) for avice on opening issues, pull requests, and coding standards. +Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards. ### Running tests -Install dev dependencies: +Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: ```sh -$ npm install -d && npm test +$ npm install && npm test ``` ### Author **Jon Schlinkert** -* [github/jonschlinkert](https://github.com/jonschlinkert) -* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) +* [GitHub Profile](https://github.com/jonschlinkert) +* [Twitter Profile](https://twitter.com/jonschlinkert) +* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert) ### License -Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert). -Released under the [MIT license](https://github.com/verbose/verb/blob/master/LICENSE). +Copyright © 2024, [Jon Schlinkert](https://github.com/jonschlinkert). +Released under the [MIT License](LICENSE). *** -_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.28, on July 29, 2016._ +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on July 10, 2024._ \ No newline at end of file diff --git a/verbfile.js b/verbfile.js index f32a4b60..0f8185f2 100644 --- a/verbfile.js +++ b/verbfile.js @@ -1,20 +1,20 @@ 'use strict'; -var through = require('through2'); -var reflinks = require('gulp-reflinks'); -var format = require('gulp-format-md'); -var utils = require('./lib/utils'); +const through = require('through2'); +const reflinks = require('gulp-reflinks'); +const format = require('gulp-format-md'); +const utils = require('./lib/utils'); module.exports = function(app) { app.use(require('verb-generate-readme')); app.task('docs', ['setup'], function(cb) { app.layouts('docs/src/templates/layouts/*.md'); - return app.src('docs/src/*.md', {layout: 'default'}) + return app.src('docs/src/*.md', { layout: 'default' }) .pipe(app.renderFile('*')) .pipe(reflinks()) .pipe(format()) - .pipe(app.dest('docs')) + .pipe(app.dest('docs')); }); app.task('default', ['readme'], function() {