├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── contrib
├── examples
│ ├── deg-to-rad.js
│ ├── pi.js
│ ├── rad-to-deg.js
│ ├── to-deg.js
│ └── to-rad.js
├── get-path.js
├── help.js
├── running-tasks.js
└── standard-task.js
├── gulpfile.js
├── index.js
├── lib
├── dependency-error.js
├── gulp-di.js
├── normalize-path.js
├── parse-argument.js
├── read-directory.js
└── resolver.js
├── modules
└── paths.js
├── package-lock.json
├── package.json
├── tasks
├── default.js
├── examples.js
├── mocha.js
└── semistandard.js
└── test
├── 00.js
├── 01-parse-argument.js
├── 02-resolver.js
├── 03-read-directory.js
├── 10-gulp-di.js
├── 11-builtin.js
└── 20-contrib.js
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-pro
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "lts/*"
4 | - "node"
5 | - "12"
6 | - "11"
7 | - "10"
8 | - "9"
9 |
10 | env:
11 | - ENV="development"
12 |
13 | before_script:
14 | - npm install -g gulp
15 |
16 | install:
17 | - npm install
18 |
19 | after_success: 'npm run coveralls'
20 |
21 | script: npm test
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 0.1.0 - 11/27/2019
2 |
3 | - Adapting to changes in gulp 4.0 (#9)
4 | - Removing CI tests for older Node.js versions below 5.x.
5 | - Removing support for Node below 9.x.
6 |
7 | 0.0.4 - 11/11/2016
8 |
9 | - task() and module() may take absolute or relative paths
10 | - using a fork of [node-introspect](https://github.com/orzarchi/node-introspect.git) instead of [parse-function](https://github.com/tunnckoCore/parse-function)
11 | - adopting [https://github.com/Flet/semistandard](semistandard) style
12 |
13 | 0.0.33 - 05/03/2016
14 |
15 | - Updating depencendies and adapting to changes
16 |
17 | 0.0.32 - 04/02/2016
18 |
19 | - Correcting an example in this file
20 |
21 | 0.0.31 - 03/13/2016
22 |
23 | - options.argv for "runningTasks" test
24 | - options.parentDir
25 | - updating documentation
26 | - extending test suite, adding code coverage report
27 |
28 | 0.0.3 - 03/12/2016
29 |
30 | - ES2015 rewrite
31 | - CI using Travis
32 | - using and providing [lodash](https://github.com/lodash/lodash) by default
33 | - Adding "standardTask" and "getPath" helpers
34 | - supporting [gulp-load-plugins](https://www.npmjs.com/package/gulp-load-plugins)'s lazy loading of gulp plugins
35 |
36 | 0.0.2 - 02/01/2016
37 |
38 | - Updating dependencies
39 | - Adding "runningTasks" helper function
40 | - Exposing gulp-util as "gutil" by default
41 | - Parsing of multi-line comments rewritten
42 | - added new options : noModules, noBuiltin, noRunningTasks
43 | - ES2015+ support with [parse-function](https://github.com/tunnckoCore/parse-function)
44 |
45 | 0.0.1 - 12/20/2015
46 |
47 | - Initial release, incorporating Resolver and tests from [dijs](https://www.npmjs.com/package/dijs).
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [gulp](http://gulpjs.com)-di
2 |
3 |
4 |
5 |
6 |
7 |
24 |
25 | gulp-di is a dependency injection framework for the [Gulp](http://gulpjs.com)
26 | streaming build system.
27 |
28 | - Shrink your Gulpfile to less than ten lines.
29 | - Manage gulp plugins solely via npm and variables in your task module files.
30 | - No major refactoring required.
31 |
32 | See below for further examples!
33 |
34 | ## Installation
35 |
36 | ````bash
37 | $ npm install --save gulp-di
38 | ````
39 |
40 | ## Built-in dependencies
41 |
42 | You can use the following dependencies in your modules and tasks.
43 |
44 | | Name | Type | Description |
45 | |--------------|----------|-----------------------------------------------|
46 | | _ | Function | [lodash](https://github.com/lodash/lodash) |
47 | | basePath | Function | Resolves a path relative to your project path |
48 | | chalk | Object | [chalk](https://www.npmjs.com/package/chalk) for using colors when logging |
49 | | gulp | Object | The gulp instance |
50 | | gutil | Object | [gulp-util](https://www.npmjs.com/package/gulp-util) |
51 | | log | Function | [gulp-util](https://www.npmjs.com/package/gulp-util)'s log |
52 | | Package | Object | package.json as object |
53 | | runningTasks | Array | currently running gulp tasks (experimental) |
54 | | taskInfo | Object | infos about task functions (experimental) |
55 |
56 | In addition, all Gulp modules which are installed in your package.json are
57 | available in your modules, driven by [gulp-load-plugins](https://www.npmjs.com/package/gulp-load-plugins).
58 |
59 | For example, if you install the "gulp-concat" as devDependency (in your
60 | package.json) plugin using ...
61 |
62 | ````bash
63 | $ npm install --save-dev gulp-concat
64 | ````
65 |
66 | ... it will become available as dependency in each module file:
67 |
68 | ````js
69 | // tasks/concat-texts.js
70 | module.exports = (gulp, concat) => {
71 | /* Concatenates all *.txt files from src/ to public/result.txt */
72 | return gulp.src('src/**/*.txt')
73 | .pipe(concat('result.txt'))
74 | .pipe(gulp.dest('public/'));
75 | };
76 | ````
77 | Please read the API notes below for configuring a pattern for packages which
78 | do not start with "gulp-" or "gulp.*".
79 |
80 | ## Example: Basic refactoring
81 |
82 | Instead of declaring all gulp tasks in a single file, gulp-di allows you to
83 | take a more modular approach. You can separate your stream definitions from
84 | configurations while gulp-di handles the majority of your existing calls to
85 | require() for you.
86 |
87 | In this example, we will see how we can move a call to gulp.task() to a
88 | separate file.
89 |
90 | Additionally, we will see how we can detach a constant (in this case: a glob
91 | matching all files with the "jpg" extension) from the actual task.
92 |
93 | The following task
94 |
95 | ````js
96 | /* gulpfile.js (previous version) */
97 |
98 | const gulp = require('gulp');
99 | gulp.task('images', () => {
100 | return gulp.src('./**/*.jpg')
101 | .pipe(gulp.dest('output/'));
102 | });
103 | ````
104 |
105 | would be re-factored to the file at tasks/images.js as following.
106 |
107 | ````js
108 | /* tasks/images.js */
109 |
110 | module.exports = (gulp, imagesPath) => {
111 | gulp.task('images', () => {
112 | return gulp.src(imagesPath)
113 | .pipe(gulp.dest('output/'));
114 | });
115 | };
116 | ````
117 |
118 | Notice that the function uses the "imagePath" constant. Such constants can
119 | be defined in your Gulpfile files in order to separate them from the tasks.
120 |
121 | Thus, you can now use all of your Functional programming skills with Gulp and
122 | **assemble** your tasks from other declarations and build much more
123 | flexible and re-usable tasks.
124 |
125 | gulp-di should help you to reduce your Gulpfile's complexity.
126 | In this example, we will declare additionally the "imagePath" constant which is
127 | being used in our "images" task.
128 |
129 | ````js
130 | /* gulpfile.js (refactored version) */
131 |
132 | const gulp = require('gulp');
133 | let di = require('gulp-di')(gulp);
134 | .tasks('./tasks')
135 | .provide('imagesPath', 'src/**/*.jpg')
136 | .resolve();
137 | ````
138 |
139 | Additionally, you can now "inject" arbitrary values into your functions, e. g.
140 | run-time configurations, asynchronous methods for usage with Gulp's asynchronous
141 | API, constants etc.
142 |
143 | The following example uses constants and modules in order to compose a helper
144 | function.
145 |
146 | ````js
147 | /* gulpfile.js (refactored version) */
148 |
149 | const gulp = require('gulp');
150 | let di = require('gulp-di')(gulp);
151 | .tasks('./tasks')
152 | .provide('sourcePath', 'src') // Provide a string constant.
153 | .module('extensionPath', (sourcePath) => { // Add a module providing a
154 | // helper function to get a
155 | // extension name.
156 |
157 | return (extname) => `${sourcePath}/**/*.${extname}`;
158 |
159 | })
160 | .task(function (gulp, extensionPath) {
161 | gulp.task('copy-images', function () {
162 | // Copies all *.jpg images from src/ to public/
163 | return gulp.src(extensionPath('jpg')).pipe(gulp.dest('public/'));
164 | });
165 | })
166 | .resolve();
167 | ````
168 |
169 | ## API
170 |
171 | ### GulpDI(_object_ gulp, _object_ options)
172 |
173 | Creates a new GulpDI instance. The first argument must always be the gulp
174 | instance from your Gulpfile.
175 |
176 | If you specify options, these will be passed to [gulp-load-plugins](https://www.npmjs.com/package/gulp-load-plugins) under the hood ("camelize" cannot be configured at the moment).
177 |
178 | ```js
179 | const gulp = require('gulp');
180 | let di = GulpDI(gulp, {
181 | DEBUG: false, // when set to true, the plugin will log info to console. Useful for bug reporting and issue debugging
182 | pattern: ['gulp-*', 'gulp.*'], // the glob(s) to search for
183 | config: 'package.json', // where to find the plugins, by default searched up from process.cwd()
184 | scope: ['dependencies', 'devDependencies', 'peerDependencies'], // which keys in the config to look within
185 | replaceString: /^gulp(-|\.)/, // what to remove from the name of the module when adding it to the context
186 | rename: {}, // a mapping of plugins to rename
187 | renameFn: function (name) { ... } // a function to handle the renaming of plugins (the default works)
188 | });
189 | ```
190 | The following options are additionally available:
191 |
192 | | Name | Type | Description |
193 | |------------------|----------|-----------------------------------------------|
194 | | DEBUG | Boolean | Whether to log debugging information or not |
195 | | noBuiltin | Boolean | Disables helpers: basePath, log, Package |
196 | | noHelp | Boolean | Toggles the "help" task/providing taskInfo |
197 | | noModules | Boolean | Do not expose chalk, gutil and lodash (_) |
198 | | noRunningTasks | Boolean | Do not provide the "runningTasks" function |
199 |
200 | ### Chainable methods
201 |
202 | The following methods are chainable:
203 |
204 | #### .inject(_function_ fn, returnValue)
205 |
206 | Executes code which depends on the current's instance dependencies.
207 | Please note that you will have to resolve() your instance if you should depend
208 | on anything else than payloads which have been initialized by provide().
209 |
210 | When returnValue is set, this method returns the return value of the injected
211 | function (e.g. the function is not chainable when set to true)
212 |
213 | ````js
214 | di.provide({ 'PI' : Math.PI, 'DEG_TO_RAD' : 180 / Math.PI });
215 | let circle = di.inject(['PI','DEG_TO_RAD', (PI, DEG_TO_RAD) => {
216 | return 2 * PI * DEG_TO_RAD;
217 | }], true);
218 | console.log(circle); // 360
219 | ````
220 |
221 | #### .provide(_string_ name, _*_ payload)
222 |
223 | Provides a constant for further usage in modules and tasks. You can can also
224 | provide a hashmap of constants as following:
225 | ````js
226 | di.provide({ 'PI' : Math.PI, 'SIN' : Math.sin });
227 | ````
228 |
229 | #### .task(_function_ fn)
230 |
231 | Adds a task module. Add your provide()d dependencies to the function's argument
232 | list to require a dependency. Their order does not matter.
233 |
234 | Please note that tasks don't have names (in contrast to modules) as
235 | you shouldn't depend on them using gulp-di. Use the "deps" array when calling
236 | gulp.task() instead in order to achieve a specific task execution order.
237 |
238 | You can also use a hashmap as following:
239 |
240 | ````js
241 | di.task({
242 | /* Copies all *.png images from src/ to public/ */
243 | assets : (gulp) => gulp.src('src/**/*.png').pipe(gulp.dest('public/'))
244 | });
245 | ````
246 |
247 | #### .module(_string_ name,_function_ fn)
248 |
249 | Adds a module with the given name. In contrast to a task, a module has a name
250 | and its return value will be provided - thus, modules can be injected into tasks
251 | as well as your constants.
252 |
253 | You can provide a hashmap, too.
254 |
255 | #### .resolve()
256 |
257 | Resolves all dependencies and runs all task modules. You need to call this
258 | method after your declarations.
259 |
260 | #### .tasks(_string_ directory)
261 |
262 | Loads all tasks from the specified directory, using [require-dir](https://www.npmjs.com/package/require-dir).
263 |
264 | #### .modules(_string_ directory)
265 |
266 | Loads all modules from the specified directory.
267 |
268 | #### .byId(_string_ name)
269 |
270 | Returns the specified dependency. When you are demanding modules, please keep in
271 | mind that you will need to call resolve() beforehand, otherwise, this would
272 | return your module function.
273 |
274 | ## Using dependency injection in general
275 |
276 | By design, dependency injection is opt-in for everything, thus you can use
277 | gulp-di for use cases beyond your gulp tasks.
278 |
279 | ### Setting values
280 |
281 | When using DI, you can set values on your instance to arbitrary values. They
282 | will become available in your module files.
283 |
284 | ````js
285 | const gulp = require('gulp');
286 | let di = require('gulp-di')(gulp, { DEBUG: false });
287 | di.provide('PI', Math.PI);
288 | di.provide('RAD_TO_DEG', 180 / Math.PI);
289 | ````
290 |
291 | ### Declaring task modules
292 |
293 | You can then declare a single function or a whole directory of
294 | CommonJS modules using the tasks() method.
295 |
296 | ````js
297 | di.task((PI, RAD_TO_DEG, basePath) => {
298 | console.log((PI * 2 * RAD_TO_DEG) + '°');
299 | });
300 | ````
301 |
302 | Note that you have access to the gulp-di instance in the task function's scope.
303 | You could f.e. access the "options" object in your function as following:
304 |
305 | ````js
306 | di.task(() => {
307 | if (this.options.DEBUG) { console.log('Debug is enabled'); }
308 | });
309 | ````
310 |
311 | ### Running all modules and resolving dependencies
312 |
313 | You need to execute all provided values and modules by running resolve()
314 | afterwards.
315 |
316 | While you place calls to task(), tasks() and provide, the execution order of your
317 | depends on the declared dependencies. With resolve(), the graph is being
318 | resolved and all modules are called with the provided values.
319 |
320 | ````js
321 | di.resolve();
322 | // logs 360°
323 | ````
324 |
325 | ## Experimental functionalities
326 |
327 | Please use these features with caution for now.
328 |
329 | ### gulp help
330 |
331 | This example is included by default.
332 |
333 | An included experiment uses the API to intersect all calls to gulp.task to
334 | collect information about the tasks.
335 |
336 | It registers a gulp "help" task which logs then all comments included in
337 | gulp.task. In addition, a "taskInfo" hashmap is available for injection. It
338 | contains the information about your gulp tasks which is being also used by
339 | the "help" task.
340 |
341 | ````js
342 | const gulpDi = require('gulp-di');
343 | let di = gulpDi()
344 | .task(function (gulp, taskInfo, log, chalk) {
345 | gulp.task('my-help', function () {
346 | /* Logs basic information about tasks. */
347 | for (let name in taskInfo) {
348 | let entry = taskInfo[name];
349 | log(chalk.magenta(name), 'with dependencies', entry.deps);
350 | }
351 | });
352 | })
353 | .resolve();
354 | ````
355 | ### runningTasks
356 |
357 | This function returns an array of strings, containing all current Gulp tasks,
358 | including dependencies.
359 |
360 | ### Helper functions
361 |
362 | The following functions are currently not integrated into gulp-di, but you can
363 | require them as following:
364 |
365 | ````js
366 | const getPath = require('gulp-di/contrib/get-path');
367 | const standardTask = require('gulp-di/contrib/standard-task');
368 | ````
369 |
370 | #### getPath(_string_ key)
371 |
372 | Returns a property specified by the given dot-delimited key:
373 |
374 | ````js
375 | const data = {
376 | at : 1350506782000,
377 | ids : ['507f1f77bcf86cd799439011', '507f191e810c19729de860ea']
378 | };
379 |
380 | let PI = getPath(Math, 'PI');
381 | let at = getPath(data, 'at'));
382 | let firstId = getPath(data, 'ids.0');
383 | ````
384 |
385 | #### standardTask(_string_ name, _string_ src, _string_ dest, ...)
386 |
387 | Generates a default task, assuming you want to pipe _src_ through the plugin specified by _name_ to _dest_. All parameters following "dest" are serialized if necessary and passed
388 | to the plugin.
389 |
390 | Using this method, you will just need to install a gulp plugin with npm and set
391 | up a task as following:
392 |
393 | ````js
394 | let taskFn = standardTask('jade', 'templates/**/*.jade', 'public', { pretty : false });
395 |
396 | // Register the task function
397 | di.task(taskFn);
398 |
399 | console.log(taskFn.toString());
400 |
401 | function jadeTask(gulp, jade) {
402 | /**
403 | * Generated task function, registers a "jade" task.
404 | * @param {object} gulp The current gulp instance
405 | * @param {jade} jade The "jade" gulp plugin
406 | */
407 |
408 | gulp.task("jade", () => {
409 | /**
410 | * jade task, declared at 10-contrib.js:44
411 | */
412 | return gulp.src("templates/**/*.jade")
413 | .pipe(jade({"pretty":false}))
414 | .pipe(gulp.dest("public"));
415 | });
416 | }
417 | ````
418 |
419 | ## FAQ
420 |
421 | ### How do I insert tasks? Gulp claims "Task 'default' is not in your gulpfile" all the time!
422 |
423 | Please call the resolve() method after all of your module and task declarations.
424 |
425 | ### gulp-di does not find my module!
426 |
427 | In order to make your gulp plugins available, gulp-di uses
428 | [gulp-load-plugins](https://www.npmjs.com/package/gulp-load-plugins) internally
429 | by default.
430 |
431 | The default pattern is ['gulp-*', 'gulp.*', '!gulp-di']. You need to initialize
432 | gulp-di using the "options" object in order to make plugins with arbitrary names
433 | available:
434 |
435 | ````js
436 | const gulp = require('gulp');
437 | let di = require('gulp-di')(gulp, {
438 | pattern : ['gulp-*', 'gulp.*', '!gulp-di', 'webpack-stream']
439 | });
440 |
441 | di.task((webpackStream) => {
442 | gulp.task('webpack', () => {
443 | return gulp.src('src/client.js')
444 | .pipe(webpackStream({ output : { filename : '[name].js' }}))
445 | .pipe(gulp.dest('public'));
446 | });
447 | });
448 |
449 | di.resolve();
450 |
451 | ````
452 |
453 | ### Is it possible to add tasks asynchronously?
454 |
455 | No, you will need to set up asynchronous tasks which depend on each other in
456 | order to perform asynchronous tasks. Nevertheless, all tasks must be declared
457 | synchronously in order to use Gulp correctly.
458 |
459 | ### I dislike the camelCaseName of my favorite module, can I change it?
460 |
461 | Using [gulp-load-plugins](https://www.npmjs.com/package/gulp-load-plugins), you
462 | can change the assigned name for arbitrary modules.
463 |
464 | #### Using a ``rename`` object:
465 |
466 | ````js
467 | let di = require('gulp-di')(gulp, {
468 | rename : {
469 | webpackStream : 'webpack'
470 | }
471 | });
472 | ````
473 |
474 | #### Using a ``renameFn``:
475 | ````js
476 | let replacements = { webpackStream : 'webpack'};
477 | let di = require('gulp-di')(gulp, {
478 | renameFn : (key) => return replacements[key]
479 | });
480 | ````
481 |
482 | ## License
483 |
484 | MIT
485 |
--------------------------------------------------------------------------------
/contrib/examples/deg-to-rad.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (PI) {
4 | return PI / 180;
5 | };
6 |
--------------------------------------------------------------------------------
/contrib/examples/pi.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function () {
4 | return Math.PI;
5 | };
6 |
--------------------------------------------------------------------------------
/contrib/examples/rad-to-deg.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (PI) {
4 | return 180 / PI;
5 | };
6 |
--------------------------------------------------------------------------------
/contrib/examples/to-deg.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (RAD_TO_DEG) {
4 | return (radians) => radians * RAD_TO_DEG;
5 | };
6 |
--------------------------------------------------------------------------------
/contrib/examples/to-rad.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function (DEG_TO_RAD) {
4 | return (degrees) => degrees * DEG_TO_RAD;
5 | };
6 |
--------------------------------------------------------------------------------
/contrib/get-path.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @method getPath
5 | * @param {string} key
6 | * @returns {*|null}
7 | */
8 |
9 | function getPath (obj, key) {
10 | key = typeof key !== 'undefined' ? key : null;
11 |
12 | if (key === null) {
13 | return obj;
14 | } else if (/\./.test(key) === false) {
15 | return obj[key];
16 | }
17 |
18 | /**
19 | * Returns a payload or a nested property of any provided payload.
20 | * @method step
21 | * @param {*} object
22 | * @param {string[]} depPath
23 | * @private
24 | */
25 |
26 | function step (object, depPath) {
27 | return depPath.length ? step(object[depPath.shift()], depPath) : object;
28 | }
29 |
30 | const depPath = key.split(/\./g);
31 | return step(obj, depPath);
32 | }
33 |
34 | module.exports = getPath;
35 |
--------------------------------------------------------------------------------
/contrib/help.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const pad = require('pad');
3 | const LINE_PADDING = 5;
4 |
5 | module.exports = function HelpTask (gulp) {
6 | /**
7 | * Modifies the gulp.task() function and builds a list of all tasks.
8 | *
9 | * Adds a Gulp "help" task which lists them with additional information from
10 | * function comments.
11 | *
12 | * Please use it as following:
13 | *
14 | * ````js
15 | * module.exports = function (gulp) {
16 | * // Copies ./README.md to ./tmp
17 | *
18 | * return gulp.src('./README.md')
19 | * .pipe(gulp.dest('./tmp'))
20 | * }
21 | * ````
22 | *
23 | * This module needs to be injected before each successive require('gulp')
24 | * as it overwrites the "gulp.task" function.
25 | *
26 | * As such modifications might lead to unexpected behavior, this module is
27 | * experimental.
28 | */
29 |
30 | const Package = this.byId('Package', true) || {};
31 | const chalk = this.byId('chalk', true);
32 | const log = this.byId('log', true) || console.log.bind(console);
33 |
34 | const parseFn = require('parse-function');
35 | const extractComments = require('extract-comments');
36 | const _task = gulp.task;
37 | const taskInfo = {};
38 | const RGX_LF = /\r\n|\r|\n/g;
39 | const DEBUG = this.options.DEBUG;
40 |
41 | if (DEBUG) {
42 | log('Wrapping gulp.task() "gulp help" task...');
43 | }
44 |
45 | // Allows to use this hashmap as dependency
46 |
47 | this.provide('taskInfo', taskInfo);
48 |
49 | /**
50 | * Wraps the gulp.task() method, registers information about the provided
51 | * tasks.
52 | * @method task
53 | * @param {string} id
54 | * @param {string[]} deps
55 | * @param {function} fn
56 | */
57 |
58 | gulp.task = function (id, deps, fn) {
59 | const args = [].slice.apply(arguments);
60 |
61 | if (typeof deps === 'function') {
62 | fn = arguments[1];
63 | deps = [];
64 | }
65 |
66 | let entry = null;
67 |
68 | if (typeof fn === 'function') {
69 | const info = parseFn(fn.toString());
70 | entry = { name: id };
71 | entry.description = `Runs the ${id} task (no description)`;
72 | const comments = extractComments(info.body, {first: true});
73 | if (comments.length) {
74 | const comment = comments[0];
75 | const lines = comment.raw
76 | .split(RGX_LF)
77 | .map(function (line) {
78 | return line.replace(/( )*(\*|\/+)/g, '');
79 | });
80 | entry.description = lines;
81 | fn.description = lines;
82 | }
83 | }
84 | if (entry) {
85 | entry.deps = deps;
86 | if (DEBUG) {
87 | let line = [`Adding ${chalk.cyan(entry.name)} task`];
88 | if (deps.length) {
89 | line.push(` - depending on ${chalk.magenta(deps.join(' '))}`);
90 | }
91 | log.apply(chalk, line);
92 | }
93 | taskInfo[id] = entry;
94 | }
95 | return _task.apply(gulp, args);
96 | };
97 |
98 | // Registers the "help" task.
99 |
100 | function HelpTask (done) {
101 | /* Prints an overview over all available Gulp tasks. */
102 |
103 | let lines = [''];
104 | const paddingStr = new Array(LINE_PADDING).join(' ');
105 |
106 | lines.push(' ' + Package.name + ' ' + Package.version, '');
107 |
108 | if (Package.description && Package.description.length) {
109 | lines.push(' ' + Package.description, '');
110 | }
111 |
112 | const taskIds = Object.keys(taskInfo);
113 | const taskLengths = taskIds.map(function (id) { return id.length; });
114 | const maxLength = Math.max.apply(Math, taskLengths);
115 | const keys = Object.keys(taskInfo);
116 | keys.sort();
117 | for (var i = 0; i < keys.length; i++) {
118 | const key = keys[i];
119 | const entry = taskInfo[key];
120 | const paddingLength = maxLength;
121 | const str = new Array(paddingLength + 2).join(' ');
122 | let descriptionLine = `Runs the ${key} task.`;
123 | if (entry.description) descriptionLine = mapDescription(str, entry.description);
124 | lines.push(' ' + pad(entry.name, maxLength) + paddingStr + descriptionLine.join('\n').trim());
125 | lines.push('');
126 | }
127 |
128 | lines.forEach((line) => console.log(line));
129 |
130 | done();
131 | /**
132 | * Maps the given array of comment lines by prepending whitespace if
133 | * necessary.
134 | * @method mapDescription
135 | * @param {string} str
136 | * @param {string[]} lines
137 | * @return {string[]}
138 | * @private
139 | */
140 |
141 | function mapDescription (str, lines) {
142 | if (typeof lines === 'string') lines = [lines];
143 | return lines.map(function (line, index) {
144 | line = line.trim();
145 | return index ? str + paddingStr + line : line;
146 | });
147 | }
148 | }
149 |
150 | HelpTask.description = 'Displays initial help.';
151 | gulp.task('help', HelpTask);
152 | };
153 |
--------------------------------------------------------------------------------
/contrib/running-tasks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function RunningTasks (gulp) {
4 | const DEBUG = this.options.DEBUG;
5 | const log = this.byId('log', true) || console.log.bind(console);
6 | const gutil = this.byId('gutil', true) || { env: { _: process.argv } };
7 |
8 | /**
9 | * Adds a function returning an array of strings, containing all current
10 | * Gulp tasks, including dependencies.
11 | *
12 | * Use it as following:
13 | *
14 | * ````js
15 | * module.exports = function (gulp, log, Package, runningTasks) {
16 | * gulp.task('info', function () {
17 | * log('Building ' + chalk.magenta(Package.name) + ', running: ' + chalk.cyan(runningTasks().join(' ')))
18 | * })
19 | * }
20 | * ````
21 | *
22 | * This module is currently experimental.
23 | */
24 |
25 | if (DEBUG) {
26 | log('Adding runningTasks helper...');
27 | }
28 |
29 | this.provide('runningTasks', () => {
30 | const registry = gulp._registry;
31 | const args = this.options.argv || gutil.env._;
32 | const taskNames = Object.keys(registry.tasks());
33 | // Filter all available task names using gutil.env._
34 | const cliTasks = taskNames.filter((name) => args.indexOf(name) !== -1);
35 | return cliTasks;
36 | });
37 | };
38 |
--------------------------------------------------------------------------------
/contrib/standard-task.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-eval */
2 | 'use strict';
3 |
4 | const parseStack = require('parse-stack');
5 | const _ = require('lodash');
6 | const path = require('path');
7 |
8 | /**
9 | * Generates a default task, assuming you want to pipe "src" through the plugin
10 | * specified by "name" to "dest". All following parameters are serialized if
11 | * necessary and passed on to the plugin.
12 | *
13 | * @method standardTask
14 | * @param {string} name
15 | * @param {string} src
16 | * @param {string} dest
17 | * @returns {function}
18 | */
19 |
20 | function standardTask (name, src, dest) {
21 | let args = _.toArray(arguments).slice(3); // dependencies
22 | let stack = null;
23 | let filename = '';
24 | let line = null;
25 |
26 | try {
27 | throw new Error('');
28 | } catch (e) {
29 | stack = parseStack(e);
30 | }
31 |
32 | if (stack && stack.length > 1) {
33 | let entry = stack[1];
34 | filename = path.basename(entry.filepath);
35 | line = entry.lineNumber;
36 | }
37 |
38 | // Serialize arguments if necessary
39 |
40 | try {
41 | args = args.map(JSON.stringify).join(', ');
42 | } catch (e) {
43 | console.log(`Could not parse ${args.length} arguments:\n\n${e.stack}`);
44 | args = [];
45 | }
46 |
47 | if (!args.length) {
48 | args = null;
49 | }
50 |
51 | let fnName = _.camelCase(name);
52 | let body = `
53 |
54 | /**
55 | * Generated task function, registers a "${name}" task.
56 | * @param {object} gulp The current gulp instance
57 | * @param {function} ${name} The "${name}" gulp plugin
58 | */
59 |
60 | gulp.task("${name}", () => {
61 | /**
62 | * ${name} task, declared at ${filename}${line ? ':' + line : ''}
63 | */
64 |
65 | let plugin = this.byId("${name}");
66 |
67 | if (typeof plugin !== "function") {
68 | throw new Error("plugin '${name}' is not a function");
69 | }
70 |
71 | return gulp.src("${src}")
72 | .pipe(plugin(${args || 'void 0'}))
73 | .pipe(gulp.dest("${dest}"));
74 | });
75 | `;
76 | let taskFn = eval(`function ${fnName}Task (gulp) { ${body} }; ${fnName}Task;`);
77 | return taskFn;
78 | }
79 |
80 | module.exports = standardTask;
81 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * This is currently solely an example on how you could use
5 | * gulp-di in many ways.
6 | *
7 | * Try running
8 | *
9 | * $ gulp
10 | * $ gulp help
11 | * $ gulp log-path
12 | * $ gulp b info wait
13 | */
14 |
15 | const gulp = require('gulp');
16 | let di = require('./')(gulp, {
17 | // DEBUG: true
18 | })
19 | .modules('./modules')
20 | .tasks('./tasks')
21 | // .task(function (noDef) {
22 | // // This should fail if you un-comment it as `noDef` hasn't been defined.
23 | // })
24 | .resolve();
25 |
26 | // Using gulp's task(), comments will become available with "gulp help".
27 | //
28 | // In order to use this feature, you'll need to call gulp.task() AFTER
29 | // gulp-di though.
30 |
31 | gulp.task('wait', (done) => {
32 | /**
33 | * Waits for one second, features a
34 | * multi-line comment (see gulpfile.js).
35 | */
36 | di.inject(() => setTimeout(done, 1e3));
37 | });
38 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/gulp-di');
2 |
--------------------------------------------------------------------------------
/lib/dependency-error.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @method DependencyError
5 | * @private
6 | * @param {string} id
7 | */
8 |
9 | const DependencyError = function (id) {
10 | return new Error('Unknown dependency: ' + id);
11 | };
12 |
13 | module.exports = DependencyError;
14 |
--------------------------------------------------------------------------------
/lib/gulp-di.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = (function () {
4 | const $parseArg = require('./parse-argument');
5 | const _ = require('lodash');
6 | const chalk = require('chalk');
7 | const findup = require('findup-sync');
8 | const gutil = require('gulp-util');
9 | const loadPlugins = require('gulp-load-plugins');
10 | const path = require('path');
11 | const Resolver = require('./resolver');
12 | const defaultPatterns = ['gulp-*', 'gulp.*', '!gulp-di'];
13 | const readDirectory = require('./read-directory');
14 | const DependencyError = require('./dependency-error');
15 | const normalizePath = require('./normalize-path');
16 |
17 | let parentDir = '';
18 |
19 | /**
20 | * @module GulpDI
21 | */
22 |
23 | class GulpDI {
24 | /**
25 | * @constructor
26 | * @param {object} gulp The gulp instance to use
27 | * @param {object} options options passed to gulp-load-plugins
28 | */
29 |
30 | constructor (gulp, options) {
31 | if (typeof gulp === 'undefined' || !gulp) {
32 | throw new Error('A gulp instance must be provided as first argument');
33 | }
34 | this.byId = this.byId.bind(this);
35 | this.resolver = new Resolver();
36 | this.tasksCount = 0;
37 |
38 | this.provide('gulp', gulp);
39 | this.parseOptions(options);
40 | }
41 |
42 | /**
43 | * @method $byId
44 | * @param {string} id
45 | * @param {boolean} noError
46 | * @returns {*|null} payload
47 | */
48 |
49 | byId (id, noError) {
50 | const entry = this.resolver.byId(id);
51 | const isResolved = this.resolver.resolved.indexOf(id) !== -1;
52 | if (noError !== true) {
53 | if (entry === null) {
54 | throw new DependencyError(id);
55 | }
56 | if (!isResolved) {
57 | throw new Error(`Attempted to access unresolved dependency "${id}"`);
58 | }
59 | }
60 | return entry ? entry.payload : null;
61 | }
62 |
63 | /**
64 | * @method parseOptions
65 | * @param {object} options
66 | */
67 |
68 | parseOptions (options) {
69 | // Necessary as function parameters cannot contain dashes.
70 | options.camelize = true;
71 | if (typeof options.lazy === 'undefined') {
72 | options.lazy = true;
73 | } else {
74 | options.lazy = !!options.lazy;
75 | }
76 |
77 | this.options = options;
78 |
79 | // exposed modules
80 |
81 | if (!options.noModules) {
82 | this.provide({
83 | '_': _,
84 | 'chalk': chalk,
85 | 'gutil': gutil,
86 | 'log': gutil.log.bind(gutil)
87 | });
88 | }
89 |
90 | // built-in dependencies
91 |
92 | if (!options.noBuiltin) {
93 | this.provide({
94 | 'basePath': path.join.bind(path, parentDir),
95 | 'Package': require(options.config)
96 | });
97 | }
98 |
99 | if (!options.noHelp) {
100 | // installs a gulp.task() wrapper and registers the 'help' task
101 | this.inject(require('../contrib/help'));
102 | }
103 |
104 | if (!options.noRunningTasks) {
105 | this.inject(require('../contrib/running-tasks'));
106 | }
107 |
108 | // Provides all installed Gulp plugins according to package.json
109 | const plugins = loadPlugins(options);
110 |
111 | // When options.lazy is set, loadPlugins loads the plugin as soon as
112 | // the property of the returned "plugins" object is directly accessed.
113 |
114 | if (options.lazy) {
115 | const pluginKeys = _.keys(plugins);
116 | for (let i = 0, l = pluginKeys.length; i < l; i++) {
117 | this.providePlugin(pluginKeys[i], plugins);
118 | }
119 | } else {
120 | this.provide(plugins);
121 | }
122 | }
123 |
124 | /**
125 | * @method inject
126 | * @param {function|Array} arg
127 | * @param {Boolean} returnValue
128 | * @chainable
129 | */
130 |
131 | inject (arg, returnValue) {
132 | const info = $parseArg(arg);
133 | const retval = info.fn.apply(this, info.params.map((param) => this.byId(param, true)));
134 | return returnValue ? retval : this;
135 | }
136 |
137 | /**
138 | * @method _module
139 | * @param {string} name
140 | * @param {function} fn
141 | * @param {string} type
142 | * @chainable
143 | */
144 |
145 | module (name, fn, type) {
146 | if (typeof fn === 'string') {
147 | try {
148 | fn = require(normalizePath(parentDir, fn));
149 | } catch (e) {
150 | throw new Error(`Could not load module "${name}" from "${fn}"`);
151 | }
152 | }
153 | if (typeof name === 'object' && typeof name.length !== 'number') {
154 | const obj = arguments[0];
155 | _.each(obj, (fn, key) => this.module(key, fn, type));
156 | return this;
157 | }
158 | const info = $parseArg(fn);
159 | this.resolver.provide(name, info.params, info.fn, type || 'module');
160 | return this;
161 | }
162 |
163 | /**
164 | * @method modules
165 | * @param {string} directory
166 | * @chainable
167 | */
168 |
169 | modules (directory) {
170 | return this.module(readDirectory(normalizePath(parentDir, directory), true));
171 | }
172 |
173 | /**
174 | * @method provide
175 | * @param {string} name
176 | * @param {*} payload
177 | * @chainable
178 | */
179 |
180 | provide (name, payload) {
181 | if (typeof name === 'object') {
182 | const obj = arguments[0];
183 | _.each(obj, (payload, name) => this.provide(name, payload));
184 | return this;
185 | }
186 | this.resolver.provide(name, [], payload, 'provide');
187 | return this;
188 | }
189 |
190 | /**
191 | * @method providePlugin
192 | * @param {string} name
193 | * @param {*} payload
194 | * @chainable
195 | */
196 |
197 | providePlugin (name, plugins) {
198 | this.resolver.provideObjectNode(name, [], plugins, 'provide');
199 | return this;
200 | }
201 |
202 | /**
203 | * @method resolve
204 | * @chainable
205 | */
206 |
207 | resolve () {
208 | const resolver = this.resolver;
209 | const queue = resolver.resolve();
210 | for (let i = 0, l = queue.length; i < l; ++i) {
211 | const id = queue[i];
212 | const entry = resolver.byId(id);
213 | let retval = null;
214 | if (!entry) {
215 | throw new DependencyError(id);
216 | }
217 | if (entry.type === 'module' || entry.type === 'task') {
218 | retval = entry.payload.apply(this, entry.dependencies.map(this.byId));
219 | if (entry.type === 'module') {
220 | resolver.put(id, retval);
221 | }
222 | }
223 | }
224 | return this;
225 | }
226 |
227 | /**
228 | * @method task
229 | * @param {function|Array} fn
230 | * @chainable
231 | */
232 |
233 | task (fn) {
234 | if (typeof fn === 'object') {
235 | const obj = arguments[0];
236 | let keys = Object.keys(obj).sort();
237 | const defaultIndex = keys.indexOf('default');
238 | if (~defaultIndex) {
239 | keys.splice(defaultIndex, 1);
240 | keys.push('default');
241 | }
242 | keys.forEach((name) => this.task(obj[name]));
243 | return this;
244 | }
245 | if (typeof fn === 'string') {
246 | try {
247 | fn = require(normalizePath(parentDir, fn));
248 | } catch (e) {
249 | throw new Error(`Could not load task from "${fn}"`);
250 | }
251 | }
252 |
253 | this.module(`task_${this.tasksCount++}`, fn, 'task');
254 | return this;
255 | }
256 |
257 | /**
258 | * @method tasks
259 | * @param {string} directory
260 | * @chainable
261 | */
262 |
263 | tasks (directory) {
264 | return this.task(readDirectory(normalizePath(parentDir, directory), false));
265 | }
266 | }
267 |
268 | /**
269 | * @method
270 | * @param {object} gulp
271 | * @param {object} options
272 | * @return {GulpDI} [description]
273 | */
274 |
275 | return function (gulp, options) {
276 | options = options || {};
277 |
278 | if (typeof options.parentDir === 'string') {
279 | parentDir = options.parentDir;
280 | } else {
281 | // Set "parentDir" to index.js parent's directory name
282 | parentDir = path.dirname(module.parent.parent.filename);
283 | }
284 |
285 | options.config = options.config || findup('package.json', { cwd: parentDir });
286 |
287 | // Ensure loadPlugins being called with the default patterns (see above)
288 |
289 | options.pattern = _.chain(options.pattern || [])
290 | .concat(defaultPatterns)
291 | .uniq()
292 | .value();
293 |
294 | // Necessary to get the current `module.parent` and resolve paths correctly
295 | // @see gulp-load-plugins
296 |
297 | delete require.cache[__filename];
298 | return new GulpDI(gulp, options);
299 | };
300 | })();
301 |
--------------------------------------------------------------------------------
/lib/normalize-path.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | function normalizePath (parentDir, directory) {
4 | if (!path.isAbsolute(directory)) {
5 | return path.resolve(parentDir, directory);
6 | }
7 | return directory;
8 | }
9 |
10 | module.exports = normalizePath;
11 |
--------------------------------------------------------------------------------
/lib/parse-argument.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const introspect = require('introspect');
3 |
4 | /**
5 | * Parses the given dependency injection notation, either using a function
6 | * with dependencies as its parameters or using the minification-safe with an
7 | * array.
8 | * @method $parseArg
9 | * @private
10 | * @param {function|Array} arg
11 | * @return {object}
12 | */
13 |
14 | function $parseArg (arg) {
15 | let params = [];
16 | let fn = null;
17 | // ['dependency', function (dependency) {}]
18 |
19 | if (typeof arg === 'object' && typeof arg.length === 'number') {
20 | let l = arg.length;
21 | params = arg.slice(0, l - 1);
22 | fn = arg[l - 1];
23 | } else if (typeof arg === 'function') { // function (dependency) {}
24 | params = introspect(arg);
25 | fn = arg;
26 | } else {
27 | throw new SyntaxError('Invalid dependency injection notation');
28 | }
29 |
30 | if (typeof fn !== 'function') {
31 | throw new Error('Function expected');
32 | }
33 |
34 | for (let i = 0, l = params.length; i < l; i++) {
35 | if (typeof params[i] !== 'string') {
36 | throw new Error('Invalid dependency notation, string expected');
37 | }
38 | params[i] = params[i].trim();
39 | if (params[i].length === 0) {
40 | throw new Error('Invalid dependency notation, empty string provided');
41 | }
42 | }
43 |
44 | return { fn, params };
45 | }
46 |
47 | module.exports = $parseArg;
48 |
--------------------------------------------------------------------------------
/lib/read-directory.js:
--------------------------------------------------------------------------------
1 | const requireDir = require('require-dir');
2 |
3 | /**
4 | * @method readDirectory
5 | * @param {string} directory
6 | * @param {Object} options
7 | * @return {object}
8 | */
9 |
10 | const readDirectory = (directory, options = {}) => requireDir(directory, options);
11 |
12 | module.exports = readDirectory;
13 |
--------------------------------------------------------------------------------
/lib/resolver.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const _ = require('lodash');
3 |
4 | /**
5 | * @module Resolver
6 | * @method Resolver
7 | * @returns {object}
8 | */
9 |
10 | class Resolver {
11 | /**
12 | * @constructor
13 | */
14 | constructor () {
15 | this.resolved = [];
16 | this.queue = [];
17 | }
18 |
19 | /**
20 | * @return {number}
21 | */
22 |
23 | get length () {
24 | return this.queue.length;
25 | }
26 |
27 | /**
28 | * @method byId
29 | * @param {string} id
30 | * @private
31 | */
32 |
33 | byId (id) {
34 | return _.find(this.queue, (item) => item.key === id) || null;
35 | }
36 |
37 | /**
38 | * @method put
39 | * @param {string} key
40 | * @param {*} payload
41 | */
42 |
43 | put (key, payload) {
44 | let item = this.byId(key);
45 | if (!item) {
46 | throw new Error('Dependency ' + key + 'is not defined');
47 | }
48 | item.payload = payload;
49 | }
50 |
51 | /**
52 | * @method provide
53 | * @param {string} key
54 | * @param {string[]} dependencies
55 | * @param {*} payload
56 | * @param {string} type
57 | */
58 |
59 | provide (key, dependencies, payload, type) {
60 | this.queue.push({ key, dependencies, payload, type });
61 | }
62 |
63 | /**
64 | * @method provideObjectNode
65 | * @param {string} key
66 | * @param {string[]} dependencies
67 | * @param {*} obj
68 | * @param {string} type
69 | */
70 |
71 | provideObjectNode (key, dependencies, obj, type) {
72 | let queuedItem = { key, dependencies, type };
73 | Object.defineProperty(queuedItem, 'payload', {
74 | get: function () {
75 | return obj[key];
76 | }
77 | });
78 | this.queue.push(queuedItem);
79 | }
80 |
81 | /**
82 | * Resolves and empties the current queue.
83 | * @method resolve
84 | */
85 |
86 | resolve () {
87 | /**
88 | * @method isResolved
89 | * @param {string} id
90 | * @returns {boolean}
91 | */
92 |
93 | const isResolved = (id) => !!(~this.resolved.indexOf(id) || ~resolved.indexOf(id));
94 | let resolved = [];
95 |
96 | /**
97 | * @method _resolve
98 | * @private
99 | * @param {object} entry
100 | * @param {string[]} unresolved
101 | * @returns {string[]}
102 | */
103 |
104 | const _resolve = (entry, unresolved) => {
105 | if (isResolved(entry.key)) {
106 | return;
107 | }
108 |
109 | unresolved.push(entry.key);
110 | const dependencies = entry.dependencies.slice();
111 |
112 | for (let i = 0, l = dependencies.length; i < l; ++i) {
113 | const key = dependencies[i];
114 | if (isResolved(key)) {
115 | continue;
116 | }
117 | if (~unresolved.indexOf(key)) {
118 | throw new Error(`Circular: ${entry.key} -> ${key}`);
119 | }
120 | const queuedItem = this.byId(key);
121 | _resolve({ key, dependencies: (queuedItem && queuedItem.dependencies) || [] }, unresolved);
122 | }
123 |
124 | unresolved = _.without(unresolved, entry.key);
125 | resolved.push(entry.key);
126 | };
127 | for (let j = 0, k = this.queue.length; j < k; ++j) {
128 | _resolve(this.queue[j], []);
129 | }
130 | this.resolved = this.resolved.concat(resolved);
131 | return resolved;
132 | }
133 | }
134 |
135 | module.exports = Resolver;
136 |
--------------------------------------------------------------------------------
/modules/paths.js:
--------------------------------------------------------------------------------
1 | module.exports = (basePath) => {
2 | return {
3 | mocha: basePath('test', '**/*.js'),
4 | src: [
5 | basePath('index.js'),
6 | basePath('lib/**/*.js'),
7 | basePath('contrib/**/*.js')
8 | ],
9 | istanbul: [
10 | basePath('index.js'),
11 | basePath('lib/**/*.js'),
12 | basePath('contrib/**/*.js')
13 | ],
14 | docs: [
15 | basePath('lib/**/*.js')
16 | ],
17 | tasks: [
18 | basePath('tasks/**/*.js')
19 | ]
20 | };
21 | };
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-di",
3 | "version": "0.1.0",
4 | "description": "Dependency injection framework for the Gulp streaming build system",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "./node_modules/gulp-cli/bin/gulp.js semistandard mocha",
8 | "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
9 | },
10 | "author": {
11 | "name": "Matthias Thoemmes",
12 | "email": "git@gmx.org"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/cmtt/gulp-di.git"
17 | },
18 | "keywords": [
19 | "gulp",
20 | "plugin",
21 | "build",
22 | "system",
23 | "framework",
24 | "tool",
25 | "asset",
26 | "pipeline",
27 | "require",
28 | "dependency",
29 | "injection",
30 | "DI",
31 | "dependency injection",
32 | "gulpplugin"
33 | ],
34 | "license": "MIT",
35 | "dependencies": {
36 | "chalk": "^3.0.0",
37 | "extract-comments": "^1.1.0",
38 | "findup-sync": "^4.0.0",
39 | "gulp": "^4.0.2",
40 | "gulp-cli": "^2.2.0",
41 | "gulp-concat": "^2.6.1",
42 | "gulp-istanbul": "^1.1.3",
43 | "gulp-load-plugins": "^2.0.1",
44 | "gulp-mocha": "^7.0.2",
45 | "gulp-rename": "^1.4.0",
46 | "gulp-semistandard": "^1.0.0",
47 | "gulp-util": "^3.0.8",
48 | "introspect": "git+https://github.com/orzarchi/node-introspect.git",
49 | "lodash": "^4.17.15",
50 | "pad": "^3.2.0",
51 | "parse-stack": "^0.1.4",
52 | "require-dir": "^1.2.0"
53 | },
54 | "devDependencies": {
55 | "coveralls": "^3.0.9",
56 | "parse-function": "^2.3.2"
57 | },
58 | "semistandard": {
59 | "globals": [
60 | "afterEach",
61 | "assert",
62 | "basePath",
63 | "beforeEach",
64 | "describe",
65 | "it",
66 | "getGulpInstance",
67 | "getDiInstance"
68 | ],
69 | "esversion": 6
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tasks/default.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = (gulp, Package, basePath) => {
4 | gulp.task('default', gulp.parallel('mocha', 'semistandard'));
5 | };
6 |
--------------------------------------------------------------------------------
/tasks/examples.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = (Package, basePath, chalk, log, gulp, taskInfo, gutil, runningTasks) => {
4 | // The order of dependencies does not matter ^^^^
5 |
6 | const path = require('path');
7 |
8 | gulp.task('log-path', () => {
9 | /**
10 | * Example using the build-in constant Package from your the current
11 | * package.json as well as the path resolving helper basePath.
12 | */
13 |
14 | console.log(`${Package.name}s location: `, basePath());
15 | console.log(`this file's location: ${basePath('tasks', path.basename(__filename))}`);
16 | });
17 |
18 | // You can declare multiple gulp tasks in each file.
19 |
20 | gulp.task('task-info', (cb) => {
21 | /**
22 | * Example logging task information from contrib/help.js
23 | */
24 |
25 | console.log(`${Package.name}'s task information: `);
26 | console.log(taskInfo);
27 | cb();
28 | });
29 |
30 | gulp.task('a', (cb) => cb());
31 | gulp.task('c', (cb) => cb());
32 | gulp.task('b', gulp.series('a', 'c'));
33 |
34 | gulp.task('info', (cb) => {
35 | /**
36 | * Demonstrates logging currently running tasks.
37 | *
38 | * If you'd define tasks a, b, c (where b would depend on c and a),
39 | * runningTasks() would return:
40 | * $ gulp a
41 | * // ['a']
42 | * $ gulp b
43 | * // ['c','a','b']
44 | * $ gulp c
45 | * // ['c']
46 | */
47 |
48 | let line = [
49 | 'Building',
50 | chalk.magenta(Package.name),
51 | Package.version,
52 | '- running tasks:',
53 | chalk.cyan(runningTasks().join(', '))
54 | ];
55 | log(line.join(' '));
56 | cb();
57 | });
58 | };
59 |
--------------------------------------------------------------------------------
/tasks/mocha.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = function (gulp, paths, mocha, istanbul) {
3 | gulp.task('pre-test', () => {
4 | return gulp.src(paths.istanbul)
5 | // Covering files
6 | .pipe(istanbul())
7 | // Force `require` to return covered files
8 | .pipe(istanbul.hookRequire());
9 | });
10 |
11 | gulp.task('mocha', gulp.series('pre-test', () => {
12 | // Runs the unit tests using Mocha
13 |
14 | return gulp.src(paths.mocha, { read: false })
15 | .pipe(mocha({}))
16 | .pipe(istanbul.writeReports());
17 | }));
18 | };
19 |
--------------------------------------------------------------------------------
/tasks/semistandard.js:
--------------------------------------------------------------------------------
1 | module.exports = (gulp, semistandard, paths) => {
2 | gulp.task('semistandard', () => {
3 | return gulp.src(paths.src.concat(paths.tasks))
4 | .pipe(semistandard())
5 | .pipe(semistandard.reporter('default', {
6 | quiet: true
7 | }));
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/test/00.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const path = require('path');
3 | global.assert = require('assert');
4 | global.basePath = path.join.bind(path, __dirname, '..');
5 |
6 | const diPath = global.basePath('index.js');
7 | global.GulpDI = require(diPath);
8 |
9 | /**
10 | * @method getGulpInstance
11 | */
12 |
13 | global.getGulpInstance = () => {
14 | delete require.cache[require.resolve('gulp')];
15 | delete require.cache[require.resolve('undertaker')];
16 | delete require.cache[require.resolve('undertaker-registry')];
17 | delete require.cache[require.resolve('last-run')];
18 | const gulp = require('gulp');
19 | gulp.on('error', (e) => {
20 | console.log(e.error);
21 | throw new Error(`Gulp runtime error:\n${JSON.stringify(e)}\n`);
22 | });
23 | return gulp;
24 | };
25 |
26 | /**
27 | * @method getDiInstance
28 | */
29 |
30 | global.getDiInstance = (gulp, config) => {
31 | delete require.cache[require.resolve(diPath)];
32 | global.GulpDI = require(diPath);
33 | return new global.GulpDI(gulp, config);
34 | };
35 |
36 | global.hasTask = (gulp, taskId) => {
37 | const registry = gulp._registry;
38 | const tasks = registry.tasks();
39 | return taskId in tasks;
40 | };
41 |
--------------------------------------------------------------------------------
/test/01-parse-argument.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('parseArgument', function () {
4 | const $parseArg = require('../lib/parse-argument');
5 |
6 | let concatTask = (gulp, concat) => {
7 | gulp.task('concat', () => {
8 | return gulp
9 | .src('src/**/*.txt')
10 | .pipe(gulp.dest('docs'));
11 | });
12 | };
13 |
14 | it('parses the function notation', () => {
15 | let info = $parseArg(concatTask);
16 | assert.deepEqual(info.params, ['gulp', 'concat']);
17 | assert.equal(info.fn.toString(), concatTask.toString());
18 | });
19 |
20 | it('parses the minification-safe notation', () => {
21 | let info = $parseArg(['gulp', 'concat', concatTask]);
22 | assert.deepEqual(info.params, ['gulp', 'concat']);
23 | assert.equal(info.fn.toString(), concatTask.toString());
24 | });
25 |
26 | it('parses a function without any arguments', () => {
27 | let fn = function () {
28 | console.log(new Date() + '');
29 | };
30 | let info = $parseArg(fn);
31 | assert.deepEqual(info.params, []);
32 | assert.equal(info.fn.toString(), fn.toString());
33 | });
34 |
35 | it('parses a function without any arguments (minification-safe notation)', () => {
36 | let info = $parseArg([function () {}]);
37 | assert.deepEqual(info.params, []);
38 | assert.equal(typeof info.fn, 'function');
39 | });
40 |
41 | it('throws an error when no valid function was declared', () => {
42 | let tests = [
43 | '',
44 | 0,
45 | 1,
46 | null,
47 | void 0,
48 | {},
49 | ['test'],
50 | [null, function () {}],
51 | [0],
52 | [' ', function () {}],
53 | [' ', function () {}],
54 | ['', function () {}],
55 | [' ', function (space) {}]
56 | ];
57 | tests.forEach((test) => assert.throws(() => {
58 | let info = $parseArg(test);
59 | assert.ok(Array.isArray(info));
60 | }));
61 | });
62 |
63 | it('can be used with functions and dependencies', () => {
64 | let dependencies = {
65 | A: 'a',
66 | B: 'b',
67 | C: 'c'
68 | };
69 |
70 | let abc = ['A', 'B', 'C', (A, B, C) => `${A}${B}${C}`];
71 |
72 | // Solely changing the order in the minification-safe dependency notation
73 | let cab0 = ['C', 'A', 'B', (A, B, C) => `${A}${B}${C}`];
74 |
75 | // Using function parameters
76 | let cab1 = (C, A, B) => `${C}${A}${B}`;
77 |
78 | let getDependencies = (params) => params.map((key) => dependencies[key]);
79 |
80 | let check = (arg, expected) => {
81 | let info = $parseArg(arg);
82 | let args = getDependencies(info.params);
83 | assert.equal(expected, info.fn.apply(null, args));
84 | };
85 |
86 | check(abc, 'abc');
87 | check(cab0, 'cab');
88 | check(cab1, 'cab');
89 | });
90 | });
91 |
--------------------------------------------------------------------------------
/test/02-resolver.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Resolver', () => {
4 | const Resolver = require('../lib/resolver');
5 | let d = null;
6 |
7 | beforeEach(() => {
8 | d = new Resolver('test');
9 | });
10 |
11 | it('initializes, length is zero', () => {
12 | assert.equal(d.length, 0);
13 | assert.equal(d.resolved.length, 0);
14 | });
15 |
16 | it('increases its length', () => {
17 | d.provide('one', ['two', 'three']);
18 | assert.equal(d.length, 1);
19 | d.provide('two', ['four']);
20 | assert.equal(d.length, 2);
21 | assert.equal(d.resolved.length, 0);
22 | });
23 |
24 | it('resolves returns an empty array', () => {
25 | assert.deepEqual(d.resolve(), []);
26 | assert.equal(d.resolved.length, 0);
27 | });
28 |
29 | it('throws an error when a dependency depends on itself', () => {
30 | d.provide('A', ['A']);
31 | assert.throws(() => {
32 | d.resolve();
33 | });
34 | });
35 |
36 | it('throws an error when circular dependencies are declared', () => {
37 | d.provide('A', ['B']);
38 | d.provide('B', ['A']);
39 | assert.throws(() => {
40 | d.resolve();
41 | });
42 | });
43 |
44 | it('resolves in a correct order', () => {
45 | d.provide('test', ['one', 'two', 'three']);
46 | d.provide('two', []);
47 | d.provide('one', []);
48 | d.provide('three', []);
49 | assert.deepEqual(d.resolve(), ['one', 'two', 'three', 'test']);
50 | assert.equal(d.resolved.length, 4);
51 | });
52 |
53 | it('provide, byId', () => {
54 | d.provide('test', [], 'test', 'string');
55 | d.provide('one', ['test'], 1, 'number');
56 |
57 | assert.deepEqual(d.byId('test'), {
58 | key: 'test',
59 | dependencies: [],
60 | payload: 'test',
61 | type: 'string'
62 | });
63 | assert.deepEqual(d.byId('one'), {
64 | key: 'one',
65 | dependencies: ['test'],
66 | payload: 1,
67 | type: 'number'
68 | });
69 | assert.deepEqual(d.resolve(), ['test', 'one']);
70 | });
71 |
72 | it('put throws an error when a value was not provided', () => {
73 | assert.throws(() => {
74 | d.put('test', 'test');
75 | });
76 | });
77 |
78 | it('put', () => {
79 | d.provide('test', [], { test: false }, 'object');
80 | d.resolve();
81 | assert.deepEqual(d.byId('test'), {
82 | key: 'test',
83 | dependencies: [],
84 | payload: { test: false },
85 | type: 'object'
86 | });
87 | d.put('test', { test: true });
88 | assert.deepEqual(d.byId('test').payload, {
89 | test: true
90 | });
91 | });
92 |
93 | it('does not resolve twice', () => {
94 | let queue = [];
95 |
96 | d.provide('test0', ['test2'], 'test0');
97 | d.provide('test1', [], 'test1');
98 | d.provide('test2', ['test1'], 'test2');
99 | d.provide('test3', ['test0'], 'test3');
100 |
101 | assert.equal(d.resolved.length, 0);
102 |
103 | queue = d.resolve();
104 | assert.equal(queue.length, 4);
105 | assert.equal(d.length, 4);
106 | assert.equal(d.resolved.length, 4);
107 |
108 | queue = d.resolve();
109 | assert.equal(queue.length, 0);
110 | assert.equal(d.length, 4);
111 | assert.equal(d.resolved.length, 4);
112 |
113 | d.provide('test4', [], 'test4');
114 | assert.equal(d.length, 5);
115 | assert.equal(d.resolved.length, 4);
116 |
117 | queue = d.resolve();
118 | assert.equal(queue.length, 1);
119 | assert.equal(d.length, 5);
120 | assert.equal(d.resolved.length, 5);
121 | });
122 |
123 | it('provideObjectNode', (done) => {
124 | let object = {
125 | a: 1,
126 | b: 2
127 | };
128 |
129 | Object.defineProperty(object, 'c', {
130 | get: function () {
131 | setTimeout(done);
132 | return 3;
133 | }
134 | });
135 |
136 | d.provideObjectNode('a', ['c'], object);
137 | d.provideObjectNode('b', [], object);
138 | d.provideObjectNode('c', [], object);
139 | let queue = d.resolve();
140 | assert.deepEqual(queue, 'cab'.split(''));
141 | assert.equal(d.byId('a').payload, 1);
142 | assert.equal(d.byId('b').payload, 2);
143 | assert.equal(d.byId('c').payload, 3);
144 | });
145 | });
146 |
--------------------------------------------------------------------------------
/test/03-read-directory.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('readDirectory', () => {
4 | const readDirectory = require('../lib/read-directory');
5 | const path = require('path');
6 |
7 | it('reads examples', () => {
8 | let list = readDirectory(path.join(__dirname, '..', 'contrib/examples'));
9 | assert.equal(typeof list, 'object');
10 | let keys = Object.keys(list);
11 | assert.deepEqual(keys, [
12 | 'deg-to-rad',
13 | 'pi',
14 | 'rad-to-deg',
15 | 'to-deg',
16 | 'to-rad'
17 | ]);
18 | });
19 |
20 | it('applies options', () => {
21 | const toCamelCase = (str) => {
22 | return str.replace(/[_-][a-z]/ig, function (s) {
23 | return s.substring(1).toUpperCase();
24 | });
25 | };
26 |
27 | let list = readDirectory(path.join(__dirname, '..', 'contrib/examples'), {
28 | mapKey: (value, key) => toCamelCase(key)
29 | });
30 | assert.equal(typeof list, 'object');
31 | let keys = Object.keys(list);
32 | assert.deepEqual(keys, [
33 | 'degToRad',
34 | 'pi',
35 | 'radToDeg',
36 | 'toDeg',
37 | 'toRad'
38 | ]);
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/test/10-gulp-di.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Stream = require('stream');
3 | const path = require('path');
4 |
5 | describe('GulpDI', () => {
6 | const PI = Math.PI;
7 | const RAD_TO_DEG = 180 / PI;
8 | const INSTANCE_OPTIONS = { parentDir: basePath() };
9 |
10 | /**
11 | * Module declaration which assembles the "toDeg" function using PI and
12 | * RAD_TO_DEG
13 | *
14 | * @method toDegModule
15 | * @param {number} PI
16 | * @param {number} RAD_TO_DEG
17 | * @return {function}
18 | */
19 |
20 | function toDegModule (PI, RAD_TO_DEG) {
21 | return (radValue) => radValue * RAD_TO_DEG;
22 | }
23 |
24 | let gulp = null;
25 | let di = null;
26 |
27 | describe('Initialization', () => {
28 | beforeEach(() => {
29 | gulp = getGulpInstance();
30 | di = null;
31 | });
32 |
33 | it('initializes', () => {
34 | di = getDiInstance(gulp).resolve();
35 | });
36 |
37 | it('throws an error without gulp as first argument', () => {
38 | assert.throws(() => getDiInstance());
39 | });
40 |
41 | it('byId throws an error when a dependency was not found', () => {
42 | assert.throws(getDiInstance(gulp).byId);
43 | });
44 |
45 | it('byId with noError does not throw an error when a dependency was not found', () => {
46 | di = getDiInstance(gulp).resolve();
47 | let info = di.byId('UnknownDependency_', true);
48 | assert.equal(info, null);
49 | });
50 |
51 | it('byId throws an error when a undesolved dependency was requested', () => {
52 | assert.throws(() => {
53 | di = getDiInstance(gulp, INSTANCE_OPTIONS)
54 | .module('PI', './contrib/examples/pi')
55 | .byId('PI');
56 | });
57 | });
58 |
59 | it('provide', () => {
60 | di = getDiInstance(gulp)
61 | .provide('test', 'test')
62 | .resolve();
63 | assert.equal(di.byId('test'), 'test');
64 | });
65 |
66 | it('options.noModules', () => {
67 | di = getDiInstance(gulp, { noModules: true }).resolve();
68 | assert.throws(() => di.byId('_'));
69 | assert.throws(() => di.byId('chalk'));
70 | assert.throws(() => di.byId('gutil'));
71 | assert.throws(() => di.byId('log'));
72 | });
73 |
74 | it('options.noBuiltin', () => {
75 | di = getDiInstance(gulp, { noBuiltin: true }).resolve();
76 | assert.throws(() => di.byId('basePath'));
77 | assert.throws(() => di.byId('Package'));
78 | });
79 |
80 | it('options.noHelp', () => {
81 | di = getDiInstance(gulp, { noHelp: true }).resolve();
82 | assert.ok(!hasTask(gulp, 'help'));
83 | });
84 | });
85 |
86 | describe('Dependency injection', () => {
87 | it('throws an error when using invalid notation', () => {
88 | di = getDiInstance(gulp);
89 | assert.throws(() => {
90 | di.task();
91 | });
92 | assert.throws(() => {
93 | di.module('test', null);
94 | });
95 | assert.throws(() => {
96 | di.task('null');
97 | });
98 | assert.throws(() => {
99 | di.module('test', 0);
100 | });
101 | });
102 |
103 | it('module', () => {
104 | di = getDiInstance(gulp)
105 | .provide('PI', PI)
106 | .provide('RAD_TO_DEG', RAD_TO_DEG)
107 | .module('toDeg', toDegModule);
108 |
109 | di.resolve();
110 |
111 | let toDeg = di.byId('toDeg');
112 | let pi = di.byId('PI');
113 | assert.ok(toDeg);
114 | assert.equal(typeof toDeg, 'function');
115 | assert.equal(typeof pi, 'number');
116 |
117 | assert.equal(toDeg(pi), 180);
118 | assert.equal(toDeg(2 * pi), 360);
119 | });
120 |
121 | it('module with relative and absolute paths', () => {
122 | di = getDiInstance(gulp, INSTANCE_OPTIONS)
123 | .module('PI', './contrib/examples/pi')
124 | .module('DEG_TO_RAD', './contrib/examples/deg-to-rad')
125 | .module('RAD_TO_DEG', './contrib/examples/rad-to-deg')
126 | .module('toDeg', './contrib/examples/to-deg')
127 | .module('toRad', path.join(__dirname, '..', 'contrib/examples/to-rad'));
128 |
129 | di.resolve();
130 |
131 | let toDeg = di.byId('toDeg');
132 | let toRad = di.byId('toRad');
133 | let pi = di.byId('PI');
134 | assert.equal(typeof toDeg, 'function');
135 | assert.equal(typeof toRad, 'function');
136 |
137 | assert.equal(toDeg(pi), 180);
138 | assert.equal(toDeg(2 * pi), 360);
139 | assert.equal(toRad(180), pi);
140 | assert.equal(toRad(360), 2 * pi);
141 | });
142 |
143 | it('module throws an error with invalid paths', () => {
144 | assert.throws(() => {
145 | di = getDiInstance(gulp)
146 | .module('PI', './throws/an/error');
147 | });
148 | });
149 |
150 | it('modules', (done) => {
151 | di = getDiInstance(gulp, INSTANCE_OPTIONS)
152 | .modules('./modules')
153 | .resolve();
154 | let paths = di.byId('paths');
155 | assert.equal(typeof paths, 'object');
156 | done();
157 | });
158 |
159 | it('inject (chainable)', (done) => {
160 | di = getDiInstance(gulp)
161 | .provide('PI', PI)
162 | .provide('RAD_TO_DEG', RAD_TO_DEG)
163 | .module('toDeg', toDegModule)
164 | .resolve();
165 | let injectCalled = 0;
166 | di.inject((toDeg, PI) => {
167 | ++injectCalled;
168 | assert.equal(toDeg(PI), 180);
169 | assert.equal(toDeg(2 * PI), 360);
170 | })
171 | .inject(() => {
172 | assert.equal(injectCalled, 1);
173 | done();
174 | });
175 | });
176 |
177 | it('inject (w/ return value)', () => {
178 | di = getDiInstance(gulp)
179 | .provide('PI', PI)
180 | .provide('RAD_TO_DEG', RAD_TO_DEG)
181 | .module('toDeg', toDegModule)
182 | .resolve();
183 | let injectCalled = 0;
184 | let returnValue = di.inject((toDeg, PI) => {
185 | ++injectCalled;
186 | return toDeg(2 * PI);
187 | }, true);
188 | assert.equal(injectCalled, 1);
189 | assert.equal(returnValue, 360);
190 | });
191 |
192 | it('throws an error when a missing dependency is declared', () => {
193 | di = getDiInstance(gulp)
194 | .task(function (test) {});
195 | assert.throws(() => di.resolve());
196 | });
197 | });
198 |
199 | describe('Tasks', () => {
200 | it('task', (done) => {
201 | di = getDiInstance(gulp)
202 | .provide('PI', PI)
203 | .provide('RAD_TO_DEG', RAD_TO_DEG)
204 | .module('toDeg', toDegModule)
205 | .provide('test', 'test')
206 | .task((test, toDeg, PI) => {
207 | assert.equal(test, 'test');
208 | assert.equal(toDeg(PI), 180);
209 | assert.equal(toDeg(2 * PI), 360);
210 |
211 | done();
212 | })
213 | .resolve();
214 | });
215 |
216 | it('task with an object', (done) => {
217 | di = getDiInstance(gulp)
218 | .task({
219 | 'first': () => {
220 | done();
221 | }
222 | })
223 | .resolve();
224 | });
225 |
226 | it('tasks', (done) => {
227 | di = getDiInstance(gulp, INSTANCE_OPTIONS)
228 | .modules('./modules')
229 | .tasks('./tasks')
230 | .resolve();
231 | di.inject((gulp) => {
232 | assert.ok(hasTask(gulp, 'semistandard'), 'has "semistandard" task');
233 | done();
234 | });
235 | });
236 |
237 | it('includes gulp and can run a task', (done) => {
238 | let taskCalled = false;
239 |
240 | di = getDiInstance(gulp)
241 | .task((gulp, PI, toDeg) => {
242 | assert.ok(gulp);
243 |
244 | gulp.task('default', (cb) => {
245 | taskCalled = true;
246 | assert.equal(toDeg(2 * PI), 360);
247 | cb();
248 | });
249 | })
250 | .module('toDeg', toDegModule)
251 | .provide('PI', PI)
252 | .provide('RAD_TO_DEG', RAD_TO_DEG)
253 | .resolve();
254 |
255 | gulp.series('default', (cb) => {
256 | assert.equal(taskCalled, true);
257 | cb();
258 | done();
259 | })();
260 | });
261 |
262 | it('can concatenate all spec files', (done) => {
263 | let di = getDiInstance(gulp, { parentDir: basePath(), lazy: false });
264 | let s = new Stream.Transform();
265 | let l = 0;
266 | let count = 0;
267 | let ended = false;
268 | s.write = function (file) {
269 | ++count;
270 | l += file._contents.length;
271 | };
272 | s.on('end', () => {
273 | ended = true;
274 | });
275 |
276 | di.task((gulp, concat) => {
277 | gulp.task('concat', () => {
278 | /**
279 | * This task comment should appear in this test
280 | * and it might have multiple lines
281 | */
282 | return gulp.src('test/**/*.js')
283 | .pipe(concat('all_specs.js'))
284 | .pipe(s);
285 | // console.log('The concat command', concat)
286 | });
287 | });
288 | di.resolve();
289 | gulp.series('concat', (cb) => {
290 | assert.ok(ended, 'Stream was closed');
291 | assert.ok(l > 0, 'Read more than zero bytes');
292 | assert.equal(count, 1, 'Solely one file written');
293 | cb();
294 | done();
295 | })();
296 | });
297 | });
298 | });
299 |
--------------------------------------------------------------------------------
/test/11-builtin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Built-in', () => {
4 | let gulp = null;
5 | let di = null;
6 |
7 | beforeEach(() => {
8 | gulp = getGulpInstance();
9 | di = getDiInstance(gulp, { DEBUG: false, pattern: [], someTestSetting: '1', parentDir: basePath() });
10 | });
11 |
12 | it('gulp', (done) => {
13 | di
14 | .task((Package) => {
15 | gulp.task('default', (cb) => cb());
16 | })
17 | .resolve();
18 |
19 | gulp.series('default', (cb) => {
20 | cb();
21 | done();
22 | })();
23 | });
24 |
25 | it('Package', (done) => {
26 | di
27 | .task((Package) => {
28 | assert.equal(Package.name, 'gulp-di');
29 | done();
30 | })
31 | .resolve();
32 | });
33 |
34 | it('basePath', (done) => {
35 | di
36 | .task((basePath) => {
37 | let Package = require(basePath('package.json'));
38 | assert.equal(Package.name, 'gulp-di');
39 | done();
40 | })
41 | .resolve();
42 | });
43 |
44 | it('options', (done) => {
45 | di
46 | .task(function (basePath) {
47 | assert.equal(typeof this.options, 'object');
48 | assert.equal(this.options.someTestSetting, '1');
49 | done();
50 | })
51 | .resolve();
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/test/20-contrib.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('contrib', () => {
4 | const getPath = require(basePath('contrib', 'get-path'));
5 | const standardTask = require(basePath('contrib', 'standard-task'));
6 | const parseFn = require('parse-function');
7 | const path = require('path');
8 | const fs = require('fs');
9 | const basename = path.basename(__filename);
10 |
11 | it('getPath', () => {
12 | let obj = {
13 | first: {
14 | '1': 1,
15 | second: {
16 | '2': 2,
17 | third: {
18 | '3': 3
19 | }
20 | }
21 | }
22 | };
23 | let array = [0, 1, 2, { test: 'test' }];
24 | assert.equal(typeof getPath(obj), 'object');
25 | assert.equal(typeof getPath(obj, 'first'), 'object');
26 | assert.equal(getPath(obj, 'first.1'), 1);
27 | assert.equal(typeof getPath(obj, 'first.second'), 'object');
28 | assert.equal(getPath(obj, 'first.second.2'), 2);
29 | assert.equal(typeof getPath(obj, 'first.second.third'), 'object');
30 | assert.equal(getPath(obj, 'first.second.third.3'), 3);
31 |
32 | assert.equal(getPath(array, '0'), 0);
33 | assert.equal(getPath(array, 0), 0);
34 | assert.equal(getPath(array, '1'), 1);
35 | assert.equal(getPath(array, '2'), 2);
36 | assert.deepEqual(getPath(array, '3'), { test: 'test' });
37 | assert.equal(getPath(array, '3.test'), 'test');
38 | });
39 |
40 | it('help', (done) => {
41 | let gulp = getGulpInstance();
42 | let di = getDiInstance(gulp, { DEBUG: true });
43 | di.resolve();
44 | gulp.series('help', (cb) => {
45 | cb();
46 | done();
47 | })();
48 | });
49 |
50 | it('runningTasks', (done) => {
51 | let gulp = getGulpInstance();
52 | let di = getDiInstance(gulp, {
53 | argv: [null, null, 'mocha', 'runningTasks']
54 | });
55 | di.task((gulp, runningTasks, gutil) => {
56 | gulp.task('runningTasks', (cb) => {
57 | let tasks = runningTasks();
58 | assert.ok(Array.isArray(tasks));
59 | cb();
60 | });
61 | });
62 | di.resolve();
63 | gulp.series('runningTasks', (cb) => {
64 | cb();
65 | done();
66 | })();
67 | });
68 |
69 | describe('standardTask', () => {
70 | it('returns a function which injects solely gulp', () => {
71 | let fn = standardTask('concat', 'templates/**/*.txt', 'public', { pretty: false });
72 | let info = parseFn(fn);
73 | assert.equal(info.name, 'concatTask');
74 | assert.deepEqual(info.args, ['gulp']);
75 | });
76 |
77 | it('throws an error when initialized without a function', () => {
78 | let gulp = getGulpInstance();
79 | let di = getDiInstance(gulp);
80 |
81 | let fn = standardTask('Package', 'templates/**/*.txt', 'public', { pretty: false });
82 | di.task(fn);
83 | di.resolve();
84 | assert.throws(() => {
85 | gulp.start('Package');
86 | });
87 | });
88 |
89 | it('throws an error when an argument cannot be serialized', () => {
90 | let gulp = getGulpInstance();
91 | let di = getDiInstance(gulp);
92 | let data = {};
93 | data.data = data;
94 | let fn = standardTask('concat', 'templates/**/*.txt', 'public', data);
95 | di.task(fn);
96 | di.resolve();
97 | assert.throws(() => {
98 | gulp.start('concat');
99 | });
100 | });
101 |
102 | it('function calls are valid', () => {
103 | let fn = standardTask('concat', 'templates/**/*.txt', 'public', { pretty: false });
104 | assert.equal(typeof fn, 'function');
105 | let fnString = fn + '';
106 | let filenameIndex = fnString.indexOf(basename);
107 | let pluginNameIndex = fnString.indexOf('this.byId("concat");');
108 | assert.ok(~filenameIndex, 'current filename is inside a comment');
109 | assert.ok(~pluginNameIndex, 'task plugin is retrieved with byId("concat")');
110 | assert.equal(fnString[filenameIndex + basename.length], ':', 'line number is delimited from filename');
111 | assert.ok((/\d/).test(fnString[filenameIndex + basename.length + 1]), 'line number is present');
112 | });
113 |
114 | it('can concatenate all spec files', (done) => {
115 | let gulp = getGulpInstance();
116 | let di = getDiInstance(gulp);
117 |
118 | let fn = standardTask('concat', 'test/**/*.js', 'trash', 'all.js');
119 | let destPath = basePath('trash', 'all.js');
120 |
121 | di.task(fn);
122 |
123 | di.resolve();
124 | gulp.series('concat', (cb) => {
125 | cb();
126 | assert.ok(fs.existsSync(destPath));
127 | try {
128 | fs.unlinkSync(destPath);
129 | } catch (e) {
130 | console.log(`Could not remove ${destPath}: ${e.message}`);
131 | }
132 | done();
133 | })();
134 | });
135 | });
136 | });
137 |
--------------------------------------------------------------------------------