From 6751c0a9f83ea9d335356b989999e65af41b9a69 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Sun, 26 Mar 2017 15:24:13 -0700 Subject: [PATCH 01/22] bundle: checkpoint - Fixed logLevel parsing using visitors.Commander instead of visitors.Configuration. logLevel now can only be specified as a cli argument. Progress on sqbox command to instanciate subcommands. --- README.md | 4 ++-- lib/bin/sqbox.es6 | 3 +-- lib/bundle/bundle.es6 | 3 +-- lib/command.es6 | 3 ++- lib/visitors/commander.es6 | 15 ++++++++++++-- lib/visitors/configuration.es6 | 25 +++++++++++------------- test/lib/bin/sqbox.spec.es6 | 3 ++- test/lib/util/factory/factory.spec.es6 | 4 +++- test/lib/util/logger/logger.spec.es6 | 2 ++ test/lib/visitors/commander.spec.es6 | 10 ++++++++++ test/lib/visitors/configuration.spec.es6 | 14 ------------- test/specs/.sqbox.js | 3 +-- test/specs/.sqboxrc | 3 +-- 13 files changed, 49 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index f758e2c..c0a3d17 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ contextual help - sqbox [command] help * sqbox help - global help (list of commands and general usage) * sbox bundle --options * sbox clean -* sbox visualize +* sbox graph ``` Programmatic API @@ -35,7 +35,7 @@ Programmatic API const sqbox = require('squarebox'); sqbox.clean([opts]) .bundle([config]) - .visualize([opts]); + .graph([opts]); ``` #### Official Documentation diff --git a/lib/bin/sqbox.es6 b/lib/bin/sqbox.es6 index 8624bf0..03eac84 100644 --- a/lib/bin/sqbox.es6 +++ b/lib/bin/sqbox.es6 @@ -73,8 +73,7 @@ class SquareBox extends Command { **/ onConfiguration() { const { path } = this.options; - //console.log(_.pick(this, Command.options)); - // TODO: Factory.get(path, this.params()).run(); + Factory.get(path, _.pick(this, Command.options)).run(); return this; } diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index 5f228bc..363aada 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -19,8 +19,7 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ run() { - // TODO - console.log('Bundle.run()...'); + //console.log(this.toJSON()); return super.run(); } diff --git a/lib/command.es6 b/lib/command.es6 index 4dedf69..cf33612 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -227,7 +227,8 @@ class Command extends Visited { 'exclude', 'extensions', 'alias', - 'target' + 'target', + 'logLevel' ]; /** diff --git a/lib/visitors/commander.es6 b/lib/visitors/commander.es6 index bff0ee6..9f48742 100644 --- a/lib/visitors/commander.es6 +++ b/lib/visitors/commander.es6 @@ -9,7 +9,7 @@ import yargs from 'yargs'; import chalk from 'chalk'; import Factory from 'util/factory/factory'; import Visitor from 'util/visitor/visitor'; -import logger from 'util/logger/logger'; +import logger, { Logger } from 'util/logger/logger'; /** * Class Commander @@ -60,7 +60,18 @@ class Commander extends Visitor { * @return {visitors.Commander} **/ onParse(err, argv, output) { - return this.emit(Commander.events.parse, err, argv, output); + return this._logger(argv.logLevel).emit(Commander.events.parse, err, argv, output); + } + + /** + * Sets Logger Level + * @public + * @param {String} lvl - Logger level + * @return {visitors.Commander} + **/ + _logger(lvl) { + logger.level(Logger.level[lvl]); + return this; } /** diff --git a/lib/visitors/configuration.es6 b/lib/visitors/configuration.es6 index ef46199..5c12e51 100644 --- a/lib/visitors/configuration.es6 +++ b/lib/visitors/configuration.es6 @@ -7,7 +7,7 @@ import extend from 'extend'; import Factory from 'util/factory/factory'; import Visitor from 'util/visitor/visitor'; import QueueAsync from 'util/adt/queue-async'; -import logger, { Logger } from 'util/logger/logger'; +import logger from 'util/logger/logger'; /** * Class Configuration @@ -92,17 +92,6 @@ class Configuration extends Visitor { return this; } - /** - * Sets Logger level - * @private - * @param {String} level - logger level - * @return {visitors.Configuration} - **/ - _logger(level) { - logger.level(Logger.level[level]); - return this; - } - /** * Visit Strategy * @public @@ -146,7 +135,6 @@ class Configuration extends Visitor { if(_.defined(result.warn)) return this.onParseError(result.warn); this._source(result.source) ._target(result.target) - ._logger(result.logLevel) ._override(this.command.options); return this; } @@ -208,7 +196,16 @@ class Configuration extends Visitor { * @static * @type {Array} **/ - static cliOptions = ['scan', 'exclude', 'extensions', 'alias', 'target', 'destination', 'format']; + static cliOptions = [ + 'scan', + 'exclude', + 'extensions', + 'alias', + 'target', + 'destination', + 'format', + 'logLevel' + ]; /** * Configuration Events diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6 index 9b16533..0b3de48 100644 --- a/test/lib/bin/sqbox.spec.es6 +++ b/test/lib/bin/sqbox.spec.es6 @@ -54,7 +54,8 @@ describe('bin.SquareBox', function() { '--x', './source/dependencies/**,./source/package/**', '--e', '.js,.es6', '--a', 'common:./path/common', - '--t', 'add>umd:./dist/umd,other>cjs:./dist/cjs' + '--t', 'add>umd:./dist/umd,other>cjs:./dist/cjs', + '--lv', 'silent' ]); this.mockCommander.expects('_args') diff --git a/test/lib/util/factory/factory.spec.es6 b/test/lib/util/factory/factory.spec.es6 index 7695568..e002f32 100644 --- a/test/lib/util/factory/factory.spec.es6 +++ b/test/lib/util/factory/factory.spec.es6 @@ -3,7 +3,7 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import factory from 'util/factory/factory'; -import logger from 'util/logger/logger'; +import logger, { Logger } from 'util/logger/logger'; import Command from 'command'; import Collection from 'util/adt/collection'; @@ -91,8 +91,10 @@ describe('util.factory.Factory', function() { }); it('Should return false: resolved path doesn\'t exists (fs.statSync)', () => { + logger.level(Logger.level.output); const expLoggerOutput = this.mockLogger.expects('_stdout').atLeast(1).returns(logger); assert.isFalse(factory._validate('file-unexistent')); + logger.level(Logger.level.silent); }); }); diff --git a/test/lib/util/logger/logger.spec.es6 b/test/lib/util/logger/logger.spec.es6 index 6c71bcf..3587d20 100644 --- a/test/lib/util/logger/logger.spec.es6 +++ b/test/lib/util/logger/logger.spec.es6 @@ -36,12 +36,14 @@ describe('util.logger.Logger', function() { }); it('Should adds to the buffer by using constructor function', () => { + logger.level(Logger.level.debug); const expOut = this.mockProto.expects('_stdout') .once() .withArgs(sinon.match.string) .returns(logger); logger('hello')('world').out(logger.magenta); + logger.level(Logger.level.silent); }); }); diff --git a/test/lib/visitors/commander.spec.es6 b/test/lib/visitors/commander.spec.es6 index 84538c9..a6e0ce4 100644 --- a/test/lib/visitors/commander.spec.es6 +++ b/test/lib/visitors/commander.spec.es6 @@ -7,6 +7,7 @@ import Factory from 'util/factory/factory'; import yargs from 'yargs'; import Command from 'command'; import chalk from 'chalk'; +import logger, { Logger } from 'util/logger/logger'; describe('visitors.Commander', function() { @@ -181,6 +182,15 @@ describe('visitors.Commander', function() { }); + describe('_logger()', () => { + + it('Should apply logger configuration option to the singleton logger', () => { + assert.instanceOf(this.commander._logger('debug'), Commander); + logger.level(Logger.level.silent); + }); + + }); + describe('onParse()', () => { it('Should execute onParse handler', () => { diff --git a/test/lib/visitors/configuration.spec.es6 b/test/lib/visitors/configuration.spec.es6 index eadaa94..5ef9a29 100644 --- a/test/lib/visitors/configuration.spec.es6 +++ b/test/lib/visitors/configuration.spec.es6 @@ -187,15 +187,6 @@ describe('visitors.Configuration', function() { }); - describe('_logger()', () => { - - it('Should apply logger configuration option to the singleton logger', () => { - assert.instanceOf(this.configuration._logger('silent'), Configuration); - logger.level(Logger.level.output); - }); - - }); - describe('_override()', () => { it('Should override configuration options with cli options', () => { @@ -274,10 +265,6 @@ describe('visitors.Configuration', function() { .once() .withArgs(expResult.target) .returns(this.configuration); - const expLogger = this.mockProto.expects('_logger') - .once() - .withArgs(expResult.logLevel) - .returns(this.configuration); const expOverride = this.mockProto.expects('_override') .once() .withArgs(this.command.options) @@ -295,7 +282,6 @@ describe('visitors.Configuration', function() { .returns(this.configuration); const expSource = this.mockProto.expects('_source').never(); const expTarget = this.mockProto.expects('_target').never(); - const expLogger = this.mockProto.expects('_logger').never(); const expOverride = this.mockProto.expects('_override').never(); assert.instanceOf(this.configuration.onOptions(expResult), Configuration); diff --git a/test/specs/.sqbox.js b/test/specs/.sqbox.js index d78e20d..c8001a1 100644 --- a/test/specs/.sqbox.js +++ b/test/specs/.sqbox.js @@ -29,6 +29,5 @@ module.exports = { destination: './dist/amd', format: 'amd' } - }, - logLevel: 'debug' + } }; diff --git a/test/specs/.sqboxrc b/test/specs/.sqboxrc index 2d6e97e..940164f 100644 --- a/test/specs/.sqboxrc +++ b/test/specs/.sqboxrc @@ -25,6 +25,5 @@ "destination": "./dist/amd", "format": "amd" } - }, - "logLevel": "debug" + } } From 701e886e2610e08fede9242afbcc6d6d7ae9a6ad Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 27 Mar 2017 08:15:41 -0700 Subject: [PATCH 02/22] bundle: checkpoint - Fixed visitors.formatter.Json issue with circular references to non-native references. --- lib/bin/sqbox.es6 | 14 ++++++++------ lib/bundle/bundle.es6 | 2 +- lib/util/mixins.es6 | 12 ++++++++++++ lib/visitors/formatter/json.es6 | 8 ++++---- test/lib/bin/sqbox.spec.es6 | 34 ++++++++++++++++++++++++++------- 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/lib/bin/sqbox.es6 b/lib/bin/sqbox.es6 index 03eac84..7d33d37 100644 --- a/lib/bin/sqbox.es6 +++ b/lib/bin/sqbox.es6 @@ -23,12 +23,13 @@ class SquareBox extends Command { /** * Constructor * @public - * @param {Object} [args = {}] - Constructor arguments + * @param {Symbol} _enforcer - private enforcer symbol + * @param {Object} [...args] - Constructor arguments * @return {bin.SquareBox} **/ - constructor(...args) { + constructor(_enforcer, ...args) { super(...args); - return SquareBox.isPrivate(this.attachEvents()); + return SquareBox.isPrivate(_enforcer, this.attachEvents()); } /** @@ -122,11 +123,12 @@ class SquareBox extends Command { /** * Static enforcer validation * @static + * @param {Symbol} _enforcer - private enforcer symbol * @param {bin.SquareBox} instance - squarebox instance reference * @return {bin.SquareBox} **/ - static isPrivate(instance) { - return instance.isPrivate(enforcer); + static isPrivate(_enforcer, instance) { + return instance.isPrivate(_enforcer); } /** @@ -143,7 +145,7 @@ class SquareBox extends Command { * @return {bin.SquareBox} **/ static run(cwd = process.cwd()) { - return this.new({ cwd }).run(); + return this.new(enforcer, { cwd }).run(); } } diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index 363aada..ec351bc 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -19,7 +19,7 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ run() { - //console.log(this.toJSON()); + //console.log('OUTPUT: ', this.toJSON()); return super.run(); } diff --git a/lib/util/mixins.es6 b/lib/util/mixins.es6 index 54864a0..db0dd5c 100644 --- a/lib/util/mixins.es6 +++ b/lib/util/mixins.es6 @@ -69,6 +69,18 @@ _.mixin({ return (_.isRealObject(o) || _.isArray(o)); }, + /** + * Returns true if a given object's constructor is a native type, false otherwise. + * @public + * @param [o] {Object} + * @return {Boolean} + **/ + isNative: function(o) { + if(!_.defined(o) || !_.defined(o.constructor)) return true; + let _c = _s.strRightBack(_s.strLeft(o.constructor.toString(), '('), 'function '); + return _.contains(['String', 'Number', 'Boolean', 'Object', 'Array'], _c); + }, + /** * Returns an array of all method names of a given object. * @public diff --git a/lib/visitors/formatter/json.es6 b/lib/visitors/formatter/json.es6 index b6b7a90..ce477b4 100644 --- a/lib/visitors/formatter/json.es6 +++ b/lib/visitors/formatter/json.es6 @@ -32,10 +32,10 @@ class Json extends Visitor { * @return {Object} **/ _filterObject(m, v, k) { - if(_.isAdt(v)) return this._clean(v, v); - if(!_.isFunction(v)) { m[k] = v; return m; } - if(_.isArray(m)) m.splice(k, 1) - if(_.isRealObject(m)) delete m[k]; + if(_.isNative(v)) { + m[k] = v; + return m; + } return m; } diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6 index 0b3de48..dbbd0b3 100644 --- a/test/lib/bin/sqbox.spec.es6 +++ b/test/lib/bin/sqbox.spec.es6 @@ -4,6 +4,7 @@ **/ import SquareBox from 'bin/sqbox'; import Commander from 'visitors/commander'; +import Bundle from 'bundle/bundle'; describe('bin.SquareBox', function() { @@ -13,17 +14,20 @@ describe('bin.SquareBox', function() { }); beforeEach(() => { - if(this.sqbox) this.mockProto = this.sandbox.mock(this.sqbox); + this.mockProto = this.sandbox.mock(SquareBox.prototype); this.mockCommander = this.sandbox.mock(Commander.prototype); + this.mockBundle = this.sandbox.mock(Bundle.prototype); this.input = [process.argv[0], this.cwd]; }); afterEach(() => { - if(this.mockProto) this.mockProto.verify(); + this.mockProto.verify(); this.mockCommander.verify(); + this.mockBundle.verify(); this.sandbox.restore(); + delete this.mockBundle; delete this.mockCommander; delete this.mockProto; delete this.input; @@ -36,9 +40,20 @@ describe('bin.SquareBox', function() { describe('constructor()', () => { - it('Should get an instance', () => { - this.sqbox = require('bin/sqbox').default.new(); - assert.instanceOf(this.sqbox, SquareBox); + it('Should throw Error: Violation Error', () => { + assert.throws(() => new SquareBox, 'Private Violation'); + }); + + }); + + describe('static->run()', () => { + + it('Should get a new instance and run it (with custom cwd)', () => { + const expRun = this.mockProto.expects('run') + .once() + .returns(sinon.match.instanceOf(SquareBox)); + + SquareBox.run(__dirname); }); }); @@ -58,11 +73,16 @@ describe('bin.SquareBox', function() { '--lv', 'silent' ]); - this.mockCommander.expects('_args') + const expBundleRun = this.mockBundle.expects('run') + .once() + .returns(sinon.match.instanceOf(Bundle)); + + const expArgs = this.mockCommander.expects('_args') .once() .returns(this.input); - assert.instanceOf(this.sqbox.run(), SquareBox); + this.sqbox = SquareBox.run(); + assert.instanceOf(this.sqbox, SquareBox); }); }); From 4273e17f51880997086b4207ad2a1e99b5133bee Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 10 Apr 2017 10:41:42 -0700 Subject: [PATCH 03/22] bundle: checkpoint - Added dependsOn directive on each subcommand to let bin.SquareBox orchestrate command dependencies. Added specs es6/amd/cjs files for unit testing bundle.writer, bundle.reader and bundle.format supportive classes for the bundle command. --- lib/bin/sqbox.es6 | 72 ++++++++++++---- lib/bundle/bundle.es6 | 39 ++++++++- lib/bundle/format/amd.es6 | 0 lib/bundle/format/commonjs.es6 | 0 lib/bundle/format/es6.es6 | 0 lib/bundle/reader/reader.es6 | 0 lib/bundle/writer/writer.es6 | 0 lib/clean/clean.es6 | 14 ++- lib/command.es6 | 123 +++++++++++---------------- lib/visitors/command/properties.es6 | 44 ++++++++++ lib/visualize/graph.es6 | 16 +++- test/lib/bin/sqbox.spec.es6 | 6 +- test/lib/bundle/bundle.spec.es6 | 72 ++++++++++++++++ test/lib/command.spec.es6 | 7 +- test/specs/amd/source-a.js | 0 test/specs/cjs/source-a.js | 0 test/specs/es6/common/common-a.es6 | 0 test/specs/es6/common/common-b.es6 | 0 test/specs/es6/module-a/source-a.es6 | 0 test/specs/es6/module-b/source-b.es6 | 0 20 files changed, 291 insertions(+), 102 deletions(-) create mode 100644 lib/bundle/format/amd.es6 create mode 100644 lib/bundle/format/commonjs.es6 create mode 100644 lib/bundle/format/es6.es6 create mode 100644 lib/bundle/reader/reader.es6 create mode 100644 lib/bundle/writer/writer.es6 create mode 100644 lib/visitors/command/properties.es6 create mode 100644 test/lib/bundle/bundle.spec.es6 create mode 100644 test/specs/amd/source-a.js create mode 100644 test/specs/cjs/source-a.js create mode 100644 test/specs/es6/common/common-a.es6 create mode 100644 test/specs/es6/common/common-b.es6 create mode 100644 test/specs/es6/module-a/source-a.es6 create mode 100644 test/specs/es6/module-b/source-b.es6 diff --git a/lib/bin/sqbox.es6 b/lib/bin/sqbox.es6 index 7d33d37..4abc2b8 100644 --- a/lib/bin/sqbox.es6 +++ b/lib/bin/sqbox.es6 @@ -1,10 +1,12 @@ /** * @module bin * @author Patricio Ferreira <3dimentionar@gmail.com> +* @Notes Think about the API require('sqbox').clean({}).bundle({}).graph({}); **/ import 'util/mixins'; import extend from 'extend'; import Collection from 'util/adt/collection'; +import StackAsync from 'util/adt/stack-async'; import Factory from 'util/factory/factory'; import Command from 'command'; import CommandsList from './commands.json'; @@ -33,12 +35,14 @@ class SquareBox extends Command { } /** - * Attaches Events + * Attach Events * @public * @return {bin.SquareBox} **/ attachEvents() { - this.once(SquareBox.events.done, this.after); + this.on(Command.events.pending, _.bind(this.onCommandPending, this)); + this.on(Command.events.done, _.bind(this.onCommandDone, this)); + this.stack.on(StackAsync.events.push, _.bind(this.onCommand, this)); return this; } @@ -62,43 +66,77 @@ class SquareBox extends Command { **/ run() { this.before(); - this.configuration.parse().then(_.bind(this.onConfiguration, this)); + this.configuration.parse() + .then(_.bind(this.push, this, this, this.options.path)) + .catch(_.bind(this.after, this)); return this; } /** - * Configuration Loaded and Parsed Handler + * Subcommand push Handler * @public - * @param {visitors.Configuration} configuration - configuration visitor reference + * @param {util.adt.StackAsync} stack - stack reference + * @param {Command} command - command offered * @return {bin.SquareBox} **/ - onConfiguration() { - const { path } = this.options; - Factory.get(path, _.pick(this, Command.options)).run(); + onCommand(stack, command) { + let dependents = command.getDependsOn(); + return (dependents.length > 0) ? _.reduce(dependents, this.push, this, this) : this.after(); + } + + /** + * Command Pending Handler + * @public + * @param {Command} command - command reference on pending state + * @return {bin.SquareBox} + **/ + onCommandPending(command) { return this; } /** - * Constructor Validation + * Command Done Handler * @public - * @throws {Error} Private violation - * @param {Symbol} pte - constructor enforcer + * @param {Command} command - command reference on done state * @return {bin.SquareBox} **/ - isPrivate(pte) { - if(!_.isEqual(pte, enforcer)) throw new Error('Private Violation'); + onCommandDone(command) { return this; } /** - * Register Command Factories + * After Run * @public * @override + * @param {Error} [err] - error reference * @return {bin.SquareBox} **/ - register() { - super.register(); - Factory.registerAll(this.constructor.commands); + after(err, results) { + if(_.defined(err)) logger(err.message).fatal(); + if(this.stack.isEmpty()) return this.complete(results); + this.stack.pop().then(_.bind(this.after, this, null)).catch(_.bind(this.after, this)); + return this; + } + + /** + * SquareBox Complete Handler + * @public + * @param {Array} results - all Results + * @return {bin.SquareBox} + **/ + complete(results) { + return super.after(); + } + + /** + * Constructor Validation + * @public + * @throws {Error} Private violation + * @param {Symbol} pte - constructor enforcer + * @return {bin.SquareBox} + **/ + isPrivate(pte) { + if(!_.isEqual(pte, enforcer)) throw new Error('Private Violation'); return this; } diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index ec351bc..c4a165a 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -5,6 +5,7 @@ import _ from 'underscore'; import extend from 'extend'; import Command from 'command'; +import Factory from 'util/factory/factory'; /** * Class Bundle @@ -12,17 +13,51 @@ import Command from 'command'; **/ class Bundle extends Command { + /** + * Before Run + * @public + * @override + * @return {Promise} + **/ + before() { + super.before(); + // TODO: Set Reader/Writer up + return this; + } + /** * Run * @public * @override + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject * @return {bundle.Bundle} **/ - run() { - //console.log('OUTPUT: ', this.toJSON()); + run(resolve, reject) { return super.run(); } + /** + * List of commands that depends on + * @static + * @property dependsOn + * @type {Array} + **/ + static dependsOn = Command.dependsOn.concat([ + 'clean/clean' + ]); + + /** + * Bundle Command Visitors + * @static + * @override + * @type {Array} + **/ + // static visitors = [ + // 'bundle/reader/reader', + // 'bundle/writer/writer' + // ].concat(Command.visitors); + } export default Bundle; diff --git a/lib/bundle/format/amd.es6 b/lib/bundle/format/amd.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/format/commonjs.es6 b/lib/bundle/format/commonjs.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/format/es6.es6 b/lib/bundle/format/es6.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/reader/reader.es6 b/lib/bundle/reader/reader.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/writer/writer.es6 b/lib/bundle/writer/writer.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/clean/clean.es6 b/lib/clean/clean.es6 index 0f9eaea..60b1e1e 100644 --- a/lib/clean/clean.es6 +++ b/lib/clean/clean.es6 @@ -16,14 +16,22 @@ class Clean extends Command { * Run * @public * @override + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject * @return {clean.Clean} **/ - run() { - // TODO - console.log('Clean.run()...'); + run(resolve, reject) { return super.run(); } + /** + * List of commands that depends on + * @static + * @property dependsOn + * @type {Array} + **/ + static dependsOn = Command.dependsOn.concat([]); + } export default Clean; diff --git a/lib/command.es6 b/lib/command.es6 index cf33612..07f5c89 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -6,7 +6,9 @@ import { EventEmitter } from 'events'; import _ from 'util/mixins'; import extend from 'extend'; import Factory from 'util/factory/factory'; +import StackAsync from 'util/adt/stack-async'; import Visited from 'util/visitor/visited'; +import logger from 'util/logger/logger'; /** * Class Command @@ -20,12 +22,13 @@ class Command extends Visited { /** * Constructor * @public + * @override * @param {Object} [args = {}] - constructor arguments * @return {Command} **/ constructor(args = {}) { super(); - return this.settings(args).register().acceptAll(); + return extend(true, this.settings(args).register().acceptAll(), { stack: StackAsync.new([]) }); } /** @@ -36,11 +39,11 @@ class Command extends Visited { * @return {Command} **/ settings(options) { - return extend(true, this, this.constructor.defaults, _.pick(options, this.constructor.options)); + return extend(true, this, _.defaults(_.pick(options, this.constructor.options), this.constructor.defaults)); } /** - * Registers Visitors + * Register Visitors * @public * @return {Command} **/ @@ -52,15 +55,27 @@ class Command extends Visited { /** * Accepts All Visitors * @public - * @return {command.Command} + * @return {Command} **/ acceptAll() { return _.reduce(this.constructor.visitors, (memo, v) => memo.accept(Factory.get(v, this)), this); } + /** + * Push a dependent subcommand onto this command + * @public + * @param {Command} memo - memoized version of this command + * @param {String} path - dependent command factory path + * @return {Command} + **/ + push(memo, path) { + let opts = extend(true, { parent: memo }, _.pick(memo.toJSON(), this.constructor.options)); + memo.stack.push(Factory.register(path).get(path, opts)); + return memo; + } + /** * Proxified asynchronous next strategy - * FIXME: Implement when the command gets rejected. * @public * @override * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations @@ -69,8 +84,9 @@ class Command extends Visited { * @return {Promise} **/ next(adt, resolve, reject) { - this.once(Command.events.done, resolve); - return this.run(); + this.once(Command.events.done, resolve) + .once(Command.events.error, reject); + return this.run(resolve, reject); } /** @@ -79,15 +95,18 @@ class Command extends Visited { * @return {Command} **/ before() { + if(_.defined(this.getParent())) this.getParent().emit(Command.events.pending, this); return this.pending(); } /** * Default Run * @public + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject * @return {Command} **/ - run() { + run(resolve, reject) { return this.before().after(); } @@ -97,15 +116,18 @@ class Command extends Visited { * @return {Command} **/ after() { - return this.done(); + this.done(); + if(_.defined(this.getParent())) this.getParent().emit(Command.events.done, this); + return this; } /** * Command Pending exeuction state * @public - * @return {command.Command} + * @return {Command} **/ pending() { + logger(`[${this.constructor.name}] Started`).out(); this.emit(Command.events.pending, this); return this; } @@ -113,75 +135,21 @@ class Command extends Visited { /** * Command Done exeuction state * @public - * @return {command.Command} + * @return {Command} **/ done() { + logger(`[${this.constructor.name}] Finished`).out(); this.emit(Command.events.done, this); return this; } /** - * Retrieves source - * @public - * @return {Object} - **/ - source() { - return this.source; - } - - /** - * Retrieves and resolves scan directory (glob) - * @public - * @return {String} - **/ - scan() { - return this.source().scan; - } - - /** - * Retrieves and resolves excluded folders + * Retrieves command depedents * @public * @return {Array} **/ - exclude() { - return this.source().exclude; - } - - /** - * Retrieves list of extensions to scan - * @public - * @return {Array} - **/ - extensions() { - return this.source().extensions; - } - - /** - * Retrieves aliases for modules - * @public - * @return {Object} - **/ - alias() { - return this.source().alias; - } - - /** - * Retrieves target - * @public - * @return {Object} - **/ - target() { - return this.target; - } - - /** - * Retrieve targets by using a given predicate passed by parameter - * @public - * @param {Function} predicate - predicate to walk over the targets - * @return {Array} - **/ - targets(predicate) { - return _.defined(predicate) && _.isFunction(predicate) ? _.map(this.target, predicate, this) : []; + getDependsOn() { + return this.constructor.dependsOn; } /** @@ -199,10 +167,19 @@ class Command extends Visited { * @type {Array} **/ static visitors = [ + 'visitors/command/properties', 'visitors/formatter/json', 'visitors/async/async' ]; + /** + * List of commands that depends on + * @static + * @property dependsOn + * @type {Array} + **/ + static dependsOn = []; + /** * Command Defaults * @static @@ -228,7 +205,8 @@ class Command extends Visited { 'extensions', 'alias', 'target', - 'logLevel' + 'logLevel', + 'parent' ]; /** @@ -237,8 +215,9 @@ class Command extends Visited { * @type {Object} **/ static events = { - pending: 'commands:command:pending', - done: 'commands:command:done' + pending: 'command:pending', + error: 'command:error', + done: 'command:done' }; } diff --git a/lib/visitors/command/properties.es6 b/lib/visitors/command/properties.es6 new file mode 100644 index 0000000..143861d --- /dev/null +++ b/lib/visitors/command/properties.es6 @@ -0,0 +1,44 @@ +/** +* @module visitors.command +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'underscore'; +import Visitor from 'util/visitor/visitor'; + +/** +* Class Properties +* @extends {util.visitor.Visitor} +**/ +class Properties extends Visitor { + + /** + * Retrieve targets by using a given predicate passed by parameter + * @public + * @param {Function} predicate - predicate to walk over the targets + * @return {Array} + **/ + targets(predicate) { + return _.defined(predicate) && _.isFunction(predicate) ? _.map(this.target, predicate, this) : []; + } + + /** + * Retrieves parent command + * @public + * @return {Command} + **/ + getParent() { + return this.parent; + } + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'PropertiesVisitor'; + } + +} + +export default Properties; diff --git a/lib/visualize/graph.es6 b/lib/visualize/graph.es6 index 22b6811..ddde19d 100644 --- a/lib/visualize/graph.es6 +++ b/lib/visualize/graph.es6 @@ -16,14 +16,24 @@ class Graph extends Command { * Run * @public * @override + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject * @return {visualize.Graph} **/ - run() { - // TODO - console.log('Graph.run()...'); + run(resolve, reject) { return super.run(); } + /** + * List of commands that depends on + * @static + * @property dependsOn + * @type {Array} + **/ + static dependsOn = Command.dependsOn.concat([ + 'bundle/bundle'; + ]); + } export default Graph; diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6 index dbbd0b3..384d7c6 100644 --- a/test/lib/bin/sqbox.spec.es6 +++ b/test/lib/bin/sqbox.spec.es6 @@ -73,9 +73,9 @@ describe('bin.SquareBox', function() { '--lv', 'silent' ]); - const expBundleRun = this.mockBundle.expects('run') - .once() - .returns(sinon.match.instanceOf(Bundle)); + // const expBundleRun = this.mockBundle.expects('run') + // .once() + // .returns(sinon.match.instanceOf(Bundle)); const expArgs = this.mockCommander.expects('_args') .once() diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 new file mode 100644 index 0000000..d1912c6 --- /dev/null +++ b/test/lib/bundle/bundle.spec.es6 @@ -0,0 +1,72 @@ +/** +* @module bundle +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import Bundle from 'bundle/bundle'; + +describe('bundle.Bundle', function() { + + before(() => { + this.params = { + scan: './test/specs/es6/**', + extensions: ['.js', '.es6'], + alias: { common: 'common' }, + target: { + global: { destination: './test/specs/dist/global', format: 'ifie' }, + umd: { destination: './test/specs/dist/umd', format: 'umd' }, + cjs: { destination: './test/specs/dist/cjs', format: 'cjs' }, + amd: { destination: './test/specs/dist/amd', format: 'amd' } + } + }; + this.sandbox = sinon.sandbox.create(); + }); + + beforeEach(() => { + this.mockInstance = this.sandbox.mock(Bundle); + }); + + afterEach(() => { + this.mockInstance.verify(); + + this.sandbox.restore(); + + delete this.mockInstance; + }); + + after(() => { + delete this.sandbox; + }); + + describe('constructor()', () => { + + it('Should get a new instance', () => { + this.bundle = Bundle.new(this.params); + assert.instanceOf(this.bundle, Bundle); + }); + + }); + + describe('before()', () => { + + it('Should execute before hook', () => {}); + + }); + + describe('run()', () => { + + it('Should execute command run', () => {}); + + }); + + describe('toJSON()', () => { + + it('Should return a json representation', () => { + const exp = this.bundle.toJSON(); + assert.property(exp, 'cwd'); + assert.property(exp, 'scan'); + assert.property(exp, 'target'); + }); + + }); + +}); diff --git a/test/lib/command.spec.es6 b/test/lib/command.spec.es6 index c94b1bb..ba202fe 100644 --- a/test/lib/command.spec.es6 +++ b/test/lib/command.spec.es6 @@ -11,12 +11,15 @@ describe('Command', function() { }); beforeEach(() => { - this.mockCommand = this.sandbox.mock(Command); + this.mockInstance = this.sandbox.mock(Command); }); afterEach(() => { + this.mockInstance.verify(); + this.sandbox.restore(); - delete this.mockCommand; + + delete this.mockInstance; }); after(() => { diff --git a/test/specs/amd/source-a.js b/test/specs/amd/source-a.js new file mode 100644 index 0000000..e69de29 diff --git a/test/specs/cjs/source-a.js b/test/specs/cjs/source-a.js new file mode 100644 index 0000000..e69de29 diff --git a/test/specs/es6/common/common-a.es6 b/test/specs/es6/common/common-a.es6 new file mode 100644 index 0000000..e69de29 diff --git a/test/specs/es6/common/common-b.es6 b/test/specs/es6/common/common-b.es6 new file mode 100644 index 0000000..e69de29 diff --git a/test/specs/es6/module-a/source-a.es6 b/test/specs/es6/module-a/source-a.es6 new file mode 100644 index 0000000..e69de29 diff --git a/test/specs/es6/module-b/source-b.es6 b/test/specs/es6/module-b/source-b.es6 new file mode 100644 index 0000000..e69de29 From 6ac4d5c18e306e76a19a0a60cc6950139bf1fef6 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Tue, 11 Apr 2017 09:17:16 -0700 Subject: [PATCH 04/22] bundle: checkpoint - Fixed bin.SquareBox dependents subcommands unit test for orchestrating resolve/reject stacked execution. --- lib/bin/commands.json | 6 ++++-- lib/bin/sqbox.es6 | 23 ----------------------- lib/bundle/bundle.es6 | 2 +- lib/clean/clean.es6 | 2 +- lib/command.es6 | 9 +++++++++ lib/visitors/command/properties.es6 | 9 --------- lib/visualize/graph.es6 | 4 ++-- test/lib/bin/sqbox.spec.es6 | 25 ++++++++++++++++++------- 8 files changed, 35 insertions(+), 45 deletions(-) diff --git a/lib/bin/commands.json b/lib/bin/commands.json index e4301ca..2d4accc 100644 --- a/lib/bin/commands.json +++ b/lib/bin/commands.json @@ -17,7 +17,8 @@ "override": { "default": false }, "config": { "default": ".sqboxrc" }, "url": {}, - "target": { "default": "./dist" } + "target": { "default": "./dist" }, + "logLevel": { "alias": "lv", "default": "output" } } }, { "name": "bundle", @@ -44,6 +45,7 @@ "override": { "default": false }, "config": { "default": ".sqboxrc" }, "url": {}, - "target": { "default": "./dist" } + "target": { "default": "./dist" }, + "logLevel": { "alias": "lv", "default": "output" } } }] diff --git a/lib/bin/sqbox.es6 b/lib/bin/sqbox.es6 index 4abc2b8..6add862 100644 --- a/lib/bin/sqbox.es6 +++ b/lib/bin/sqbox.es6 @@ -40,8 +40,6 @@ class SquareBox extends Command { * @return {bin.SquareBox} **/ attachEvents() { - this.on(Command.events.pending, _.bind(this.onCommandPending, this)); - this.on(Command.events.done, _.bind(this.onCommandDone, this)); this.stack.on(StackAsync.events.push, _.bind(this.onCommand, this)); return this; } @@ -53,7 +51,6 @@ class SquareBox extends Command { * @return {bin.SquareBox} **/ before() { - super.before(); this.commander.read(); return this; } @@ -84,26 +81,6 @@ class SquareBox extends Command { return (dependents.length > 0) ? _.reduce(dependents, this.push, this, this) : this.after(); } - /** - * Command Pending Handler - * @public - * @param {Command} command - command reference on pending state - * @return {bin.SquareBox} - **/ - onCommandPending(command) { - return this; - } - - /** - * Command Done Handler - * @public - * @param {Command} command - command reference on done state - * @return {bin.SquareBox} - **/ - onCommandDone(command) { - return this; - } - /** * After Run * @public diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index c4a165a..4c802f6 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -34,7 +34,7 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ run(resolve, reject) { - return super.run(); + return super.run(resolve, reject); } /** diff --git a/lib/clean/clean.es6 b/lib/clean/clean.es6 index 60b1e1e..4b378fa 100644 --- a/lib/clean/clean.es6 +++ b/lib/clean/clean.es6 @@ -21,7 +21,7 @@ class Clean extends Command { * @return {clean.Clean} **/ run(resolve, reject) { - return super.run(); + return super.run(resolve, reject); } /** diff --git a/lib/command.es6 b/lib/command.es6 index 07f5c89..f3cdef2 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -161,6 +161,15 @@ class Command extends Visited { return this.options; } + /** + * Retrieves parent command + * @public + * @return {Command} + **/ + getParent() { + return this.parent; + } + /** * Command Visitors * @static diff --git a/lib/visitors/command/properties.es6 b/lib/visitors/command/properties.es6 index 143861d..dc7d249 100644 --- a/lib/visitors/command/properties.es6 +++ b/lib/visitors/command/properties.es6 @@ -21,15 +21,6 @@ class Properties extends Visitor { return _.defined(predicate) && _.isFunction(predicate) ? _.map(this.target, predicate, this) : []; } - /** - * Retrieves parent command - * @public - * @return {Command} - **/ - getParent() { - return this.parent; - } - /** * Visitor Name * @public diff --git a/lib/visualize/graph.es6 b/lib/visualize/graph.es6 index ddde19d..14f16db 100644 --- a/lib/visualize/graph.es6 +++ b/lib/visualize/graph.es6 @@ -21,7 +21,7 @@ class Graph extends Command { * @return {visualize.Graph} **/ run(resolve, reject) { - return super.run(); + return super.run(resolve, reject); } /** @@ -31,7 +31,7 @@ class Graph extends Command { * @type {Array} **/ static dependsOn = Command.dependsOn.concat([ - 'bundle/bundle'; + 'bundle/bundle' ]); } diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6 index 384d7c6..146d38d 100644 --- a/test/lib/bin/sqbox.spec.es6 +++ b/test/lib/bin/sqbox.spec.es6 @@ -4,7 +4,10 @@ **/ import SquareBox from 'bin/sqbox'; import Commander from 'visitors/commander'; +import Command from 'command'; +import Graph from 'visualize/graph'; import Bundle from 'bundle/bundle'; +import Clean from 'clean/clean'; describe('bin.SquareBox', function() { @@ -16,18 +19,15 @@ describe('bin.SquareBox', function() { beforeEach(() => { this.mockProto = this.sandbox.mock(SquareBox.prototype); this.mockCommander = this.sandbox.mock(Commander.prototype); - this.mockBundle = this.sandbox.mock(Bundle.prototype); this.input = [process.argv[0], this.cwd]; }); afterEach(() => { this.mockProto.verify(); this.mockCommander.verify(); - this.mockBundle.verify(); this.sandbox.restore(); - delete this.mockBundle; delete this.mockCommander; delete this.mockProto; delete this.input; @@ -63,7 +63,7 @@ describe('bin.SquareBox', function() { it('Should run the command', () => { this.input = this.input.concat([ 'sqbox', - 'bundle', + 'graph', '--config', 'test/specs/.sqboxrc', '--s', './source/**', '--x', './source/dependencies/**,./source/package/**', @@ -73,9 +73,18 @@ describe('bin.SquareBox', function() { '--lv', 'silent' ]); - // const expBundleRun = this.mockBundle.expects('run') - // .once() - // .returns(sinon.match.instanceOf(Bundle)); + const stubCommandAfter = this.sandbox.stub(Command.prototype, 'getParent', () => this.sqbox); + + const stubRun = (construct) => { + return (resolve, reject) => { + resolve({}); + return construct.prototype; + }; + }; + + const stubCleanRun = this.sandbox.stub(Clean.prototype, 'run', stubRun(Clean)); + const stubBundleRun = this.sandbox.stub(Bundle.prototype, 'run', stubRun(Bundle)); + const stubGraphRun = this.sandbox.stub(Graph.prototype, 'run', stubRun(Graph)); const expArgs = this.mockCommander.expects('_args') .once() @@ -83,6 +92,8 @@ describe('bin.SquareBox', function() { this.sqbox = SquareBox.run(); assert.instanceOf(this.sqbox, SquareBox); + + Command.prototype.getParent.restore(); }); }); From b67401b19ec9522d7748e28e7491a7318b4f0e60 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Tue, 25 Apr 2017 09:40:07 -0700 Subject: [PATCH 05/22] bundle: checkpoint - progress on building bundle supported AST element types. Helpers and Visitors added. Implemented Promise based iteration over tasks to perform for AST Reading/Writing metadata from source code. --- lib/bundle/bundle.es6 | 33 +++++---- lib/bundle/format/amd.es6 | 18 +++++ lib/bundle/format/commonjs.es6 | 18 +++++ lib/bundle/format/es6.es6 | 18 +++++ lib/bundle/format/format.es6 | 62 ++++++++++++++++ lib/bundle/reader/reader.es6 | 0 lib/bundle/task/reader/reader.es6 | 56 +++++++++++++++ lib/bundle/task/task.es6 | 101 +++++++++++++++++++++++++++ lib/bundle/task/writer/writer.es6 | 52 ++++++++++++++ lib/bundle/types/annotation.es6 | 18 +++++ lib/bundle/types/export.es6 | 18 +++++ lib/bundle/types/import.es6 | 18 +++++ lib/bundle/types/type.es6 | 67 ++++++++++++++++++ lib/bundle/writer/writer.es6 | 0 lib/command.es6 | 2 +- test/lib/bundle/bundle.spec.es6 | 11 ++- test/specs/es6/common/common-a.es6 | 6 ++ test/specs/es6/common/common-b.es6 | 4 ++ test/specs/es6/module-a/source-a.es6 | 7 ++ test/specs/es6/module-b/source-b.es6 | 6 ++ 20 files changed, 492 insertions(+), 23 deletions(-) create mode 100644 lib/bundle/format/format.es6 delete mode 100644 lib/bundle/reader/reader.es6 create mode 100644 lib/bundle/task/reader/reader.es6 create mode 100644 lib/bundle/task/task.es6 create mode 100644 lib/bundle/task/writer/writer.es6 create mode 100644 lib/bundle/types/annotation.es6 create mode 100644 lib/bundle/types/export.es6 create mode 100644 lib/bundle/types/import.es6 create mode 100644 lib/bundle/types/type.es6 delete mode 100644 lib/bundle/writer/writer.es6 diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index 4c802f6..2ff2096 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -14,27 +14,30 @@ import Factory from 'util/factory/factory'; class Bundle extends Command { /** - * Before Run + * Run * @public * @override - * @return {Promise} + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @return {bundle.Bundle} **/ - before() { - super.before(); - // TODO: Set Reader/Writer up + run(resolve, reject) { + Promise.all([this.read(), this.write()]) + .then(_.bind(this.after, this)) + .catch(_.bind(this.after, this)); return this; } /** - * Run + * After Run * @public * @override - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Array|Error} result result reference * @return {bundle.Bundle} **/ - run(resolve, reject) { - return super.run(resolve, reject); + after(result) { + if(_.instanceOf(result, Error)) return this.emit(Command.events.error, result); + return this.done(); } /** @@ -48,15 +51,15 @@ class Bundle extends Command { ]); /** - * Bundle Command Visitors + * List of visitors * @static * @override * @type {Array} **/ - // static visitors = [ - // 'bundle/reader/reader', - // 'bundle/writer/writer' - // ].concat(Command.visitors); + static visitors = Command.visitors.concat([ + 'bundle/task/reader/reader', + 'bundle/task/writer/writer' + ]); } diff --git a/lib/bundle/format/amd.es6 b/lib/bundle/format/amd.es6 index e69de29..c478fea 100644 --- a/lib/bundle/format/amd.es6 +++ b/lib/bundle/format/amd.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.format +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Format from 'bundle/format/format'; +import logger from 'util/logger/logger'; + +/** +* Class Amd +* @extends {bundle.format.Format} +**/ +class Amd extends Format { + +} + +export default Amd; diff --git a/lib/bundle/format/commonjs.es6 b/lib/bundle/format/commonjs.es6 index e69de29..8995659 100644 --- a/lib/bundle/format/commonjs.es6 +++ b/lib/bundle/format/commonjs.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.format +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Format from 'bundle/format/format'; +import logger from 'util/logger/logger'; + +/** +* Class CommonJs +* @extends {bundle.format.Format} +**/ +class CommonJs extends Format { + +} + +export default CommonJs; diff --git a/lib/bundle/format/es6.es6 b/lib/bundle/format/es6.es6 index e69de29..83728ed 100644 --- a/lib/bundle/format/es6.es6 +++ b/lib/bundle/format/es6.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.format +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Format from 'bundle/format/format'; +import logger from 'util/logger/logger'; + +/** +* Class Es6 +* @extends {bundle.format.Format} +**/ +class Es6 extends Format { + +} + +export default Es6; diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 new file mode 100644 index 0000000..a23153c --- /dev/null +++ b/lib/bundle/format/format.es6 @@ -0,0 +1,62 @@ +/** +* @module bundle.format +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import { EventEmitter } from 'events'; +import _ from 'util/mixins'; +import extend from 'extend'; +import jp from 'jsonpath'; +import Collection from 'util/adt/collection'; +import logger from 'util/logger/logger'; + +/** +* Class Format +* @extends {events.EventEmitter} +**/ +class Format extends EventEmitter { + + /** + * Constructor + * @public + * @param {Any} [...args] - constructor arguments + * @return {bundle.format.Format} + **/ + constructor() { + super(); + return this; + } + + /** + * Method Wrapper for querying AST + * @public + * @param {String} [expr = ''] - json path query + * @param {Object} [o = {}] - object to query + * @return {Any} + **/ + query(expr = '', o = {}) { + return this.result(jp.query(expr, o)); + } + + /** + * Default Result Handler + * @public + * @param {Any} out - ast query result + * @return {bundle.format.Format} + **/ + result(out) { + return this; + } + + /** + * Static Constructor + * @static + * @param {Any} [...args] - constructor arguments + * @return {bundle.format.Format} + **/ + static new(...args) { + return new this(...args); + } + +} + +export default Format; diff --git a/lib/bundle/reader/reader.es6 b/lib/bundle/reader/reader.es6 deleted file mode 100644 index e69de29..0000000 diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 new file mode 100644 index 0000000..c7b2119 --- /dev/null +++ b/lib/bundle/task/reader/reader.es6 @@ -0,0 +1,56 @@ +/** +* @module bundle.reader +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import minimatch from 'minimatch'; +import StackAsync from 'util/adt/stack-async'; +import Task from 'bundle/task/task'; + +/** +* Class Reader +* @desc +* Responsible for traversing javascript files from directories and subdirectories to build an AST +* representation in order to query for AST elements and apply transformation. +* @extends {bundle.task.Task} +**/ +class Reader extends Task { + + /** + * Read Strategy + * @public + * @param {util.visitor.Visited} vi - visited instance reference + * @return {Promise} + **/ + read(vi) { + return this.types.on(StackAsync.events.next, _.bind(this.onType, this, Reader.events.read)).pop(); + } + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'ReaderVisitor'; + } + + /** + * Events + * @static + * @property events + * @type {Object} + **/ + static events = { + + /** + * @event read + **/ + read: 'bundle:task:reader:read' + + }; + +} + +export default Reader; diff --git a/lib/bundle/task/task.es6 b/lib/bundle/task/task.es6 new file mode 100644 index 0000000..21964d4 --- /dev/null +++ b/lib/bundle/task/task.es6 @@ -0,0 +1,101 @@ +/** +* @module bundle.task +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Collection from 'util/adt/collection'; +import StackAsync from 'util/adt/stack-async'; +import Factory from 'util/factory/factory'; +import Visitor from 'util/visitor/visitor'; +import logger from 'util/logger/logger'; + +/** +* Class Task +* @extends {util.visitor.Visitor} +**/ +class Task extends Visitor { + + /** + * Constructor + * @public + * @override + * @param {bundle.Bundle} bundle Bundle command reference + * @return {bundle.task.Task} + **/ + constructor(bundle) { + super(extend(_.pick(bundle.toJSON(), bundle.constructor.options), { types: StackAsync.new([]) })); + return this.registerAll(); + } + + /** + * Registers and load all types for this task + * @public + * @return {bundle.task.Task} + **/ + registerAll() { + return this.constructor.types.reduce(this.register, this, this); + } + + /** + * Register and load a signle type for this task + * @public + * @param {bundle.task.Task} memo memoized reference of this task + * @param {String} path current type path to the factory + * @return {bundle.task.Task} + **/ + register(memo, path) { + memo.types.push(Factory.register(path).get(path, this)); + return memo; + } + + /** + * Default Type execution Handler + * @public + * @param {String} [eventName = Task.events.execute] event name to emit + * @return {bundle.task} + **/ + onType(eventName = Task.events.execute) { + this.emit(eventName, this); + return this; + } + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'TaskVisitor'; + } + + /** + * Supported Types + * @static + * @property types + * @type {util.adt.Collection} + **/ + static types = Collection.new([ + 'bundle/types/export', + 'bundle/types/import', + 'bundle/types/annotation' + ]); + + /** + * Events + * @static + * @property events + * @type {Object} + **/ + static events = { + + /** + * @event execute + **/ + execute: 'bundle:task:execute' + + }; + +} + +export default Task; diff --git a/lib/bundle/task/writer/writer.es6 b/lib/bundle/task/writer/writer.es6 new file mode 100644 index 0000000..d01231b --- /dev/null +++ b/lib/bundle/task/writer/writer.es6 @@ -0,0 +1,52 @@ +/** +* @module bundle.writer +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import StackAsync from 'util/adt/stack-async'; +import Task from 'bundle/task/task'; + +/** +* Class Writer +* @extends {bundle.task.Task} +**/ +class Writer extends Task { + + /** + * Write Strategy + * @public + * @param {util.visitor.Visited} vi - visited instance reference + * @return {Promise} + **/ + write(vi) { + return this.types.on(StackAsync.events.next, _.bind(this.onType, this, Writer.events.write)).pop(); + } + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'WriterVisitor'; + } + + /** + * Events + * @static + * @property events + * @type {Object} + **/ + static events = { + + /** + * @event write + **/ + write: 'bundle:task:writer:write' + + }; + +} + +export default Writer; diff --git a/lib/bundle/types/annotation.es6 b/lib/bundle/types/annotation.es6 new file mode 100644 index 0000000..85bbe28 --- /dev/null +++ b/lib/bundle/types/annotation.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.types +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Type from 'bundle/types/type'; +import logger from 'util/logger/logger'; + +/** +* Class Annotation +* @extends {bundle.types.Type} +**/ +class Annotation extends Type { + +} + +export default Annotation; diff --git a/lib/bundle/types/export.es6 b/lib/bundle/types/export.es6 new file mode 100644 index 0000000..fc64b58 --- /dev/null +++ b/lib/bundle/types/export.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.types +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Type from 'bundle/types/type'; +import logger from 'util/logger/logger'; + +/** +* Class Export +* @extends {bundle.types.Type} +**/ +class Export extends Type { + +} + +export default Export; diff --git a/lib/bundle/types/import.es6 b/lib/bundle/types/import.es6 new file mode 100644 index 0000000..6bf39f3 --- /dev/null +++ b/lib/bundle/types/import.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.types +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Type from 'bundle/types/type'; +import logger from 'util/logger/logger'; + +/** +* Class Import +* @extends {bundle.types.Type} +**/ +class Import extends Type { + +} + +export default Import; diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 new file mode 100644 index 0000000..05a4cf8 --- /dev/null +++ b/lib/bundle/types/type.es6 @@ -0,0 +1,67 @@ +/** +* @module bundle.types +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Visited from 'util/visitor/visited'; +import logger from 'util/logger/logger'; + +/** +* Class Type +* @extends {util.visitor.Visited} +**/ +class Type extends Visited { + + /** + * Constructor + * @public + * @override + * @param {bundle.Operator} type - TDB + * @return {bundle.types.Type} + **/ + constructor(type) { + return super({ type }); + } + + /** + * Asynchronous next strategy + * @public + * @method next + * @param adt {visitors.async.Asynchronous} adt used for asynchronous operations + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject + * @return {Promise} + **/ + next(adt, resolve, reject) { + if(_.defined(this.type.read)) return this.read(resolve, reject); + if(_.defined(this.type.write)) return this.write(resolve, reject); + } + + /** + * Default Read strategy + * @public + * @method read + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject + * @return {Promise} + **/ + read(resolve, reject) { + return resolve(this); + } + + /** + * Default Write strategy + * @public + * @method write + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject + * @return {Promise} + **/ + write(resolve, reject) { + return resolve(this); + } + +} + +export default Type; diff --git a/lib/bundle/writer/writer.es6 b/lib/bundle/writer/writer.es6 deleted file mode 100644 index e69de29..0000000 diff --git a/lib/command.es6 b/lib/command.es6 index f3cdef2..f2e1d4a 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -78,7 +78,7 @@ class Command extends Visited { * Proxified asynchronous next strategy * @public * @override - * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations + * @param adt {visitors.async.Asynchronous} adt used for asynchronous operations * @param resolve {Function} asynchronous promise's resolve * @param reject {Function} asynchronous promise's reject * @return {Promise} diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index d1912c6..426d158 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -46,15 +46,12 @@ describe('bundle.Bundle', function() { }); - describe('before()', () => { - - it('Should execute before hook', () => {}); - - }); - describe('run()', () => { - it('Should execute command run', () => {}); + it('Should execute command run', () => { + // TODO: stubPromise for read and writer on method run + this.bundle.run(); + }); }); diff --git a/test/specs/es6/common/common-a.es6 b/test/specs/es6/common/common-a.es6 index e69de29..f6f7201 100644 --- a/test/specs/es6/common/common-a.es6 +++ b/test/specs/es6/common/common-a.es6 @@ -0,0 +1,6 @@ +/** +* @sqbox({ bundle: "common" }) +**/ +import CommonB from 'common/common-b'; + +export default () => { console.log('common/CommonA'); }; diff --git a/test/specs/es6/common/common-b.es6 b/test/specs/es6/common/common-b.es6 index e69de29..fc954b5 100644 --- a/test/specs/es6/common/common-b.es6 +++ b/test/specs/es6/common/common-b.es6 @@ -0,0 +1,4 @@ +/** +* @sqbox({ bundle: "module-a" }) +**/ +export default () => { console.log('common/CommonB'); }; diff --git a/test/specs/es6/module-a/source-a.es6 b/test/specs/es6/module-a/source-a.es6 index e69de29..4d02710 100644 --- a/test/specs/es6/module-a/source-a.es6 +++ b/test/specs/es6/module-a/source-a.es6 @@ -0,0 +1,7 @@ +/** +* @sqbox({ bundle: "module-a" }) +**/ +import CommonA from 'common/common-a'; +import CommonB from 'common/common-b'; + +export default () => { console.log('module-a/SourceA'); }; diff --git a/test/specs/es6/module-b/source-b.es6 b/test/specs/es6/module-b/source-b.es6 index e69de29..095b1d4 100644 --- a/test/specs/es6/module-b/source-b.es6 +++ b/test/specs/es6/module-b/source-b.es6 @@ -0,0 +1,6 @@ +/** +* @sqbox({ bundle: "module-b" }) +**/ +import CommonA from 'common/common-a'; + +export default () => { console.log('module-b/SourceB'); }; From 054b6fc3c2b0bf85e94df41163119721e5fa12ce Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Tue, 25 Apr 2017 11:30:02 -0700 Subject: [PATCH 06/22] bundle: checkpoint - added external configuration option (alias l). Refactored bundle.types and bundle.format. Added iife, umd formats and specs. --- lib/bin/commands.json | 1 + lib/bundle/bundle.es6 | 1 + lib/bundle/format/{ => amd}/amd.es6 | 2 +- lib/bundle/format/amd/template.es6 | 16 ++++++++++ lib/bundle/format/{ => cjs}/commonjs.es6 | 2 +- lib/bundle/format/cjs/template.es6 | 25 ++++++++++++++++ lib/bundle/format/{ => es6}/es6.es6 | 2 +- lib/bundle/format/es6/template.es6 | 25 ++++++++++++++++ lib/bundle/format/iife/iife.es6 | 18 +++++++++++ lib/bundle/format/umd/umd.es6 | 18 +++++++++++ lib/bundle/task/task.es6 | 6 ++-- lib/bundle/task/writer/writer.es6 | 2 ++ .../types/{ => annotation}/annotation.es6 | 2 +- lib/bundle/types/{ => export}/export.es6 | 2 +- lib/bundle/types/{ => import}/import.es6 | 2 +- lib/command.es6 | 1 + lib/visitors/configuration.es6 | 2 ++ .../configuration/formatter/external.es6 | 15 ++++++++++ test/lib/bin/sqbox.spec.es6 | 1 + test/lib/bundle/bundle.spec.es6 | 3 +- .../configuration/formatter/external.spec.es6 | 30 +++++++++++++++++++ test/specs/.sqbox.js | 5 ++-- test/specs/.sqboxrc | 5 ++-- 23 files changed, 172 insertions(+), 14 deletions(-) rename lib/bundle/format/{ => amd}/amd.es6 (91%) create mode 100644 lib/bundle/format/amd/template.es6 rename lib/bundle/format/{ => cjs}/commonjs.es6 (92%) create mode 100644 lib/bundle/format/cjs/template.es6 rename lib/bundle/format/{ => es6}/es6.es6 (91%) create mode 100644 lib/bundle/format/es6/template.es6 create mode 100644 lib/bundle/format/iife/iife.es6 create mode 100644 lib/bundle/format/umd/umd.es6 rename lib/bundle/types/{ => annotation}/annotation.es6 (90%) rename lib/bundle/types/{ => export}/export.es6 (91%) rename lib/bundle/types/{ => import}/import.es6 (91%) create mode 100644 lib/visitors/configuration/formatter/external.es6 create mode 100644 test/lib/visitors/configuration/formatter/external.spec.es6 diff --git a/lib/bin/commands.json b/lib/bin/commands.json index 2d4accc..49fcbf2 100644 --- a/lib/bin/commands.json +++ b/lib/bin/commands.json @@ -33,6 +33,7 @@ "exclude": { "alias": "x" }, "extensions": { "alias": "e", "default": ".js,.jsx,.es6,.es" }, "alias": { "alias": "a", "default": {} }, + "external": { "alias": "l", "default": "" }, "target": { "alias": "t", "default": "dist>umd:./dist" }, "logLevel": { "alias": "lv", "default": "output" } } diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index 2ff2096..5dbc88d 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -22,6 +22,7 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ run(resolve, reject) { + // TODO: Review Promise.All (First Read, then write) Synchronous Promise.all([this.read(), this.write()]) .then(_.bind(this.after, this)) .catch(_.bind(this.after, this)); diff --git a/lib/bundle/format/amd.es6 b/lib/bundle/format/amd/amd.es6 similarity index 91% rename from lib/bundle/format/amd.es6 rename to lib/bundle/format/amd/amd.es6 index c478fea..6fa99aa 100644 --- a/lib/bundle/format/amd.es6 +++ b/lib/bundle/format/amd/amd.es6 @@ -1,5 +1,5 @@ /** -* @module bundle.format +* @module bundle.format.amd * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/bundle/format/amd/template.es6 b/lib/bundle/format/amd/template.es6 new file mode 100644 index 0000000..d49b913 --- /dev/null +++ b/lib/bundle/format/amd/template.es6 @@ -0,0 +1,16 @@ +/** +* @module bundle.format.amd +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; + +/** +* Multiple Imports +* @static +* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @return {Function} +**/ +export const _imports = _.template(`/** <%= m.name %> **/ + define([<%= m.dependencies.join(', ') %>], function(<%= m.ids.join(', ') %>) { + <%= m.content %> + });`); diff --git a/lib/bundle/format/commonjs.es6 b/lib/bundle/format/cjs/commonjs.es6 similarity index 92% rename from lib/bundle/format/commonjs.es6 rename to lib/bundle/format/cjs/commonjs.es6 index 8995659..28462dc 100644 --- a/lib/bundle/format/commonjs.es6 +++ b/lib/bundle/format/cjs/commonjs.es6 @@ -1,5 +1,5 @@ /** -* @module bundle.format +* @module bundle.format.cjs * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/bundle/format/cjs/template.es6 b/lib/bundle/format/cjs/template.es6 new file mode 100644 index 0000000..a8d6da9 --- /dev/null +++ b/lib/bundle/format/cjs/template.es6 @@ -0,0 +1,25 @@ +/** +* @module bundle.format.cjs +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; + +/** +* Single Import +* @static +* @param {Array} id dependency id +* @param {Array} dependency dependency path +* @return {Function} +**/ +export const _import = _.template(`var <%= id %> = require('<%= dependency %>');`); + +/** +* Multiple Imports +* @static +* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @return {Function} +**/ +export const _imports = _.template(`/** <%= m.name %> **/ + ${_.reduce(m.dependencies, function(memo, dependency, ix) { + memo += (_import({ id: m.ids[ix], dependency }) + '\n'); return memo; + }, '')}<%= m.content %>`; diff --git a/lib/bundle/format/es6.es6 b/lib/bundle/format/es6/es6.es6 similarity index 91% rename from lib/bundle/format/es6.es6 rename to lib/bundle/format/es6/es6.es6 index 83728ed..1ea026a 100644 --- a/lib/bundle/format/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -1,5 +1,5 @@ /** -* @module bundle.format +* @module bundle.format.es6 * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/bundle/format/es6/template.es6 b/lib/bundle/format/es6/template.es6 new file mode 100644 index 0000000..2a2b6c1 --- /dev/null +++ b/lib/bundle/format/es6/template.es6 @@ -0,0 +1,25 @@ +/** +* @module bundle.format.es6 +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; + +/** +* Single Import +* @static +* @param {Array} id dependency id +* @param {Array} dependency dependency path +* @return {Function} +**/ +export const _import = _.template(`import <%= id %> from '<%= dependency %>'`); + +/** +* Multiple Imports +* @static +* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @return {Function} +**/ +export const _imports = _.template(`/** <%= m.name %> **/ + ${_.reduce(m.dependencies, function(memo, dependency, ix) { + memo += (_import({ id: m.ids[ix], dependency }) + '\n'); return memo; + }, '')}<%= m.content %>`; diff --git a/lib/bundle/format/iife/iife.es6 b/lib/bundle/format/iife/iife.es6 new file mode 100644 index 0000000..0fa76de --- /dev/null +++ b/lib/bundle/format/iife/iife.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.format.iife +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Format from 'bundle/format/format'; +import logger from 'util/logger/logger'; + +/** +* Class Iife +* @extends {bundle.format.Format} +**/ +class Iife extends Format { + +} + +export default Iife; diff --git a/lib/bundle/format/umd/umd.es6 b/lib/bundle/format/umd/umd.es6 new file mode 100644 index 0000000..d650569 --- /dev/null +++ b/lib/bundle/format/umd/umd.es6 @@ -0,0 +1,18 @@ +/** +* @module bundle.format.umd +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Format from 'bundle/format/format'; +import logger from 'util/logger/logger'; + +/** +* Class Umd +* @extends {bundle.format.Format} +**/ +class Umd extends Format { + +} + +export default Umd; diff --git a/lib/bundle/task/task.es6 b/lib/bundle/task/task.es6 index 21964d4..064c0bb 100644 --- a/lib/bundle/task/task.es6 +++ b/lib/bundle/task/task.es6 @@ -76,9 +76,9 @@ class Task extends Visitor { * @type {util.adt.Collection} **/ static types = Collection.new([ - 'bundle/types/export', - 'bundle/types/import', - 'bundle/types/annotation' + 'bundle/types/export/export', + 'bundle/types/import/import', + 'bundle/types/annotation/annotation' ]); /** diff --git a/lib/bundle/task/writer/writer.es6 b/lib/bundle/task/writer/writer.es6 index d01231b..f1bf75d 100644 --- a/lib/bundle/task/writer/writer.es6 +++ b/lib/bundle/task/writer/writer.es6 @@ -9,6 +9,8 @@ import Task from 'bundle/task/task'; /** * Class Writer +* @desc +* Responsible for writing bundle files with dependency information gathered from a Reader source. * @extends {bundle.task.Task} **/ class Writer extends Task { diff --git a/lib/bundle/types/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 similarity index 90% rename from lib/bundle/types/annotation.es6 rename to lib/bundle/types/annotation/annotation.es6 index 85bbe28..84115a7 100644 --- a/lib/bundle/types/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -1,5 +1,5 @@ /** -* @module bundle.types +* @module bundle.types.annotation * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/bundle/types/export.es6 b/lib/bundle/types/export/export.es6 similarity index 91% rename from lib/bundle/types/export.es6 rename to lib/bundle/types/export/export.es6 index fc64b58..f20966c 100644 --- a/lib/bundle/types/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -1,5 +1,5 @@ /** -* @module bundle.types +* @module bundle.types.export * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/bundle/types/import.es6 b/lib/bundle/types/import/import.es6 similarity index 91% rename from lib/bundle/types/import.es6 rename to lib/bundle/types/import/import.es6 index 6bf39f3..accc8af 100644 --- a/lib/bundle/types/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -1,5 +1,5 @@ /** -* @module bundle.types +* @module bundle.types.import * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/command.es6 b/lib/command.es6 index f2e1d4a..5e78f89 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -213,6 +213,7 @@ class Command extends Visited { 'exclude', 'extensions', 'alias', + 'external', 'target', 'logLevel', 'parent' diff --git a/lib/visitors/configuration.es6 b/lib/visitors/configuration.es6 index 5c12e51..fa8e238 100644 --- a/lib/visitors/configuration.es6 +++ b/lib/visitors/configuration.es6 @@ -186,6 +186,7 @@ class Configuration extends Visitor { **/ static formatters = [ 'visitors/configuration/formatter/alias', + 'visitors/configuration/formatter/external', 'visitors/configuration/formatter/exclude', 'visitors/configuration/formatter/extensions', 'visitors/configuration/formatter/target' @@ -201,6 +202,7 @@ class Configuration extends Visitor { 'exclude', 'extensions', 'alias', + 'external', 'target', 'destination', 'format', diff --git a/lib/visitors/configuration/formatter/external.es6 b/lib/visitors/configuration/formatter/external.es6 new file mode 100644 index 0000000..1a2c09b --- /dev/null +++ b/lib/visitors/configuration/formatter/external.es6 @@ -0,0 +1,15 @@ +/** +* @module visitors.configuration.formatter +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'underscore'; + +/** +* External Formatter +* @static +* @param {String} [input = ''] - input to format +* @return {Object} +**/ +export default (input = '') => { + return (_.isString(input) && input.length > 0) ? input.split(',') : []; +}; diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6 index 146d38d..b84db95 100644 --- a/test/lib/bin/sqbox.spec.es6 +++ b/test/lib/bin/sqbox.spec.es6 @@ -69,6 +69,7 @@ describe('bin.SquareBox', function() { '--x', './source/dependencies/**,./source/package/**', '--e', '.js,.es6', '--a', 'common:./path/common', + '--l', 'jquery,react', '--t', 'add>umd:./dist/umd,other>cjs:./dist/cjs', '--lv', 'silent' ]); diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index 426d158..cdcd496 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -11,8 +11,9 @@ describe('bundle.Bundle', function() { scan: './test/specs/es6/**', extensions: ['.js', '.es6'], alias: { common: 'common' }, + external: ['jquery'], target: { - global: { destination: './test/specs/dist/global', format: 'ifie' }, + iife: { destination: './test/specs/dist/iife', format: 'iife' }, umd: { destination: './test/specs/dist/umd', format: 'umd' }, cjs: { destination: './test/specs/dist/cjs', format: 'cjs' }, amd: { destination: './test/specs/dist/amd', format: 'amd' } diff --git a/test/lib/visitors/configuration/formatter/external.spec.es6 b/test/lib/visitors/configuration/formatter/external.spec.es6 new file mode 100644 index 0000000..07817c4 --- /dev/null +++ b/test/lib/visitors/configuration/formatter/external.spec.es6 @@ -0,0 +1,30 @@ +/** +* @module visitors.configuration.formatter +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import external from 'visitors/configuration/formatter/external'; + +describe('visitors.configuration.formatter.External', function() { + + describe('external()', () => { + + it('Should transform parameter external', () => { + const input = 'jquery,react'; + const exp = external(input); + + assert.isArray(exp); + assert.oneOf('jquery', exp); + assert.oneOf('react', exp); + }); + + it('Should NOT transform parameter external', () => { + let exp = external(); + assert.isTrue(_.isEmpty(exp)); + + exp = external({}); + assert.isTrue(_.isEmpty(exp)); + }); + + }); + +}); diff --git a/test/specs/.sqbox.js b/test/specs/.sqbox.js index c8001a1..f087cef 100644 --- a/test/specs/.sqbox.js +++ b/test/specs/.sqbox.js @@ -10,12 +10,13 @@ module.exports = { alias: { common: 'shared/common', libraries: 'libs' - } + }, + external: ['jquery'] }, target: { global: { destination: './dist/global', - format: 'ifie' + format: 'iife' }, umd: { destination: './dist/umd', diff --git a/test/specs/.sqboxrc b/test/specs/.sqboxrc index 940164f..45c10f0 100644 --- a/test/specs/.sqboxrc +++ b/test/specs/.sqboxrc @@ -6,12 +6,13 @@ "alias": { "common": "shared/common", "libraries": "libs" - } + }, + "external": ["jquery"] }, "target": { "global": { "destination": "./dist/global", - "format": "ifie" + "format": "iife" }, "umd": { "destination": "./dist/umd", From e80c15680e6823fcb60569baa002731505dc36a9 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Wed, 26 Apr 2017 09:23:17 -0700 Subject: [PATCH 07/22] bundle: checkpoint - Added escodegen production dependency for writing javascript output. Working on specs for imports and exports. Setting up initial drafts for unit testing cases (amd, cjs, es6, iife and umd). --- lib/bundle/format/amd/template.es6 | 39 ++++++++-- lib/bundle/format/cjs/template.es6 | 47 +++++++++--- lib/bundle/format/es6/template.es6 | 57 +++++++++++--- lib/bundle/format/iife/template.es6 | 47 ++++++++++++ lib/bundle/format/umd/template.es6 | 80 ++++++++++++++++++++ package.json | 1 + test/lib/bundle/format/amd/template.spec.es6 | 33 ++++++++ test/lib/bundle/format/cjs/template.spec.es6 | 34 +++++++++ 8 files changed, 312 insertions(+), 26 deletions(-) create mode 100644 lib/bundle/format/iife/template.es6 create mode 100644 lib/bundle/format/umd/template.es6 create mode 100644 test/lib/bundle/format/amd/template.spec.es6 create mode 100644 test/lib/bundle/format/cjs/template.spec.es6 diff --git a/lib/bundle/format/amd/template.es6 b/lib/bundle/format/amd/template.es6 index d49b913..9c02861 100644 --- a/lib/bundle/format/amd/template.es6 +++ b/lib/bundle/format/amd/template.es6 @@ -1,16 +1,43 @@ /** +* Draft +* @version 1.0.0 * @module bundle.format.amd * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; /** -* Multiple Imports +* Import Template +* @private +* @property __it__ +* @type {Function} +**/ +const __it__ = _.template(`/** <<%= name %>> **/ +define(["<%= dependencies.join('", "') %>"], function(<%= ids.join(', ') %>) { + <%= content %> +});`); + +/** +* Export Template +* @todo Support for "Bindings" and review multiple exports +* @private +* @property __et__ +* @type {Function} +**/ +const __et__ = _.template(`return <%= exports %>;`); + +/** +* Imports +* @static +* @param {{ name: "", dependencies: [], ids: [], content: "" }} attrs parameters to the template +* @return {Function} +**/ +export const imports = (attrs = {}) => { return __it__(attrs); }; + +/** +* Exports * @static -* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @param {{ exports: {} }} attrs parameters to the template * @return {Function} **/ -export const _imports = _.template(`/** <%= m.name %> **/ - define([<%= m.dependencies.join(', ') %>], function(<%= m.ids.join(', ') %>) { - <%= m.content %> - });`); +export const exports = (attrs = {}) => { return __et__(attrs); }; diff --git a/lib/bundle/format/cjs/template.es6 b/lib/bundle/format/cjs/template.es6 index a8d6da9..6d42baa 100644 --- a/lib/bundle/format/cjs/template.es6 +++ b/lib/bundle/format/cjs/template.es6 @@ -1,25 +1,52 @@ /** +* Draft +* @version 1.0.0 * @module bundle.format.cjs * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import extend from 'extend'; /** -* Single Import +* Single Import Template +* @private +* @property __sit__ +* @type {Function} +**/ +const __sit__ = _.template(`var <%= id %> = require('<%= dependency %>');`); + +/** +* Imports Template +* @private +* @property __it__ +* @type {Function} +**/ +const __it__ = _.template(`/** <%= name %> **/ +<% print(_.reduce(dependencies, function(memo, dependency, ix) { + memo += (__sit__({ id: ids[ix], dependency }) + '\n'); return memo; +}, '')); %>\n<%= content %>`); + +/** +* Exports Template +* @todo Support for "bindings" and review multiple exports +* @private +* @property __et__ +* @type {Function} +**/ +const __et__ = _.template(`module.exports = <%= exports %>`); + +/** +* Imports * @static -* @param {Array} id dependency id -* @param {Array} dependency dependency path +* @param {{ name: "", dependencies: [], ids: [], content: "" }} attrs parameters to the template * @return {Function} **/ -export const _import = _.template(`var <%= id %> = require('<%= dependency %>');`); +export const imports = (attrs = {}) => { return it(extend({}, attrs, { _, __sit__ })); }; /** -* Multiple Imports +* Exports * @static -* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @param {{ exports: {} }} attrs parameters to the template * @return {Function} **/ -export const _imports = _.template(`/** <%= m.name %> **/ - ${_.reduce(m.dependencies, function(memo, dependency, ix) { - memo += (_import({ id: m.ids[ix], dependency }) + '\n'); return memo; - }, '')}<%= m.content %>`; +export const exports = (attrs = {}) => { return __et__(attrs); }; diff --git a/lib/bundle/format/es6/template.es6 b/lib/bundle/format/es6/template.es6 index 2a2b6c1..7346740 100644 --- a/lib/bundle/format/es6/template.es6 +++ b/lib/bundle/format/es6/template.es6 @@ -1,25 +1,62 @@ /** +* Draft +* @version 1.0.0 * @module bundle.format.es6 * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import extend from 'extend'; /** -* Single Import +* Single Import Template +* @todo Support for: +* - import {id} from '{dependency}'; +* - import { {id1}, {id2}, {}... } from {dependency}; +* - import * as {id} from {dependency} +* @private +* @property __sit__ +* @type {Function} +**/ +const __sit__ = _.template(`import <%= id %> from '<%= dependency %>'`); + +/** +* Import Template +* @private +* @property __it__ +* @type {Function} +**/ +const __it__ = _.template(`/** <%= name %> **/ +<% print(_.reduce(dependencies, function(memo, dependency, ix) { +memo += (__sit__({ id: ids[ix], dependency }) + '\n'); return memo; +}, '') + <%= content %>)`); + +/** +* Exports Template +* @todo Support for: +* - export {export} +* - export default {export} +* @private +* @property __it__ +* @type {Function} +**/ +const __et__ = _.template(`export <%= export %>`); + +/** +* Imports * @static -* @param {Array} id dependency id -* @param {Array} dependency dependency path +* @param {{ name: "", dependencies: [], ids: [], content: "" }} attrs parameters to the template * @return {Function} **/ -export const _import = _.template(`import <%= id %> from '<%= dependency %>'`); +export const imports = (attrs = {}) => { + return __it__(extend({}, attrs, { _, __sit__ })); +}; /** -* Multiple Imports +* Exports * @static -* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @param {{ exports: {} }} attrs parameters to the template * @return {Function} **/ -export const _imports = _.template(`/** <%= m.name %> **/ - ${_.reduce(m.dependencies, function(memo, dependency, ix) { - memo += (_import({ id: m.ids[ix], dependency }) + '\n'); return memo; - }, '')}<%= m.content %>`; +export const exports = (attrs = {}) => { + return __et__(extend({}, attrs)); +}; diff --git a/lib/bundle/format/iife/template.es6 b/lib/bundle/format/iife/template.es6 new file mode 100644 index 0000000..8e02493 --- /dev/null +++ b/lib/bundle/format/iife/template.es6 @@ -0,0 +1,47 @@ +/** +* Draft +* @version 1.0.0 +* @module bundle.format.iife +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; + +/** +* Import Template +* @private +* @property __it__ +* @type {Function} +**/ +const __it__ = _.template(`/** <%= name %> **/ +(function(<%= ids.join(', ') %>) { + <%= content %> +})(<%= ids.join(', ') %>);`); + +/** +* Export Template +* @private +* @property __et__ +* @type {Function} +**/ +const __et__ = _.template(``); + +/** +* Imports +* @static +* @param {{ name: "", dependencies: [], ids: [], content: "" }} m parameters to the template +* @return {Function} +**/ +export const imports = (attrs = {}) => { + return __it__(extend({}, attrs)); +}; + +/** +* Exports +* @static +* @param {{ exports: {} }} attrs parameters to the template +* @return {Function} +**/ +export const exports = (attrs = {}) => { + return __et__(extend({}, attrs)); +}; diff --git a/lib/bundle/format/umd/template.es6 b/lib/bundle/format/umd/template.es6 new file mode 100644 index 0000000..ed6dbbe --- /dev/null +++ b/lib/bundle/format/umd/template.es6 @@ -0,0 +1,80 @@ +/** +* Draft +* @version 1.0.0 +* @module bundle.format.umd +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; + +/** +* Import Template +* @private +* @property __it__ +* @type {Function} +**/ +const __it__ = _.template(`/** <%= name %> **/ + (function(root, factory) { + if(typeof define === 'function' && define.amd) { + define(["<%= dependencies.join('", "') %>"], factory); + } else if(typeof module === 'object' && module.exports) { + module.exports = factory(<% print(_.map(dependencies, __gd__, '', this).join(', ')); %>); + } else { + <% print(_.reduce(ids, __gi__, '', this, dependencies)); %> + } + })(this, function(<%= ids.join(', ') %>) { + <%= content %> + });`); + +/** +* Global Dependency Ids Import +* @private +* @static +* @param {Array} dependencies list of dependency paths +* @param {String} memo memoized output +* @param {String} id current dependency id +* @param {Number} ix current dependency index +* @return {String} +**/ +const __gi__ = (dependencies, memo, id, ix) => { + return (memo += `root[${id}] = root[${dependencies[ix]}];\n`); +}; + +/** +* Global Dependencies Import +* @private +* @static +* @param {String} dependency current dependency path +* @return {String} +**/ +const __gd__ = (dependency) => { + return `require('${dependency}')`; +}; + +/** +* Export Template +* @private +* @property name +* @type {Function} +**/ +const __et__ = _.template(``); + +/** +* Imports +* @static +* @param {{ name: "", dependencies: [], ids: [], content: "" }} m bundle metadata +* @return {Function} +**/ +export const _imports = (attrs = {}) => { + return __it__(extend(false, {}, attrs, { __gi, __gd })); +}; + +/** +* Exports +* @static +* @param {{ exports: {} }} attrs parameters to the template +* @return {Function} +**/ +export const exports = (attrs = {}) => { + return __et__(extend({}, attrs)); +}; diff --git a/package.json b/package.json index 16e4d6c..d8ef3f6 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "babel-register": "6.23.0", "chalk": "1.1.3", "d3": "4.7.3", + "escodegen": "^1.8.1", "extend": "3.0.0", "fs-extra": "2.0.0", "glob": "7.1.1", diff --git a/test/lib/bundle/format/amd/template.spec.es6 b/test/lib/bundle/format/amd/template.spec.es6 new file mode 100644 index 0000000..1c23b28 --- /dev/null +++ b/test/lib/bundle/format/amd/template.spec.es6 @@ -0,0 +1,33 @@ +/** +* @module bundle.format.amd +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import * as Amd from 'bundle/format/amd/template'; + +describe('bundle.format.amd.Template', function() { + + before(() => { + this.input = { + name: 'bundle', + dependencies: ['react', 'common/module'], + ids: ['React', 'Module'], + content: 'return MyModule;' + }; + }); + + after(() => { + delete this.input; + }); + + describe('template()', () => { + + it('Should output amd imports', () => { + const result = Amd.imports(this.input); + assert.include(result, `/** <${this.input.name}> **/`); + assert.include(result, `define(`); + assert.include(result, `return MyModule;`); + }); + + }); + +}); diff --git a/test/lib/bundle/format/cjs/template.spec.es6 b/test/lib/bundle/format/cjs/template.spec.es6 new file mode 100644 index 0000000..7d762b7 --- /dev/null +++ b/test/lib/bundle/format/cjs/template.spec.es6 @@ -0,0 +1,34 @@ +/** +* @module bundle.format.cjs +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +//import * as Cjs from 'bundle/format/cjs/template'; + +describe.skip('bundle.format.cjs.Template', function() { + + before(() => { + this.input = { + name: 'bundle', + dependencies: ['react', 'common/module'], + ids: ['React', 'Module'], + content: 'return MyModule;' + }; + }); + + after(() => { + delete this.input; + }); + + describe('template()', () => { + + it('Should output amd imports', () => { + //const result = Cjs.imports(this.input); + //console.log(result); + // assert.include(result, `/** <${this.input.name}> **/`); + // assert.include(result, `define(`); + // assert.include(result, `return MyModule;`); + }); + + }); + +}); From 6d6abbda3d3992fb4ab6b5d0beefb783880e17b3 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Wed, 26 Apr 2017 09:56:48 -0700 Subject: [PATCH 08/22] bundle: checkpoint - Added bundle.task.metadata.Metadata domain ADT as an interface to drive collected data from Reader to Writer. --- lib/bundle/task/metadata/bundle.es6 | 0 lib/bundle/task/metadata/file.es6 | 0 lib/bundle/task/metadata/metadata.es6 | 94 ++++++++++++++++++++ test/lib/bundle/format/cjs/template.spec.es6 | 1 + 4 files changed, 95 insertions(+) create mode 100644 lib/bundle/task/metadata/bundle.es6 create mode 100644 lib/bundle/task/metadata/file.es6 create mode 100644 lib/bundle/task/metadata/metadata.es6 diff --git a/lib/bundle/task/metadata/bundle.es6 b/lib/bundle/task/metadata/bundle.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/task/metadata/file.es6 b/lib/bundle/task/metadata/file.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/task/metadata/metadata.es6 b/lib/bundle/task/metadata/metadata.es6 new file mode 100644 index 0000000..7689fbf --- /dev/null +++ b/lib/bundle/task/metadata/metadata.es6 @@ -0,0 +1,94 @@ +/** +* @module bundle.task.metadata +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import { EventEmitter } from 'events'; +import Collection from 'util/adt/collection'; +import Bundle from 'bundle/task/metadata/bundle'; +import File from 'bundle/task/metadata/file'; + +/** +* Class Metadata +* @version 1.0.0 +* @extends {events.EventEmitter} +* +* @desc +* This is the general ADT used by squarebox to perform operations on it, as a result of collecting metadata +* by the AST parsing library and later on, used by the AST writer library to generate output. +* Here the general structure specs: +* +* @example +* [Metadata] => { +* bundle: { +* name: {uniqueId}, +* target: {path}, +* format: {currentFormat} +* }, +* files: [{ +* source: {path}, +* ast: {object} +* }, ...] +* } +**/ +class Metadata extends EventEmitter { + + /** + * Constructor + * @public + * @param {Any} [...args] constructor arguments + * @return {bundle.task.metadata.Metadata} + **/ + constructor(...args) { + super(); + return extend(true, this, { bundle: Bundle.new(), files: Collection.new([], { interface: File }) }); + } + + /** + * Metadata Property Definition + * @static + * @property files + * @type {Array} + **/ + static properties = [ + 'bundle', + 'files' + ]; + + /** + * Bundle Property Definition + * @static + * @property bundle + * @type {Array} + **/ + static bundle = [ + 'name', + 'target', + 'format' + ]; + + /** + * Bundle File Property Definition + * @static + * @property files + * @type {Array} + **/ + static files = [ + 'source', + 'ast' + ]; + + /** + * Static Constructor + * @static + * @param {Any} [...args] constructor arguments + * @return {bundle.task.metadata.Metadata} + **/ + static new(...args) { + return new this(...args); + } + +} + +export default Metadata; diff --git a/test/lib/bundle/format/cjs/template.spec.es6 b/test/lib/bundle/format/cjs/template.spec.es6 index 7d762b7..0846d78 100644 --- a/test/lib/bundle/format/cjs/template.spec.es6 +++ b/test/lib/bundle/format/cjs/template.spec.es6 @@ -9,6 +9,7 @@ describe.skip('bundle.format.cjs.Template', function() { before(() => { this.input = { name: 'bundle', + files: [], dependencies: ['react', 'common/module'], ids: ['React', 'Module'], content: 'return MyModule;' From 6226f231d09561f7050dcab31fa7cbbe1110fdc8 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Fri, 28 Apr 2017 10:15:28 -0700 Subject: [PATCH 09/22] bundle: checkpoint - progress on metadata parsing via glob, fs and acorn over files. Setup scan and excludes. --- lib/bin/sqbox.es6 | 8 +-- lib/bundle/bundle.es6 | 57 +++++++++++++++++-- lib/bundle/task/metadata/bundle.es6 | 50 ++++++++++++++++ lib/bundle/task/metadata/file.es6 | 49 ++++++++++++++++ lib/bundle/task/metadata/metadata.es6 | 66 +++++++++++----------- lib/bundle/task/reader/reader.es6 | 60 +++++++++++++++++++- lib/bundle/task/task.es6 | 14 ++++- lib/bundle/task/writer/writer.es6 | 14 ++++- lib/bundle/types/annotation/annotation.es6 | 12 ++++ lib/bundle/types/type.es6 | 14 ++--- lib/command.es6 | 34 +++-------- lib/util/visitor/visited.es6 | 34 +++++++++++ lib/visitors/command/properties.es6 | 24 +++++++- test/lib/bundle/bundle.spec.es6 | 12 +++- 14 files changed, 360 insertions(+), 88 deletions(-) diff --git a/lib/bin/sqbox.es6 b/lib/bin/sqbox.es6 index 6add862..22d5ad3 100644 --- a/lib/bin/sqbox.es6 +++ b/lib/bin/sqbox.es6 @@ -125,15 +125,15 @@ class SquareBox extends Command { static commands = Collection.new(CommandsList); /** - * SquareBox visitors + * Command Visitors * @static * @override - * @type {Array} + * @type {util.adt.Collection} **/ - static visitors = [ + static visitors = Collection.new(Command.visitors.toJSON().concat([ 'visitors/commander', 'visitors/configuration' - ].concat(Command.visitors); + ])); /** * Static enforcer validation diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index 5dbc88d..eba98c4 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -4,7 +4,9 @@ **/ import _ from 'underscore'; import extend from 'extend'; +import Collection from 'util/adt/collection'; import Command from 'command'; +import Metadata from 'bundle/task/metadata/metadata'; import Factory from 'util/factory/factory'; /** @@ -13,6 +15,17 @@ import Factory from 'util/factory/factory'; **/ class Bundle extends Command { + /** + * Constructor + * @public + * @override + * @param {Object} [args = {}] constructor attributes + * @return {bundle.Bundle} + **/ + constructor(args = {}) { + return super(extend(true, args, { bundles: Collection.new([], { interface: Metadata }) })); + } + /** * Run * @public @@ -22,13 +35,32 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ run(resolve, reject) { - // TODO: Review Promise.All (First Read, then write) Synchronous - Promise.all([this.read(), this.write()]) + this.before().actions() .then(_.bind(this.after, this)) .catch(_.bind(this.after, this)); return this; } + /** + * Asynchronous Actions Run + * @public + * @return {Promise} + **/ + actions() { + return _.reduce([this.read, this.write], this.action, Promise.resolve()); + } + + /** + * Action Run + * @public + * @param {Promise} memo memoized promise that chains asynchronous actions synchronously + * @param {Function} action current asynchronous action + * @return {Promise} + **/ + action(memo, action) { + return memo.then(action); + } + /** * After Run * @public @@ -44,7 +76,7 @@ class Bundle extends Command { /** * List of commands that depends on * @static - * @property dependsOn + * @override * @type {Array} **/ static dependsOn = Command.dependsOn.concat([ @@ -52,15 +84,28 @@ class Bundle extends Command { ]); /** - * List of visitors + * Command options * @static * @override * @type {Array} **/ - static visitors = Command.visitors.concat([ + static options = Command.options.concat([ + 'bundles', + 'sources', + 'excludes', + 'targets' + ]); + + /** + * List of visitors + * @static + * @override + * @type {util.adt.Collection} + **/ + static visitors = Collection.new(Command.visitors.toJSON().concat([ 'bundle/task/reader/reader', 'bundle/task/writer/writer' - ]); + ])); } diff --git a/lib/bundle/task/metadata/bundle.es6 b/lib/bundle/task/metadata/bundle.es6 index e69de29..0b6dd87 100644 --- a/lib/bundle/task/metadata/bundle.es6 +++ b/lib/bundle/task/metadata/bundle.es6 @@ -0,0 +1,50 @@ +/** +* @module bundle.task.metadata +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Visited from 'util/visitor/visited'; + +/** +* Class Bundle +* @extends {util.visitor.Visited} +**/ +class Bundle extends Visited { + + /** + * Constructor + * @public + * @param {Any} [...args] constructor arguments + * @return {bundle.task.metadata.Bundle} + **/ + constructor(...args) { + super(_.object(Bundle.properties, [])); + return this.registerAll().parse(...args); + } + + /** + * Parse Strategy + * @public + * @param {Object} [attrs = {}] attributes to parse + * @return {bundle.task.metadata.Bundle} + **/ + parse(attrs = {}) { + return extend(true, this, _.pick(attrs, this.constructor.properties)); + } + + /** + * Property Definition + * @static + * @property properties + * @type {Array} + **/ + static properties = [ + 'name', + 'target', + 'format' + ]; + +} + +export default Bundle; diff --git a/lib/bundle/task/metadata/file.es6 b/lib/bundle/task/metadata/file.es6 index e69de29..0fb28ec 100644 --- a/lib/bundle/task/metadata/file.es6 +++ b/lib/bundle/task/metadata/file.es6 @@ -0,0 +1,49 @@ +/** +* @module bundle.task.metadata +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Visited from 'util/visitor/visited'; + +/** +* Class File +* @extends {util.visitor.Visited} +**/ +class File extends Visited { + + /** + * Constructor + * @public + * @param {Any} [...args] constructor arguments + * @return {bundle.task.metadata.File} + **/ + constructor(...args) { + super(_.object(File.properties, [])); + return this.registerAll().parse(...args); + } + + /** + * Parse Strategy + * @public + * @param {Object} [attrs = {}] attributes to parse + * @return {bundle.task.metadata.File} + **/ + parse(attrs = {}) { + return extend(true, this, _.pick(attrs, this.constructor.properties)); + } + + /** + * Property Definition + * @static + * @property properties + * @type {Array} + **/ + static properties = [ + 'source', + 'ast' + ]; + +} + +export default File; diff --git a/lib/bundle/task/metadata/metadata.es6 b/lib/bundle/task/metadata/metadata.es6 index 7689fbf..f7d1a22 100644 --- a/lib/bundle/task/metadata/metadata.es6 +++ b/lib/bundle/task/metadata/metadata.es6 @@ -4,15 +4,15 @@ **/ import _ from 'util/mixins'; import extend from 'extend'; -import { EventEmitter } from 'events'; import Collection from 'util/adt/collection'; +import Visited from 'util/visitor/visited'; import Bundle from 'bundle/task/metadata/bundle'; import File from 'bundle/task/metadata/file'; /** * Class Metadata * @version 1.0.0 -* @extends {events.EventEmitter} +* @extends {util.visitor.Visited} * * @desc * This is the general ADT used by squarebox to perform operations on it, as a result of collecting metadata @@ -32,7 +32,7 @@ import File from 'bundle/task/metadata/file'; * }, ...] * } **/ -class Metadata extends EventEmitter { +class Metadata extends Visited { /** * Constructor @@ -41,53 +41,51 @@ class Metadata extends EventEmitter { * @return {bundle.task.metadata.Metadata} **/ constructor(...args) { - super(); - return extend(true, this, { bundle: Bundle.new(), files: Collection.new([], { interface: File }) }); + super({ bundle: Bundle.new(), files: Collection.new([], { interface: File }) }); + return this.registerAll().parse(...args); } /** - * Metadata Property Definition - * @static - * @property files - * @type {Array} + * Parse Strategy + * @public + * @param {Object} attrs metadata attributes to parse + * @return {bundle.task.metadata.Metadata} **/ - static properties = [ - 'bundle', - 'files' - ]; + parse(attrs = {}) { + this.bundle.parse(attrs.bundle); + this.files.set(attrs.files); + return extend(true, this, _.pick(attrs, this.constructor.properties)); + } /** - * Bundle Property Definition - * @static - * @property bundle - * @type {Array} + * Returns a json representation of the instance of this class + * @public + * @override + * @param {visitors.formatter.Json} [ctx] - context reference + * @return {Object} **/ - static bundle = [ - 'name', - 'target', - 'format' - ]; + toJSON() { + return { bundle: this.bundle.toJSON(), files: this.files.toJSON() }; + } /** - * Bundle File Property Definition + * Property Definition * @static - * @property files + * @property properties * @type {Array} **/ - static files = [ - 'source', - 'ast' - ]; + static properties = []; /** - * Static Constructor + * Compound Property Definition * @static - * @param {Any} [...args] constructor arguments - * @return {bundle.task.metadata.Metadata} + * @property compound + * @type {Array} **/ - static new(...args) { - return new this(...args); - } + static compound = [ + 'bundle', + 'files' + ]; } diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index c7b2119..9229d73 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -1,10 +1,14 @@ /** -* @module bundle.reader +* @module bundle.task.reader * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import fs from 'fs-extra'; +import path from 'path'; import extend from 'extend'; -import minimatch from 'minimatch'; +import glob from 'glob'; +import acorn from 'acorn'; +import Collection from 'util/adt/collection'; import StackAsync from 'util/adt/stack-async'; import Task from 'bundle/task/task'; @@ -24,7 +28,45 @@ class Reader extends Task { * @return {Promise} **/ read(vi) { - return this.types.on(StackAsync.events.next, _.bind(this.onType, this, Reader.events.read)).pop(); + // TODO: read => acorn => Metadata.new() + this.bundles.addAll(this.files().reduce(this.parse, [], this)); + return this.types.pop(); + } + + /** + * File Parsing Strategy + * @public + * @param {Array} memo memoized array that will hold metadata found + * @param {String} file file path to parse + * @return {bundle.task.reader.Reader} + **/ + parse(memo, file) { + //console.log('File: ', file); + return {}; + } + + /** + * Retrieves Files + * @public + * @return {util.adt.Collection} + **/ + files() { + return Collection.new(glob.sync(this.sources(), _.defaults({ + cwd: this.cwd, + ignore: this.excludes() + }, Reader.globOptions))); + } + + /** + * Type execution Handler + * @public + * @override + * @param {String} [eventName = Task.events.execute] event name to emit + * @return {bundle.task.reader.Reader} + **/ + onType(eventName = Task.events.execute) { + this.emit(Reader.events.read, this); + return this; } /** @@ -36,6 +78,18 @@ class Reader extends Task { return 'ReaderVisitor'; } + /** + * Glob Options + * @public + * @property globOptions + * @type {Object} + **/ + static globOptions = { + strict: true, + nosort: true, + nodir: true + }; + /** * Events * @static diff --git a/lib/bundle/task/task.es6 b/lib/bundle/task/task.es6 index 064c0bb..d856d89 100644 --- a/lib/bundle/task/task.es6 +++ b/lib/bundle/task/task.es6 @@ -24,8 +24,18 @@ class Task extends Visitor { * @return {bundle.task.Task} **/ constructor(bundle) { - super(extend(_.pick(bundle.toJSON(), bundle.constructor.options), { types: StackAsync.new([]) })); - return this.registerAll(); + super(extend(_.pick(bundle, bundle.constructor.options), { types: StackAsync.new([]) })); + return this.attachEvents().registerAll(); + } + + /** + * Attach Events + * @public + * @return {bundle.task.Task} + **/ + attachEvents() { + this.types.on(StackAsync.events.next, _.bind(this.onType, this)); + return this; } /** diff --git a/lib/bundle/task/writer/writer.es6 b/lib/bundle/task/writer/writer.es6 index f1bf75d..a8d2f47 100644 --- a/lib/bundle/task/writer/writer.es6 +++ b/lib/bundle/task/writer/writer.es6 @@ -22,7 +22,19 @@ class Writer extends Task { * @return {Promise} **/ write(vi) { - return this.types.on(StackAsync.events.next, _.bind(this.onType, this, Writer.events.write)).pop(); + return this.types.pop(); + } + + /** + * Type execution Handler + * @public + * @override + * @param {String} [eventName = Task.events.execute] event name to emit + * @return {bundle.task} + **/ + onType(eventName = Task.events.execute) { + this.emit(Writer.events.write, this); + return this; } /** diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 84115a7..02fc6a8 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -13,6 +13,18 @@ import logger from 'util/logger/logger'; **/ class Annotation extends Type { + /** + * Annotation Read strategy + * @public + * @override + * @param resolve {Function} asynchronous promise's resolve + * @param reject {Function} asynchronous promise's reject + * @return {Promise} + **/ + read(resolve, reject) { + return resolve(this); + } + } export default Annotation; diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index 05a4cf8..cd7883c 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -17,31 +17,30 @@ class Type extends Visited { * Constructor * @public * @override - * @param {bundle.Operator} type - TDB + * @param {bundle.task.Task} task - current task reference * @return {bundle.types.Type} **/ - constructor(type) { - return super({ type }); + constructor(task) { + return super({ task }); } /** * Asynchronous next strategy * @public - * @method next * @param adt {visitors.async.Asynchronous} adt used for asynchronous operations * @param resolve {Function} asynchronous promise's resolve * @param reject {Function} asynchronous promise's reject * @return {Promise} **/ next(adt, resolve, reject) { - if(_.defined(this.type.read)) return this.read(resolve, reject); - if(_.defined(this.type.write)) return this.write(resolve, reject); + if(_.defined(this.task.read)) return this.read(resolve, reject); + if(_.defined(this.task.write)) return this.write(resolve, reject); + return resolve(this); } /** * Default Read strategy * @public - * @method read * @param resolve {Function} asynchronous promise's resolve * @param reject {Function} asynchronous promise's reject * @return {Promise} @@ -53,7 +52,6 @@ class Type extends Visited { /** * Default Write strategy * @public - * @method write * @param resolve {Function} asynchronous promise's resolve * @param reject {Function} asynchronous promise's reject * @return {Promise} diff --git a/lib/command.es6 b/lib/command.es6 index 5e78f89..7b260fc 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -5,6 +5,7 @@ import { EventEmitter } from 'events'; import _ from 'util/mixins'; import extend from 'extend'; +import Collection from 'util/adt/collection'; import Factory from 'util/factory/factory'; import StackAsync from 'util/adt/stack-async'; import Visited from 'util/visitor/visited'; @@ -28,7 +29,7 @@ class Command extends Visited { **/ constructor(args = {}) { super(); - return extend(true, this.settings(args).register().acceptAll(), { stack: StackAsync.new([]) }); + return extend(true, this.settings(args).registerAll(this.dirname), { stack: StackAsync.new([]) }); } /** @@ -42,25 +43,6 @@ class Command extends Visited { return extend(true, this, _.defaults(_.pick(options, this.constructor.options), this.constructor.defaults)); } - /** - * Register Visitors - * @public - * @return {Command} - **/ - register() { - Factory.basePath(this.dirname).registerAll(this.constructor.visitors); - return this; - } - - /** - * Accepts All Visitors - * @public - * @return {Command} - **/ - acceptAll() { - return _.reduce(this.constructor.visitors, (memo, v) => memo.accept(Factory.get(v, this)), this); - } - /** * Push a dependent subcommand onto this command * @public @@ -173,13 +155,13 @@ class Command extends Visited { /** * Command Visitors * @static - * @type {Array} + * @override + * @type {util.adt.Collection} **/ - static visitors = [ - 'visitors/command/properties', - 'visitors/formatter/json', - 'visitors/async/async' - ]; + static visitors = Collection.new(Visited.visitors.toJSON().concat([ + 'visitors/async/async', + 'visitors/command/properties' + ])); /** * List of commands that depends on diff --git a/lib/util/visitor/visited.es6 b/lib/util/visitor/visited.es6 index ecd64ae..f1377fb 100644 --- a/lib/util/visitor/visited.es6 +++ b/lib/util/visitor/visited.es6 @@ -6,6 +6,8 @@ import { EventEmitter } from 'events'; import _ from 'underscore'; import extend from 'extend'; import Visitor from 'util/visitor/visitor'; +import Factory from 'util/factory/factory'; +import Collection from 'util/adt/collection'; import InterfaceException from 'util/exception/proxy/interface'; /** @@ -25,6 +27,28 @@ class Visited extends EventEmitter { return extend(true, this, ...args); } + /** + * Default all visitors registration + * @public + * @param {String} dirname - factory base directory name + * @return {util.visitor.Visited} + **/ + registerAll(dirname) { + Factory.basePath(dirname); + return this.constructor.visitors.reduce(this.register, this, this); + } + + /** + * Default single visitor registration + * @public + * @param {util.visitor.Visited} memo memoized reference of the instance of this class + * @param {String} path visitor path to register and load. + * @return {util.visitor.Visited} + **/ + register(memo, path) { + return memo.accept(Factory.register(path).get(path, this)); + } + /** * Returns true if a given visitor is defined and an instance of util.visitor.Visitor, false otherwise * @public @@ -45,6 +69,16 @@ class Visited extends EventEmitter { return this.validate(visitor) ? visitor.visit(this) : this; } + /** + * Default list of visitors + * @static + * @property visitors + * @type {util.adt.Collection} + **/ + static visitors = Collection.new([ + 'visitors/formatter/json' + ]); + /** * Static Constructor * @static diff --git a/lib/visitors/command/properties.es6 b/lib/visitors/command/properties.es6 index dc7d249..165f57d 100644 --- a/lib/visitors/command/properties.es6 +++ b/lib/visitors/command/properties.es6 @@ -2,7 +2,8 @@ * @module visitors.command * @author Patricio Ferreira <3dimentionar@gmail.com> **/ -import _ from 'underscore'; +import _ from 'util/mixins'; +import path from 'path'; import Visitor from 'util/visitor/visitor'; /** @@ -11,6 +12,27 @@ import Visitor from 'util/visitor/visitor'; **/ class Properties extends Visitor { + /** + * Resolve scan sources + * @public + * @return {String} + **/ + sources() { + return path.resolve(`${this.scan}/**/*+(${this.extensions.join('|')})`); + } + + /** + * Resolve excludes + * @public + * @return {Array} + **/ + excludes() { + return _.reduce(this.exclude, (memo, pattern) => { + memo.push(path.resolve(this.scan, pattern)); + return memo; + }, []); + } + /** * Retrieve targets by using a given predicate passed by parameter * @public diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index cdcd496..3577361 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -8,8 +8,10 @@ describe('bundle.Bundle', function() { before(() => { this.params = { - scan: './test/specs/es6/**', + scan: './test/specs/es6', extensions: ['.js', '.es6'], + exclude: [], + external: ['react'], alias: { common: 'common' }, external: ['jquery'], target: { @@ -49,11 +51,15 @@ describe('bundle.Bundle', function() { describe('run()', () => { - it('Should execute command run', () => { - // TODO: stubPromise for read and writer on method run + it('Should execute command run over specs/es6', () => { this.bundle.run(); }); + xit('Should execute command run over specs/amd', () => {}); + xit('Should execute command run over specs/cjs', () => {}); + xit('Should execute command run over specs/iife', () => {}); + xit('Should execute command run over specs/umd', () => {}); + }); describe('toJSON()', () => { From 463cf22ea6ea33be34e8fa62ed034bc350f0ddd2 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 1 May 2017 08:27:10 -0700 Subject: [PATCH 10/22] bundle: checkpoint - comments for apidocs clean up. Added support for QueueAsync and StackAsync to pass n number or arguments to the next async strategy. Refactored bundle.format.Format to extend util.visitor.Visitor. --- lib/bundle/bundle.es6 | 6 +++- lib/bundle/format/format.es6 | 16 ++-------- lib/bundle/task/reader/reader.es6 | 37 ++++++++++++++++------ lib/bundle/types/annotation/annotation.es6 | 7 ++-- lib/bundle/types/type.es6 | 21 ++++++------ lib/clean/clean.es6 | 4 +-- lib/command.es6 | 7 ++-- lib/util/adt/queue-async.es6 | 17 ++++++---- lib/util/adt/stack-async.es6 | 29 +++++++++-------- lib/visitors/async/async.es6 | 9 +++--- test/lib/bundle/bundle.spec.es6 | 1 + test/lib/visitors/async/async.spec.es6 | 2 +- 12 files changed, 89 insertions(+), 67 deletions(-) diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index eba98c4..da09277 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -8,6 +8,7 @@ import Collection from 'util/adt/collection'; import Command from 'command'; import Metadata from 'bundle/task/metadata/metadata'; import Factory from 'util/factory/factory'; +import logger from 'util/logger/logger'; /** * Class Bundle @@ -69,7 +70,10 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ after(result) { - if(_.instanceOf(result, Error)) return this.emit(Command.events.error, result); + if(_.instanceOf(result, Error)) { + logger(result).warn(); + return this.emit(Command.events.error, result); + } return this.done(); } diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index a23153c..4c1c473 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -2,18 +2,18 @@ * @module bundle.format * @author Patricio Ferreira <3dimentionar@gmail.com> **/ -import { EventEmitter } from 'events'; import _ from 'util/mixins'; import extend from 'extend'; import jp from 'jsonpath'; +import Visitor from 'util/visitor/visitor'; import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** * Class Format -* @extends {events.EventEmitter} +* @extends {util.visitor.Visitor} **/ -class Format extends EventEmitter { +class Format extends Visitor { /** * Constructor @@ -47,16 +47,6 @@ class Format extends EventEmitter { return this; } - /** - * Static Constructor - * @static - * @param {Any} [...args] - constructor arguments - * @return {bundle.format.Format} - **/ - static new(...args) { - return new this(...args); - } - } export default Format; diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index 9229d73..2ccd638 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -7,7 +7,7 @@ import fs from 'fs-extra'; import path from 'path'; import extend from 'extend'; import glob from 'glob'; -import acorn from 'acorn'; +import * as acorn from 'acorn'; import Collection from 'util/adt/collection'; import StackAsync from 'util/adt/stack-async'; import Task from 'bundle/task/task'; @@ -28,21 +28,29 @@ class Reader extends Task { * @return {Promise} **/ read(vi) { - // TODO: read => acorn => Metadata.new() - this.bundles.addAll(this.files().reduce(this.parse, [], this)); - return this.types.pop(); + return this.types.pop({}, false, this.files().reduce(this.get, [], this)); } /** * File Parsing Strategy * @public * @param {Array} memo memoized array that will hold metadata found - * @param {String} file file path to parse + * @param {String} source file path to parse * @return {bundle.task.reader.Reader} **/ - parse(memo, file) { - //console.log('File: ', file); - return {}; + get(memo, source) { + memo.push({ source, ast: this.parse(source) }); + return memo; + } + + /** + * Acorn Parsing Strategy + * @public + * @param {String} source file path to parse + * @return {Object} + **/ + parse(source) { + return acorn.parse(fs.readFileSync(source, { encoding: 'utf8' }), Reader.acornOptions); } /** @@ -79,7 +87,7 @@ class Reader extends Task { } /** - * Glob Options + * Default Glob Options * @public * @property globOptions * @type {Object} @@ -90,6 +98,17 @@ class Reader extends Task { nodir: true }; + /** + * Default Acorn Options + * @static + * @property acornOptions + * @type {Object} + **/ + static acornOptions = { + ecmaVersion: 8, + sourceType: 'module' + }; + /** * Events * @static diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 02fc6a8..fa4852e 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -17,11 +17,12 @@ class Annotation extends Type { * Annotation Read strategy * @public * @override - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {Array} files - files parsed * @return {Promise} **/ - read(resolve, reject) { + read(resolve, reject, files) { return resolve(this); } diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index cd7883c..3b1b03f 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -27,13 +27,13 @@ class Type extends Visited { /** * Asynchronous next strategy * @public - * @param adt {visitors.async.Asynchronous} adt used for asynchronous operations - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {Array} files list of files parsed * @return {Promise} **/ - next(adt, resolve, reject) { - if(_.defined(this.task.read)) return this.read(resolve, reject); + next(resolve, reject, files) { + if(_.defined(this.task.read)) return this.read(resolve, reject, files); if(_.defined(this.task.write)) return this.write(resolve, reject); return resolve(this); } @@ -41,19 +41,20 @@ class Type extends Visited { /** * Default Read strategy * @public - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {Array} files list of files parsed * @return {Promise} **/ - read(resolve, reject) { + read(resolve, reject, files) { return resolve(this); } /** * Default Write strategy * @public - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject * @return {Promise} **/ write(resolve, reject) { diff --git a/lib/clean/clean.es6 b/lib/clean/clean.es6 index 4b378fa..d050b33 100644 --- a/lib/clean/clean.es6 +++ b/lib/clean/clean.es6 @@ -16,8 +16,8 @@ class Clean extends Command { * Run * @public * @override - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject * @return {clean.Clean} **/ run(resolve, reject) { diff --git a/lib/command.es6 b/lib/command.es6 index 7b260fc..a5dcf52 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -60,12 +60,11 @@ class Command extends Visited { * Proxified asynchronous next strategy * @public * @override - * @param adt {visitors.async.Asynchronous} adt used for asynchronous operations - * @param resolve {Function} asynchronous promise's resolve - * @param reject {Function} asynchronous promise's reject + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject * @return {Promise} **/ - next(adt, resolve, reject) { + next(resolve, reject) { this.once(Command.events.done, resolve) .once(Command.events.error, reject); return this.run(resolve, reject); diff --git a/lib/util/adt/queue-async.es6 b/lib/util/adt/queue-async.es6 index f485e3c..ea90f83 100644 --- a/lib/util/adt/queue-async.es6 +++ b/lib/util/adt/queue-async.es6 @@ -66,12 +66,13 @@ class QueueAsync extends Queue { * @override * @param {Object} [opts = {}] - additional options * @param {Boolean} [next = false] - async queue already started + * @param {Any} [...args] additonal arguments * @return {Promise} **/ - async poll(opts = {}, next = false) { + async poll(opts = {}, next = false, ...args) { if(!next) this._resetLast(); - const res = await this.next(opts); - return this.onNext(res, opts); + const res = await this.next(opts, ...args); + return this.onNext(res, opts, ...args); } /** @@ -79,12 +80,13 @@ class QueueAsync extends Queue { * @public * @emits {QueueAsync.events.next} - when opts.silent is false or undefined * @param {Object} [opts] - additional options + * @param {Any} [...args] additonal arguments * @return {Promise} **/ - next(opts) { + next(opts, ...args) { let element = super.poll(opts); if(!opts.silent) this.emit(QueueAsync.events.next, element); - return element.execute(this); + return element.execute(this, ...args); } /** @@ -92,11 +94,12 @@ class QueueAsync extends Queue { * @public * @param {Promise} res - current promise (resolved or rejected) * @param {Object} [opts] - additional options + * @param {Any} [...args] additonal arguments * @return {Any} **/ - onNext(res, opts) { + onNext(res, opts, ...args) { this._last.push(res); - return this.isEmpty() ? this.end(opts) : this.poll(opts, true); + return this.isEmpty() ? this.end(opts) : this.poll(opts, true, ...args); } /** diff --git a/lib/util/adt/stack-async.es6 b/lib/util/adt/stack-async.es6 index bc69f46..8916aab 100644 --- a/lib/util/adt/stack-async.es6 +++ b/lib/util/adt/stack-async.es6 @@ -39,8 +39,8 @@ class StackAsync extends Stack { * Default instanciation strategy for new elements added in this collection * @private * @override - * @param e {Any} element to instanciate - * @param opts {Object} additional options + * @param {Any} e element to instanciate + * @param {Object} opts additional options * @return {Any} **/ _new(e, opts) { @@ -64,14 +64,15 @@ class StackAsync extends Stack { * @public * @async * @override - * @param [opts = {}] {Object} additional options + * @param {Object} [opts = {}] additional options * @param {Boolean} [next = false] - async queue already started + * @param {Any} [...args] additonal arguments * @return {Promise} **/ - async pop(opts = {}, next = false) { + async pop(opts = {}, next = false, ...args) { if(!next) this._resetLast(); - const res = await this.next(opts); - return this.onNext(res, opts); + const res = await this.next(opts, ...args); + return this.onNext(res, opts, ...args); } /** @@ -79,31 +80,33 @@ class StackAsync extends Stack { * @public * @emits {StackAsync.events.next} - when opts.silent = false or undefined * @param [opts] {Object} additional options + * @param {Any} [...args] additonal arguments * @return {Promise} **/ - next(opts) { + next(opts, ...args) { let element = super.pop(opts); if(!opts.silent) this.emit(StackAsync.events.next, element); - return element.execute(this); + return element.execute(this, ...args); } /** * Retrieves and removes the head of this queue, or returns null if this queue is empty * @public - * @param res {Promise} promise reference with resolution (resolved or rejected) - * @param [opts] {Object} additional options + * @param {Promise} res promise reference with resolution (resolved or rejected) + * @param {Object} [opts] additional options + * @param {Any} [...args] additonal arguments * @return {Promise} **/ - onNext(res, opts) { + onNext(res, opts, ...args) { this._last.push(res); - return this.isEmpty() ? this.end(opts) : this.pop(opts, true); + return this.isEmpty() ? this.end(opts) : this.pop(opts, true, ...args); } /** * Asynchronous Queue end * @public * @emits {StackAsync.events.end} - when opts.silent is false or undefined - * @param [opts = {}] {Object} additional options + * @param {Object} [opts = {}] additional options * @return {util.adt.StackAsync} **/ end(opts) { diff --git a/lib/visitors/async/async.es6 b/lib/visitors/async/async.es6 index c9b7038..944f0d8 100644 --- a/lib/visitors/async/async.es6 +++ b/lib/visitors/async/async.es6 @@ -28,12 +28,12 @@ class Asynchronous extends Visitor { * Note: This method was designed (and it's most likely) to be overriden by * {@link util.visitor.Visited} subclasses that use this visitor. * @public - * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations * @param resolve {Function} asynchronous promise's resolve * @param reject {Function} asynchronous promise's reject + * @param {Any} [...args] additonal arguments * @return {visitors.async.Asynchronous} **/ - next(adt, resolve, reject) { + next(resolve, reject) { resolve(); return this; } @@ -43,10 +43,11 @@ class Asynchronous extends Visitor { * @public * @param {util.visitor.Visited} [ctx] - context reference * @param {visitors.async.Asynchronous} adt - reference to adt using this interface on their elements + * @param {Any} [...args] additonal arguments * @return {Promise} **/ - execute(ctx, adt) { - return new Promise((resolve, reject) => ctx.next(adt, resolve, reject)); + execute(ctx, adt, ...args) { + return new Promise((resolve, reject) => ctx.next(resolve, reject, ...args)); } /** diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index 3577361..fcde1c4 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -52,6 +52,7 @@ describe('bundle.Bundle', function() { describe('run()', () => { it('Should execute command run over specs/es6', () => { + this.bundle.run(); }); diff --git a/test/lib/visitors/async/async.spec.es6 b/test/lib/visitors/async/async.spec.es6 index 979e671..ca6f9e7 100644 --- a/test/lib/visitors/async/async.spec.es6 +++ b/test/lib/visitors/async/async.spec.es6 @@ -43,7 +43,7 @@ describe('visitors.async.Asynchronous', function() { it('Should call promise\'s resolve as a default strategy', () => { const resolveSpy = this.sandbox.spy(); const exp = Asynchronous.new({}); - assert.instanceOf(exp.next({}, resolveSpy), Asynchronous); + assert.instanceOf(exp.next(resolveSpy), Asynchronous); assert.isTrue(resolveSpy.calledOnce); }); From 5ec538ce1bac1a8dc938e10f3c88a03002a76036 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Tue, 2 May 2017 08:42:07 -0700 Subject: [PATCH 11/22] bundle: checkpoint - progress on Annotation parsing. --- lib/bundle/task/reader/reader.es6 | 12 ++++++- lib/bundle/types/annotation/annotation.es6 | 42 ++++++++++++++++++++-- lib/bundle/types/export/export.es6 | 26 ++++++++++++++ lib/bundle/types/import/import.es6 | 26 ++++++++++++++ lib/bundle/types/type.es6 | 31 +++++++++++----- 5 files changed, 124 insertions(+), 13 deletions(-) diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index 2ccd638..15e6e95 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -23,12 +23,22 @@ class Reader extends Task { /** * Read Strategy + * Switch parameters * @public * @param {util.visitor.Visited} vi - visited instance reference * @return {Promise} **/ read(vi) { - return this.types.pop({}, false, this.files().reduce(this.get, [], this)); + return this.types.pop({}, false, this); + } + + /** + * Retrieves files metadata + * @public + * @return {Array} + **/ + getFiles() { + return this.files().reduce(this.get, [], this); } /** diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index fa4852e..e9f019e 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -19,11 +19,47 @@ class Annotation extends Type { * @override * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {Array} files - files parsed + * @param {bundle.task.Task} task current task * @return {Promise} **/ - read(resolve, reject, files) { - return resolve(this); + read(resolve, reject, task) { + task.bundles.addAll(this.annotations(task.getFiles()), { new: _.bind(this.create, this) }); + return super.read(resolve, reject); + } + + /** + * Create Bundle Metadata + * @public + * @param {Object} bundle annotation information + * @return {util.adt.Collection} + **/ + create(bundle) { + // TODO: + console.log('Instanciate Metadata: ', bundle); + return bundle; + } + + /** + * Read all annotations of all files captured + * @public + * @param {Array} files files parsed by Reader Task + * @return {Array} + **/ + annotations(files) { + return _.reduce(files, this.annotation, [], this); + } + + /** + * Read annotations from a single file + * @public + * @param {Array} memo memoized list of files used to store parsed annotations + * @param {Object} file current file metadata to capture + * @return {Array} + **/ + annotation(memo, file) { + // TODO: Factory of format gathered from command! + //console.log(file.ast); + return memo; } } diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index f20966c..246dbbc 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -13,6 +13,32 @@ import logger from 'util/logger/logger'; **/ class Export extends Type { + /** + * Annotation Read strategy + * @public + * @override + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {bundle.task.Task} task current task + * @return {Promise} + **/ + read(resolve, reject) { + return super.read(resolve, reject); + } + + /** + * Annotation Write strategy + * @public + * @override + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {bundle.task.Task} task current task + * @return {Promise} + **/ + write(resolve, reject) { + return super.write(resolve, reject); + } + } export default Export; diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index accc8af..0d3bec7 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -13,6 +13,32 @@ import logger from 'util/logger/logger'; **/ class Import extends Type { + /** + * Annotation Read strategy + * @public + * @override + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {bundle.task.Task} task current task + * @return {Promise} + **/ + read(resolve, reject) { + return super.read(resolve, reject); + } + + /** + * Annotation Write strategy + * @public + * @override + * @param {Function} resolve asynchronous promise's resolve + * @param {Function} reject asynchronous promise's reject + * @param {bundle.task.Task} task current task + * @return {Promise} + **/ + write(resolve, reject) { + return super.write(resolve, reject); + } + } export default Import; diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index 3b1b03f..d8c39f8 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -25,17 +25,29 @@ class Type extends Visited { } /** - * Asynchronous next strategy + * Resolves Task Execution + * @public + * @param {Function} reject asynchronous promise's reject + * @return {bundle.types.Type} + **/ + resolve(reject) { + switch(this.task.name) { + case 'ReaderVisitor': return _.bind(this.read, this); + case 'WriterVisitor': return _.bind(this.write, this); + default: return reject; + } + } + + /** + * Default Asynchronous next strategy * @public * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {Array} files list of files parsed + * @param {Any} [...args] list of arguments * @return {Promise} **/ - next(resolve, reject, files) { - if(_.defined(this.task.read)) return this.read(resolve, reject, files); - if(_.defined(this.task.write)) return this.write(resolve, reject); - return resolve(this); + next(resolve, reject, ...args) { + return this.resolve(reject)(resolve, reject, ...args); } /** @@ -43,10 +55,10 @@ class Type extends Visited { * @public * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {Array} files list of files parsed + * @param {Any} [...args] list of arguments * @return {Promise} **/ - read(resolve, reject, files) { + read(resolve, reject, ...args) { return resolve(this); } @@ -55,9 +67,10 @@ class Type extends Visited { * @public * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject + * @param {Any} [...args] list of arguments * @return {Promise} **/ - write(resolve, reject) { + write(resolve, reject, ...args) { return resolve(this); } From 6989cf53e98ca935e77aa285979031350b83bcc3 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Thu, 4 May 2017 11:18:29 -0700 Subject: [PATCH 12/22] bundle: checkpoint - replaced jsonpath with astq library to query abstract syntax tree and avoid 'walkers' provided by acorn. --- lib/bundle/format/amd/amd.es6 | 12 +++++++ .../format/cjs/{commonjs.es6 => cjs.es6} | 12 +++++++ lib/bundle/format/es6/es6.es6 | 12 +++++++ lib/bundle/format/format.es6 | 19 ++++++------ lib/bundle/format/iife/iife.es6 | 12 +++++++ lib/bundle/format/standard/standard.es6 | 31 +++++++++++++++++++ lib/bundle/format/umd/umd.es6 | 12 +++++++ lib/bundle/types/annotation/annotation.es6 | 4 +-- lib/bundle/types/type.es6 | 18 ++++++++++- lib/util/visitor/visited.es6 | 2 +- package.json | 1 + 11 files changed, 121 insertions(+), 14 deletions(-) rename lib/bundle/format/cjs/{commonjs.es6 => cjs.es6} (58%) create mode 100644 lib/bundle/format/standard/standard.es6 diff --git a/lib/bundle/format/amd/amd.es6 b/lib/bundle/format/amd/amd.es6 index 6fa99aa..2205a9b 100644 --- a/lib/bundle/format/amd/amd.es6 +++ b/lib/bundle/format/amd/amd.es6 @@ -13,6 +13,18 @@ import logger from 'util/logger/logger'; **/ class Amd extends Format { + /** + * AMD AST Query + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} o - object to query + * @param {String} expr - json path query + * @return {Any} + **/ + amd(ctx, o, expr) { + return this.query(o, expr); + } + } export default Amd; diff --git a/lib/bundle/format/cjs/commonjs.es6 b/lib/bundle/format/cjs/cjs.es6 similarity index 58% rename from lib/bundle/format/cjs/commonjs.es6 rename to lib/bundle/format/cjs/cjs.es6 index 28462dc..398bdeb 100644 --- a/lib/bundle/format/cjs/commonjs.es6 +++ b/lib/bundle/format/cjs/cjs.es6 @@ -13,6 +13,18 @@ import logger from 'util/logger/logger'; **/ class CommonJs extends Format { + /** + * CommonJs AST Query + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} o - object to query + * @param {String} expr - json path query + * @return {Any} + **/ + cjs(ctx, o, expr) { + return this.query(o, expr); + } + } export default CommonJs; diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index 1ea026a..6d6216e 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -13,6 +13,18 @@ import logger from 'util/logger/logger'; **/ class Es6 extends Format { + /** + * ES6 AST Query + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} o - object to query + * @param {String} expr - json path query + * @return {Any} + **/ + es6(ctx, o, expr) { + return this.query(o, expr); + } + } export default Es6; diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index 4c1c473..ec768a5 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -4,13 +4,14 @@ **/ import _ from 'util/mixins'; import extend from 'extend'; -import jp from 'jsonpath'; +import AstQ from 'astq'; // documentation - https://www.npmjs.com/package/astq import Visitor from 'util/visitor/visitor'; import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** * Class Format +* @Note: Subjected to refactor formats ast -> format (formats and templates), ast -> query (astq wrapper visitor) * @extends {util.visitor.Visitor} **/ class Format extends Visitor { @@ -18,23 +19,22 @@ class Format extends Visitor { /** * Constructor * @public - * @param {Any} [...args] - constructor arguments + * @override * @return {bundle.format.Format} **/ constructor() { - super(); - return this; + return super({ astq: new AstQ() }); } /** - * Method Wrapper for querying AST + * Method Wrapper for querying Abstract Syntax Tree (AST) * @public - * @param {String} [expr = ''] - json path query - * @param {Object} [o = {}] - object to query + * @param {Object} [ast = {}] AST to query + * @param {String} [expr = ''] json path query * @return {Any} **/ - query(expr = '', o = {}) { - return this.result(jp.query(expr, o)); + query(ctx, ast = {}, expr = '') { + return this.result(this.astq.query(ast, expr)); } /** @@ -44,6 +44,7 @@ class Format extends Visitor { * @return {bundle.format.Format} **/ result(out) { + console.log('Result: ', out); return this; } diff --git a/lib/bundle/format/iife/iife.es6 b/lib/bundle/format/iife/iife.es6 index 0fa76de..0906722 100644 --- a/lib/bundle/format/iife/iife.es6 +++ b/lib/bundle/format/iife/iife.es6 @@ -13,6 +13,18 @@ import logger from 'util/logger/logger'; **/ class Iife extends Format { + /** + * Iife AST Query + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} o - object to query + * @param {String} expr - json path query + * @return {Any} + **/ + iife(ctx, o, expr) { + return this.query(o, expr); + } + } export default Iife; diff --git a/lib/bundle/format/standard/standard.es6 b/lib/bundle/format/standard/standard.es6 new file mode 100644 index 0000000..d1b3cab --- /dev/null +++ b/lib/bundle/format/standard/standard.es6 @@ -0,0 +1,31 @@ +/** +* @module bundle.format.standard +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import extend from 'extend'; +import Format from 'bundle/format/format'; +import logger from 'util/logger/logger'; + +/** +* Class Standard +* @extends {bundle.format.Format} +**/ +class Standard extends Format { + + /** + * Standard AST Query + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} o - object to query + * @param {String} expr - json path query + * @return {Any} + **/ + standard(ctx, o, expr) { + console.log(o, expr); + return this.query(o, expr); + } + +} + +export default Standard; diff --git a/lib/bundle/format/umd/umd.es6 b/lib/bundle/format/umd/umd.es6 index d650569..70ac255 100644 --- a/lib/bundle/format/umd/umd.es6 +++ b/lib/bundle/format/umd/umd.es6 @@ -13,6 +13,18 @@ import logger from 'util/logger/logger'; **/ class Umd extends Format { + /** + * UMD AST Query + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} o - object to query + * @param {String} expr - json path query + * @return {Any} + **/ + umd(ctx, o, expr) { + return this.query(o, expr); + } + } export default Umd; diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index e9f019e..5e484d8 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -34,7 +34,6 @@ class Annotation extends Type { * @return {util.adt.Collection} **/ create(bundle) { - // TODO: console.log('Instanciate Metadata: ', bundle); return bundle; } @@ -57,8 +56,7 @@ class Annotation extends Type { * @return {Array} **/ annotation(memo, file) { - // TODO: Factory of format gathered from command! - //console.log(file.ast); + // HERE... this.standard(file.ast.body, '$..[0]'); return memo; } diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index d8c39f8..a804983 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -4,6 +4,7 @@ **/ import _ from 'util/mixins'; import extend from 'extend'; +import Collection from 'util/adt/collection'; import Visited from 'util/visitor/visited'; import logger from 'util/logger/logger'; @@ -21,7 +22,7 @@ class Type extends Visited { * @return {bundle.types.Type} **/ constructor(task) { - return super({ task }); + return super({ task }).registerAll(); } /** @@ -74,6 +75,21 @@ class Type extends Visited { return resolve(this); } + /** + * List of Visitors + * @static + * @property visitors + * @type {util.adt.Collection} + **/ + static visitors = Collection.new(Visited.visitors.toJSON().concat([ + 'bundle/format/standard/standard', + 'bundle/format/es6/es6', + 'bundle/format/cjs/cjs', + 'bundle/format/amd/amd', + 'bundle/format/iife/iife', + 'bundle/format/umd/umd' + ])); + } export default Type; diff --git a/lib/util/visitor/visited.es6 b/lib/util/visitor/visited.es6 index f1377fb..2c7bf58 100644 --- a/lib/util/visitor/visited.es6 +++ b/lib/util/visitor/visited.es6 @@ -34,7 +34,7 @@ class Visited extends EventEmitter { * @return {util.visitor.Visited} **/ registerAll(dirname) { - Factory.basePath(dirname); + if(_.defined(dirname)) Factory.basePath(dirname); return this.constructor.visitors.reduce(this.register, this, this); } diff --git a/package.json b/package.json index d8ef3f6..d8a015f 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ }, "dependencies": { "acorn": "4.0.11", + "astq": "2.0.2", "babel-plugin-module-resolver": "2.5.0", "babel-polyfill": "6.23.0", "babel-preset-es2015": "6.22.0", From ff525da670d0ecdcfacb0d55c179373cf0def131 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Fri, 5 May 2017 16:42:15 -0700 Subject: [PATCH 13/22] bundle: checkpoint - implemented annotation parsing. json5 added as a dependency to read annotation metadata. --- lib/bundle/format/format.es6 | 5 ++- lib/bundle/format/standard/standard.es6 | 28 +++++++++++-- lib/bundle/task/metadata/bundle.es6 | 4 +- lib/bundle/task/metadata/file.es6 | 3 +- lib/bundle/task/metadata/metadata.es6 | 8 +--- lib/bundle/task/reader/reader.es6 | 9 ++-- lib/bundle/types/annotation/annotation.es6 | 48 ++++++++++++++++------ lib/util/mixins.es6 | 11 +++++ package.json | 1 + test/specs/es6/common/common-a.es6 | 2 +- test/specs/es6/common/common-b.es6 | 2 +- test/specs/es6/module-a/source-a.es6 | 4 +- test/specs/es6/module-b/source-b.es6 | 2 +- 13 files changed, 89 insertions(+), 38 deletions(-) diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index ec768a5..dbe2efa 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -31,10 +31,11 @@ class Format extends Visitor { * @public * @param {Object} [ast = {}] AST to query * @param {String} [expr = ''] json path query + * @param {Any} [...args] additional arguments * @return {Any} **/ - query(ctx, ast = {}, expr = '') { - return this.result(this.astq.query(ast, expr)); + query(ctx, ast = {}, expr = '', ...args) { + return this.result(this.astq.query(ast, expr, ...args)); } /** diff --git a/lib/bundle/format/standard/standard.es6 b/lib/bundle/format/standard/standard.es6 index d1b3cab..f5e49e8 100644 --- a/lib/bundle/format/standard/standard.es6 +++ b/lib/bundle/format/standard/standard.es6 @@ -13,17 +13,39 @@ import logger from 'util/logger/logger'; **/ class Standard extends Format { + /** + * Clean Up + * @public + * @param {Array} comments list of all comments + * @return {Array} + **/ + _clean(comments) { + return _.map(comments, (comment) => _.trimSpecial(comment.value)); + } + /** * Standard AST Query * @public * @param {util.visitor.Visited} ctx context visited * @param {Object} o - object to query * @param {String} expr - json path query + * @param {Any} [...args] additional arguments * @return {Any} **/ - standard(ctx, o, expr) { - console.log(o, expr); - return this.query(o, expr); + standard(ctx, o, expr, ...args) { + return this.query(ctx, o, expr, ...args); + } + + /** + * Perform Search of annotations over comments in the source code + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Array} comments list of all comments + * @param {Function} predicate predicate walker + * @return {Object} + **/ + search(ctx, comments = [], predicate = () => true) { + return _.find(this._clean(comments), predicate); } } diff --git a/lib/bundle/task/metadata/bundle.es6 b/lib/bundle/task/metadata/bundle.es6 index 0b6dd87..da23408 100644 --- a/lib/bundle/task/metadata/bundle.es6 +++ b/lib/bundle/task/metadata/bundle.es6 @@ -40,9 +40,7 @@ class Bundle extends Visited { * @type {Array} **/ static properties = [ - 'name', - 'target', - 'format' + 'name' ]; } diff --git a/lib/bundle/task/metadata/file.es6 b/lib/bundle/task/metadata/file.es6 index 0fb28ec..c9ec644 100644 --- a/lib/bundle/task/metadata/file.es6 +++ b/lib/bundle/task/metadata/file.es6 @@ -41,7 +41,8 @@ class File extends Visited { **/ static properties = [ 'source', - 'ast' + 'ast', + 'comments' ]; } diff --git a/lib/bundle/task/metadata/metadata.es6 b/lib/bundle/task/metadata/metadata.es6 index f7d1a22..d51310a 100644 --- a/lib/bundle/task/metadata/metadata.es6 +++ b/lib/bundle/task/metadata/metadata.es6 @@ -21,11 +21,7 @@ import File from 'bundle/task/metadata/file'; * * @example * [Metadata] => { -* bundle: { -* name: {uniqueId}, -* target: {path}, -* format: {currentFormat} -* }, +* bundle: {name} * files: [{ * source: {path}, * ast: {object} @@ -52,7 +48,7 @@ class Metadata extends Visited { * @return {bundle.task.metadata.Metadata} **/ parse(attrs = {}) { - this.bundle.parse(attrs.bundle); + this.bundle.parse(attrs); this.files.set(attrs.files); return extend(true, this, _.pick(attrs, this.constructor.properties)); } diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index 15e6e95..b526586 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -49,7 +49,8 @@ class Reader extends Task { * @return {bundle.task.reader.Reader} **/ get(memo, source) { - memo.push({ source, ast: this.parse(source) }); + let comments = [], ast = this.parse(source, extend(false, { onComment: comments }, Reader.acornOptions)); + memo.push({ source, ast, comments }); return memo; } @@ -59,8 +60,8 @@ class Reader extends Task { * @param {String} source file path to parse * @return {Object} **/ - parse(source) { - return acorn.parse(fs.readFileSync(source, { encoding: 'utf8' }), Reader.acornOptions); + parse(source, ...args) { + return acorn.parse(fs.readFileSync(source, { encoding: 'utf8' }), ...args); } /** @@ -115,7 +116,7 @@ class Reader extends Task { * @type {Object} **/ static acornOptions = { - ecmaVersion: 8, + ecmaVersion: 6, sourceType: 'module' }; diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 5e484d8..7141b91 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -3,6 +3,8 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import _s from 'underscore.string'; +import json5 from 'json5'; import extend from 'extend'; import Type from 'bundle/types/type'; import logger from 'util/logger/logger'; @@ -23,21 +25,10 @@ class Annotation extends Type { * @return {Promise} **/ read(resolve, reject, task) { - task.bundles.addAll(this.annotations(task.getFiles()), { new: _.bind(this.create, this) }); + task.bundles.addAll(this.annotations(task.getFiles())); return super.read(resolve, reject); } - /** - * Create Bundle Metadata - * @public - * @param {Object} bundle annotation information - * @return {util.adt.Collection} - **/ - create(bundle) { - console.log('Instanciate Metadata: ', bundle); - return bundle; - } - /** * Read all annotations of all files captured * @public @@ -56,10 +47,41 @@ class Annotation extends Type { * @return {Array} **/ annotation(memo, file) { - // HERE... this.standard(file.ast.body, '$..[0]'); + let annotation = this.search(file.comments, _.bind(this.match, this)); + memo.push(this.extract(annotation)); return memo; } + /** + * Annotation Matcher Evaluation + * @public + * @param {Object} comment current comment to evaluate + * @return {Boolean} + **/ + match(comment) { + return _s.startsWith(comment, `${Annotation.name}(`) && _s.endsWith(comment, ')'); + } + + /** + * Extract Metadata from annotation declaration using json5 specification + * @public + * @param {String} expr annotation expression + * @return {Object} + **/ + extract(expr) { + return json5.parse(_s.rtrim(_s.ltrim(expr, `${Annotation.name}(`), ')')); + } + + /** + * Annotation Name + * @static + * @property name + * @type {String} + **/ + static get name() { + return '@sqbox'; + } + } export default Annotation; diff --git a/lib/util/mixins.es6 b/lib/util/mixins.es6 index db0dd5c..b468887 100644 --- a/lib/util/mixins.es6 +++ b/lib/util/mixins.es6 @@ -113,6 +113,17 @@ _.mixin({ **/ defined: function(o) { return (!_.isUndefined(o) && !_.isNull(o)); + }, + + /** + * Trims Globally any special characters from a given expression as string + * Special characters include: '*,\\,\n,\t,\r' + * @public + * @param {String} [expr = ''] expression to trim + * @return {String} + **/ + trimSpecial: function(expr = '') { + return _s.replaceAll(_s.clean(expr), /(\*|\n|\\|\t|\r)*/g, ''); } }); diff --git a/package.json b/package.json index d8a015f..8f8369c 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "dependencies": { "acorn": "4.0.11", "astq": "2.0.2", + "json5": "0.5.1", "babel-plugin-module-resolver": "2.5.0", "babel-polyfill": "6.23.0", "babel-preset-es2015": "6.22.0", diff --git a/test/specs/es6/common/common-a.es6 b/test/specs/es6/common/common-a.es6 index f6f7201..e1ca3ce 100644 --- a/test/specs/es6/common/common-a.es6 +++ b/test/specs/es6/common/common-a.es6 @@ -1,5 +1,5 @@ /** -* @sqbox({ bundle: "common" }) +* @sqbox({ name: "common" }) **/ import CommonB from 'common/common-b'; diff --git a/test/specs/es6/common/common-b.es6 b/test/specs/es6/common/common-b.es6 index fc954b5..8cdec30 100644 --- a/test/specs/es6/common/common-b.es6 +++ b/test/specs/es6/common/common-b.es6 @@ -1,4 +1,4 @@ /** -* @sqbox({ bundle: "module-a" }) +* @sqbox({ name: "module-a" }) **/ export default () => { console.log('common/CommonB'); }; diff --git a/test/specs/es6/module-a/source-a.es6 b/test/specs/es6/module-a/source-a.es6 index 4d02710..b0f343a 100644 --- a/test/specs/es6/module-a/source-a.es6 +++ b/test/specs/es6/module-a/source-a.es6 @@ -1,6 +1,4 @@ -/** -* @sqbox({ bundle: "module-a" }) -**/ +// @sqbox({ name: "module-a" }) import CommonA from 'common/common-a'; import CommonB from 'common/common-b'; diff --git a/test/specs/es6/module-b/source-b.es6 b/test/specs/es6/module-b/source-b.es6 index 095b1d4..af41eb8 100644 --- a/test/specs/es6/module-b/source-b.es6 +++ b/test/specs/es6/module-b/source-b.es6 @@ -1,5 +1,5 @@ /** -* @sqbox({ bundle: "module-b" }) +* @sqbox({ name: "module-b" }) **/ import CommonA from 'common/common-a'; From c29ae3f71726d303a8afce6aae48c8a6ea229c42 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 8 May 2017 10:57:41 -0700 Subject: [PATCH 14/22] bundle: checkpoint - Refactored es6 specs testing files. Also, added validations against metadata & annotation parsing. Extracted bundle.types.annotation.Annotation methods into a helpers utility for annotation pattern matching/filtering. --- lib/bundle/format/format.es6 | 41 +++++++++--- lib/bundle/format/standard/standard.es6 | 22 ------- lib/bundle/task/metadata/bundle.es6 | 14 +++- lib/bundle/task/metadata/metadata.es6 | 9 +-- lib/bundle/task/reader/reader.es6 | 4 +- lib/bundle/types/annotation/annotation.es6 | 66 ++++++++----------- lib/bundle/types/annotation/helpers.es6 | 58 ++++++++++++++++ lib/bundle/types/export/export.es6 | 5 +- lib/bundle/types/import/import.es6 | 5 +- lib/bundle/types/type.es6 | 1 + lib/util/adt/collection.es6 | 2 +- .../es6/common/{common-a.es6 => c-a.es6} | 3 - .../es6/common/{common-b.es6 => c-b.es6} | 3 - test/specs/es6/common/common.es6 | 5 ++ test/specs/es6/module-a/s-a-1.es6 | 4 ++ test/specs/es6/module-a/s-a-2.es6 | 3 + test/specs/es6/module-a/source-a.es6 | 6 +- test/specs/es6/module-b/s-b.es6 | 3 + test/specs/es6/module-b/source-b.es6 | 6 +- test/specs/schema.md | 51 ++++++++++++++ 20 files changed, 215 insertions(+), 96 deletions(-) create mode 100644 lib/bundle/types/annotation/helpers.es6 rename test/specs/es6/common/{common-a.es6 => c-a.es6} (72%) rename test/specs/es6/common/{common-b.es6 => c-b.es6} (59%) create mode 100644 test/specs/es6/common/common.es6 create mode 100644 test/specs/es6/module-a/s-a-1.es6 create mode 100644 test/specs/es6/module-a/s-a-2.es6 create mode 100644 test/specs/es6/module-b/s-b.es6 create mode 100644 test/specs/schema.md diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index dbe2efa..d0da204 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -27,28 +27,51 @@ class Format extends Visitor { } /** - * Method Wrapper for querying Abstract Syntax Tree (AST) + * Clean Up * @public - * @param {Object} [ast = {}] AST to query - * @param {String} [expr = ''] json path query - * @param {Any} [...args] additional arguments - * @return {Any} + * @param {Array} comments list of all comments + * @return {Array} **/ - query(ctx, ast = {}, expr = '', ...args) { - return this.result(this.astq.query(ast, expr, ...args)); + _clean(comments) { + return _.map(comments, (comment) => _.trimSpecial(comment.value)); } /** * Default Result Handler - * @public + * @private * @param {Any} out - ast query result * @return {bundle.format.Format} **/ - result(out) { + _result(out) { console.log('Result: ', out); return this; } + /** + * Perform search of annotations over comments in the source code and retrieves + * the first occurence when predicates pass the evaluation + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Array} comments list of all comments + * @param {Function} [predicate = () => false] predicate walker + * @return {Object} + **/ + search(ctx, comments = [], predicate = () => false) { + return _.find(this._clean(comments), predicate); + } + + /** + * Method Wrapper for querying Abstract Syntax Tree (AST) + * @public + * @param {Object} [ast = {}] AST to query + * @param {String} [expr = ''] json path query + * @param {Any} [...args] additional arguments + * @return {Any} + **/ + query(ctx, ast = {}, expr = '', ...args) { + return this._result(this.astq.query(ast, expr, ...args)); + } + } export default Format; diff --git a/lib/bundle/format/standard/standard.es6 b/lib/bundle/format/standard/standard.es6 index f5e49e8..715ee52 100644 --- a/lib/bundle/format/standard/standard.es6 +++ b/lib/bundle/format/standard/standard.es6 @@ -13,16 +13,6 @@ import logger from 'util/logger/logger'; **/ class Standard extends Format { - /** - * Clean Up - * @public - * @param {Array} comments list of all comments - * @return {Array} - **/ - _clean(comments) { - return _.map(comments, (comment) => _.trimSpecial(comment.value)); - } - /** * Standard AST Query * @public @@ -36,18 +26,6 @@ class Standard extends Format { return this.query(ctx, o, expr, ...args); } - /** - * Perform Search of annotations over comments in the source code - * @public - * @param {util.visitor.Visited} ctx context visited - * @param {Array} comments list of all comments - * @param {Function} predicate predicate walker - * @return {Object} - **/ - search(ctx, comments = [], predicate = () => true) { - return _.find(this._clean(comments), predicate); - } - } export default Standard; diff --git a/lib/bundle/task/metadata/bundle.es6 b/lib/bundle/task/metadata/bundle.es6 index da23408..c282dd6 100644 --- a/lib/bundle/task/metadata/bundle.es6 +++ b/lib/bundle/task/metadata/bundle.es6 @@ -5,6 +5,7 @@ import _ from 'util/mixins'; import extend from 'extend'; import Visited from 'util/visitor/visited'; +import File from 'bundle/task/metadata/file'; /** * Class Bundle @@ -19,7 +20,7 @@ class Bundle extends Visited { * @return {bundle.task.metadata.Bundle} **/ constructor(...args) { - super(_.object(Bundle.properties, [])); + super({ name: null, target: File.new() }); return this.registerAll().parse(...args); } @@ -30,9 +31,20 @@ class Bundle extends Visited { * @return {bundle.task.metadata.Bundle} **/ parse(attrs = {}) { + this.target.parse(attrs.target); return extend(true, this, _.pick(attrs, this.constructor.properties)); } + /** + * Compound Property Definition + * @static + * @property compound + * @type {Array} + **/ + static compound = [ + 'target' + ]; + /** * Property Definition * @static diff --git a/lib/bundle/task/metadata/metadata.es6 b/lib/bundle/task/metadata/metadata.es6 index d51310a..91ae2c4 100644 --- a/lib/bundle/task/metadata/metadata.es6 +++ b/lib/bundle/task/metadata/metadata.es6 @@ -21,10 +21,11 @@ import File from 'bundle/task/metadata/file'; * * @example * [Metadata] => { -* bundle: {name} +* bundle: {uniqueName}, * files: [{ * source: {path}, -* ast: {object} +* ast: {object}, +* comments: [] * }, ...] * } **/ @@ -48,7 +49,7 @@ class Metadata extends Visited { * @return {bundle.task.metadata.Metadata} **/ parse(attrs = {}) { - this.bundle.parse(attrs); + this.bundle.parse(attrs.bundle); this.files.set(attrs.files); return extend(true, this, _.pick(attrs, this.constructor.properties)); } @@ -60,7 +61,7 @@ class Metadata extends Visited { * @param {visitors.formatter.Json} [ctx] - context reference * @return {Object} **/ - toJSON() { + toJSON(ctx) { return { bundle: this.bundle.toJSON(), files: this.files.toJSON() }; } diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index b526586..7156a6a 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -29,7 +29,7 @@ class Reader extends Task { * @return {Promise} **/ read(vi) { - return this.types.pop({}, false, this); + return this.types.pop({}, false, this.bundles, this.getFiles()); } /** @@ -116,7 +116,7 @@ class Reader extends Task { * @type {Object} **/ static acornOptions = { - ecmaVersion: 6, + ecmaVersion: 8, sourceType: 'module' }; diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 7141b91..d1a1690 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -6,7 +6,9 @@ import _ from 'util/mixins'; import _s from 'underscore.string'; import json5 from 'json5'; import extend from 'extend'; +import Collection from 'util/adt/collection'; import Type from 'bundle/types/type'; +import * as Helpers from 'bundle/types/annotation/helpers'; import logger from 'util/logger/logger'; /** @@ -21,67 +23,53 @@ class Annotation extends Type { * @override * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {bundle.task.Task} task current task + * @param {util.adt.Collection} bundles list of bundles + * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject, task) { - task.bundles.addAll(this.annotations(task.getFiles())); + read(resolve, reject, bundles, files) { + this.annotations(files).reduce(this.create, bundles, this); return super.read(resolve, reject); } + /** + * Create Bundle Metadata + * @public + * @param {util.adt.Collection} memo memoized list of bundles + * @param {Object} meta data extracted from annotation + * @return {util.adt.Collection} + **/ + create(memo, meta) { + if(!Helpers.containsBy(memo, meta.name)) memo.add({ bundle: meta }); + return memo; + } + /** * Read all annotations of all files captured * @public * @param {Array} files files parsed by Reader Task - * @return {Array} + * @return {util.adt.Collection} **/ annotations(files) { - return _.reduce(files, this.annotation, [], this); + return _.reduce(files, this.annotation, Collection.new(), this); } /** * Read annotations from a single file * @public - * @param {Array} memo memoized list of files used to store parsed annotations + * @param {util.adt.Collection} memo memoized collection of files used to store parsed annotations * @param {Object} file current file metadata to capture - * @return {Array} + * @return {util.adt.Collection} **/ annotation(memo, file) { - let annotation = this.search(file.comments, _.bind(this.match, this)); - memo.push(this.extract(annotation)); + let annotation = this.search(file.comments, Helpers.match); + if(_.defined(annotation)) { + let meta = Helpers.extract(annotation); + if(Helpers.valid(meta)) memo.add({ name: meta.name, target: file }); + } return memo; } - /** - * Annotation Matcher Evaluation - * @public - * @param {Object} comment current comment to evaluate - * @return {Boolean} - **/ - match(comment) { - return _s.startsWith(comment, `${Annotation.name}(`) && _s.endsWith(comment, ')'); - } - - /** - * Extract Metadata from annotation declaration using json5 specification - * @public - * @param {String} expr annotation expression - * @return {Object} - **/ - extract(expr) { - return json5.parse(_s.rtrim(_s.ltrim(expr, `${Annotation.name}(`), ')')); - } - - /** - * Annotation Name - * @static - * @property name - * @type {String} - **/ - static get name() { - return '@sqbox'; - } - } export default Annotation; diff --git a/lib/bundle/types/annotation/helpers.es6 b/lib/bundle/types/annotation/helpers.es6 new file mode 100644 index 0000000..8bfdd90 --- /dev/null +++ b/lib/bundle/types/annotation/helpers.es6 @@ -0,0 +1,58 @@ +/** +* @module bundle.types.annotation +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import _s from 'underscore.string'; +import json5 from 'json5'; + +/** +* Annotation name +* @private +* @property annotation +* @type {String} +**/ +const annotation = '@sqbox'; + +/** +* Metadata Contain Matcher +* Will return true if the bundle with a given name already exists inside the collection, false otherwise +* @public +* @param {util.adt.Collection} collection list of bundles to perform look up +* @param {String} name bundle name to look for +* @return {Boolean} +**/ +export const containsBy = (collection, name) => { + return _.defined(collection.find((meta) => (meta.bundle.name.toLowerCase() === name.toLowerCase()))); +}; + +/** +* Annotation Matcher: returns true if annotation was found, false otherwise +* @public +* @param {String} expr annotation found to be match +* @return {Boolean} +**/ +export const match = (expr) => { + return _s.startsWith(expr, `${annotation}(`) && _s.endsWith(expr, ')'); +}; + +/** +* Annotation Metadata Extraction +* Will extract, parse and return annotation information using json5 specification +* @public +* @param {String} expr annotation found to be match +* @return {Boolean} +**/ +export const extract = (expr = '') => { + return json5.parse(_s.rtrim(_s.ltrim(expr, `${annotation}(`), ')')); +}; + +/** +* Returns true if the metadata extracted is valid, false otherwise +* @public +* @param {Object} meta annotation metadata information extracted +* @return {Boolean} +**/ +export const valid = (meta) => { + return (_.defined(meta.name) && _.isString(meta.name) && meta.name.length > 0); +}; diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index 246dbbc..f23ccc6 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -19,10 +19,11 @@ class Export extends Type { * @override * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {bundle.task.Task} task current task + * @param {util.adt.Collection} bundles list of bundles + * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject) { + read(resolve, reject, bundles) { return super.read(resolve, reject); } diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index 0d3bec7..7b48ef9 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -19,10 +19,11 @@ class Import extends Type { * @override * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {bundle.task.Task} task current task + * @param {util.adt.Collection} bundles list of bundles + * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject) { + read(resolve, reject, bundles) { return super.read(resolve, reject); } diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index a804983..88590b0 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -82,6 +82,7 @@ class Type extends Visited { * @type {util.adt.Collection} **/ static visitors = Collection.new(Visited.visitors.toJSON().concat([ + 'bundle/format/format', 'bundle/format/standard/standard', 'bundle/format/es6/es6', 'bundle/format/cjs/cjs', diff --git a/lib/util/adt/collection.es6 b/lib/util/adt/collection.es6 index 1beed2c..930a00e 100644 --- a/lib/util/adt/collection.es6 +++ b/lib/util/adt/collection.es6 @@ -143,7 +143,7 @@ class Collection extends EventEmitter { } /** - * Returns true if the a given element existis in this collection, false otherwise + * Returns true if the a given element exists in this collection, false otherwise * @public * @param element {Any} element to evaluate * @return {Boolean} diff --git a/test/specs/es6/common/common-a.es6 b/test/specs/es6/common/c-a.es6 similarity index 72% rename from test/specs/es6/common/common-a.es6 rename to test/specs/es6/common/c-a.es6 index e1ca3ce..9714862 100644 --- a/test/specs/es6/common/common-a.es6 +++ b/test/specs/es6/common/c-a.es6 @@ -1,6 +1,3 @@ -/** -* @sqbox({ name: "common" }) -**/ import CommonB from 'common/common-b'; export default () => { console.log('common/CommonA'); }; diff --git a/test/specs/es6/common/common-b.es6 b/test/specs/es6/common/c-b.es6 similarity index 59% rename from test/specs/es6/common/common-b.es6 rename to test/specs/es6/common/c-b.es6 index 8cdec30..5c17099 100644 --- a/test/specs/es6/common/common-b.es6 +++ b/test/specs/es6/common/c-b.es6 @@ -1,4 +1 @@ -/** -* @sqbox({ name: "module-a" }) -**/ export default () => { console.log('common/CommonB'); }; diff --git a/test/specs/es6/common/common.es6 b/test/specs/es6/common/common.es6 new file mode 100644 index 0000000..f5d5cf5 --- /dev/null +++ b/test/specs/es6/common/common.es6 @@ -0,0 +1,5 @@ +/** +* @sqbox({ name: "common" }) +**/ +import commonA from 'common/c-a'; +import commonB from 'common/c-b'; diff --git a/test/specs/es6/module-a/s-a-1.es6 b/test/specs/es6/module-a/s-a-1.es6 new file mode 100644 index 0000000..fbd3c4d --- /dev/null +++ b/test/specs/es6/module-a/s-a-1.es6 @@ -0,0 +1,4 @@ +import CommonA from 'common/common-a'; +import CommonB from 'common/common-b'; + +export default () => { console.log('module-a/SourceA'); }; diff --git a/test/specs/es6/module-a/s-a-2.es6 b/test/specs/es6/module-a/s-a-2.es6 new file mode 100644 index 0000000..48490d5 --- /dev/null +++ b/test/specs/es6/module-a/s-a-2.es6 @@ -0,0 +1,3 @@ +import CommonB from 'common/common-b'; + +export default () => { console.log('module-a/SourceA'); }; diff --git a/test/specs/es6/module-a/source-a.es6 b/test/specs/es6/module-a/source-a.es6 index b0f343a..5a0d733 100644 --- a/test/specs/es6/module-a/source-a.es6 +++ b/test/specs/es6/module-a/source-a.es6 @@ -1,5 +1,3 @@ // @sqbox({ name: "module-a" }) -import CommonA from 'common/common-a'; -import CommonB from 'common/common-b'; - -export default () => { console.log('module-a/SourceA'); }; +import SourceA1 from 'module-a/s-a-1'; +import SourceA2 from 'module-a/s-a-2'; diff --git a/test/specs/es6/module-b/s-b.es6 b/test/specs/es6/module-b/s-b.es6 new file mode 100644 index 0000000..d1da304 --- /dev/null +++ b/test/specs/es6/module-b/s-b.es6 @@ -0,0 +1,3 @@ +import CommonA from 'common/common-a'; + +export default () => { console.log('module-b/SourceB'); }; diff --git a/test/specs/es6/module-b/source-b.es6 b/test/specs/es6/module-b/source-b.es6 index af41eb8..5d7ffba 100644 --- a/test/specs/es6/module-b/source-b.es6 +++ b/test/specs/es6/module-b/source-b.es6 @@ -1,6 +1,4 @@ /** -* @sqbox({ name: "module-b" }) +* @sqbox({ name: 'module-b' }) **/ -import CommonA from 'common/common-a'; - -export default () => { console.log('module-b/SourceB'); }; +import SourceB from 'module-b/s-b'; diff --git a/test/specs/schema.md b/test/specs/schema.md new file mode 100644 index 0000000..d02408d --- /dev/null +++ b/test/specs/schema.md @@ -0,0 +1,51 @@ +### Complex Scheme of use cases + +@sqbox({ name: 'libs' }) +file: libs + - requires lib-a + - requires lib-b + +---- +result: ['lib-a', 'lib-b'] +---- + +@sqbox({ name: 'common' }) +file: common-a + - requires common-b + +@sqbox({ name: 'common' }) +file: common-b + - requires lib-a + +@sqbox({ name: 'common' }) +file: common-c + - no requires + +---- +result: ['common-a', 'common-b', 'common-c', (a) => 'libs'] +---- + +@sqbox({ name: 'module-b', boxed: ['common'] }) +file: source-b - doesn't need common-b + - requires lib-b + - requires common-a + +---- +result: ['source-b', 'common-a', (a) => 'libs'] +---- + +@sqbox({ name: 'module-a' }) +file: source-a-1 - doesn't need common-c + - requires lib-a + - requires common-a + - requires common-b + +@sqbox({ name: 'module-a', boxed: ['common'] }) +file: source-a-2 -> doesn't need common-a, common-b + - requires lib-b + - requires common-c + - requires source-a-1 + +---- +result: ['source-a-1', 'source-a-2', 'common-a', 'common-b', 'common-c', (a) => 'libs'] +---- From f956c35c83e27ea9d653c666260bcecfc6cf8d53 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Wed, 10 May 2017 08:58:48 -0700 Subject: [PATCH 15/22] bundle: checkpoint - Moving into bundle.types.import.Import for parsing dependencies from es6 import declarations from the AST. Removed bundle.format.standard.Standard. Moving astq query declaration to bundle.format.* formats. --- lib/bundle/format/es6/es6.es6 | 32 ++++++++++++ lib/bundle/format/format.es6 | 14 +++--- lib/bundle/format/standard/standard.es6 | 31 ------------ lib/bundle/task/metadata/bundle.es6 | 9 ++++ lib/bundle/types/annotation/annotation.es6 | 4 +- lib/bundle/types/export/export.es6 | 2 + lib/bundle/types/export/helpers.es6 | 7 +++ lib/bundle/types/import/helpers.es6 | 53 ++++++++++++++++++++ lib/bundle/types/import/import.es6 | 33 ++++++++---- lib/bundle/types/type.es6 | 1 - test/integration/project-a/package.json | 2 +- test/integration/project-b/package.json | 2 +- test/integration/project-c/package.json | 2 +- test/lib/bundle/bundle.spec.es6 | 6 --- test/lib/bundle/format/cjs/template.spec.es6 | 12 ----- test/specs/amd/source-a.js | 0 test/specs/cjs/source-a.js | 0 test/specs/es6/libs.es6 | 5 ++ test/specs/es6/libs/amd-lib.js | 8 +++ test/specs/es6/libs/cjs-lib.js | 6 +++ 20 files changed, 157 insertions(+), 72 deletions(-) delete mode 100644 lib/bundle/format/standard/standard.es6 create mode 100644 lib/bundle/types/export/helpers.es6 create mode 100644 lib/bundle/types/import/helpers.es6 delete mode 100644 test/specs/amd/source-a.js delete mode 100644 test/specs/cjs/source-a.js create mode 100644 test/specs/es6/libs.es6 create mode 100644 test/specs/es6/libs/amd-lib.js create mode 100644 test/specs/es6/libs/cjs-lib.js diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index 6d6216e..bba6403 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -25,6 +25,38 @@ class Es6 extends Format { return this.query(o, expr); } + /** + * ASTQ Import Declaration Query + * @static + * @property importDeclaration + * @type {String} + **/ + static importDeclaration = '// * [@type == "ImportDeclaration"]'; + + /** + * ASTQ Import Identifier Query + * @static + * @property importIdentifier + * @type {String} + **/ + static importIdentifier = '// * [@type == "ImportDefaultSpecifier"]'; + + /** + * ASTQ Export Declaration Query + * @static + * @property exportDeclaration + * @type {String} + **/ + static exportDeclaration = '// * [@type == "ExportDeclaration"]'; + + /** + * ASTQ Export Identifier Query + * @static + * @property exportIdentifier + * @type {String} + **/ + static exportIdentifier = '// * [@type == "ExportDefaultSpecifier"]'; + } export default Es6; diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index d0da204..a943947 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -39,12 +39,11 @@ class Format extends Visitor { /** * Default Result Handler * @private - * @param {Any} out - ast query result - * @return {bundle.format.Format} + * @param {Array} out - ast query result + * @return {Any} **/ - _result(out) { - console.log('Result: ', out); - return this; + _result(out = [], cb) { + return cb(Collection.new(out)); } /** @@ -65,11 +64,12 @@ class Format extends Visitor { * @public * @param {Object} [ast = {}] AST to query * @param {String} [expr = ''] json path query + * @param {Function} [cb = () => {}] * @param {Any} [...args] additional arguments * @return {Any} **/ - query(ctx, ast = {}, expr = '', ...args) { - return this._result(this.astq.query(ast, expr, ...args)); + query(ctx, ast = {}, expr = '', cb = () => {}, ...args) { + return this._result(this.astq.query(ast, expr, ...args), cb); } } diff --git a/lib/bundle/format/standard/standard.es6 b/lib/bundle/format/standard/standard.es6 deleted file mode 100644 index 715ee52..0000000 --- a/lib/bundle/format/standard/standard.es6 +++ /dev/null @@ -1,31 +0,0 @@ -/** -* @module bundle.format.standard -* @author Patricio Ferreira <3dimentionar@gmail.com> -**/ -import _ from 'util/mixins'; -import extend from 'extend'; -import Format from 'bundle/format/format'; -import logger from 'util/logger/logger'; - -/** -* Class Standard -* @extends {bundle.format.Format} -**/ -class Standard extends Format { - - /** - * Standard AST Query - * @public - * @param {util.visitor.Visited} ctx context visited - * @param {Object} o - object to query - * @param {String} expr - json path query - * @param {Any} [...args] additional arguments - * @return {Any} - **/ - standard(ctx, o, expr, ...args) { - return this.query(ctx, o, expr, ...args); - } - -} - -export default Standard; diff --git a/lib/bundle/task/metadata/bundle.es6 b/lib/bundle/task/metadata/bundle.es6 index c282dd6..9332cf7 100644 --- a/lib/bundle/task/metadata/bundle.es6 +++ b/lib/bundle/task/metadata/bundle.es6 @@ -35,6 +35,15 @@ class Bundle extends Visited { return extend(true, this, _.pick(attrs, this.constructor.properties)); } + /** + * Returns a json representation of the instance of this class + * @public + * @return {Object} + **/ + toJSON() { + return { name: this.name, target: this.target.toJSON() }; + } + /** * Compound Property Definition * @static diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index d1a1690..0f05122 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -3,12 +3,10 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; -import _s from 'underscore.string'; -import json5 from 'json5'; import extend from 'extend'; -import Collection from 'util/adt/collection'; import Type from 'bundle/types/type'; import * as Helpers from 'bundle/types/annotation/helpers'; +import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index f23ccc6..37da08d 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -5,6 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Type from 'bundle/types/type'; +import * as Helpers from 'bundle/types/export/helpers'; +import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** diff --git a/lib/bundle/types/export/helpers.es6 b/lib/bundle/types/export/helpers.es6 new file mode 100644 index 0000000..58ed091 --- /dev/null +++ b/lib/bundle/types/export/helpers.es6 @@ -0,0 +1,7 @@ +/** +* @module bundle.types.export +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import _s from 'underscore.string'; +import Collection from 'util/adt/collection'; diff --git a/lib/bundle/types/import/helpers.es6 b/lib/bundle/types/import/helpers.es6 new file mode 100644 index 0000000..221dcae --- /dev/null +++ b/lib/bundle/types/import/helpers.es6 @@ -0,0 +1,53 @@ +/** +* @module bundle.types.import +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import _s from 'underscore.string'; +import Collection from 'util/adt/collection'; + +/** +* Import Declaration Handler +* Captures expressions like: +* - import [Identifier] from [path]; +* - import [{ Identifier, ... }] from [path]; +* - import * as [Identifier] from [path]; +* @public +* @param {bundle.types.import.Import} type import type reference +* @param {util.adt.Collection} imports collection of import declarations +* @return {util.adt.Collection} +**/ +export const onImport = (type, imports) => { + //imports.reduce(_.bind(onImportIdentifier, this, type), Collection.new()); +}; + +/** +* Import Identifier Declaration Handler +* Captures expressions within ImportDeclarations such us: +* - [Identifier] +* - [{ Identifier, ... }] +* - * as [Identifier] +* @public +* @param {Function} query astq query function +* @param {util.adt.Collection} memo memoized collection of identifiers to augment +* @param {astq.Node} declaration current import declaration node reference +* @return {util.adt.Collection} +**/ +export const onImportIdentifier = (type, memo, declaration) => { + console.log(declaration); + //query(declaration, ) +}; + +/** +* Import Identifier Declaration Handler +* Captures expressions within ImportDeclarations such us: +* - [Identifier] +* @public +* @param {Function} query astq query function +* @param {util.adt.Collection} memo memoized collection of identifiers to augment +* @param {astq.Node} declaration current import declaration node reference +* @return {util.adt.Collection} +**/ +export const onImportPath = (query, memo, declaration) => { + +}; diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index 7b48ef9..9209f4e 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -5,6 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Type from 'bundle/types/type'; +import * as Helpers from 'bundle/types/import/helpers'; +import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** @@ -23,21 +25,34 @@ class Import extends Type { * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject, bundles) { + read(resolve, reject, bundles, files) { + this.dependencies(bundles, files); return super.read(resolve, reject); } /** - * Annotation Write strategy + * Read Dependencies * @public - * @override - * @param {Function} resolve asynchronous promise's resolve - * @param {Function} reject asynchronous promise's reject - * @param {bundle.task.Task} task current task - * @return {Promise} + * @param {util.adt.Collection} bundles all bundles captured by annotations + * @param {Array} files complete list of files parsed + * @return {bundle.types.import.Import} + **/ + dependencies(bundles, files) { + bundles.reduce(this.dependency, Collection.new(), this); + return this; + } + + /** + * Read Dependencies on a single file + * @public + * @param {util.adt.Collection} memo memoized collection to augment + * @param {bundle.task.metadata.Metadata} metadata instance of meta found as a bundle + * @return {util.adt.Collection} **/ - write(resolve, reject) { - return super.write(resolve, reject); + dependency(memo, metadata) { + const { target } = metadata.bundle; + //this.query(target.ast, Helpers.importDeclaration, _.bind(Helpers.onImport, Helpers, this)); + return memo; } } diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index 88590b0..d6332e5 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -83,7 +83,6 @@ class Type extends Visited { **/ static visitors = Collection.new(Visited.visitors.toJSON().concat([ 'bundle/format/format', - 'bundle/format/standard/standard', 'bundle/format/es6/es6', 'bundle/format/cjs/cjs', 'bundle/format/amd/amd', diff --git a/test/integration/project-a/package.json b/test/integration/project-a/package.json index e51003a..d6b3033 100644 --- a/test/integration/project-a/package.json +++ b/test/integration/project-a/package.json @@ -2,7 +2,7 @@ "name": "project-a", "version": "1.0.0", "private": true, - "description": "SquareBox & Babel", + "description": "SquareBox & Babel - Integration Test Project", "keywords": [ "squarebox", "babel" diff --git a/test/integration/project-b/package.json b/test/integration/project-b/package.json index 8499597..ed53ec1 100644 --- a/test/integration/project-b/package.json +++ b/test/integration/project-b/package.json @@ -2,7 +2,7 @@ "name": "project-b", "version": "1.0.0", "private": true, - "description": "SquareBox & Typescript", + "description": "SquareBox & Typescript - Integration Test Project", "keywords": [ "squarebox", "typescript" diff --git a/test/integration/project-c/package.json b/test/integration/project-c/package.json index a50cbf6..2fdc667 100644 --- a/test/integration/project-c/package.json +++ b/test/integration/project-c/package.json @@ -2,7 +2,7 @@ "name": "project-c", "version": "1.0.0", "private": true, - "description": "SquareBox & Legacy ES5", + "description": "SquareBox & Legacy ES5 - Integration Test Project", "keywords": [ "squarebox", "legacy" diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index fcde1c4..58c0149 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -11,7 +11,6 @@ describe('bundle.Bundle', function() { scan: './test/specs/es6', extensions: ['.js', '.es6'], exclude: [], - external: ['react'], alias: { common: 'common' }, external: ['jquery'], target: { @@ -56,11 +55,6 @@ describe('bundle.Bundle', function() { this.bundle.run(); }); - xit('Should execute command run over specs/amd', () => {}); - xit('Should execute command run over specs/cjs', () => {}); - xit('Should execute command run over specs/iife', () => {}); - xit('Should execute command run over specs/umd', () => {}); - }); describe('toJSON()', () => { diff --git a/test/lib/bundle/format/cjs/template.spec.es6 b/test/lib/bundle/format/cjs/template.spec.es6 index 0846d78..baae2e5 100644 --- a/test/lib/bundle/format/cjs/template.spec.es6 +++ b/test/lib/bundle/format/cjs/template.spec.es6 @@ -20,16 +20,4 @@ describe.skip('bundle.format.cjs.Template', function() { delete this.input; }); - describe('template()', () => { - - it('Should output amd imports', () => { - //const result = Cjs.imports(this.input); - //console.log(result); - // assert.include(result, `/** <${this.input.name}> **/`); - // assert.include(result, `define(`); - // assert.include(result, `return MyModule;`); - }); - - }); - }); diff --git a/test/specs/amd/source-a.js b/test/specs/amd/source-a.js deleted file mode 100644 index e69de29..0000000 diff --git a/test/specs/cjs/source-a.js b/test/specs/cjs/source-a.js deleted file mode 100644 index e69de29..0000000 diff --git a/test/specs/es6/libs.es6 b/test/specs/es6/libs.es6 new file mode 100644 index 0000000..6ea28a5 --- /dev/null +++ b/test/specs/es6/libs.es6 @@ -0,0 +1,5 @@ +/** +* @sqbox({ name: "libs" }) +**/ +import AmdLib from 'libs/amd-lib'; +import CommonJsLib from 'libs/cjs-lib'; diff --git a/test/specs/es6/libs/amd-lib.js b/test/specs/es6/libs/amd-lib.js new file mode 100644 index 0000000..3276d1c --- /dev/null +++ b/test/specs/es6/libs/amd-lib.js @@ -0,0 +1,8 @@ +/** +* Simulation of a AMD Library +**/ +define(['jquery'], function($) { + + return { type: 'Amd', jquery: $ }; + +}); diff --git a/test/specs/es6/libs/cjs-lib.js b/test/specs/es6/libs/cjs-lib.js new file mode 100644 index 0000000..795c03c --- /dev/null +++ b/test/specs/es6/libs/cjs-lib.js @@ -0,0 +1,6 @@ +/** +* Simulation of a CommonJS Library +**/ +var $ = require('jquery'); + +module.exports = { type: 'CommonJs', jquery: $ }; From 51278c3fa1d8b3ae76108a66da1525d9b93fbe4a Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 15 May 2017 15:22:22 -0700 Subject: [PATCH 16/22] bundle: checkpoint - Progress on CommonJs Import declaration astq queries to pull dependencies. Refactored query, and search (comments) methods. --- README.md | 34 +++++----- lib/bundle/format/amd/amd.es6 | 44 ++++++++++++- lib/bundle/format/cjs/cjs.es6 | 51 ++++++++++++++- lib/bundle/format/es6/es6.es6 | 56 ++++++++++++----- lib/bundle/format/format.es6 | 73 +++++++++++++++------- lib/bundle/types/annotation/annotation.es6 | 12 +++- lib/bundle/types/common/comment.es6 | 27 ++++++++ lib/bundle/types/export/export.es6 | 10 +++ lib/bundle/types/import/helpers.es6 | 4 +- lib/bundle/types/import/import.es6 | 17 ++++- lib/bundle/types/type.es6 | 61 +++++++++++++++++- test/lib/bundle/bundle.spec.es6 | 1 - test/specs/es6/libs.es6 | 4 ++ test/specs/es6/libs/amd-lib.js | 2 +- test/specs/es6/libs/cjs-lib.js | 1 + test/specs/es6/libs/dependencies/lib-a.js | 3 + test/specs/es6/libs/dependencies/lib-b.js | 3 + test/specs/schema.md | 17 +++++ 18 files changed, 353 insertions(+), 67 deletions(-) create mode 100644 lib/bundle/types/common/comment.es6 create mode 100644 test/specs/es6/libs/dependencies/lib-a.js create mode 100644 test/specs/es6/libs/dependencies/lib-b.js diff --git a/README.md b/README.md index c0a3d17..f64d250 100644 --- a/README.md +++ b/README.md @@ -15,24 +15,30 @@ Experimental ES6/CommonJS/AMD Module Bundler ```npm install [-g] squarebox``` -#### Usage +### Usage -CLI Commands +```sqbox [command] [options]``` -``` -global option: -c (sqbox.js|.sqboxrc|URL) | Default: ./.sqboxrc -contextual help - sqbox [command] help +#### Commands -* sqbox help - global help (list of commands and general usage) -* sbox bundle --options -* sbox clean -* sbox graph -``` +* **help** - Global Help +* **bundle help** - Contextual help for command ```bundle``` +* **bundle** - Bundles your project + * Options + * [-c, --config] (sqbox.js, .sqboxrc, or a remote url) | Default: [currentDirectory]/.sqboxrc + * TODO Overrides... +* **clean** - Clean destination folder + * Options + * ...TODO +* **graph** - Generates a graphical report of your current bundling strategy + * Options + * ...TODO -Programmatic API +#### Programmatic API ``` const sqbox = require('squarebox'); +// Or ES6: import sqbox from 'squarebox'; sqbox.clean([opts]) .bundle([config]) .graph([opts]); @@ -41,16 +47,16 @@ sqbox.clean([opts]) #### Official Documentation ``` -TODO +TODO: Official Website / APIDocs ``` -#### Configuration +#### Contribute ``` TODO ``` -#### Contribute +#### License ``` TODO diff --git a/lib/bundle/format/amd/amd.es6 b/lib/bundle/format/amd/amd.es6 index 2205a9b..8491659 100644 --- a/lib/bundle/format/amd/amd.es6 +++ b/lib/bundle/format/amd/amd.es6 @@ -19,10 +19,50 @@ class Amd extends Format { * @param {util.visitor.Visited} ctx context visited * @param {Object} o - object to query * @param {String} expr - json path query + * @param {Any} [args...] additional arguments * @return {Any} **/ - amd(ctx, o, expr) { - return this.query(o, expr); + amd(ctx, o, expr, ...args) { + console.log('amd querying...'); + return this.query(o, expr, ...args); + } + + /** + * AMD AST Query by a given type + * @param {util.visitor.Visited} ctx context visited + * @param {astq.Node} ast ast to query + * @param {bundle.types.Type} type type used to query + * @param {Any} [args...] additional arguments + * @return {Any} + **/ + amdByType(ctx, ast, type, ...args) { + logger('amdByType querying...').out(); + return this.queryByType(ctx, ast, type, ...args); + } + + /** + * ASTQ AMD Import Declaration Query + * @static + * @property QAMD_ImportDeclaration + * @type {String} + **/ + static QAMD_ImportDeclaration = '// *'; + + /** + * ASTQ AMD Import Declaration Query + * @static + * @property QAMD_ExportDeclaration + * @type {String} + **/ + static QAMD_ExportDeclaration = '// *'; + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'AmdVisitor'; } } diff --git a/lib/bundle/format/cjs/cjs.es6 b/lib/bundle/format/cjs/cjs.es6 index 398bdeb..cd35148 100644 --- a/lib/bundle/format/cjs/cjs.es6 +++ b/lib/bundle/format/cjs/cjs.es6 @@ -19,10 +19,57 @@ class CommonJs extends Format { * @param {util.visitor.Visited} ctx context visited * @param {Object} o - object to query * @param {String} expr - json path query + * @param {Any} [args...] additional arguments * @return {Any} **/ - cjs(ctx, o, expr) { - return this.query(o, expr); + cjs(ctx, o, expr, ...args) { + return this.query(o, expr, ...args); + } + + /** + * CommonJs AST Query by a given type + * @param {util.visitor.Visited} ctx context visited + * @param {astq.Node} ast ast to query + * @param {bundle.types.Type} type type used to query + * @param {Any} [args...] additional arguments + * @return {Any} + **/ + cjsByType(ctx, ast, type, ...args) { + return this.queryByType(ctx, ast, type, ...args); + } + + /** + * ASTQ CJS Import Declaration Query + * @static + * @property QCJS_ImportDeclaration + * @type {String} + **/ + static QCJS_ImportDeclaration = ` + /VariableDeclaration [ + in( * [@kind == 'var' || @kind == 'let' || @kind == 'const']) && + /VariableDeclarator /CallExpression /Identifier [@name == 'require'] + ], /ExpressionStatement [ + /AssignmentExpression /CallExpression /Identifier [@name == 'require'] + ], /CallExpression [ + /Identifier [@name == 'require'] + ] + `; + + /** + * ASTQ CJS Import Declaration Query + * @static + * @property QCJS_ExportDeclaration + * @type {String} + **/ + static QCJS_ExportDeclaration = '// Todo'; + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'CjsVisitor'; } } diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index bba6403..9323518 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -17,45 +17,69 @@ class Es6 extends Format { * ES6 AST Query * @public * @param {util.visitor.Visited} ctx context visited - * @param {Object} o - object to query - * @param {String} expr - json path query + * @param {astq.Node} ast ast to query + * @param {String} expr astq expression + * @param {Any} [args...] additional arguments * @return {Any} **/ - es6(ctx, o, expr) { - return this.query(o, expr); + es6(ctx, ast, expr, ...args) { + console.log('es6 querying...'); + return this.query(o, expr, ...args); } /** - * ASTQ Import Declaration Query + * ES6 AST Query by a given type + * @param {util.visitor.Visited} ctx context visited + * @param {astq.Node} ast ast to query + * @param {bundle.types.Type} type type used to query + * @param {Any} [args...] additional arguments + * @return {Any} + **/ + es6ByType(ctx, ast, type, ...args) { + logger('es6ByType querying...').out(); + return this.queryByType(ctx, ast, type, ...args); + } + + /** + * ASTQ ES6 Import Declaration Query * @static - * @property importDeclaration + * @property QES6_ImportDeclaration * @type {String} **/ - static importDeclaration = '// * [@type == "ImportDeclaration"]'; + static QES6_ImportDeclaration = '// * [@type == "ImportDeclaration"]'; /** - * ASTQ Import Identifier Query + * ASTQ ES6 Import Identifier Query * @static - * @property importIdentifier + * @property QES6_ImportIdentifier * @type {String} **/ - static importIdentifier = '// * [@type == "ImportDefaultSpecifier"]'; + static QES6_ImportIdentifier = '// * [@type == "ImportDefaultSpecifier"]'; /** - * ASTQ Export Declaration Query + * ASTQ ES6 Export Declaration Query * @static - * @property exportDeclaration + * @property QES6_ExportDeclaration * @type {String} **/ - static exportDeclaration = '// * [@type == "ExportDeclaration"]'; + static QES6_ExportDeclaration = '// * [@type == "ExportDeclaration"]'; /** - * ASTQ Export Identifier Query + * ASTQ ES6 Export Identifier Query * @static - * @property exportIdentifier + * @property QES6_ExportIdentifier * @type {String} **/ - static exportIdentifier = '// * [@type == "ExportDefaultSpecifier"]'; + static QES6_ExportIdentifier = '// * [@type == "ExportDefaultSpecifier"]'; + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'Es6Visitor'; + } } diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index a943947..8b2e4de 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -3,6 +3,7 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import _s from 'underscore.string'; import extend from 'extend'; import AstQ from 'astq'; // documentation - https://www.npmjs.com/package/astq import Visitor from 'util/visitor/visitor'; @@ -27,50 +28,74 @@ class Format extends Visitor { } /** - * Clean Up + * Resolves Query Type of element to query * @public - * @param {Array} comments list of all comments - * @return {Array} + * @param {bundle.types.Type} type - type to resolve + * @return {String} **/ - _clean(comments) { - return _.map(comments, (comment) => _.trimSpecial(comment.value)); + which(type) { + let format = _s.strLeft(this.name, 'Visitor').toUpperCase(), + prop = `Q${format}_${Format.types[type.name.toLowerCase()]}`; + if(_.defined(this.constructor[prop])) return this.constructor[prop]; + logger(`Type ${type.name} not found.`).fatal(); + } + + /** + * Method wrapper for querying Abstract Syntax Tree (AST) by a given type + * @public + * @param {astq.Node} [ast] astq node to query + * @param {Function} [cb = () => {}] optional callback on result + * @param {bundle.types.Type} type element type + * @param {Any} [...args] additional arguments + * @return {Any} + **/ + queryByType(ctx, ast, cb, type, ...args) { + return this.query(ctx, ast, this.which(type), cb, ...args); + } + + /** + * Method Wrapper for querying Abstract Syntax Tree (AST) + * @public + * @param {Object} [ast = {}] AST to query + * @param {String} [expr = ''] astq expression + * @param {Function} [cb = () => {}] optional callback on result + * @param {Any} [...args] additional arguments + * @return {Any} + **/ + query(ctx, ast = {}, expr = '', cb = () => {}, ...args) { + return this.onQuery(this.astq.query(ast, expr, ...args), cb); } /** * Default Result Handler - * @private + * @public * @param {Array} out - ast query result + * @param {Function} cb - callback on result * @return {Any} **/ - _result(out = [], cb) { + onQuery(out = [], cb) { return cb(Collection.new(out)); } /** - * Perform search of annotations over comments in the source code and retrieves - * the first occurence when predicates pass the evaluation + * Visitor Name * @public - * @param {util.visitor.Visited} ctx context visited - * @param {Array} comments list of all comments - * @param {Function} [predicate = () => false] predicate walker - * @return {Object} + * @type {String} **/ - search(ctx, comments = [], predicate = () => false) { - return _.find(this._clean(comments), predicate); + get name() { + return 'FormatVisitor'; } /** - * Method Wrapper for querying Abstract Syntax Tree (AST) + * Query Types * @public - * @param {Object} [ast = {}] AST to query - * @param {String} [expr = ''] json path query - * @param {Function} [cb = () => {}] - * @param {Any} [...args] additional arguments - * @return {Any} + * @property types + * @type {Object} **/ - query(ctx, ast = {}, expr = '', cb = () => {}, ...args) { - return this._result(this.astq.query(ast, expr, ...args), cb); - } + static types = { + import: 'ImportDeclaration', + export: 'ExportDeclaration' + }; } diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 0f05122..44931c0 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -60,7 +60,7 @@ class Annotation extends Type { * @return {util.adt.Collection} **/ annotation(memo, file) { - let annotation = this.search(file.comments, Helpers.match); + let annotation = this.comments(file.comments, Helpers.match); if(_.defined(annotation)) { let meta = Helpers.extract(annotation); if(Helpers.valid(meta)) memo.add({ name: meta.name, target: file }); @@ -68,6 +68,16 @@ class Annotation extends Type { return memo; } + /** + * Type Name + * @public + * @property name + * @type {String} + **/ + get name() { + return 'Annotation'; + } + } export default Annotation; diff --git a/lib/bundle/types/common/comment.es6 b/lib/bundle/types/common/comment.es6 new file mode 100644 index 0000000..ba76adc --- /dev/null +++ b/lib/bundle/types/common/comment.es6 @@ -0,0 +1,27 @@ +/** +* @module bundle.types.common +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import _s from 'underscore.string'; + +/** +* Iterates over the comments and trims comments characters from the value +* @private +* @param {Array} comments - collection of comments +* @return {Array} +**/ +const clean = (comments) => { + return _.map(comments, (comment) => _.trimSpecial(comment.value)); +}; + +/** +* Perform a look up of comments and filter comments based on a given predicate +* @public +* @param {Array} comments list of all comments +* @param {Function} [predicate = () => false] predicate walker +* @return {Array} +**/ +export const search = (comments = [], predicate = () => false) => { + return _.find(clean(comments), predicate); +}; diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index 37da08d..40512e1 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -42,6 +42,16 @@ class Export extends Type { return super.write(resolve, reject); } + /** + * Type Name + * @public + * @property name + * @type {String} + **/ + get name() { + return 'Export'; + } + } export default Export; diff --git a/lib/bundle/types/import/helpers.es6 b/lib/bundle/types/import/helpers.es6 index 221dcae..ec449ad 100644 --- a/lib/bundle/types/import/helpers.es6 +++ b/lib/bundle/types/import/helpers.es6 @@ -17,7 +17,8 @@ import Collection from 'util/adt/collection'; * @param {util.adt.Collection} imports collection of import declarations * @return {util.adt.Collection} **/ -export const onImport = (type, imports) => { +export const onImport = (imports) => { + //console.log('onImport: ', imports.toJSON()); //imports.reduce(_.bind(onImportIdentifier, this, type), Collection.new()); }; @@ -34,7 +35,6 @@ export const onImport = (type, imports) => { * @return {util.adt.Collection} **/ export const onImportIdentifier = (type, memo, declaration) => { - console.log(declaration); //query(declaration, ) }; diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index 9209f4e..95f55d5 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -26,7 +26,7 @@ class Import extends Type { * @return {Promise} **/ read(resolve, reject, bundles, files) { - this.dependencies(bundles, files); + this.dependencies(bundles); return super.read(resolve, reject); } @@ -37,7 +37,7 @@ class Import extends Type { * @param {Array} files complete list of files parsed * @return {bundle.types.import.Import} **/ - dependencies(bundles, files) { + dependencies(bundles) { bundles.reduce(this.dependency, Collection.new(), this); return this; } @@ -51,10 +51,21 @@ class Import extends Type { **/ dependency(memo, metadata) { const { target } = metadata.bundle; - //this.query(target.ast, Helpers.importDeclaration, _.bind(Helpers.onImport, Helpers, this)); + // FIXME: + this.collectByType(target.ast, this, _.bind(Helpers.onImport, Helpers), ['cjs']); return memo; } + /** + * Type Name + * @public + * @property name + * @type {String} + **/ + get name() { + return 'Import'; + } + } export default Import; diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index d6332e5..d25a491 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -4,6 +4,7 @@ **/ import _ from 'util/mixins'; import extend from 'extend'; +import * as Comments from 'bundle/types/common/comment'; import Collection from 'util/adt/collection'; import Visited from 'util/visitor/visited'; import logger from 'util/logger/logger'; @@ -51,6 +52,47 @@ class Type extends Visited { return this.resolve(reject)(resolve, reject, ...args); } + /** + * Generic Strategy that filters and collects comments gathered from source code + * @public + * @param {Array} [comments = []] collection of comments + * @param {Function} [predicate = () => false] predicate used to filter comments + * @return {Object} + **/ + comments(comments = [], predicate = () => false) { + return Comments.search(comments, predicate); + } + + /** + * Generic Strategy to execute astq queries on a given list of formats. + * Will collect and flatten query results into a single list of astq result objects. + * By default all the formats will be used with the following order: es6, commonjs and amd. + * @public + * @param {astq.Node} ast current source ast to detect + * @param {Array} [formats = Type.formats] list formats + * @param {Any} [...args] additional arguments + * @return {Array} + **/ + collect(ast, formats = Type.formats, ...args) { + return Collection.new(formats).map((format) => this[format](ast, ...args)); + } + + /** + * Generic Strategy to execute astq queries on a given list of formats and type of ast element + * Will collect and flatten query results into a single list of astq result objects. + * By default all the formats will be used with the following order: es6, commonjs and amd. + * @public + * @param {astq.Node} ast current source ast to detect + * @param {bundle.types.Type} type current element type + * @param {Function} [cb] optional callback + * @param {Array} [formats = Type.formats] list formats + * @param {Any} [...args] additional arguments + * @return {Array} + **/ + collectByType(ast, type, cb, formats = Type.formats, ...args) { + return this.collect(ast, _.map(formats, (format) => `${format}ByType`), cb, type, ...args); + } + /** * Default Read strategy * @public @@ -75,6 +117,24 @@ class Type extends Visited { return resolve(this); } + /** + * Type Name + * @public + * @property name + * @type {String} + **/ + get name() { + return 'Type'; + } + + /** + * Default Formats + * @static + * @property formats + * @type {Array} + **/ + static formats = ['es6', 'cjs', 'amd']; + /** * List of Visitors * @static @@ -82,7 +142,6 @@ class Type extends Visited { * @type {util.adt.Collection} **/ static visitors = Collection.new(Visited.visitors.toJSON().concat([ - 'bundle/format/format', 'bundle/format/es6/es6', 'bundle/format/cjs/cjs', 'bundle/format/amd/amd', diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index 58c0149..a98884b 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -51,7 +51,6 @@ describe('bundle.Bundle', function() { describe('run()', () => { it('Should execute command run over specs/es6', () => { - this.bundle.run(); }); diff --git a/test/specs/es6/libs.es6 b/test/specs/es6/libs.es6 index 6ea28a5..1c25b91 100644 --- a/test/specs/es6/libs.es6 +++ b/test/specs/es6/libs.es6 @@ -1,5 +1,9 @@ /** * @sqbox({ name: "libs" }) **/ +let some = require('module-a/s-a-2'); +noidentifier = require('module-a/s-a-1'); +require('someother'); + import AmdLib from 'libs/amd-lib'; import CommonJsLib from 'libs/cjs-lib'; diff --git a/test/specs/es6/libs/amd-lib.js b/test/specs/es6/libs/amd-lib.js index 3276d1c..c27f759 100644 --- a/test/specs/es6/libs/amd-lib.js +++ b/test/specs/es6/libs/amd-lib.js @@ -1,7 +1,7 @@ /** * Simulation of a AMD Library **/ -define(['jquery'], function($) { +define(['jquery', 'dependencies/lib-a'], function($, LibA) { return { type: 'Amd', jquery: $ }; diff --git a/test/specs/es6/libs/cjs-lib.js b/test/specs/es6/libs/cjs-lib.js index 795c03c..6d360d0 100644 --- a/test/specs/es6/libs/cjs-lib.js +++ b/test/specs/es6/libs/cjs-lib.js @@ -2,5 +2,6 @@ * Simulation of a CommonJS Library **/ var $ = require('jquery'); +var LibB = require('dependencies/lib-b'); module.exports = { type: 'CommonJs', jquery: $ }; diff --git a/test/specs/es6/libs/dependencies/lib-a.js b/test/specs/es6/libs/dependencies/lib-a.js new file mode 100644 index 0000000..2fb36b8 --- /dev/null +++ b/test/specs/es6/libs/dependencies/lib-a.js @@ -0,0 +1,3 @@ +define([], function() { + console.log('Library A'); +}); diff --git a/test/specs/es6/libs/dependencies/lib-b.js b/test/specs/es6/libs/dependencies/lib-b.js new file mode 100644 index 0000000..520dddf --- /dev/null +++ b/test/specs/es6/libs/dependencies/lib-b.js @@ -0,0 +1,3 @@ +module.exports = function() { + console.log('Library B'); +}; diff --git a/test/specs/schema.md b/test/specs/schema.md index d02408d..d26602c 100644 --- a/test/specs/schema.md +++ b/test/specs/schema.md @@ -49,3 +49,20 @@ file: source-a-2 -> doesn't need common-a, common-b ---- result: ['source-a-1', 'source-a-2', 'common-a', 'common-b', 'common-c', (a) => 'libs'] ---- + +#### Common Pipelines + +source - compile - optimization + +source (es6 - import/export) -> Es5 -> sqbox + +- use of transformation via babel-plugin-transform-amd +- use of transformation via babel-plugin-transform-cjs + +source (typescript - import/export | referenceId) -> Es5 -> sqbox + +- use transformation from typescript + +source (es5 | require | define) -> sqbox + +- No Transformation From 60680bb36e83119b1c3ea44dc188ee5e4947aa57 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 5 Jun 2017 09:03:28 -0700 Subject: [PATCH 17/22] bundle: checkpoint - Progress refactoring & Import Type query implementations to cover CommonJs and Es6 cases. --- lib/bundle/format/cjs/cjs.es6 | 13 ++- lib/bundle/format/cjs/template.es6 | 16 ++++ lib/bundle/format/es6/es6.es6 | 27 +----- lib/bundle/format/es6/template.es6 | 34 ++++++- lib/bundle/format/format.es6 | 9 +- lib/bundle/types/common/collector.es6 | 92 +++++++++++++++++++ lib/bundle/types/common/comment.es6 | 66 +++++++++---- lib/bundle/types/export/export.es6 | 28 +++++- lib/bundle/types/export/reader.es6 | 16 ++++ lib/bundle/types/export/writer.es6 | 16 ++++ lib/bundle/types/import/import.es6 | 11 ++- .../types/import/{helpers.es6 => reader.es6} | 15 +-- .../{export/helpers.es6 => import/writer.es6} | 3 +- lib/bundle/types/type.es6 | 43 +-------- test/specs/es6/libs.es6 | 4 - 15 files changed, 279 insertions(+), 114 deletions(-) create mode 100644 lib/bundle/types/common/collector.es6 create mode 100644 lib/bundle/types/export/reader.es6 create mode 100644 lib/bundle/types/export/writer.es6 rename lib/bundle/types/import/{helpers.es6 => reader.es6} (77%) rename lib/bundle/types/{export/helpers.es6 => import/writer.es6} (76%) diff --git a/lib/bundle/format/cjs/cjs.es6 b/lib/bundle/format/cjs/cjs.es6 index cd35148..e926b44 100644 --- a/lib/bundle/format/cjs/cjs.es6 +++ b/lib/bundle/format/cjs/cjs.es6 @@ -50,7 +50,7 @@ class CommonJs extends Format { /VariableDeclarator /CallExpression /Identifier [@name == 'require'] ], /ExpressionStatement [ /AssignmentExpression /CallExpression /Identifier [@name == 'require'] - ], /CallExpression [ + ], /ExpressionStatement /CallExpression [ /Identifier [@name == 'require'] ] `; @@ -61,7 +61,16 @@ class CommonJs extends Format { * @property QCJS_ExportDeclaration * @type {String} **/ - static QCJS_ExportDeclaration = '// Todo'; + static QCJS_ExportDeclaration = ` + /ExpressionStatement /AssignmentExpression [ + @operator == '=' && + @left != 'undefined' && + /MemberExpression [ + @property != 'undefined' && + /Identifier [@name == 'exports'] + ] + ] + `; /** * Visitor Name diff --git a/lib/bundle/format/cjs/template.es6 b/lib/bundle/format/cjs/template.es6 index 6d42baa..41a1478 100644 --- a/lib/bundle/format/cjs/template.es6 +++ b/lib/bundle/format/cjs/template.es6 @@ -7,6 +7,22 @@ import _ from 'util/mixins'; import extend from 'extend'; +/** +* CommonJs Import Cases +* --------------------- +* +* 1) [let, var, const] n1 = require(path1); +* 2) n2 = require(path2); +* 3) require(path2); +* +* CommonJs Export Cases +* --------------------- +* +* 1) module.exports = {o1}; +* 2) var m = module; +* m.exports = {o2}; +**/ + /** * Single Import Template * @private diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index 9323518..d4c9854 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -23,7 +23,6 @@ class Es6 extends Format { * @return {Any} **/ es6(ctx, ast, expr, ...args) { - console.log('es6 querying...'); return this.query(o, expr, ...args); } @@ -31,13 +30,11 @@ class Es6 extends Format { * ES6 AST Query by a given type * @param {util.visitor.Visited} ctx context visited * @param {astq.Node} ast ast to query - * @param {bundle.types.Type} type type used to query * @param {Any} [args...] additional arguments * @return {Any} **/ - es6ByType(ctx, ast, type, ...args) { - logger('es6ByType querying...').out(); - return this.queryByType(ctx, ast, type, ...args); + es6ByType(ctx, ast, ...args) { + return this.queryByType(ctx, ast, ...args); } /** @@ -46,15 +43,7 @@ class Es6 extends Format { * @property QES6_ImportDeclaration * @type {String} **/ - static QES6_ImportDeclaration = '// * [@type == "ImportDeclaration"]'; - - /** - * ASTQ ES6 Import Identifier Query - * @static - * @property QES6_ImportIdentifier - * @type {String} - **/ - static QES6_ImportIdentifier = '// * [@type == "ImportDefaultSpecifier"]'; + static QES6_ImportDeclaration = `/ImportDeclaration`; /** * ASTQ ES6 Export Declaration Query @@ -62,15 +51,7 @@ class Es6 extends Format { * @property QES6_ExportDeclaration * @type {String} **/ - static QES6_ExportDeclaration = '// * [@type == "ExportDeclaration"]'; - - /** - * ASTQ ES6 Export Identifier Query - * @static - * @property QES6_ExportIdentifier - * @type {String} - **/ - static QES6_ExportIdentifier = '// * [@type == "ExportDefaultSpecifier"]'; + static QES6_ExportDeclaration = '/ExportDeclaration'; /** * Visitor Name diff --git a/lib/bundle/format/es6/template.es6 b/lib/bundle/format/es6/template.es6 index 7346740..eb470f8 100644 --- a/lib/bundle/format/es6/template.es6 +++ b/lib/bundle/format/es6/template.es6 @@ -7,12 +7,38 @@ import _ from 'util/mixins'; import extend from 'extend'; +/** +* ES6 Import Cases: +* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import +* ---------------------- +* +* - import d{} +* +* ES6 Export Cases: +* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export +* ---------------------- +* let o1 = { name: 'export1' }, +* o2 = { name: 'export2' }, +* o3 = { name: 'export3' }; +* const complex = { complex1: 1 }; +* +* - export { o1 }; +* - export { o2 as a2 }; +* - export let a1 = o1, a3 = o3; +* - export { complex as default }; +* +* - export * from 'libs/amd-lib'; +* - export { i1, i2 } from 'libs/cjs-lib'; +* - export { o1 as i3, o2 as i4 } from 'libs/cjs-lib'; +* +* Extra Cases: +* - export default complex; +* - export default function() {} +* - export default function func() {} +**/ + /** * Single Import Template -* @todo Support for: -* - import {id} from '{dependency}'; -* - import { {id1}, {id2}, {}... } from {dependency}; -* - import * as {id} from {dependency} * @private * @property __sit__ * @type {Function} diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index 8b2e4de..9a77305 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -44,13 +44,11 @@ class Format extends Visitor { * Method wrapper for querying Abstract Syntax Tree (AST) by a given type * @public * @param {astq.Node} [ast] astq node to query - * @param {Function} [cb = () => {}] optional callback on result - * @param {bundle.types.Type} type element type * @param {Any} [...args] additional arguments * @return {Any} **/ - queryByType(ctx, ast, cb, type, ...args) { - return this.query(ctx, ast, this.which(type), cb, ...args); + queryByType(ctx, ast, ...args) { + return this.query(ctx, ast, this.which(ctx), ...args); } /** @@ -74,6 +72,9 @@ class Format extends Visitor { * @return {Any} **/ onQuery(out = [], cb) { + // if(out.length > 0) { + // logger(JSON.stringify(out, null, 1)).warn(); + // } return cb(Collection.new(out)); } diff --git a/lib/bundle/types/common/collector.es6 b/lib/bundle/types/common/collector.es6 new file mode 100644 index 0000000..7a83f40 --- /dev/null +++ b/lib/bundle/types/common/collector.es6 @@ -0,0 +1,92 @@ +/** +* @module bundle.types.common +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import Collection from 'util/adt/collection'; +import Visitor from 'util/visitor/visitor'; + +/** +* Class Collector +* @extends {util.visitor.Visitor} +**/ +class Collector extends Visitor { + + /** + * Iterate over the list of formats and executes astq query based on the format. + * Will collect and flatten query results into a single list of astq result objects. + * @public + * @param {astq.Node} ast current source ast to detect + * @param {Array} methods list of query methods to execute + * @param {Any} [...args] additional arguments + * @return {Array} + **/ + iterate(ast, methods, ...args) { + return Collection.new(methods).map((method) => method(ast, ...args)); + } + + /** + * Generic Strategy to execute astq queries on a given list of formats. + * By default all the formats will be used with the following order: es6, commonjs and amd. + * @public + * @param {bundle.types.Type} ctx type context + * @param {astq.Node} ast current source ast to detect + * @param {Array} [formats = Type.formats] list formats + * @param {Any} [...args] additional arguments + * @return {Array} + **/ + collect(ctx, ast, formats = Type.formats, ...args) { + return this.iterate(ast, this.formatsByName(formats, ctx), ...args); + } + + /** + * Filter Formats by Name + * @public + * @param {Array} [formats = []] list of formats to filter + * @param {bundle.types.Type} ctx type context + * @return {Array} + **/ + formatsByName(formats = [], ctx) { + return _.filter(formats, (format) => { return !_.defined(ctx[format]); }); + } + + /** + * Generic Strategy to execute astq queries on a given list of formats and type of ast element + * By default all the formats will be used with the following order: es6, commonjs and amd. + * @public + * @param {bundle.types.Type} ctx type context + * @param {astq.Node} ast current source ast to detect + * @param {Array} [formats = Type.formats] list formats + * @param {Any} [...args] additional arguments + * @return {Array} + **/ + collectByType(ctx, ast, formats = Type.formats, ...args) { + return this.iterate(ast, this.formatsByType(formats, ctx), ...args); + } + + /** + * Filter Formats by Type + * @public + * @param {Array} [formats = []] list of formats + * @param {bundle.types.Type} ctx type context + * @return {Array} + **/ + formatsByType(formats = [], ctx) { + return _.chain(formats).map((format) => { + let method = `${format}ByType`; + return _.defined(ctx[method]) ? ctx[method] : null; + }).compact().value(); + } + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'CollectorVisitor'; + } + +} + +export default Collector; diff --git a/lib/bundle/types/common/comment.es6 b/lib/bundle/types/common/comment.es6 index ba76adc..79a67a1 100644 --- a/lib/bundle/types/common/comment.es6 +++ b/lib/bundle/types/common/comment.es6 @@ -4,24 +4,56 @@ **/ import _ from 'util/mixins'; import _s from 'underscore.string'; +import Visitor from 'util/visitor/visitor'; /** -* Iterates over the comments and trims comments characters from the value -* @private -* @param {Array} comments - collection of comments -* @return {Array} +* Class Comments +* @extends {util.visitor.Visitor} **/ -const clean = (comments) => { - return _.map(comments, (comment) => _.trimSpecial(comment.value)); -}; +class Comments extends Visitor { -/** -* Perform a look up of comments and filter comments based on a given predicate -* @public -* @param {Array} comments list of all comments -* @param {Function} [predicate = () => false] predicate walker -* @return {Array} -**/ -export const search = (comments = [], predicate = () => false) => { - return _.find(clean(comments), predicate); -}; + /** + * Iterates over the comments and trims comments characters from the value + * @private + * @param {Array} comments - collection of comments + * @return {Array} + **/ + _clean(comments) { + return _.map(comments, (comment) => _.trimSpecial(comment.value)); + } + + /** + * Perform a look up of comments and filter comments based on a given predicate + * @private + * @param {Array} comments list of all comments + * @param {Function} [predicate = () => false] predicate walker + * @return {Array} + **/ + _search(comments = [], predicate = () => false) { + return _.find(this._clean(comments), predicate); + } + + /** + * Generic Strategy that filters and collects comments gathered from source code + * @public + * @param {bundle.types.Type} ctx type context + * @param {Array} [comments = []] collection of comments + * @param {Function} [predicate = () => false] predicate used to filter comments + * @return {Object} + **/ + comments(ctx, comments = [], predicate = () => false) { + return this._search(comments, predicate); + } + + /** + * Visitor Name + * @public + * @type {String} + **/ + get name() { + return 'CommentsVisitor'; + } + +} + +export default Comments; diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index 40512e1..196c5fd 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -5,7 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Type from 'bundle/types/type'; -import * as Helpers from 'bundle/types/export/helpers'; +import * as Reader from 'bundle/types/export/reader'; +import * as Writer from 'bundle/types/export/writer'; import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; @@ -26,9 +27,34 @@ class Export extends Type { * @return {Promise} **/ read(resolve, reject, bundles) { + this.exports(bundles); return super.read(resolve, reject); } + /** + * Read Exports + * @public + * @param {util.adt.Collection} bundles all bundles captured by annotations + * @return {bundle.types.import.Import} + **/ + exports(bundles) { + bundles.reduce(this.export, Collection.new(), this); + return this; + } + + /** + * Read Exports on a single file + * @public + * @param {util.adt.Collection} memo memoized collection to augment + * @param {bundle.task.metadata.Metadata} metadata instance of meta found as a bundle + * @return {util.adt.Collection} + **/ + export(memo, metadata) { + const { target } = metadata.bundle; + //this.collectByType(target.ast, ['es6'], _.bind(Helpers.onExport, Helpers)); + return memo; + } + /** * Annotation Write strategy * @public diff --git a/lib/bundle/types/export/reader.es6 b/lib/bundle/types/export/reader.es6 new file mode 100644 index 0000000..0be6de4 --- /dev/null +++ b/lib/bundle/types/export/reader.es6 @@ -0,0 +1,16 @@ +/** +* Export Reader Helpers +* @module bundle.types.export +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import _s from 'underscore.string'; +import Collection from 'util/adt/collection'; + +/** +* Export Declaration Handler +* @public +* @param {util.adt.Collection} exports collection of export declarations +* @return {util.adt.Collection} +**/ +export const onExport = (exports) => {}; diff --git a/lib/bundle/types/export/writer.es6 b/lib/bundle/types/export/writer.es6 new file mode 100644 index 0000000..0d4a655 --- /dev/null +++ b/lib/bundle/types/export/writer.es6 @@ -0,0 +1,16 @@ +/** +* Export Writer Helpers +* @module bundle.types.export +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import _s from 'underscore.string'; +import Collection from 'util/adt/collection'; + +/** +* Export Declaration Handler +* @public +* @param {util.adt.Collection} exports collection of export declarations +* @return {util.adt.Collection} +**/ +export const onExport = (exports) => {}; diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index 95f55d5..d6669f4 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -5,7 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Type from 'bundle/types/type'; -import * as Helpers from 'bundle/types/import/helpers'; +import * as Reader from 'bundle/types/import/reader'; +import * as Writer from 'bundle/types/import/writer'; import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; @@ -31,10 +32,9 @@ class Import extends Type { } /** - * Read Dependencies + * Read Imports * @public * @param {util.adt.Collection} bundles all bundles captured by annotations - * @param {Array} files complete list of files parsed * @return {bundle.types.import.Import} **/ dependencies(bundles) { @@ -51,8 +51,9 @@ class Import extends Type { **/ dependency(memo, metadata) { const { target } = metadata.bundle; - // FIXME: - this.collectByType(target.ast, this, _.bind(Helpers.onImport, Helpers), ['cjs']); + // if(target.source.indexOf('libs.es6') !== -1) { + this.collectByType(target.ast, ['es6'], function() {}); + // } return memo; } diff --git a/lib/bundle/types/import/helpers.es6 b/lib/bundle/types/import/reader.es6 similarity index 77% rename from lib/bundle/types/import/helpers.es6 rename to lib/bundle/types/import/reader.es6 index ec449ad..4b7fcb7 100644 --- a/lib/bundle/types/import/helpers.es6 +++ b/lib/bundle/types/import/reader.es6 @@ -1,4 +1,5 @@ /** +* Import Reader Helpers * @module bundle.types.import * @author Patricio Ferreira <3dimentionar@gmail.com> **/ @@ -8,10 +9,6 @@ import Collection from 'util/adt/collection'; /** * Import Declaration Handler -* Captures expressions like: -* - import [Identifier] from [path]; -* - import [{ Identifier, ... }] from [path]; -* - import * as [Identifier] from [path]; * @public * @param {bundle.types.import.Import} type import type reference * @param {util.adt.Collection} imports collection of import declarations @@ -24,10 +21,6 @@ export const onImport = (imports) => { /** * Import Identifier Declaration Handler -* Captures expressions within ImportDeclarations such us: -* - [Identifier] -* - [{ Identifier, ... }] -* - * as [Identifier] * @public * @param {Function} query astq query function * @param {util.adt.Collection} memo memoized collection of identifiers to augment @@ -35,13 +28,11 @@ export const onImport = (imports) => { * @return {util.adt.Collection} **/ export const onImportIdentifier = (type, memo, declaration) => { - //query(declaration, ) + // TODO }; /** * Import Identifier Declaration Handler -* Captures expressions within ImportDeclarations such us: -* - [Identifier] * @public * @param {Function} query astq query function * @param {util.adt.Collection} memo memoized collection of identifiers to augment @@ -49,5 +40,5 @@ export const onImportIdentifier = (type, memo, declaration) => { * @return {util.adt.Collection} **/ export const onImportPath = (query, memo, declaration) => { - + // TODO }; diff --git a/lib/bundle/types/export/helpers.es6 b/lib/bundle/types/import/writer.es6 similarity index 76% rename from lib/bundle/types/export/helpers.es6 rename to lib/bundle/types/import/writer.es6 index 58ed091..895ca6e 100644 --- a/lib/bundle/types/export/helpers.es6 +++ b/lib/bundle/types/import/writer.es6 @@ -1,5 +1,6 @@ /** -* @module bundle.types.export +* Import Writer Helpers +* @module bundle.types.import * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index d25a491..ec898e7 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -52,47 +52,6 @@ class Type extends Visited { return this.resolve(reject)(resolve, reject, ...args); } - /** - * Generic Strategy that filters and collects comments gathered from source code - * @public - * @param {Array} [comments = []] collection of comments - * @param {Function} [predicate = () => false] predicate used to filter comments - * @return {Object} - **/ - comments(comments = [], predicate = () => false) { - return Comments.search(comments, predicate); - } - - /** - * Generic Strategy to execute astq queries on a given list of formats. - * Will collect and flatten query results into a single list of astq result objects. - * By default all the formats will be used with the following order: es6, commonjs and amd. - * @public - * @param {astq.Node} ast current source ast to detect - * @param {Array} [formats = Type.formats] list formats - * @param {Any} [...args] additional arguments - * @return {Array} - **/ - collect(ast, formats = Type.formats, ...args) { - return Collection.new(formats).map((format) => this[format](ast, ...args)); - } - - /** - * Generic Strategy to execute astq queries on a given list of formats and type of ast element - * Will collect and flatten query results into a single list of astq result objects. - * By default all the formats will be used with the following order: es6, commonjs and amd. - * @public - * @param {astq.Node} ast current source ast to detect - * @param {bundle.types.Type} type current element type - * @param {Function} [cb] optional callback - * @param {Array} [formats = Type.formats] list formats - * @param {Any} [...args] additional arguments - * @return {Array} - **/ - collectByType(ast, type, cb, formats = Type.formats, ...args) { - return this.collect(ast, _.map(formats, (format) => `${format}ByType`), cb, type, ...args); - } - /** * Default Read strategy * @public @@ -142,6 +101,8 @@ class Type extends Visited { * @type {util.adt.Collection} **/ static visitors = Collection.new(Visited.visitors.toJSON().concat([ + 'bundle/types/common/collector', + 'bundle/types/common/comment', 'bundle/format/es6/es6', 'bundle/format/cjs/cjs', 'bundle/format/amd/amd', diff --git a/test/specs/es6/libs.es6 b/test/specs/es6/libs.es6 index 1c25b91..6ea28a5 100644 --- a/test/specs/es6/libs.es6 +++ b/test/specs/es6/libs.es6 @@ -1,9 +1,5 @@ /** * @sqbox({ name: "libs" }) **/ -let some = require('module-a/s-a-2'); -noidentifier = require('module-a/s-a-1'); -require('someother'); - import AmdLib from 'libs/amd-lib'; import CommonJsLib from 'libs/cjs-lib'; From dc8e75e51ec5c221431edb3437539fd790483c14 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 12 Jun 2017 14:06:36 -0700 Subject: [PATCH 18/22] bundle: checkpoint - Refactored and progress on import queries and ast parsing. --- lib/bundle/format/amd/amd.es6 | 101 ++++++++++-- lib/bundle/format/cjs/cjs.es6 | 98 +++++++++++- lib/bundle/format/es6/es6.es6 | 110 ++++++++++++- lib/bundle/format/format.es6 | 147 +++++++++++++++--- .../metadata/{bundle.es6 => dependency.es6} | 41 ++--- lib/bundle/task/metadata/file.es6 | 50 ------ lib/bundle/task/metadata/metadata.es6 | 53 ++++--- lib/bundle/task/reader/reader.es6 | 35 +++-- lib/bundle/task/task.es6 | 10 ++ lib/bundle/task/writer/writer.es6 | 13 +- lib/bundle/types/annotation/annotation.es6 | 42 ++--- lib/bundle/types/annotation/helpers.es6 | 30 +++- lib/bundle/types/common/collector.es6 | 43 +++-- lib/bundle/types/export/export.es6 | 27 ++-- lib/bundle/types/import/import.es6 | 123 ++++++++++++--- lib/bundle/types/import/reader.es6 | 68 +++++--- lib/bundle/types/type.es6 | 51 +++--- lib/util/mixins.es6 | 12 ++ package.json | 2 +- test/specs/es6/libs.es6 | 6 +- 20 files changed, 785 insertions(+), 277 deletions(-) rename lib/bundle/task/metadata/{bundle.es6 => dependency.es6} (63%) delete mode 100644 lib/bundle/task/metadata/file.es6 diff --git a/lib/bundle/format/amd/amd.es6 b/lib/bundle/format/amd/amd.es6 index 8491659..5889bdf 100644 --- a/lib/bundle/format/amd/amd.es6 +++ b/lib/bundle/format/amd/amd.es6 @@ -17,27 +17,88 @@ class Amd extends Format { * AMD AST Query * @public * @param {util.visitor.Visited} ctx context visited - * @param {Object} o - object to query - * @param {String} expr - json path query * @param {Any} [args...] additional arguments * @return {Any} **/ - amd(ctx, o, expr, ...args) { - console.log('amd querying...'); - return this.query(o, expr, ...args); + amd(ctx, ...args) { + return this.query(ctx, ...args); } /** * AMD AST Query by a given type * @param {util.visitor.Visited} ctx context visited * @param {astq.Node} ast ast to query - * @param {bundle.types.Type} type type used to query * @param {Any} [args...] additional arguments * @return {Any} **/ - amdByType(ctx, ast, type, ...args) { - logger('amdByType querying...').out(); - return this.queryByType(ctx, ast, type, ...args); + amdByType(ctx, ast, ...args) { + return this.queryByType(ctx, ast, ...args); + } + + /** + * AMD AST QUery by a given element + * @param {util.visitor.Visited} ctx context visited + * @param {astq.Node} ast ast to query + * @param {Any} [args...] additional arguments + * @return {Any} + **/ + amdByElement(ctx, ast, ...args) { + return this.queryByElement(ctx, ast, ...args); + } + + /** + * AMD Resolves Import Specifier for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency new dependency to add + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + amdResolveImportSpecifier(ctx, dependency) { + if(!_.defined(dependency.import)) dependency.import = {}; + // TODO + return extend(false, dependency.import, { id: '' }); + } + + /** + * AMD Resolves Import Path for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency new dependency to add + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + amdResolveImportPath(ctx, dependency) { + if(!_.defined(dependency.import)) dependency.import = {}; + // TODO + return extend(false, dependency.import, { path: '' }); + } + + /** + * AMD Resolves Parent for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @return {Object} + **/ + amdResolveParent(ctx, dependency, metadata) { + return super.resolveParent(dependency, metadata); + } + + /** + * AMD Resolves AST Injection for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} ast ast root node for current dependency + * @return {Object} + **/ + amdResolveAst(ctx, dependency, metadata, ast) { + return super.resolveAst(dependency, ast); } /** @@ -46,15 +107,31 @@ class Amd extends Format { * @property QAMD_ImportDeclaration * @type {String} **/ - static QAMD_ImportDeclaration = '// *'; + static QAMD_ImportDeclaration = '/TODO'; /** - * ASTQ AMD Import Declaration Query + * ASTQ AMD Import Specifier Query + * @static + * @property QAMD_ImportSpecifier + * @type {String} + **/ + static QAMD_ImportSpecifier = `/TODO`; + + /** + * ASTQ AMD Export Declaration Query * @static * @property QAMD_ExportDeclaration * @type {String} **/ - static QAMD_ExportDeclaration = '// *'; + static QAMD_ExportDeclaration = '/TODO'; + + /** + * ASTQ AMD Export Specifier Query + * @static + * @property QAMD_ExportSpecifier + * @type {String} + **/ + static QAMD_ExportSpecifier = `/TODO`; /** * Visitor Name diff --git a/lib/bundle/format/cjs/cjs.es6 b/lib/bundle/format/cjs/cjs.es6 index e926b44..20157ac 100644 --- a/lib/bundle/format/cjs/cjs.es6 +++ b/lib/bundle/format/cjs/cjs.es6 @@ -17,25 +17,89 @@ class CommonJs extends Format { * CommonJs AST Query * @public * @param {util.visitor.Visited} ctx context visited - * @param {Object} o - object to query - * @param {String} expr - json path query * @param {Any} [args...] additional arguments * @return {Any} **/ - cjs(ctx, o, expr, ...args) { - return this.query(o, expr, ...args); + cjs(ctx, ...args) { + return this.query(ctx, ...args); } /** * CommonJs AST Query by a given type * @param {util.visitor.Visited} ctx context visited * @param {astq.Node} ast ast to query - * @param {bundle.types.Type} type type used to query * @param {Any} [args...] additional arguments * @return {Any} **/ - cjsByType(ctx, ast, type, ...args) { - return this.queryByType(ctx, ast, type, ...args); + cjsByType(ctx, ast, ...args) { + return this.queryByType(ctx, ast, ...args); + } + + /** + * CommonJs AST QUery by a given element + * @param {util.visitor.Visited} ctx context visited + * @param {astq.Node} ast ast to query + * @param {Any} [args...] additional arguments + * @return {Any} + **/ + cjsByElement(ctx, ast, ...args) { + return this.queryByElement(ctx, ast, ...args); + } + + /** + * CommonJs Resolves Import Specifier for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency new dependency to add + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + cjsResolveImportSpecifier(ctx, dependency) { + if(!_.defined(dependency.import)) dependency.import = {}; + // TODO + return extend(false, dependency.import, { id: '' }); + } + + /** + * CommonJs Resolves Import Path for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency new dependency to add + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + cjsResolveImportPath(ctx, dependency) { + if(!_.defined(dependency.import)) dependency.import = {}; + // TODO + return extend(false, dependency.import, { path: '' }); + } + + /** + * CommonJs Resolves Parent for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + cjsResolveParent(ctx, dependency, metadata) { + return super.resolveParent(dependency, metadata); + } + + /** + * CommonJs Resolves AST Injection for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} ast ast root node for current dependency + * @return {Object} + **/ + cjsResolveAst(ctx, dependency, metadata, ast) { + return super.resolveAst(dependency, ast); } /** @@ -55,6 +119,16 @@ class CommonJs extends Format { ] `; + /** + * ASTQ CJS Import Specifier Query + * @static + * @property QCJS_ImportSpecifier + * @type {String} + **/ + static QCJS_ImportSpecifier = ` + /TODO + `; + /** * ASTQ CJS Import Declaration Query * @static @@ -72,6 +146,16 @@ class CommonJs extends Format { ] `; + /** + * ASTQ CJS Export Specifier Query + * @static + * @property QCJS_ExportSpecifierDeclaration + * @type {String} + **/ + static QCJS_ExportSpecifier = ` + /TODO + `; + /** * Visitor Name * @public diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index d4c9854..30e2c25 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -5,6 +5,7 @@ import _ from 'util/mixins'; import extend from 'extend'; import Format from 'bundle/format/format'; +import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** @@ -17,13 +18,11 @@ class Es6 extends Format { * ES6 AST Query * @public * @param {util.visitor.Visited} ctx context visited - * @param {astq.Node} ast ast to query - * @param {String} expr astq expression * @param {Any} [args...] additional arguments * @return {Any} **/ - es6(ctx, ast, expr, ...args) { - return this.query(o, expr, ...args); + es6(ctx, ...args) { + return this.query(ctx, ...args); } /** @@ -37,6 +36,74 @@ class Es6 extends Format { return this.queryByType(ctx, ast, ...args); } + /** + * ES6 AST QUery by a given element + * @param {util.visitor.Visited} ctx context visited + * @param {astq.Node} ast ast to query + * @param {Any} [args...] additional arguments + * @return {Any} + **/ + es6ByElement(ctx, ast, ...args) { + return this.queryByElement(ctx, ast, ...args); + } + + /** + * ES6 Resolves Import Specifier for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency new dependency to add + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference + * @return {Object} + **/ + es6ResolveImportSpecifier(ctx, dependency, metadata, node) { + if(!_.defined(dependency.import)) dependency.import = { modules: Collection.new() }; + this.addModule(dependency.import.modules, node, node.local, node.imported); + return dependency; + } + + /** + * ES6 Resolves Import Path for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency new dependency to add + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + es6ResolveImportPath(ctx, dependency, metadata, node, ast) { + dependency.import.path = ast.source.value; + return dependency; + } + + /** + * ES6 Resolves Parent for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @return {Object} + **/ + es6ResolveParent(ctx, dependency, metadata) { + return super.resolveParent(dependency, metadata); + } + + /** + * ES6 Resolves AST Injection for a given dependency and returns it + * @public + * @param {util.visitor.Visited} ctx context visited + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference + * @return {Object} + **/ + es6ResolveAst(ctx, dependency, metadata) { + return super.resolveAst(dependency, ''); + } + /** * ASTQ ES6 Import Declaration Query * @static @@ -45,13 +112,46 @@ class Es6 extends Format { **/ static QES6_ImportDeclaration = `/ImportDeclaration`; + /** + * ASTQ ES6 Import Specifier Query + * @static + * @property QES6_ImportSpecifierDeclaration + * @type {String} + **/ + static QES6_ImportSpecifier = ` + /Literal, + /ImportDefaultSpecifier, + /ImportSpecifier, + /ImportNamespaceSpecifier + `; + + // /ImportDefaultSpecifier /Identifier, + // /ImportSpecifier [/:imported Identifier || /:local Identifier], + // /ImportNamespaceSpecifier /Identifier + + /** + * ASTQ ES6 Import Path Query + * @static + * @property QES6_ImportPath + * @type {String} + **/ + static QES6_ImportPath = `/Literal`; + /** * ASTQ ES6 Export Declaration Query * @static * @property QES6_ExportDeclaration * @type {String} **/ - static QES6_ExportDeclaration = '/ExportDeclaration'; + static QES6_ExportDeclaration = `/ExportDeclaration`; + + /** + * ASTQ ES6 Export Specifier Query + * @static + * @property QES6_ExportSpecifierDeclaration + * @type {String} + **/ + static QES6_ExportSpecifier = `/ExportDefaultSpecifier`; /** * Visitor Name diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index 9a77305..2c3c4da 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -30,14 +30,28 @@ class Format extends Visitor { /** * Resolves Query Type of element to query * @public - * @param {bundle.types.Type} type - type to resolve + * @param {String} format the format + * @param {String} type element type * @return {String} **/ - which(type) { - let format = _s.strLeft(this.name, 'Visitor').toUpperCase(), - prop = `Q${format}_${Format.types[type.name.toLowerCase()]}`; + which(format, type) { + const prop = `Q${format}_${type}`; if(_.defined(this.constructor[prop])) return this.constructor[prop]; - logger(`Type ${type.name} not found.`).fatal(); + logger(`Format ${format} -> Type ${type} not found.`).fatal(); + } + + /** + * Method Wrapper for querying Abstract Syntax Tree (AST) + * @public + * @param {Object} [ast = {}] AST to query + * @param {String} [expr = ''] astq expression + * @param {Function} [cb] optional callback on result + * @param {Any} [...args] additional arguments + * @return {Any} + **/ + query(ctx, ast = {}, expr = '', cb, ...args) { + if(!_.defined(cb)) cb = () => {}; + return this.onQuery(this.astq.query(ast, expr, ...args), cb); } /** @@ -48,34 +62,118 @@ class Format extends Visitor { * @return {Any} **/ queryByType(ctx, ast, ...args) { - return this.query(ctx, ast, this.which(ctx), ...args); + return this.query(ctx, ast, this.which(this.getName(), Format.types[ctx.getName()]), ...args); } /** - * Method Wrapper for querying Abstract Syntax Tree (AST) + * Method wrapper for querying Abstract Syntax Tree (AST) by a given element * @public - * @param {Object} [ast = {}] AST to query - * @param {String} [expr = ''] astq expression - * @param {Function} [cb = () => {}] optional callback on result + * @param {astq.Node} [ast] astq node to query + * @param {String} element element name to query * @param {Any} [...args] additional arguments * @return {Any} **/ - query(ctx, ast = {}, expr = '', cb = () => {}, ...args) { - return this.onQuery(this.astq.query(ast, expr, ...args), cb); + queryByElement(ctx, ast, element, ...args) { + return this.query(ctx, ast, this.which(this.getName(), Format.elements[element]), ...args); } /** - * Default Result Handler + * Default Query Result Handler * @public * @param {Array} out - ast query result * @param {Function} cb - callback on result * @return {Any} **/ onQuery(out = [], cb) { - // if(out.length > 0) { - // logger(JSON.stringify(out, null, 1)).warn(); - // } - return cb(Collection.new(out)); + return this.onResult(out, cb); + } + + /** + * Default Result Handler + * @public + * @param {Array} results ast query result list + * @param {Function} callback callback + * @return {Any} + **/ + onResult(results, callback) { + callback(Collection.new(results), this.getName()); + return results; + } + + /** + * Retrieves Module for a given dependency modules list. Returns null if module was not found + * @public + * @param {util.adt.Collection} modules current list of dependency modules to add into + * @param {String} id module id + * @return {Object} + **/ + getModule(modules, id) { + if(!_.defined(id)) return true; + return modules.findWhere({ id: id }); + } + + /** + * Add Module to a given dependency if it doesn't exists based on module id + * @public + * @param {util.adt.Collection} modules current list of dependency modules to add into + * @param {astq.Node} node module literal value path + * @param {String} id module id + * @param {String} [alias] module alias + * @return {util.adt.Collection} + **/ + addModule(modules, node, local, imported) { + let _module = {}; + // Continue here... + if(local && !imported) { + _module = { id: local.name }; + } else if(local && imported) { + _module = { id: local.name, alias: imported.name }; + } + return !this.getModule(modules, _module.id) ? modules.add(_module) : modules; + } + + /** + * Removes Module from a given depependency if it exists, based on module id + * @public + * @param {util.adt.Collection} modules current list of dependency modules to remove from + * @param {String} id module id + * @param {String} [alias] module alias + * @return {util.adt.Collection} + **/ + removeModule(dependency, id, alias) { + return modules; + } + + /** + * Generic AST Injection for a given dependency and returns it + * @public + * @param {Object} dependency dependency to inject ast + * @param {astq.Node} ast ast root node for current dependency + * @return {Object} + **/ + resolveAst(dependency, ast) { + return extend(false, dependency, { input: ast }); + } + + /** + * Generic Parent Resolution for a given dependency and returns it + * @public + * @param {Object} dependency dependency to resolve parent + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @return {Object} + **/ + resolveParent(dependency, metadata) { + let name = metadata.getName(); + return extend(false, dependency, { parent: _.defined(name) ? name : _.uuid() }); + } + + /** + * Retrieve Format Name + * @public + * @return {String} + **/ + getName() { + return _s.strLeftBack(this.name, 'Visitor').toUpperCase(); } /** @@ -88,7 +186,7 @@ class Format extends Visitor { } /** - * Query Types + * Query types * @public * @property types * @type {Object} @@ -98,6 +196,19 @@ class Format extends Visitor { export: 'ExportDeclaration' }; + /** + * Query elements + * @public + * @property elements + * @type {Object} + **/ + static elements = { + ImportSpecifier: 'ImportSpecifier', + ImportId: 'ImportId', + ImportPath: 'ImportPath', + ExportSpecifier: 'ExportSpecifier', + }; + } export default Format; diff --git a/lib/bundle/task/metadata/bundle.es6 b/lib/bundle/task/metadata/dependency.es6 similarity index 63% rename from lib/bundle/task/metadata/bundle.es6 rename to lib/bundle/task/metadata/dependency.es6 index 9332cf7..5f3a96b 100644 --- a/lib/bundle/task/metadata/bundle.es6 +++ b/lib/bundle/task/metadata/dependency.es6 @@ -5,22 +5,21 @@ import _ from 'util/mixins'; import extend from 'extend'; import Visited from 'util/visitor/visited'; -import File from 'bundle/task/metadata/file'; /** -* Class Bundle +* Class Dependency * @extends {util.visitor.Visited} **/ -class Bundle extends Visited { +class Dependency extends Visited { /** * Constructor * @public * @param {Any} [...args] constructor arguments - * @return {bundle.task.metadata.Bundle} + * @return {bundle.task.metadata.Dependency} **/ constructor(...args) { - super({ name: null, target: File.new() }); + super(_.object(Dependency.properties, [_.uuid()])); return this.registerAll().parse(...args); } @@ -28,32 +27,29 @@ class Bundle extends Visited { * Parse Strategy * @public * @param {Object} [attrs = {}] attributes to parse - * @return {bundle.task.metadata.Bundle} + * @return {bundle.task.metadata.Dependency} **/ parse(attrs = {}) { - this.target.parse(attrs.target); return extend(true, this, _.pick(attrs, this.constructor.properties)); } /** * Returns a json representation of the instance of this class * @public + * @override * @return {Object} **/ toJSON() { - return { name: this.name, target: this.target.toJSON() }; + return { + id: this.id, + parent: this.parent, + import: this.import, + export: this.export, + input: this.input, + output: this.output + }; } - /** - * Compound Property Definition - * @static - * @property compound - * @type {Array} - **/ - static compound = [ - 'target' - ]; - /** * Property Definition * @static @@ -61,9 +57,14 @@ class Bundle extends Visited { * @type {Array} **/ static properties = [ - 'name' + 'id', + 'parent', + 'import', + 'export', + 'input', + 'output' ]; } -export default Bundle; +export default Dependency; diff --git a/lib/bundle/task/metadata/file.es6 b/lib/bundle/task/metadata/file.es6 deleted file mode 100644 index c9ec644..0000000 --- a/lib/bundle/task/metadata/file.es6 +++ /dev/null @@ -1,50 +0,0 @@ -/** -* @module bundle.task.metadata -* @author Patricio Ferreira <3dimentionar@gmail.com> -**/ -import _ from 'util/mixins'; -import extend from 'extend'; -import Visited from 'util/visitor/visited'; - -/** -* Class File -* @extends {util.visitor.Visited} -**/ -class File extends Visited { - - /** - * Constructor - * @public - * @param {Any} [...args] constructor arguments - * @return {bundle.task.metadata.File} - **/ - constructor(...args) { - super(_.object(File.properties, [])); - return this.registerAll().parse(...args); - } - - /** - * Parse Strategy - * @public - * @param {Object} [attrs = {}] attributes to parse - * @return {bundle.task.metadata.File} - **/ - parse(attrs = {}) { - return extend(true, this, _.pick(attrs, this.constructor.properties)); - } - - /** - * Property Definition - * @static - * @property properties - * @type {Array} - **/ - static properties = [ - 'source', - 'ast', - 'comments' - ]; - -} - -export default File; diff --git a/lib/bundle/task/metadata/metadata.es6 b/lib/bundle/task/metadata/metadata.es6 index 91ae2c4..8598a4d 100644 --- a/lib/bundle/task/metadata/metadata.es6 +++ b/lib/bundle/task/metadata/metadata.es6 @@ -6,8 +6,7 @@ import _ from 'util/mixins'; import extend from 'extend'; import Collection from 'util/adt/collection'; import Visited from 'util/visitor/visited'; -import Bundle from 'bundle/task/metadata/bundle'; -import File from 'bundle/task/metadata/file'; +import Dependency from 'bundle/task/metadata/dependency'; /** * Class Metadata @@ -20,13 +19,25 @@ import File from 'bundle/task/metadata/file'; * Here the general structure specs: * * @example -* [Metadata] => { -* bundle: {uniqueName}, -* files: [{ -* source: {path}, -* ast: {object}, -* comments: [] -* }, ...] +* [bundle.task.metadata.Metadata] => { +* path: {}, - File path where the annotation was found +* params: {annotationCapturedParams}, - required name: {unique bundle name} +* input: {ast}, +* dependents: [{ [bundle.task.metadata.Dependency] - 1-dimensional array - no nesting +* id: {uuid}, +* parent: {id|bundleName}, - if top level. +* import: { path: {}, modules: [{ id: {string}, alias: {string} }] }, +* export: [{ id: {object} }, ...], +* input: {ast}, +* output: {ast} +* }, { +* id: {uuid}, +* parent: {id}, +* import: { path: {}, modules: [{ id: {string}, alias: {string} }] }, +* export: [{ id: {object} }, ...], +* input: {ast}, +* output: {ast} +* }] * } **/ class Metadata extends Visited { @@ -38,7 +49,7 @@ class Metadata extends Visited { * @return {bundle.task.metadata.Metadata} **/ constructor(...args) { - super({ bundle: Bundle.new(), files: Collection.new([], { interface: File }) }); + super({ dependencies: Collection.new([], { interface: Dependency }) }); return this.registerAll().parse(...args); } @@ -49,20 +60,17 @@ class Metadata extends Visited { * @return {bundle.task.metadata.Metadata} **/ parse(attrs = {}) { - this.bundle.parse(attrs.bundle); - this.files.set(attrs.files); + this.dependencies.set(attrs.dependencies); return extend(true, this, _.pick(attrs, this.constructor.properties)); } /** - * Returns a json representation of the instance of this class + * Retrieves bundle name * @public - * @override - * @param {visitors.formatter.Json} [ctx] - context reference - * @return {Object} + * @return {String} **/ - toJSON(ctx) { - return { bundle: this.bundle.toJSON(), files: this.files.toJSON() }; + getName() { + return _.defined(this.params) && _.defined(this.params.name) ? this.params.name : null; } /** @@ -71,7 +79,11 @@ class Metadata extends Visited { * @property properties * @type {Array} **/ - static properties = []; + static properties = [ + 'path', + 'input', + 'params' + ]; /** * Compound Property Definition @@ -80,8 +92,7 @@ class Metadata extends Visited { * @type {Array} **/ static compound = [ - 'bundle', - 'files' + 'dependencies' ]; } diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index 7156a6a..7befa25 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -29,28 +29,30 @@ class Reader extends Task { * @return {Promise} **/ read(vi) { - return this.types.pop({}, false, this.bundles, this.getFiles()); + return this.types.pop({}, false, this); } /** * Retrieves files metadata * @public - * @return {Array} + * @return {util.adt.Collection} **/ - getFiles() { - return this.files().reduce(this.get, [], this); + files() { + if(_.defined(this.parsedFiles)) return this.parsedFiles; + extend(false, this, { parsedFiles: this.readFiles().reduce(this.get, Collection.new(), this) }); + return this.parsedFiles; } /** * File Parsing Strategy * @public - * @param {Array} memo memoized array that will hold metadata found - * @param {String} source file path to parse + * @param {util.adt.Collection} memo memoized collection that will hold metadata found + * @param {String} path file path to parse * @return {bundle.task.reader.Reader} **/ - get(memo, source) { - let comments = [], ast = this.parse(source, extend(false, { onComment: comments }, Reader.acornOptions)); - memo.push({ source, ast, comments }); + get(memo, path) { + let comments = [], input = this.parse(path, extend(false, { onComment: comments }, Reader.acornOptions)); + memo.add({ path, input, comments }); return memo; } @@ -65,11 +67,11 @@ class Reader extends Task { } /** - * Retrieves Files + * Read Project files * @public * @return {util.adt.Collection} **/ - files() { + readFiles() { return Collection.new(glob.sync(this.sources(), _.defaults({ cwd: this.cwd, ignore: this.excludes() @@ -88,13 +90,22 @@ class Reader extends Task { return this; } + /** + * Retrieves Task Name + * @public + * @return {String} + **/ + getName() { + return 'read'; + } + /** * Visitor Name * @public * @type {String} **/ get name() { - return 'ReaderVisitor'; + return 'Reader'; } /** diff --git a/lib/bundle/task/task.es6 b/lib/bundle/task/task.es6 index d856d89..fc35cd9 100644 --- a/lib/bundle/task/task.es6 +++ b/lib/bundle/task/task.es6 @@ -3,6 +3,7 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import _s from 'underscore.string'; import extend from 'extend'; import Collection from 'util/adt/collection'; import StackAsync from 'util/adt/stack-async'; @@ -70,6 +71,15 @@ class Task extends Visitor { return this; } + /** + * Retrieves Task Name + * @public + * @return {String} + **/ + getName() { + return 'task'; + } + /** * Visitor Name * @public diff --git a/lib/bundle/task/writer/writer.es6 b/lib/bundle/task/writer/writer.es6 index a8d2f47..1d398d8 100644 --- a/lib/bundle/task/writer/writer.es6 +++ b/lib/bundle/task/writer/writer.es6 @@ -22,7 +22,7 @@ class Writer extends Task { * @return {Promise} **/ write(vi) { - return this.types.pop(); + return this.types.pop({}, false, this); } /** @@ -37,13 +37,22 @@ class Writer extends Task { return this; } + /** + * Retrieves Task Name + * @public + * @return {String} + **/ + getName() { + return 'write'; + } + /** * Visitor Name * @public * @type {String} **/ get name() { - return 'WriterVisitor'; + return 'Writer'; } /** diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 44931c0..2ed41e9 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -18,53 +18,45 @@ class Annotation extends Type { /** * Annotation Read strategy * @public - * @override - * @param {Function} resolve asynchronous promise's resolve - * @param {Function} reject asynchronous promise's reject - * @param {util.adt.Collection} bundles list of bundles - * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject, bundles, files) { - this.annotations(files).reduce(this.create, bundles, this); - return super.read(resolve, reject); + read() { + this.annotations().reduce(this.create, this.reader.bundles, this); + return this.resolve(this); } /** - * Create Bundle Metadata + * Read all annotations of all files captured * @public - * @param {util.adt.Collection} memo memoized list of bundles - * @param {Object} meta data extracted from annotation * @return {util.adt.Collection} **/ - create(memo, meta) { - if(!Helpers.containsBy(memo, meta.name)) memo.add({ bundle: meta }); - return memo; + annotations() { + return this.reader.files().reduce(this.annotation, Collection.new(), this); } /** - * Read all annotations of all files captured + * Create Bundle Metadata * @public - * @param {Array} files files parsed by Reader Task + * @param {util.adt.Collection} memo memoized list of bundles + * @param {Object} meta data extracted from annotation * @return {util.adt.Collection} **/ - annotations(files) { - return _.reduce(files, this.annotation, Collection.new(), this); + create(memo, meta) { + if(!Helpers.containsBy(memo, meta)) memo.add(meta); + return memo; } /** * Read annotations from a single file * @public * @param {util.adt.Collection} memo memoized collection of files used to store parsed annotations - * @param {Object} file current file metadata to capture + * @param {Object} captured current captured file metadata * @return {util.adt.Collection} **/ - annotation(memo, file) { - let annotation = this.comments(file.comments, Helpers.match); - if(_.defined(annotation)) { - let meta = Helpers.extract(annotation); - if(Helpers.valid(meta)) memo.add({ name: meta.name, target: file }); - } + annotation(memo, captured) { + const { comments, path, input } = captured; + let annotation = this.comments(comments, Helpers.match); + if(_.defined(annotation)) memo.add({ path, input, params: Helpers.extract(annotation) }); return memo; } diff --git a/lib/bundle/types/annotation/helpers.es6 b/lib/bundle/types/annotation/helpers.es6 index 8bfdd90..8e7e766 100644 --- a/lib/bundle/types/annotation/helpers.es6 +++ b/lib/bundle/types/annotation/helpers.es6 @@ -16,14 +16,36 @@ const annotation = '@sqbox'; /** * Metadata Contain Matcher -* Will return true if the bundle with a given name already exists inside the collection, false otherwise +* Will return true if the metadata with a given name already exists inside the collection. * @public * @param {util.adt.Collection} collection list of bundles to perform look up -* @param {String} name bundle name to look for +* @param {Object} meta bundle metadata to evaluate * @return {Boolean} **/ -export const containsBy = (collection, name) => { - return _.defined(collection.find((meta) => (meta.bundle.name.toLowerCase() === name.toLowerCase()))); +export const containsBy = (collection, meta) => { + return _.defined(collection.find((current) => (matchPath(current, meta) && matchParams(current, meta)))); +}; + +/** +* Metadata Path Matcher +* Will return true if a metadata object exists with the same path and the given path. +* @public +* @param {Object} current current metadata +* @param {String} path given metadata path to compare +* @return {Boolean} +**/ +export const matchPath = (current, path) => { return (current.path === path); }; + +/** +* Metadata Params Matcher +* Will return true if a metadata object exists with the same params and the given params. +* @public +* @param {Object} current current metadata +* @param {Object} params given metadata params to compare +* @return {Boolean} +**/ +export const matchParams = (current, params = {}) => { + return _.defined(params.name) && (params.name === current.params.name); }; /** diff --git a/lib/bundle/types/common/collector.es6 b/lib/bundle/types/common/collector.es6 index 7a83f40..a5f97e4 100644 --- a/lib/bundle/types/common/collector.es6 +++ b/lib/bundle/types/common/collector.es6 @@ -5,6 +5,8 @@ import _ from 'util/mixins'; import Collection from 'util/adt/collection'; import Visitor from 'util/visitor/visitor'; +import Type from 'bundle/types/Type'; +import Format from 'bundle/format/format'; /** * Class Collector @@ -18,11 +20,12 @@ class Collector extends Visitor { * @public * @param {astq.Node} ast current source ast to detect * @param {Array} methods list of query methods to execute + * @param {Function} [cb = () => {}] optional callback * @param {Any} [...args] additional arguments * @return {Array} **/ iterate(ast, methods, ...args) { - return Collection.new(methods).map((method) => method(ast, ...args)); + return Collection.new(_.chain(methods).map((method) => method(ast, ...args)).flatten().value()); } /** @@ -31,12 +34,14 @@ class Collector extends Visitor { * @public * @param {bundle.types.Type} ctx type context * @param {astq.Node} ast current source ast to detect + * @param {String} expr ast query expression + * @param {Function} [cb] optional callback * @param {Array} [formats = Type.formats] list formats * @param {Any} [...args] additional arguments * @return {Array} **/ - collect(ctx, ast, formats = Type.formats, ...args) { - return this.iterate(ast, this.formatsByName(formats, ctx), ...args); + collect(ctx, ast, expr, cb, formats = Type.formats, ...args) { + return this.iterate(ast, this.formatsByType(formats, ctx), expr, cb, ...args); } /** @@ -46,22 +51,39 @@ class Collector extends Visitor { * @param {bundle.types.Type} ctx type context * @return {Array} **/ - formatsByName(formats = [], ctx) { - return _.filter(formats, (format) => { return !_.defined(ctx[format]); }); + formatsByType(formats = [], ctx) { + return _.chain(formats).map((format) => _.defined(ctx[format]) ? ctx[format] : null).compact().value(); } /** - * Generic Strategy to execute astq queries on a given list of formats and type of ast element + * Generic Strategy to execute astq queries on a given list of formats * By default all the formats will be used with the following order: es6, commonjs and amd. * @public * @param {bundle.types.Type} ctx type context * @param {astq.Node} ast current source ast to detect + * @param {Function} [cb] optional callback * @param {Array} [formats = Type.formats] list formats * @param {Any} [...args] additional arguments * @return {Array} **/ - collectByType(ctx, ast, formats = Type.formats, ...args) { - return this.iterate(ast, this.formatsByType(formats, ctx), ...args); + collectByType(ctx, ast, cb, formats = Type.formats, ...args) { + return this.iterate(ast, this.formatsByName(formats, ctx, 'ByType'), cb, ...args); + } + + /** + * Generic Strategy to execute astq queries on a given list of formats + * By default all the formats will be used with the following order: es6, commonjs and amd. + * @public + * @param {bundle.types.Type} ctx type context + * @param {astq.Node} ast current source ast to detect + * @param {String} element element name to query + * @param {Function} [cb] optional callback + * @param {Array} [formats = Type.formats] list formats + * @param {Any} [...args] additional arguments + * @return {Array} + **/ + collectByElement(ctx, ast, element, cb, formats = Type.formats, ...args) { + return this.iterate(ast, this.formatsByName(formats, ctx, 'ByElement'), element, cb, ...args); } /** @@ -69,11 +91,12 @@ class Collector extends Visitor { * @public * @param {Array} [formats = []] list of formats * @param {bundle.types.Type} ctx type context + * @param {String} type predicate type qualifier * @return {Array} **/ - formatsByType(formats = [], ctx) { + formatsByName(formats = [], ctx, type) { return _.chain(formats).map((format) => { - let method = `${format}ByType`; + let method = `${format}${type}`; return _.defined(ctx[method]) ? ctx[method] : null; }).compact().value(); } diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index 196c5fd..268fac0 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -17,18 +17,14 @@ import logger from 'util/logger/logger'; class Export extends Type { /** - * Annotation Read strategy + * Export Read strategy * @public * @override - * @param {Function} resolve asynchronous promise's resolve - * @param {Function} reject asynchronous promise's reject - * @param {util.adt.Collection} bundles list of bundles - * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject, bundles) { - this.exports(bundles); - return super.read(resolve, reject); + read() { + this.exports(); + return this.resolve(this); } /** @@ -37,8 +33,8 @@ class Export extends Type { * @param {util.adt.Collection} bundles all bundles captured by annotations * @return {bundle.types.import.Import} **/ - exports(bundles) { - bundles.reduce(this.export, Collection.new(), this); + exports() { + this.reader.bundles.reduce(this.export, Collection.new(), this); return this; } @@ -50,22 +46,19 @@ class Export extends Type { * @return {util.adt.Collection} **/ export(memo, metadata) { - const { target } = metadata.bundle; + const { path, input } = metadata; //this.collectByType(target.ast, ['es6'], _.bind(Helpers.onExport, Helpers)); return memo; } /** - * Annotation Write strategy + * Export Write strategy * @public * @override - * @param {Function} resolve asynchronous promise's resolve - * @param {Function} reject asynchronous promise's reject - * @param {bundle.task.Task} task current task * @return {Promise} **/ - write(resolve, reject) { - return super.write(resolve, reject); + write() { + return this.resolve(this); } /** diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index d6669f4..3a09b58 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -5,9 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Type from 'bundle/types/type'; -import * as Reader from 'bundle/types/import/reader'; -import * as Writer from 'bundle/types/import/writer'; import Collection from 'util/adt/collection'; +import Format from 'bundle/format/format'; import logger from 'util/logger/logger'; /** @@ -17,44 +16,95 @@ import logger from 'util/logger/logger'; class Import extends Type { /** - * Annotation Read strategy + * Import Read strategy * @public * @override - * @param {Function} resolve asynchronous promise's resolve - * @param {Function} reject asynchronous promise's reject - * @param {util.adt.Collection} bundles list of bundles - * @param {Array} files list of files * @return {Promise} **/ - read(resolve, reject, bundles, files) { - this.dependencies(bundles); - return super.read(resolve, reject); + read() { + return this.reader.bundles.reduce(this.readDependencies, true, this) ? this.resolve(this) : this.reject(this); } /** - * Read Imports + * Recursive Strategy to query dependencies and attach them into the metadata. * @public - * @param {util.adt.Collection} bundles all bundles captured by annotations - * @return {bundle.types.import.Import} + * @param {Boolean} memo memoized boolean result + * @param {bundle.task.metadata.Metadata} metadata instance of meta found as a bundle + * @param {Number} ix current metadata index + * @param {util.adt.Collection} collection original metadata collection + * @return {Boolean} **/ - dependencies(bundles) { - bundles.reduce(this.dependency, Collection.new(), this); - return this; + readDependencies(memo, metadata, ix, collection) { + return this.collectByType(metadata.input, _.bind(this.dependency, this, metadata)) + .reduce(this.onDependenciesRead, memo, this); } /** - * Read Dependencies on a single file + * Resolves & creates dependency * @public - * @param {util.adt.Collection} memo memoized collection to augment - * @param {bundle.task.metadata.Metadata} metadata instance of meta found as a bundle + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {util.adt.Collection} dependencies list of dependencies found * @return {util.adt.Collection} **/ - dependency(memo, metadata) { - const { target } = metadata.bundle; - // if(target.source.indexOf('libs.es6') !== -1) { - this.collectByType(target.ast, ['es6'], function() {}); - // } - return memo; + dependency(metadata, dependencies, format) { + if(metadata.path.indexOf('libs.es6') !== -1) { + return dependencies.reduce((metadata, ast) => { + let d = metadata.dependencies.add(this.createDependency(metadata, ast, format)).toJSON(); + // console.log('D >>', d.import.modules); + // console.log('-----------------------'); + return metadata; + }, metadata, this); + } + } + + /** + * Creates Dependency + * @public + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} ast current dependency ast reference + * @param {String} format current format being resolved + * @return {Object} + **/ + createDependency(metadata, ast, format) { + return this.collectByElement(ast, Format.elements.ImportSpecifier) + .reduce(_.bind(this.resolveDependency, this, format, metadata, ast), {}); + } + + /** + * Resolve Dependency + * @param {String} format current format being resolved + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} ast current original source ast reference + * @param {Object} dependency current new dependency + * @param {astq.Node} node current dependency node reference + **/ + resolveDependency(format, metadata, ast, dependency, node) { + return Import.resolversBy(format, this).reduce((dependency, resolve) => { + return resolve(dependency, metadata, node, ast); + }, dependency); + } + + /** + * Dependencies Query Result Handler + * @public + * @param {Boolean} memo memoized boolean result + * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {Array} results list of ast results + * @return {bundle.type.import.Import} + **/ + onDependenciesRead(memo, metadata) { + // TODO: Triggers Recursive operation over dependencies + //results.map(_.bind(this.create, this, memo, this.reader.files()), this); + } + + /** + * Import Write strategy + * @public + * @override + * @return {Promise} + **/ + write() { + return this.resolve(this); } /** @@ -67,6 +117,29 @@ class Import extends Type { return 'Import'; } + /** + * Resolvers by Format + * @static + * @param {String} format + * @return {Array} + **/ + static resolversBy = (format, instance) => { + return Collection.new(_.map(Import.resolvers, (name) => instance[`${format.toLowerCase()}${name}`])); + } + + /** + * Method Name Resolvers + * @static + * @property resolvers + * @type {Array} + **/ + static resolvers = [ + 'ResolveImportSpecifier', + 'ResolveImportPath', + 'ResolveParent', + 'ResolveAst' + ]; + } export default Import; diff --git a/lib/bundle/types/import/reader.es6 b/lib/bundle/types/import/reader.es6 index 4b7fcb7..69902af 100644 --- a/lib/bundle/types/import/reader.es6 +++ b/lib/bundle/types/import/reader.es6 @@ -5,40 +5,70 @@ **/ import _ from 'util/mixins'; import _s from 'underscore.string'; +import extend from 'extend'; import Collection from 'util/adt/collection'; +// memo.add({ +// parent: resolveParent(), +// import: { id: '', path: '' }, +// export: null, +// input: later, +// outout: null +// }); + /** -* Import Declaration Handler +* Creates dependency * @public -* @param {bundle.types.import.Import} type import type reference -* @param {util.adt.Collection} imports collection of import declarations +* @param {bundle.task.metadata.Metadata} metadata memoized metadata +* @param {String} format current format +* @param {astq.Node} ast current dependency ast * @return {util.adt.Collection} **/ -export const onImport = (imports) => { - //console.log('onImport: ', imports.toJSON()); - //imports.reduce(_.bind(onImportIdentifier, this, type), Collection.new()); +export const addDependency = (type, format, metadata, ast) => { + let results = type.collectByElement(ast, 'ImportSpecifier'); + let dependency = metadata.dependencies.add(results.reduce(resolveImportSpecifier, {})); + resolveParent(metadata, dependency); + console.log(format); + return metadata; }; /** -* Import Identifier Declaration Handler +* Import Declaration Handler * @public -* @param {Function} query astq query function -* @param {util.adt.Collection} memo memoized collection of identifiers to augment -* @param {astq.Node} declaration current import declaration node reference -* @return {util.adt.Collection} +* @param {Object} dependency memoized dependency +* @param {astq.Node} node current astq node result +* @param {String} format current format used +* @return {bundle.task.metadata.Dependency} **/ -export const onImportIdentifier = (type, memo, declaration) => { - // TODO +export const resolveImportSpecifier = (dependency, node, format) => { + dependency.import = { id: '' }; // TODO + return resolveImportPath(dependency, node, format); +}; + +/** +* Resolves Dependency Parent +* @public +* @param {bundle.task.metadata.Metadata} metadata current metadata +* @param {astq.Node} node current node +* @return {bundle.task.metadata.Metadata} +**/ +export const resolveParent = (metadata, dependency) => { + let name = metadata.getName(); + return extend(false, dependency, { parent: _.defined(name) ? name : _.uuid() }); +}; + +export const resolveAst = (dependency, node) => { + return extend(false, dependency, { input: node }); }; /** * Import Identifier Declaration Handler * @public -* @param {Function} query astq query function -* @param {util.adt.Collection} memo memoized collection of identifiers to augment -* @param {astq.Node} declaration current import declaration node reference -* @return {util.adt.Collection} +* @param {astq.Node} node ast node import declaration +* @param {bundle.task.metadata.Dependency} current current dependency +* @return {bundle.task.metadata.Dependency} **/ -export const onImportPath = (query, memo, declaration) => { - // TODO +export const resolveImportPath = (dependency, node, format) => { + if(node.type === 'Literal') extend(false, dependency.import, { path: node.value }); + return dependency; }; diff --git a/lib/bundle/types/type.es6 b/lib/bundle/types/type.es6 index ec898e7..9c05f72 100644 --- a/lib/bundle/types/type.es6 +++ b/lib/bundle/types/type.es6 @@ -3,6 +3,7 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import _s from 'underscore.string'; import extend from 'extend'; import * as Comments from 'bundle/types/common/comment'; import Collection from 'util/adt/collection'; @@ -19,61 +20,55 @@ class Type extends Visited { * Constructor * @public * @override - * @param {bundle.task.Task} task - current task reference * @return {bundle.types.Type} **/ - constructor(task) { - return super({ task }).registerAll(); + constructor() { + return super().registerAll(); } /** - * Resolves Task Execution + * Resolves Task Action * @public - * @param {Function} reject asynchronous promise's reject - * @return {bundle.types.Type} + * @param {String} task current task name + * @param {Any} [...args] list of arguments + * @return {Promise} **/ - resolve(reject) { - switch(this.task.name) { - case 'ReaderVisitor': return _.bind(this.read, this); - case 'WriterVisitor': return _.bind(this.write, this); - default: return reject; - } + action(task, ...args) { + return _.defined(this[task]) ? this[task](...args) : this.resolve(this); } /** - * Default Asynchronous next strategy + * Injects a given task into this type * @public * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {Any} [...args] list of arguments - * @return {Promise} + * @param {bundle.task.Task} task current task reference + * @return {bundle.types.Type} **/ - next(resolve, reject, ...args) { - return this.resolve(reject)(resolve, reject, ...args); + inject(resolve, reject, task) { + return extend(false, this, { resolve, reject, [task.name.toLowerCase()]: task }); } /** - * Default Read strategy + * Default Asynchronous next strategy * @public * @param {Function} resolve asynchronous promise's resolve * @param {Function} reject asynchronous promise's reject - * @param {Any} [...args] list of arguments + * @param {bundle.task.Task} task current task reference + * @param {Any} [...args] optional additional arguments * @return {Promise} **/ - read(resolve, reject, ...args) { - return resolve(this); + next(resolve, reject, task, ...args) { + return this.inject(resolve, reject, task).action(task.getName(), ...args); } /** - * Default Write strategy + * Retrieves element type name * @public - * @param {Function} resolve asynchronous promise's resolve - * @param {Function} reject asynchronous promise's reject - * @param {Any} [...args] list of arguments - * @return {Promise} + * @return {String} **/ - write(resolve, reject, ...args) { - return resolve(this); + getName() { + return _s.strLeft(this.name, 'Visitor').toLowerCase(); } /** diff --git a/lib/util/mixins.es6 b/lib/util/mixins.es6 index b468887..69a5045 100644 --- a/lib/util/mixins.es6 +++ b/lib/util/mixins.es6 @@ -46,6 +46,18 @@ _.mixin({ }, this).flatten().filter((v, k) => _.defined(v)).value(); }, + /** + * Generates and return a UUID (Universally Unique Identifier) + * @public + * @return {String} + **/ + uuid: function() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = (c === 'x') ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + }, + /** * Return true if a given object is a real object (Not an array or a Date for example), false otherwise. * @public diff --git a/package.json b/package.json index 8f8369c..fe945f7 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "babel-register": "6.23.0", "chalk": "1.1.3", "d3": "4.7.3", - "escodegen": "^1.8.1", + "escodegen": "1.8.1", "extend": "3.0.0", "fs-extra": "2.0.0", "glob": "7.1.1", diff --git a/test/specs/es6/libs.es6 b/test/specs/es6/libs.es6 index 6ea28a5..288c13d 100644 --- a/test/specs/es6/libs.es6 +++ b/test/specs/es6/libs.es6 @@ -1,5 +1,9 @@ /** * @sqbox({ name: "libs" }) **/ -import AmdLib from 'libs/amd-lib'; +import 'libs/cjs-lib'; import CommonJsLib from 'libs/cjs-lib'; +import * as AmdLib from 'libs/amd-lib'; +import { component } from 'libs/other'; +import First, { A, B as C } from 'libs/combination'; +import Second, { B as C } from 'libs/alias'; From 8fa687af56b2bbae67bc24efe136446e454067da Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Mon, 12 Jun 2017 16:28:33 -0700 Subject: [PATCH 19/22] bundle - checkpoint: fixed import path. --- lib/bundle/format/format.es6 | 1 - lib/bundle/types/common/collector.es6 | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index 2c3c4da..d6c1109 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -123,7 +123,6 @@ class Format extends Visitor { **/ addModule(modules, node, local, imported) { let _module = {}; - // Continue here... if(local && !imported) { _module = { id: local.name }; } else if(local && imported) { diff --git a/lib/bundle/types/common/collector.es6 b/lib/bundle/types/common/collector.es6 index a5f97e4..7ab503e 100644 --- a/lib/bundle/types/common/collector.es6 +++ b/lib/bundle/types/common/collector.es6 @@ -5,7 +5,7 @@ import _ from 'util/mixins'; import Collection from 'util/adt/collection'; import Visitor from 'util/visitor/visitor'; -import Type from 'bundle/types/Type'; +import Type from 'bundle/types/type'; import Format from 'bundle/format/format'; /** From 4a5b9e132e23dce152eaf7a8820dad08ba97ffdc Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Tue, 13 Jun 2017 11:58:42 -0700 Subject: [PATCH 20/22] bundle: checkpoint - Added basePath to config path (fixed unit tests). Added more utility functions to Property Visitor to resolve aliases and individual source files. Remove Reader/Writer helpers per file. Refactored Import modules resolution per format (using a helpers located in bundle.format.[format].Helper. --- lib/bin/commands.json | 1 + lib/bundle/bundle.es6 | 2 + lib/bundle/format/amd/helpers.es6 | 0 lib/bundle/format/cjs/helpers.es6 | 0 lib/bundle/format/cjs/template.es6 | 10 +--- lib/bundle/format/es6/es6.es6 | 13 ++-- lib/bundle/format/es6/helpers.es6 | 75 ++++++++++++++++++++++++ lib/bundle/format/es6/template.es6 | 6 -- lib/bundle/format/format.es6 | 43 -------------- lib/bundle/task/reader/reader.es6 | 18 +++++- lib/bundle/types/export/export.es6 | 2 - lib/bundle/types/export/reader.es6 | 16 ----- lib/bundle/types/export/writer.es6 | 16 ----- lib/bundle/types/import/import.es6 | 27 ++++----- lib/bundle/types/import/reader.es6 | 74 ----------------------- lib/bundle/types/import/writer.es6 | 8 --- lib/command.es6 | 1 + lib/visitors/command/properties.es6 | 42 +++++++++++-- lib/visitors/configuration.es6 | 1 + test/lib/bin/sqbox.spec.es6 | 5 +- test/lib/bundle/bundle.spec.es6 | 7 ++- test/lib/visitors/configuration.spec.es6 | 14 ++--- test/specs/.sqbox.js | 10 ++-- test/specs/.sqboxrc | 10 ++-- test/specs/es6/libs.es6 | 8 +-- 25 files changed, 179 insertions(+), 230 deletions(-) create mode 100644 lib/bundle/format/amd/helpers.es6 create mode 100644 lib/bundle/format/cjs/helpers.es6 create mode 100644 lib/bundle/format/es6/helpers.es6 delete mode 100644 lib/bundle/types/export/reader.es6 delete mode 100644 lib/bundle/types/export/writer.es6 delete mode 100644 lib/bundle/types/import/reader.es6 delete mode 100644 lib/bundle/types/import/writer.es6 diff --git a/lib/bin/commands.json b/lib/bin/commands.json index 49fcbf2..a1f7ceb 100644 --- a/lib/bin/commands.json +++ b/lib/bin/commands.json @@ -29,6 +29,7 @@ "override": { "default": false }, "config": { "default": ".sqboxrc" }, "url": { "alias": "u" }, + "basePath": { "alias": "b", "default": "./" }, "scan": { "alias": "s", "default": "." }, "exclude": { "alias": "x" }, "extensions": { "alias": "e", "default": ".js,.jsx,.es6,.es" }, diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index da09277..fd7aa42 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -95,7 +95,9 @@ class Bundle extends Command { **/ static options = Command.options.concat([ 'bundles', + 'file', 'sources', + 'aliases', 'excludes', 'targets' ]); diff --git a/lib/bundle/format/amd/helpers.es6 b/lib/bundle/format/amd/helpers.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/format/cjs/helpers.es6 b/lib/bundle/format/cjs/helpers.es6 new file mode 100644 index 0000000..e69de29 diff --git a/lib/bundle/format/cjs/template.es6 b/lib/bundle/format/cjs/template.es6 index 41a1478..a193cf5 100644 --- a/lib/bundle/format/cjs/template.es6 +++ b/lib/bundle/format/cjs/template.es6 @@ -8,19 +8,11 @@ import _ from 'util/mixins'; import extend from 'extend'; /** -* CommonJs Import Cases -* --------------------- -* -* 1) [let, var, const] n1 = require(path1); -* 2) n2 = require(path2); -* 3) require(path2); -* * CommonJs Export Cases * --------------------- * * 1) module.exports = {o1}; -* 2) var m = module; -* m.exports = {o2}; +* 2) var m = module; m.exports = {o2}; **/ /** diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index 30e2c25..9633d0a 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -5,6 +5,7 @@ import _ from 'util/mixins'; import extend from 'extend'; import Format from 'bundle/format/format'; +import * as Helpers from 'bundle/format/es6/helpers'; import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; @@ -59,7 +60,7 @@ class Es6 extends Format { **/ es6ResolveImportSpecifier(ctx, dependency, metadata, node) { if(!_.defined(dependency.import)) dependency.import = { modules: Collection.new() }; - this.addModule(dependency.import.modules, node, node.local, node.imported); + Helpers.add(dependency.import.modules, node); return dependency; } @@ -73,7 +74,7 @@ class Es6 extends Format { * @return {Object} **/ es6ResolveImportPath(ctx, dependency, metadata, node, ast) { - dependency.import.path = ast.source.value; + dependency.import.path = ctx.reader.aliases(ast.source.value); return dependency; } @@ -119,16 +120,12 @@ class Es6 extends Format { * @type {String} **/ static QES6_ImportSpecifier = ` - /Literal, /ImportDefaultSpecifier, /ImportSpecifier, - /ImportNamespaceSpecifier + /ImportNamespaceSpecifier, + /Literal `; - // /ImportDefaultSpecifier /Identifier, - // /ImportSpecifier [/:imported Identifier || /:local Identifier], - // /ImportNamespaceSpecifier /Identifier - /** * ASTQ ES6 Import Path Query * @static diff --git a/lib/bundle/format/es6/helpers.es6 b/lib/bundle/format/es6/helpers.es6 new file mode 100644 index 0000000..565006d --- /dev/null +++ b/lib/bundle/format/es6/helpers.es6 @@ -0,0 +1,75 @@ +/** +* @module bundle.format.es6 +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; + +/** +* Retrieves module for a given dependency module list by id. Returns null if module is not found. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {String} id module id to retrieve +* @return {Object} +**/ +export const get = (modules, id) => { + if(!_.defined(id)) return true; + return modules.findWhere({ id }); +}; + +/** +* Resolves Module for a given astq node if it's default import. +* @public +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveDefault = (node = {}) => { + return (node.local && !node.imported) ? { id: node.local.name } : node; +}; + +/** +* Resolves Module for a given astq node if it's named import. +* @public +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveAliased = (node = {}) => { + return (node.local && node.imported) ? { id: node.imported.name, alias: node.local.name } : node; +}; + +/** +* Resolves Module for a given astq node if it doesn't have named imports (default or aliased). +* Only if module collection is empty (means no named imports has been detected). +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveUnnamed = (modules, node = {}) => { + return (node.type === 'Literal' && modules.isEmpty()) ? { id: node.value } : node; +}; + + +/** +* Adds a new module into a given dependency module list based on current node ast. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {astq.Node} node current node +* @return {util.adt.Collection} +**/ +export const add = (modules, node) => { + let _module = resolveUnnamed(modules, resolveAliased(resolveDefault(node))); + if(!get(modules, _module.id)) modules.add(_module); + return modules; +}; + +/** +* Removes an existing module from a given dependency module list by module +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {Object} module module to remove +* @return {util.adt.Collection} +**/ +export const remove = (modules, module) => { + // TODO + return modules; +}; diff --git a/lib/bundle/format/es6/template.es6 b/lib/bundle/format/es6/template.es6 index eb470f8..588ea21 100644 --- a/lib/bundle/format/es6/template.es6 +++ b/lib/bundle/format/es6/template.es6 @@ -8,12 +8,6 @@ import _ from 'util/mixins'; import extend from 'extend'; /** -* ES6 Import Cases: -* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import -* ---------------------- -* -* - import d{} -* * ES6 Export Cases: * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export * ---------------------- diff --git a/lib/bundle/format/format.es6 b/lib/bundle/format/format.es6 index d6c1109..20e1b34 100644 --- a/lib/bundle/format/format.es6 +++ b/lib/bundle/format/format.es6 @@ -100,49 +100,6 @@ class Format extends Visitor { return results; } - /** - * Retrieves Module for a given dependency modules list. Returns null if module was not found - * @public - * @param {util.adt.Collection} modules current list of dependency modules to add into - * @param {String} id module id - * @return {Object} - **/ - getModule(modules, id) { - if(!_.defined(id)) return true; - return modules.findWhere({ id: id }); - } - - /** - * Add Module to a given dependency if it doesn't exists based on module id - * @public - * @param {util.adt.Collection} modules current list of dependency modules to add into - * @param {astq.Node} node module literal value path - * @param {String} id module id - * @param {String} [alias] module alias - * @return {util.adt.Collection} - **/ - addModule(modules, node, local, imported) { - let _module = {}; - if(local && !imported) { - _module = { id: local.name }; - } else if(local && imported) { - _module = { id: local.name, alias: imported.name }; - } - return !this.getModule(modules, _module.id) ? modules.add(_module) : modules; - } - - /** - * Removes Module from a given depependency if it exists, based on module id - * @public - * @param {util.adt.Collection} modules current list of dependency modules to remove from - * @param {String} id module id - * @param {String} [alias] module alias - * @return {util.adt.Collection} - **/ - removeModule(dependency, id, alias) { - return modules; - } - /** * Generic AST Injection for a given dependency and returns it * @public diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index 7befa25..5b5ed70 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -39,7 +39,7 @@ class Reader extends Task { **/ files() { if(_.defined(this.parsedFiles)) return this.parsedFiles; - extend(false, this, { parsedFiles: this.readFiles().reduce(this.get, Collection.new(), this) }); + extend(false, this, { parsedFiles: this.readFiles().reduce(this.add, Collection.new(), this) }); return this.parsedFiles; } @@ -50,12 +50,24 @@ class Reader extends Task { * @param {String} path file path to parse * @return {bundle.task.reader.Reader} **/ - get(memo, path) { + add(memo, path) { let comments = [], input = this.parse(path, extend(false, { onComment: comments }, Reader.acornOptions)); memo.add({ path, input, comments }); return memo; } + /** + * Retrieve parsed file (if it was already parsed), returns null otherwise + * @public + * @param {String} input input file path + * @return {Object} + **/ + get(input) { + let file = this.parsedFiles.findWhere({ path: this.file(input, true) }); + //console.log(this.parsedFiles); + return file; // (`Found: ${input} -> ` + _.defined(file)); + } + /** * Acorn Parsing Strategy * @public @@ -67,7 +79,7 @@ class Reader extends Task { } /** - * Read Project files + * Read Files * @public * @return {util.adt.Collection} **/ diff --git a/lib/bundle/types/export/export.es6 b/lib/bundle/types/export/export.es6 index 268fac0..a75b762 100644 --- a/lib/bundle/types/export/export.es6 +++ b/lib/bundle/types/export/export.es6 @@ -5,8 +5,6 @@ import _ from 'util/mixins'; import extend from 'extend'; import Type from 'bundle/types/type'; -import * as Reader from 'bundle/types/export/reader'; -import * as Writer from 'bundle/types/export/writer'; import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; diff --git a/lib/bundle/types/export/reader.es6 b/lib/bundle/types/export/reader.es6 deleted file mode 100644 index 0be6de4..0000000 --- a/lib/bundle/types/export/reader.es6 +++ /dev/null @@ -1,16 +0,0 @@ -/** -* Export Reader Helpers -* @module bundle.types.export -* @author Patricio Ferreira <3dimentionar@gmail.com> -**/ -import _ from 'util/mixins'; -import _s from 'underscore.string'; -import Collection from 'util/adt/collection'; - -/** -* Export Declaration Handler -* @public -* @param {util.adt.Collection} exports collection of export declarations -* @return {util.adt.Collection} -**/ -export const onExport = (exports) => {}; diff --git a/lib/bundle/types/export/writer.es6 b/lib/bundle/types/export/writer.es6 deleted file mode 100644 index 0d4a655..0000000 --- a/lib/bundle/types/export/writer.es6 +++ /dev/null @@ -1,16 +0,0 @@ -/** -* Export Writer Helpers -* @module bundle.types.export -* @author Patricio Ferreira <3dimentionar@gmail.com> -**/ -import _ from 'util/mixins'; -import _s from 'underscore.string'; -import Collection from 'util/adt/collection'; - -/** -* Export Declaration Handler -* @public -* @param {util.adt.Collection} exports collection of export declarations -* @return {util.adt.Collection} -**/ -export const onExport = (exports) => {}; diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index 3a09b58..76e90be 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -22,7 +22,8 @@ class Import extends Type { * @return {Promise} **/ read() { - return this.reader.bundles.reduce(this.readDependencies, true, this) ? this.resolve(this) : this.reject(this); + return this.reader.bundles.reduce(this.readDependencies, true, this) ? + this.resolve(this) : this.reject(this); } /** @@ -34,9 +35,9 @@ class Import extends Type { * @param {util.adt.Collection} collection original metadata collection * @return {Boolean} **/ - readDependencies(memo, metadata, ix, collection) { - return this.collectByType(metadata.input, _.bind(this.dependency, this, metadata)) - .reduce(this.onDependenciesRead, memo, this); + readDependencies(memo, metadata) { + this.collectByType(metadata.input, _.bind(this.dependency, this, metadata)); + return metadata.dependencies.reduce(this.onDependenciesRead, memo, this); } /** @@ -44,17 +45,14 @@ class Import extends Type { * @public * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {util.adt.Collection} dependencies list of dependencies found + * @param {String} format current format * @return {util.adt.Collection} **/ dependency(metadata, dependencies, format) { - if(metadata.path.indexOf('libs.es6') !== -1) { - return dependencies.reduce((metadata, ast) => { - let d = metadata.dependencies.add(this.createDependency(metadata, ast, format)).toJSON(); - // console.log('D >>', d.import.modules); - // console.log('-----------------------'); - return metadata; - }, metadata, this); - } + return dependencies.reduce((metadata, ast) => { + metadata.dependencies.add(this.createDependency(metadata, ast, format)); + return metadata; + }, metadata, this); } /** @@ -92,9 +90,10 @@ class Import extends Type { * @param {Array} results list of ast results * @return {bundle.type.import.Import} **/ - onDependenciesRead(memo, metadata) { - // TODO: Triggers Recursive operation over dependencies + onDependenciesRead(memo, dependency) { + console.log(this.reader.get(dependency.import.path)); //results.map(_.bind(this.create, this, memo, this.reader.files()), this); + return memo; } /** diff --git a/lib/bundle/types/import/reader.es6 b/lib/bundle/types/import/reader.es6 deleted file mode 100644 index 69902af..0000000 --- a/lib/bundle/types/import/reader.es6 +++ /dev/null @@ -1,74 +0,0 @@ -/** -* Import Reader Helpers -* @module bundle.types.import -* @author Patricio Ferreira <3dimentionar@gmail.com> -**/ -import _ from 'util/mixins'; -import _s from 'underscore.string'; -import extend from 'extend'; -import Collection from 'util/adt/collection'; - -// memo.add({ -// parent: resolveParent(), -// import: { id: '', path: '' }, -// export: null, -// input: later, -// outout: null -// }); - -/** -* Creates dependency -* @public -* @param {bundle.task.metadata.Metadata} metadata memoized metadata -* @param {String} format current format -* @param {astq.Node} ast current dependency ast -* @return {util.adt.Collection} -**/ -export const addDependency = (type, format, metadata, ast) => { - let results = type.collectByElement(ast, 'ImportSpecifier'); - let dependency = metadata.dependencies.add(results.reduce(resolveImportSpecifier, {})); - resolveParent(metadata, dependency); - console.log(format); - return metadata; -}; - -/** -* Import Declaration Handler -* @public -* @param {Object} dependency memoized dependency -* @param {astq.Node} node current astq node result -* @param {String} format current format used -* @return {bundle.task.metadata.Dependency} -**/ -export const resolveImportSpecifier = (dependency, node, format) => { - dependency.import = { id: '' }; // TODO - return resolveImportPath(dependency, node, format); -}; - -/** -* Resolves Dependency Parent -* @public -* @param {bundle.task.metadata.Metadata} metadata current metadata -* @param {astq.Node} node current node -* @return {bundle.task.metadata.Metadata} -**/ -export const resolveParent = (metadata, dependency) => { - let name = metadata.getName(); - return extend(false, dependency, { parent: _.defined(name) ? name : _.uuid() }); -}; - -export const resolveAst = (dependency, node) => { - return extend(false, dependency, { input: node }); -}; - -/** -* Import Identifier Declaration Handler -* @public -* @param {astq.Node} node ast node import declaration -* @param {bundle.task.metadata.Dependency} current current dependency -* @return {bundle.task.metadata.Dependency} -**/ -export const resolveImportPath = (dependency, node, format) => { - if(node.type === 'Literal') extend(false, dependency.import, { path: node.value }); - return dependency; -}; diff --git a/lib/bundle/types/import/writer.es6 b/lib/bundle/types/import/writer.es6 deleted file mode 100644 index 895ca6e..0000000 --- a/lib/bundle/types/import/writer.es6 +++ /dev/null @@ -1,8 +0,0 @@ -/** -* Import Writer Helpers -* @module bundle.types.import -* @author Patricio Ferreira <3dimentionar@gmail.com> -**/ -import _ from 'util/mixins'; -import _s from 'underscore.string'; -import Collection from 'util/adt/collection'; diff --git a/lib/command.es6 b/lib/command.es6 index a5dcf52..d9fb74c 100644 --- a/lib/command.es6 +++ b/lib/command.es6 @@ -190,6 +190,7 @@ class Command extends Visited { 'env', 'dirname', 'cwd', + 'basePath', 'scan', 'exclude', 'extensions', diff --git a/lib/visitors/command/properties.es6 b/lib/visitors/command/properties.es6 index 165f57d..dbd7c19 100644 --- a/lib/visitors/command/properties.es6 +++ b/lib/visitors/command/properties.es6 @@ -3,6 +3,7 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; +import _s from 'underscore.string'; import path from 'path'; import Visitor from 'util/visitor/visitor'; @@ -12,23 +13,53 @@ import Visitor from 'util/visitor/visitor'; **/ class Properties extends Visitor { + /** + * Resolve a single file or path expression realtive to the base path specified in the configuration + * @public + * @param {bundle.Command} ctx current command + * @param {String} input relative path to resolve + * @param {Boolean} [ext = false] using use extensions + * @return {String} + **/ + file(ctx, input, ext = false) { + let expr = ext ? `${input}.+(${this.extensions.join('|')})` : input; + return path.resolve(this.basePath, input); + } + /** * Resolve scan sources * @public + * @param {bundle.Command} ctx current command + * @return {String} + **/ + sources(ctx) { + return this.file(ctx, this.scan, true); + } + + /** + * Resolve alias for a given input path by replacing, using the alias configuration, + * any alias expression that match the begining of the input with final path specified. + * If no matches are found, the path will remain intact. + * @public + * @param {bundle.Command} ctx current command + * @param {String} input input path to resolve * @return {String} **/ - sources() { - return path.resolve(`${this.scan}/**/*+(${this.extensions.join('|')})`); + aliases(ctx, input) { + return _.reduce(this.alias, (memo, replace, alias) => { + return _s.startsWith(memo, alias) ? _s.replaceAll(memo, alias, replace) : memo; + }, input, this); } /** * Resolve excludes * @public + * @param {bundle.Command} ctx current command * @return {Array} **/ - excludes() { + excludes(ctx) { return _.reduce(this.exclude, (memo, pattern) => { - memo.push(path.resolve(this.scan, pattern)); + memo.push(path.resolve(this.basePath, this.scan, pattern)); return memo; }, []); } @@ -36,10 +67,11 @@ class Properties extends Visitor { /** * Retrieve targets by using a given predicate passed by parameter * @public + * @param {bundle.Command} ctx current command * @param {Function} predicate - predicate to walk over the targets * @return {Array} **/ - targets(predicate) { + targets(ctx, predicate) { return _.defined(predicate) && _.isFunction(predicate) ? _.map(this.target, predicate, this) : []; } diff --git a/lib/visitors/configuration.es6 b/lib/visitors/configuration.es6 index fa8e238..82366a8 100644 --- a/lib/visitors/configuration.es6 +++ b/lib/visitors/configuration.es6 @@ -198,6 +198,7 @@ class Configuration extends Visitor { * @type {Array} **/ static cliOptions = [ + 'basePath', 'scan', 'exclude', 'extensions', diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6 index b84db95..984cf78 100644 --- a/test/lib/bin/sqbox.spec.es6 +++ b/test/lib/bin/sqbox.spec.es6 @@ -65,8 +65,9 @@ describe('bin.SquareBox', function() { 'sqbox', 'graph', '--config', 'test/specs/.sqboxrc', - '--s', './source/**', - '--x', './source/dependencies/**,./source/package/**', + '--b', './source', + '--s', '**/*', + '--x', 'dependencies/**,package/**', '--e', '.js,.es6', '--a', 'common:./path/common', '--l', 'jquery,react', diff --git a/test/lib/bundle/bundle.spec.es6 b/test/lib/bundle/bundle.spec.es6 index a98884b..26afeb5 100644 --- a/test/lib/bundle/bundle.spec.es6 +++ b/test/lib/bundle/bundle.spec.es6 @@ -8,10 +8,11 @@ describe('bundle.Bundle', function() { before(() => { this.params = { - scan: './test/specs/es6', - extensions: ['.js', '.es6'], + basePath: './test/specs/es6', + scan: '**/*', + extensions: ['js', 'es6', 'es'], exclude: [], - alias: { common: 'common' }, + alias: { dependencies: "libs/dependencies" }, external: ['jquery'], target: { iife: { destination: './test/specs/dist/iife', format: 'iife' }, diff --git a/test/lib/visitors/configuration.spec.es6 b/test/lib/visitors/configuration.spec.es6 index 5ef9a29..cb102b7 100644 --- a/test/lib/visitors/configuration.spec.es6 +++ b/test/lib/visitors/configuration.spec.es6 @@ -107,7 +107,7 @@ describe('visitors.Configuration', function() { describe('_format()', () => { it('Should apply formatter to the current configuration option', () => { - const options = { scan: './src/**', extensions: '.js,.es6' }; + const options = { basePath: './src', scan: '**/*', extensions: '.js,.es6' }; const expTransform = ['.js', '.es6']; const formatterPath = 'visitors/configuration/formatter/extensions'; const expFormatterPath = this.mockProto.expects('formatterPath') @@ -135,7 +135,7 @@ describe('visitors.Configuration', function() { }); it('Should NOT apply formatter to the current configuration option', () => { - const options = { scan: './src/**' }; + const options = { basePath: './src', scan: '**/*' }; const formatterPath = 'visitors/configuration/formatter/scan'; const expFormatterPath = this.mockProto.expects('formatterPath') .once() @@ -161,7 +161,7 @@ describe('visitors.Configuration', function() { describe('_source()', () => { it('Should apply source configuration options to the current command', () => { - const options = { scan: './src/**', extensions: ['.js', '.es6'], unrecognized: true }; + const options = { basePath: './src', scan: '**/*', extensions: ['.js', '.es6'], unrecognized: true }; assert.instanceOf(this.configuration._source(options), Configuration); assert.property(this.command, 'scan'); @@ -190,10 +190,10 @@ describe('visitors.Configuration', function() { describe('_override()', () => { it('Should override configuration options with cli options', () => { - const options = { scan: './src/**', extensions: ['.js', '.es6'], unrecognized: true }; + const options = { basePath: './src', scan: '**/*', extensions: ['.js', '.es6'], unrecognized: true }; const filtered = _.omit(options, 'unrecognized'); const expFormat = this.mockProto.expects('_format') - .exactly(2) + .exactly(3) .withArgs(filtered, sinon.match.any, sinon.match.string) .returns(filtered); @@ -253,7 +253,7 @@ describe('visitors.Configuration', function() { it('Should perform options transformations', () => { const expResult = { - source: { scan: './src/**' }, + source: { basePath: './src', scan: '**/*' }, target: { cjs: { destination: './dist', format: 'cjs' } }, logLevel: 'silent' }; @@ -292,7 +292,7 @@ describe('visitors.Configuration', function() { describe('parse()', () => { it('Should create methods and resolve configuration option source', () => { - const expResult = { source: { scan: './src/**' } }; + const expResult = { source: { basePath: './src', scan: '**/*' } }; const queueStubPromise = this.sandbox.stub().returnsPromise(); const expCreate = this.mockProto.expects('_create') .exactly(2) diff --git a/test/specs/.sqbox.js b/test/specs/.sqbox.js index f087cef..deba292 100644 --- a/test/specs/.sqbox.js +++ b/test/specs/.sqbox.js @@ -4,12 +4,12 @@ **/ module.exports = { source: { - scan: './src/**', - exclude: ['./src/dependencies/**'], - extensions: ['.js', '.es6', '.es'], + basePath: './es6', + scan: '**/*', + exclude: ['dependencies/**'], + extensions: ['js', 'es6', 'es'], alias: { - common: 'shared/common', - libraries: 'libs' + dependencies: 'libs/dependencies' }, external: ['jquery'] }, diff --git a/test/specs/.sqboxrc b/test/specs/.sqboxrc index 45c10f0..acaf617 100644 --- a/test/specs/.sqboxrc +++ b/test/specs/.sqboxrc @@ -1,11 +1,11 @@ { "source": { - "scan": "./src/**", - "exclude": ["./src/dependencies/**"], - "extensions": [".js", ".es6"], + "basePath": "./es6", + "scan": "**/*", + "exclude": ["dependencies/**"], + "extensions": ["js", "es6", "es"], "alias": { - "common": "shared/common", - "libraries": "libs" + "dependencies": "libs/dependencies" }, "external": ["jquery"] }, diff --git a/test/specs/es6/libs.es6 b/test/specs/es6/libs.es6 index 288c13d..a880d23 100644 --- a/test/specs/es6/libs.es6 +++ b/test/specs/es6/libs.es6 @@ -1,9 +1,9 @@ /** * @sqbox({ name: "libs" }) **/ -import 'libs/cjs-lib'; import CommonJsLib from 'libs/cjs-lib'; import * as AmdLib from 'libs/amd-lib'; -import { component } from 'libs/other'; -import First, { A, B as C } from 'libs/combination'; -import Second, { B as C } from 'libs/alias'; +// import { component } from 'libs/other'; +// import First, { A, B as C } from 'libs/combination'; +// import Second, { B as C } from 'libs/alias'; +// import 'libs/cjs-lib'; From 61475be7eda4ad8d7e7c020c0207e0e051942266 Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Fri, 30 Jun 2017 08:57:22 -0700 Subject: [PATCH 21/22] bundle: checkpoint - progress on amd imports parsing. --- lib/bundle/bundle.es6 | 8 +- lib/bundle/format/amd/amd.es6 | 44 ++++++--- lib/bundle/format/amd/helpers.es6 | 79 +++++++++++++++ lib/bundle/format/cjs/cjs.es6 | 42 ++++---- lib/bundle/format/cjs/helpers.es6 | 86 ++++++++++++++++ lib/bundle/format/es6/es6.es6 | 12 +-- lib/bundle/task/reader/helpers.es6 | 110 +++++++++++++++++++++ lib/bundle/task/reader/reader.es6 | 105 +++++++------------- lib/bundle/types/annotation/annotation.es6 | 2 +- lib/bundle/types/common/collector.es6 | 2 +- lib/bundle/types/import/import.es6 | 63 ++++++++---- lib/visitors/command/properties.es6 | 12 ++- test/specs/es6/libs/amd-lib.js | 10 +- test/specs/es6/libs/cjs-lib.js | 7 +- 14 files changed, 435 insertions(+), 147 deletions(-) create mode 100644 lib/bundle/task/reader/helpers.es6 diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6 index fd7aa42..ce93afc 100644 --- a/lib/bundle/bundle.es6 +++ b/lib/bundle/bundle.es6 @@ -24,7 +24,10 @@ class Bundle extends Command { * @return {bundle.Bundle} **/ constructor(args = {}) { - return super(extend(true, args, { bundles: Collection.new([], { interface: Metadata }) })); + return super(extend(true, args, { + files: Collection.new(), + bundles: Collection.new([], { interface: Metadata }) + })); } /** @@ -96,7 +99,8 @@ class Bundle extends Command { static options = Command.options.concat([ 'bundles', 'file', - 'sources', + 'files', + 'extension', 'aliases', 'excludes', 'targets' diff --git a/lib/bundle/format/amd/amd.es6 b/lib/bundle/format/amd/amd.es6 index 5889bdf..eea4e37 100644 --- a/lib/bundle/format/amd/amd.es6 +++ b/lib/bundle/format/amd/amd.es6 @@ -5,6 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Format from 'bundle/format/format'; +import * as Helpers from 'bundle/format/amd/helpers'; +import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** @@ -53,12 +55,13 @@ class Amd extends Format { * @param {Object} dependency new dependency to add * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ - amdResolveImportSpecifier(ctx, dependency) { - if(!_.defined(dependency.import)) dependency.import = {}; - // TODO - return extend(false, dependency.import, { id: '' }); + amdResolveImportSpecifier(ctx, dependency, metadata, node, ast) { + if(!_.defined(dependency.import)) dependency.import = { modules: Collection.new() }; + Helpers.add(dependency.import.modules, node); + return dependency; } /** @@ -68,12 +71,12 @@ class Amd extends Format { * @param {Object} dependency new dependency to add * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ - amdResolveImportPath(ctx, dependency) { - if(!_.defined(dependency.import)) dependency.import = {}; - // TODO - return extend(false, dependency.import, { path: '' }); + amdResolveImportPath(ctx, dependency, metadata, node, ast) { + dependency.import.path = ctx.reader.file(Helpers.resolvePath(ctx, ast, node), false); + return dependency; } /** @@ -82,6 +85,8 @@ class Amd extends Format { * @param {util.visitor.Visited} ctx context visited * @param {Object} dependency dependency to resolve parent * @param {bundle.task.metadata.Metadata} metadata current metadata + * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ amdResolveParent(ctx, dependency, metadata) { @@ -94,11 +99,12 @@ class Amd extends Format { * @param {util.visitor.Visited} ctx context visited * @param {Object} dependency dependency to resolve parent * @param {bundle.task.metadata.Metadata} metadata current metadata - * @param {astq.Node} ast ast root node for current dependency + * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ - amdResolveAst(ctx, dependency, metadata, ast) { - return super.resolveAst(dependency, ast); + amdResolveAst(ctx, dependency, metadata) { + return super.resolveAst(dependency, ''); } /** @@ -107,7 +113,9 @@ class Amd extends Format { * @property QAMD_ImportDeclaration * @type {String} **/ - static QAMD_ImportDeclaration = '/TODO'; + static QAMD_ImportDeclaration = ` + /ExpressionStatement /CallExpression [/Identifier [@name == 'define']] + `; /** * ASTQ AMD Import Specifier Query @@ -115,7 +123,17 @@ class Amd extends Format { * @property QAMD_ImportSpecifier * @type {String} **/ - static QAMD_ImportSpecifier = `/TODO`; + static QAMD_ImportSpecifier = ` + /ArrayExpression /:elements Literal + `; + + /** + * ASTQ ES6 Import Path Query + * @static + * @property QES6_ImportPath + * @type {String} + **/ + static QAMD_ImportPath = `/FunctionExpression`; /** * ASTQ AMD Export Declaration Query diff --git a/lib/bundle/format/amd/helpers.es6 b/lib/bundle/format/amd/helpers.es6 index e69de29..8db51cb 100644 --- a/lib/bundle/format/amd/helpers.es6 +++ b/lib/bundle/format/amd/helpers.es6 @@ -0,0 +1,79 @@ +/** +* @module bundle.format.amd +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; + +/** +* Retrieves module for a given dependency module list by id. Returns null if module is not found. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {String} id module id to retrieve +* @return {Object} +**/ +export const get = (modules, id) => { + if(!_.defined(id)) return true; + return modules.findWhere({ id }); +}; + +/** +* Resolves Module for a given astq node if it's a named require. +* @public +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveAliased = (node = {}) => { + // TODO + return node; +}; + +/** +* Resolves Module for a given astq node if it doesn't have named require. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveNamed = (modules, node = {}) => { + return { id: node.value }; +}; + +/** +* Resolve Dependency path based on a given node +* @public +* @param {bundle.type.Type} type current type in used +* @param {astq.Node} [node = {}] node to resolve +* @return {String} +**/ +export const resolvePath = (type, ast, node) => { + let out = ''; + //console.log('GIVEN NODE: ', node); + //console.log('PATHS: ', type.collectByElement(ast, 'ImportPath', undefined, ['amd']).get(0)); + return out; +}; + +/** +* Adds a new module into a given dependency module list based on current node ast. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {astq.Node} node current node +* @return {util.adt.Collection} +**/ +export const add = (modules, node) => { + //console.log('AMD Specifier', node.value); + let _module = resolveNamed(modules, resolveAliased(node)); + if(!get(modules, _module.id)) modules.add(_module); + return modules; +}; + +/** +* Removes an existing module from a given dependency module list by module +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {Object} module module to remove +* @return {util.adt.Collection} +**/ +export const remove = (modules, module) => { + // TODO + return modules; +}; diff --git a/lib/bundle/format/cjs/cjs.es6 b/lib/bundle/format/cjs/cjs.es6 index 20157ac..418d6d4 100644 --- a/lib/bundle/format/cjs/cjs.es6 +++ b/lib/bundle/format/cjs/cjs.es6 @@ -5,6 +5,8 @@ import _ from 'util/mixins'; import extend from 'extend'; import Format from 'bundle/format/format'; +import * as Helpers from 'bundle/format/cjs/helpers'; +import Collection from 'util/adt/collection'; import logger from 'util/logger/logger'; /** @@ -55,10 +57,10 @@ class CommonJs extends Format { * @param {astq.Node} node current node ast * @return {Object} **/ - cjsResolveImportSpecifier(ctx, dependency) { - if(!_.defined(dependency.import)) dependency.import = {}; - // TODO - return extend(false, dependency.import, { id: '' }); + cjsResolveImportSpecifier(ctx, dependency, metadata, node) { + if(!_.defined(dependency.import)) dependency.import = { modules: Collection.new() }; + Helpers.add(dependency.import.modules, node); + return dependency; } /** @@ -68,12 +70,12 @@ class CommonJs extends Format { * @param {Object} dependency new dependency to add * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ - cjsResolveImportPath(ctx, dependency) { - if(!_.defined(dependency.import)) dependency.import = {}; - // TODO - return extend(false, dependency.import, { path: '' }); + cjsResolveImportPath(ctx, dependency, metadata, node, ast) { + dependency.import.path = ctx.reader.file(Helpers.resolvePath(ast), false); + return dependency; } /** @@ -83,6 +85,7 @@ class CommonJs extends Format { * @param {Object} dependency dependency to resolve parent * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ cjsResolveParent(ctx, dependency, metadata) { @@ -95,11 +98,12 @@ class CommonJs extends Format { * @param {util.visitor.Visited} ctx context visited * @param {Object} dependency dependency to resolve parent * @param {bundle.task.metadata.Metadata} metadata current metadata - * @param {astq.Node} ast ast root node for current dependency + * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ - cjsResolveAst(ctx, dependency, metadata, ast) { - return super.resolveAst(dependency, ast); + cjsResolveAst(ctx, dependency, metadata) { + return super.resolveAst(dependency, ''); } /** @@ -109,24 +113,22 @@ class CommonJs extends Format { * @type {String} **/ static QCJS_ImportDeclaration = ` - /VariableDeclaration [ - in( * [@kind == 'var' || @kind == 'let' || @kind == 'const']) && - /VariableDeclarator /CallExpression /Identifier [@name == 'require'] - ], /ExpressionStatement [ - /AssignmentExpression /CallExpression /Identifier [@name == 'require'] - ], /ExpressionStatement /CallExpression [ - /Identifier [@name == 'require'] - ] + /VariableDeclaration [/VariableDeclarator [/CallExpression [/Identifier [@name == 'require']]]], + /ExpressionStatement [/AssignmentExpression [/CallExpression [/Identifier [@name == 'require']]]], + /ExpressionStatement [/CallExpression [/Identifier [@name == 'require']]] `; /** * ASTQ CJS Import Specifier Query + * @FIXME: require('hello'); * @static * @property QCJS_ImportSpecifier * @type {String} **/ static QCJS_ImportSpecifier = ` - /TODO + /VariableDeclarator /Identifier, + /AssignmentExpression /Identifier, + /CallExpression `; /** diff --git a/lib/bundle/format/cjs/helpers.es6 b/lib/bundle/format/cjs/helpers.es6 index e69de29..31cd9b0 100644 --- a/lib/bundle/format/cjs/helpers.es6 +++ b/lib/bundle/format/cjs/helpers.es6 @@ -0,0 +1,86 @@ +/** +* @module bundle.format.cjs +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; + +/** +* Retrieves module for a given dependency module list by id. Returns null if module is not found. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {String} id module id to retrieve +* @return {Object} +**/ +export const get = (modules, id) => { + if(!_.defined(id)) return true; + return modules.findWhere({ id }); +}; + +/** +* Resolves Module for a given astq node if it's a named require. +* i.e: const { version } = require('jquery'); +* @public +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveAliased = (node = {}) => { + // TODO + return node; +}; + +/** +* Resolves Module for a given astq node if it doesn't have named require. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {astq.Node} [node = {}] node to resolve +* @return {Object|astq.Node} +**/ +export const resolveNamed = (modules, node = {}) => { + return { id: (node.name ? node.name : node.arguments[0].value) }; +}; + +/** +* Resolve Dependency path based on a given node +* @public +* @param {astq.Node} [node = {}] node to resolve +* @return {String} +**/ +export const resolvePath = (node) => { + let out = ''; + // CASE: let $ = require('jquery'); + if(node.declarations && node.declarations[0] && node.declarations[0].init) + out = node.declarations[0].init.arguments[0].value; + // CASE: LibB = require('dependencies/lib-b'); + if(node.expression && node.expression.right) + out = node.expression.right.arguments[0].value; + // CASE: require('hello'); + if(node.expression && node.expression.arguments) + out = node.expression.arguments[0].value; + // TODO: CASE: case const { version } = require('jquery'); + return out; +}; + +/** +* Adds a new module into a given dependency module list based on current node ast. +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {astq.Node} node current node +* @return {util.adt.Collection} +**/ +export const add = (modules, node) => { + let _module = resolveNamed(modules, resolveAliased(node)); + if(!get(modules, _module.id)) modules.add(_module); + return modules; +}; + +/** +* Removes an existing module from a given dependency module list by module +* @public +* @param {util.adt.Collection} modules current list of dependency modules +* @param {Object} module module to remove +* @return {util.adt.Collection} +**/ +export const remove = (modules, module) => { + // TODO + return modules; +}; diff --git a/lib/bundle/format/es6/es6.es6 b/lib/bundle/format/es6/es6.es6 index 9633d0a..a2cb19e 100644 --- a/lib/bundle/format/es6/es6.es6 +++ b/lib/bundle/format/es6/es6.es6 @@ -71,10 +71,11 @@ class Es6 extends Format { * @param {Object} dependency new dependency to add * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ es6ResolveImportPath(ctx, dependency, metadata, node, ast) { - dependency.import.path = ctx.reader.aliases(ast.source.value); + dependency.import.path = ctx.reader.file(ast.source.value, false); return dependency; } @@ -85,6 +86,7 @@ class Es6 extends Format { * @param {Object} dependency dependency to resolve parent * @param {bundle.task.metadata.Metadata} metadata current metadata * @param {astq.Node} node current node ast + * @param {astq.Node} ast current original source ast reference * @return {Object} **/ es6ResolveParent(ctx, dependency, metadata) { @@ -126,14 +128,6 @@ class Es6 extends Format { /Literal `; - /** - * ASTQ ES6 Import Path Query - * @static - * @property QES6_ImportPath - * @type {String} - **/ - static QES6_ImportPath = `/Literal`; - /** * ASTQ ES6 Export Declaration Query * @static diff --git a/lib/bundle/task/reader/helpers.es6 b/lib/bundle/task/reader/helpers.es6 new file mode 100644 index 0000000..4715901 --- /dev/null +++ b/lib/bundle/task/reader/helpers.es6 @@ -0,0 +1,110 @@ +/** +* @module bundle.task.reader +* @author Patricio Ferreira <3dimentionar@gmail.com> +**/ +import _ from 'util/mixins'; +import fs from 'fs-extra'; +import extend from 'extend'; +import glob from 'glob'; +import * as acorn from 'acorn'; +import Collection from 'util/adt/collection'; + +/** +* Default Acorn Options +* @private +* @property acornOptions +* @type {Object} +**/ +const acornOptions = { + ecmaVersion: 8, + sourceType: 'module' +}; + +/** +* Default Glob Options +* @private +* @property globOptions +* @type {Object} +**/ +const globOptions = { + strict: true, + nosort: true, + nodir: true +}; + +/** +* Performs a look up over the parsed files by using the file path +* and retrieves the file reference if it's found, returns null otherwise. +* @private +* @param {String|util.adt.Collection} [input = []] single path or a collection of paths +* @return {Object} +**/ +const get = (list, input = []) => { + let paths = _.isString(input) ? Collection.new([input]) : input; + return list.find((file) => paths.contains(file.path)); +}; + + +/** +* Add a new file into the parsed file list +* @public +* @param {util.adt.Collection} list parsed file list +* @param {String} path file path to remove +* @param {Boolean} [skipParse = false] skip ast parsing +* @return {util.adt.Collection} +**/ +export const add = (list, path, skipParse = false) => { + list.add(!skipParse ? parse(path, acornOptions) : path); + return list; +}; + +/** +* Remove existing parsed file from the list by path +* @public +* @param {util.adt.Collection} list parsed file list +* @param {String} path file path to remove +* @return {util.adt.Collection} +**/ +export const remove = (list, path) => { + return list.removeBy((file) => (file === path)); +}; + +/** +* Perform a file look up over the list of parsed files, for a given input and retrieves it when found. +* If the file is not found, this method will add it and returns the reference. +* @private +* @param {util.adt.Collection} files list of parsed files +* @param {String} input file path to resolve +* @param {Any} [...args] additional and optional arguments +* @return {util.adt.Collection} +**/ +export const resolve = (list, input) => { + let found = get(list, input); + if(!_.defined(found)) add(list, input); + return list; +}; + +/** +* Read a single file by pattern or explicit path +* @public +* @param {String} cwd base path +* @param {String} path path to read files from (pattern or explicit path) +* @param {Array} [ignore = []] optional ignore list of directories/files +* @return {util.adt.Collection} +**/ +export const read = (cwd, path, ignore = []) => { + return Collection.new(glob.sync(path, _.defaults({ cwd, ignore }, globOptions))); +}; + +/** +* Retrieve a given file from the files list +* @public +* @param {String} path file path +* @param {Object} [options = {}] parser options +* @param {Array} [comments = []] comments initial array +* @return {Object} +**/ +export const parse = (path, options = {}, comments = []) => { + let opts = extend(false, { onComment: comments }, options); + return { path, comments, input: acorn.parse(fs.readFileSync(path, { encoding: 'utf8' }), opts) }; +}; diff --git a/lib/bundle/task/reader/reader.es6 b/lib/bundle/task/reader/reader.es6 index 5b5ed70..2039962 100644 --- a/lib/bundle/task/reader/reader.es6 +++ b/lib/bundle/task/reader/reader.es6 @@ -3,13 +3,8 @@ * @author Patricio Ferreira <3dimentionar@gmail.com> **/ import _ from 'util/mixins'; -import fs from 'fs-extra'; -import path from 'path'; -import extend from 'extend'; -import glob from 'glob'; -import * as acorn from 'acorn'; +import * as Helpers from 'bundle/task/reader/helpers'; import Collection from 'util/adt/collection'; -import StackAsync from 'util/adt/stack-async'; import Task from 'bundle/task/task'; /** @@ -29,65 +24,45 @@ class Reader extends Task { * @return {Promise} **/ read(vi) { - return this.types.pop({}, false, this); + return this.all(this.scan).types.pop({}, false, this); } /** - * Retrieves files metadata + * Read and parse all files by an optional resolver function. + * If no custom resolver is provided, the default resolver will be used. * @public - * @return {util.adt.Collection} - **/ - files() { - if(_.defined(this.parsedFiles)) return this.parsedFiles; - extend(false, this, { parsedFiles: this.readFiles().reduce(this.add, Collection.new(), this) }); - return this.parsedFiles; - } - - /** - * File Parsing Strategy - * @public - * @param {util.adt.Collection} memo memoized collection that will hold metadata found - * @param {String} path file path to parse + * @param {String} pattern source pattern to read file/s from + * @param {Function} [resolver = this.helper.resolve] resolver function that manipulates parsed files * @return {bundle.task.reader.Reader} **/ - add(memo, path) { - let comments = [], input = this.parse(path, extend(false, { onComment: comments }, Reader.acornOptions)); - memo.add({ path, input, comments }); - return memo; - } - - /** - * Retrieve parsed file (if it was already parsed), returns null otherwise - * @public - * @param {String} input input file path - * @return {Object} - **/ - get(input) { - let file = this.parsedFiles.findWhere({ path: this.file(input, true) }); - //console.log(this.parsedFiles); - return file; // (`Found: ${input} -> ` + _.defined(file)); + all(pattern, resolver = this.helper.resolve) { + let readFiles = this.helper.read(this.cwd, this.file(pattern, true), this.excludes()) + readFiles.reduce(resolver, this.externals(path)); + return this; } /** - * Acorn Parsing Strategy + * Filter parsed files by path * @public - * @param {String} source file path to parse - * @return {Object} + * @param {String} path file path + * @return {Array} **/ - parse(source, ...args) { - return acorn.parse(fs.readFileSync(source, { encoding: 'utf8' }), ...args); + allByPath(path) { + return Collection.new(this.files.filter((file) => file.path.indexOf(path) !== -1)); } /** - * Read Files + * Resolve pattern with external configuration. + * If a pattern matches an external dependency, + * it will be added to the collection with the flag external set to true. * @public - * @return {util.adt.Collection} + * @param {util.adt.Collection} files list of files read + * @param {String} path source pattern/path to resolve + * @return {String} **/ - readFiles() { - return Collection.new(glob.sync(this.sources(), _.defaults({ - cwd: this.cwd, - ignore: this.excludes() - }, Reader.globOptions))); + externals(path) { + if(_.contains(this.external, path)) this.helper.add(this.files, { path, external: true }, true); + return this.files; } /** @@ -112,36 +87,22 @@ class Reader extends Task { } /** - * Visitor Name + * Reader's Helper * @public - * @type {String} + * @type {bundle.task.reader.helpers} **/ - get name() { - return 'Reader'; + get helper() { + return Helpers; } /** - * Default Glob Options + * Visitor Name * @public - * @property globOptions - * @type {Object} - **/ - static globOptions = { - strict: true, - nosort: true, - nodir: true - }; - - /** - * Default Acorn Options - * @static - * @property acornOptions - * @type {Object} + * @type {String} **/ - static acornOptions = { - ecmaVersion: 8, - sourceType: 'module' - }; + get name() { + return 'Reader'; + } /** * Events diff --git a/lib/bundle/types/annotation/annotation.es6 b/lib/bundle/types/annotation/annotation.es6 index 2ed41e9..c47e3b6 100644 --- a/lib/bundle/types/annotation/annotation.es6 +++ b/lib/bundle/types/annotation/annotation.es6 @@ -31,7 +31,7 @@ class Annotation extends Type { * @return {util.adt.Collection} **/ annotations() { - return this.reader.files().reduce(this.annotation, Collection.new(), this); + return this.reader.files.reduce(this.annotation, Collection.new(), this); } /** diff --git a/lib/bundle/types/common/collector.es6 b/lib/bundle/types/common/collector.es6 index 7ab503e..cf9a771 100644 --- a/lib/bundle/types/common/collector.es6 +++ b/lib/bundle/types/common/collector.es6 @@ -25,7 +25,7 @@ class Collector extends Visitor { * @return {Array} **/ iterate(ast, methods, ...args) { - return Collection.new(_.chain(methods).map((method) => method(ast, ...args)).flatten().value()); + return Collection.new(_.chain(methods).map((method) => method(ast, ...args)).flatten().uniq().value()); } /** diff --git a/lib/bundle/types/import/import.es6 b/lib/bundle/types/import/import.es6 index 76e90be..818cafe 100644 --- a/lib/bundle/types/import/import.es6 +++ b/lib/bundle/types/import/import.es6 @@ -22,12 +22,12 @@ class Import extends Type { * @return {Promise} **/ read() { - return this.reader.bundles.reduce(this.readDependencies, true, this) ? - this.resolve(this) : this.reject(this); + let result = this.reader.bundles.reduce(this.readBundle, true, this); + return result ? this.resolve(this) : this.reject(this); } /** - * Recursive Strategy to query dependencies and attach them into the metadata. + * Read all bundles * @public * @param {Boolean} memo memoized boolean result * @param {bundle.task.metadata.Metadata} metadata instance of meta found as a bundle @@ -35,9 +35,22 @@ class Import extends Type { * @param {util.adt.Collection} collection original metadata collection * @return {Boolean} **/ - readDependencies(memo, metadata) { - this.collectByType(metadata.input, _.bind(this.dependency, this, metadata)); - return metadata.dependencies.reduce(this.onDependenciesRead, memo, this); + readBundle(memo, metadata) { + return this.readDependencies(memo, metadata, metadata.input); + } + + /** + * Recursive Strategy to query dependencies and attach them into the metadata. + * @public + * @param {Boolean} memo memoized boolean result + * @param {bundle.task.metadata.Metadata} metadata current bundle reference + * @param {astq.Node} input current ast node used to collect + * @param {Object} [file] current parsed file + * @return {Boolean} + **/ + readDependencies(memo, metadata, input, file) { + this.collectByType(input, _.bind(this.dependency, this, metadata)); + return metadata.dependencies.reduce(_.bind(this.onDependenciesRead, this, metadata), memo); } /** @@ -64,6 +77,7 @@ class Import extends Type { * @return {Object} **/ createDependency(metadata, ast, format) { + //if(format === 'AMD') console.log('AMD AST: ', ast); return this.collectByElement(ast, Format.elements.ImportSpecifier) .reduce(_.bind(this.resolveDependency, this, format, metadata, ast), {}); } @@ -77,7 +91,7 @@ class Import extends Type { * @param {astq.Node} node current dependency node reference **/ resolveDependency(format, metadata, ast, dependency, node) { - return Import.resolversBy(format, this).reduce((dependency, resolve) => { + return Import.by(format, this).reduce((dependency, resolve) => { return resolve(dependency, metadata, node, ast); }, dependency); } @@ -85,14 +99,27 @@ class Import extends Type { /** * Dependencies Query Result Handler * @public - * @param {Boolean} memo memoized boolean result * @param {bundle.task.metadata.Metadata} metadata current metadata - * @param {Array} results list of ast results - * @return {bundle.type.import.Import} + * @param {Boolean} memo memoized boolean result + * @param {bundle.task.metadata.Dependency} dependency current dependency + * @return {Boolean} **/ - onDependenciesRead(memo, dependency) { - console.log(this.reader.get(dependency.import.path)); - //results.map(_.bind(this.create, this, memo, this.reader.files()), this); + onDependenciesRead(metadata, memo, dependency) { + //console.log('------------------------'); + let out = this.reader.all(dependency.import) + .allByPath(dependency.import.path); + // .reduce((memo, file) => { + // console.log(file.path); + // let out = this.readDependencies(memo, metadata, file.input, file) + // console.log('------'); + // return out; + // }, memo); + if(dependency.import.path.indexOf('amd-lib') !== -1) { + //console.log('I: ', out.get(0).input.body[0].expression.arguments); + //console.log('******************'); + this.collectByType(out.get(0).input, _.bind(this.dependency, this, metadata), ['amd']); + //console.log(metadata.path, metadata.dependencies._collection); + } return memo; } @@ -122,17 +149,17 @@ class Import extends Type { * @param {String} format * @return {Array} **/ - static resolversBy = (format, instance) => { - return Collection.new(_.map(Import.resolvers, (name) => instance[`${format.toLowerCase()}${name}`])); + static by = (format, instance) => { + return Collection.new(_.map(Import.elements, (name) => instance[`${format.toLowerCase()}${name}`])); } /** - * Method Name Resolvers + * Import Element Resolvers * @static - * @property resolvers + * @property elements * @type {Array} **/ - static resolvers = [ + static elements = [ 'ResolveImportSpecifier', 'ResolveImportPath', 'ResolveParent', diff --git a/lib/visitors/command/properties.es6 b/lib/visitors/command/properties.es6 index dbd7c19..1c4f260 100644 --- a/lib/visitors/command/properties.es6 +++ b/lib/visitors/command/properties.es6 @@ -23,17 +23,19 @@ class Properties extends Visitor { **/ file(ctx, input, ext = false) { let expr = ext ? `${input}.+(${this.extensions.join('|')})` : input; - return path.resolve(this.basePath, input); + return path.resolve(this.basePath, this.aliases(ctx, expr)); } /** - * Resolve scan sources + * Resolve extension based on input and applies it to the path * @public * @param {bundle.Command} ctx current command + * @param {String} input resolved path including the extension + * @param {String} path path to attach the extension into from the input * @return {String} **/ - sources(ctx) { - return this.file(ctx, this.scan, true); + extension(ctx, input, path) { + return (input && input.indexOf(path) !== -1) ? `${path}${_s.strRightBack(input, path)}` : path; } /** @@ -59,7 +61,7 @@ class Properties extends Visitor { **/ excludes(ctx) { return _.reduce(this.exclude, (memo, pattern) => { - memo.push(path.resolve(this.basePath, this.scan, pattern)); + memo.push(this.file(ctx, pattern, false)); return memo; }, []); } diff --git a/test/specs/es6/libs/amd-lib.js b/test/specs/es6/libs/amd-lib.js index c27f759..2984c83 100644 --- a/test/specs/es6/libs/amd-lib.js +++ b/test/specs/es6/libs/amd-lib.js @@ -1,8 +1,12 @@ /** * Simulation of a AMD Library **/ -define(['jquery', 'dependencies/lib-a'], function($, LibA) { - - return { type: 'Amd', jquery: $ }; +// define(['jquery', 'dependencies/lib-a'], function($, LibA) { +// return { type: 'Amd', jquery: $ }; +// }); +define(['require', 'exports', 'dependencies/lib-a'], function(require, exports, LibA) { + exports.something = function() { + return require('dependencies/lib-a'); + }; }); diff --git a/test/specs/es6/libs/cjs-lib.js b/test/specs/es6/libs/cjs-lib.js index 6d360d0..13b03e2 100644 --- a/test/specs/es6/libs/cjs-lib.js +++ b/test/specs/es6/libs/cjs-lib.js @@ -1,7 +1,8 @@ /** -* Simulation of a CommonJS Library +* Simulation of a CommonJs Library **/ -var $ = require('jquery'); -var LibB = require('dependencies/lib-b'); +let $ = require('jquery'); +LibB = require('dependencies/lib-b'); +require('hello'); module.exports = { type: 'CommonJs', jquery: $ }; From 200de1067698f8859033302559a9d9188ebf795a Mon Sep 17 00:00:00 2001 From: kuakman <3dimentionar@gmail.com> Date: Wed, 12 Jul 2017 10:00:31 -0700 Subject: [PATCH 22/22] bundle: checkpoint - added gitter badge and webhook for push notifications. --- .gitignore | 3 +++ .travis.yml | 7 +++++++ README.md | 1 + 3 files changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index a0d231d..7324306 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ node_modules # Logs & System *.log *.pid + +# IDE +.idea diff --git a/.travis.yml b/.travis.yml index 9a56ac8..6edae9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,10 @@ script: - npm run it after_success: - test $TRAVIS_BRANCH = "master" && npm run coveralls +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/19547337979e0c84f48d + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/README.md b/README.md index f64d250..e28a45a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://travis-ci.org/nahuelio/squarebox.svg?branch=master)](https://travis-ci.org/nahuelio/squarebox) [![Coverage Status](https://coveralls.io/repos/github/nahuelio/squarebox/badge.svg)](https://coveralls.io/github/nahuelio/squarebox) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sqbox/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Version](https://img.shields.io/badge/Version-1.0.0-blue.svg?style=flat)]() [![Version](https://img.shields.io/badge/License-MIT-blue.svg?style=flat)](http://www.opensource.org/licenses/mit-license.php) [![GitHub stars](https://img.shields.io/github/stars/nahuelio/squarebox.svg?style=social&label=Stars)]()