diff --git a/.gitignore b/.gitignore index aeb3a3b..93af094 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,13 @@ node_modules/ components/ experiments/ .idea/ +*.iml +bower_components/ +.DS_Store *~ +.project +.setting +/npm-debug.log +/sauce_connect.log* +.buildpath +.classpath diff --git a/builder/cram.js b/builder/cram.js index ba7088d..88584a6 100644 --- a/builder/cram.js +++ b/builder/cram.js @@ -1,238 +1,216 @@ -/** @license MIT License (c) copyright B Cavalier & J Hann */ - -/** - * wire/cram/builder plugin - * Builder plugin for cram - * https://github.com/cujojs/cram - * - * wire is part of the cujo.js family of libraries (http://cujojs.com/) - * - * Licensed under the MIT License at: - * http://www.opensource.org/licenses/mit-license.php - */ -(function(define) { -define(function(require) { - - var when, unfold, defaultModuleRegex, defaultSpecRegex, replaceIdsRegex, - removeCommentsRx, splitSpecsRegex; - - when = require('when'); - unfold = require('when/unfold'); - - // default dependency regex - defaultModuleRegex = /\.(module|create)$/; - defaultSpecRegex = /\.(wire\.spec|wire)$/; - // adapted from cram's scan function: - //replaceIdsRegex = /(define)\s*\(\s*(?:\s*["']([^"']*)["']\s*,)?(?:\s*\[([^\]]+)\]\s*,)?\s*(function)?\s*(?:\(([^)]*)\))?/g; - replaceIdsRegex = /(define)\s*\(\s*(?:\s*["']([^"']*)["']\s*,)?(?:\s*\[([^\]]*)\]\s*,)?/; - removeCommentsRx = /\/\*[\s\S]*?\*\/|\/\/.*?[\n\r]/g; - splitSpecsRegex = /\s*,\s*/; - - return { - normalize: normalize, - compile: compile - }; - - function normalize(resourceId, toAbsId) { - return resourceId ? toAbsId(resourceId.split("!")[0]) : resourceId; - } - - function compile(wireId, resourceId, require, io, config) { - // Track all modules seen in wire spec, so we only include them once - var specIds, defines, seenModules, childSpecRegex, - moduleRegex; - - defines = []; - seenModules = {}; - moduleRegex = defaultModuleRegex; - childSpecRegex = defaultSpecRegex; - - // Get config values - if(config) { - if(config.moduleRegex) moduleRegex = new RegExp(config.moduleRegex); - if(config.childSpecRegex) childSpecRegex = new RegExp(config.childSpecRegex); - } - - // Grab the spec module id, *or comma separated list of spec module ids* - // Split in case it's a comma separated list of spec ids - specIds = resourceId.split(splitSpecsRegex); - - return when.map(specIds, function(specId) { - return processSpec(specId); - }).then(write, io.error); - - // For each spec id, add the spec itself as a dependency, and then - // scan the spec contents to find all modules that it needs (e.g. - // "module" and "create") - function processSpec(specId) { - var dependencies, ids; - - dependencies = []; - ids = [specId]; - - addDep(wireId); - - return unfold(fetchNextSpec, endOfList, scanSpec, ids) - .then(function() { - return generateDefine(specId, dependencies); - } - ); - - - function fetchNextSpec() { - var id, dfd; - - id = ids.shift(); - dfd = when.defer(); - - require( - [id], - function(spec) { dfd.resolve([spec, ids]); }, - dfd.reject - ); - - return dfd.promise; - } - - function scanSpec(spec) { - scanPlugins(spec); - scanObj(spec); - } - - function scanObj(obj, path) { - // Scan all keys. This might be the spec itself, or any sub-object-literal - // in the spec. - for (var name in obj) { - scanItem(obj[name], createPath(path, name)); - } - } - - function scanItem(it, path) { - // Determine the kind of thing we're looking at - // 1. If it's a string, and the key is module or create, then assume it - // is a moduleId, and add it as a dependency. - // 2. If it's an object or an array, scan it recursively - // 3. If it's a wire spec, add it to the list of spec ids - if (isSpec(path) && typeof it === 'string') { - addSpec(it); - - } else if (isDep(path) && typeof it === 'string') { - // Get module def - addDep(it); - - } else if (isStrictlyObject(it)) { - // Descend into subscope - scanObj(it, path); - - } else if (Array.isArray(it)) { - // Descend into array - var arrayPath = path + '[]'; - it.forEach(function(arrayItem) { - scanItem(arrayItem, arrayPath); - }); - } - } - - function scanPlugins(spec) { - var plugins = spec.$plugins || spec.plugins; - - if(Array.isArray(plugins)) { - plugins.forEach(addPlugin); - } else if(typeof plugins === 'object') { - Object.keys(plugins).forEach(function(key) { - addPlugin(plugins[key]); - }); - } - } - - function addPlugin(plugin) { - if(typeof plugin === 'string') { - addDep(plugin); - } else if(typeof plugin === 'object' && plugin.module) { - addDep(plugin.module); - } - } - - function addDep(moduleId) { - if(!(moduleId in seenModules)) { - dependencies.push(moduleId); - seenModules[moduleId] = moduleId; - } - } - - function addSpec(specId) { - if(!(specId in seenModules)) { - ids.push(specId); - } - addDep(specId); - } - } - - function generateDefine(specId, dependencies) { - var dfd, buffer; - - dfd = when.defer(); - - io.read(ensureExtension(specId, 'js'), function(specText) { - buffer = injectIds(specText, specId, dependencies); - - defines.push(buffer); - dfd.resolve(); - - }, dfd.reject); - - return dfd.promise; - } - - function write() { - // protect against prior code that may have omitted a semi-colon - io.write('\n;' + defines.join('\n')); - } - - function isDep(path) { - return moduleRegex.test(path); - } - - function isSpec(path) { - return childSpecRegex.test(path); - } - } - - function createPath(path, name) { - return path ? (path + '.' + name) : name - } - - function isStrictlyObject(it) { - return (it && Object.prototype.toString.call(it) == '[object Object]'); - } - - function ensureExtension(id, ext) { - return id.lastIndexOf('.') <= id.lastIndexOf('/') - ? id + '.' + ext - : id; - } - - function injectIds (moduleText, absId, moduleIds) { - // note: replaceIdsRegex removes commas, parens, and brackets - return moduleText.replace(removeCommentsRx, '').replace(replaceIdsRegex, function (m, def, mid, depIds) { - - // merge deps, but not args since they're not referenced in module - if (depIds) moduleIds = moduleIds.concat(depIds); - - moduleIds = moduleIds.map(quoted).join(', '); - if (moduleIds) moduleIds = '[' + moduleIds + '], '; - - return def + '(' + quoted(absId) + ', ' + moduleIds; - }); - } - - function quoted (id) { - return '"' + id + '"'; - } - - function endOfList(ids) { - return !ids.length; - } - -}); -}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); +/** @license MIT License (c) copyright B Cavalier & J Hann */ + +/** + * wire/cram/builder plugin + * Builder plugin for cram + * https://github.com/cujojs/cram + * + * wire is part of the cujo.js family of libraries (http://cujojs.com/) + * + * Licensed under the MIT License at: + * http://www.opensource.org/licenses/mit-license.php + */ +(function(define) { +define(function(require) { + + var when, unfold, defaultModuleRegex, defaultSpecRegex, splitSpecsRegex; + + when = require('when'); + unfold = require('when/unfold'); + parser = require('./cram/parser'); + + // default dependency regex + defaultModuleRegex = /\.(module|create)$/; + defaultSpecRegex = /\.(wire\.spec|wire)$/; + splitSpecsRegex = /\s*,\s*/; + + return { + normalize: normalize, + compile: compile + }; + + function normalize(resourceId, toAbsId) { + return resourceId ? toAbsId(resourceId.split("!")[0]) : resourceId; + } + + function compile(wireId, resourceId, require, io, config) { + // Track all modules seen in wire spec, so we only include them once + var specIds, defines, seenModules, childSpecRegex, + moduleRegex; + + defines = []; + seenModules = {}; + moduleRegex = defaultModuleRegex; + childSpecRegex = defaultSpecRegex; + + // Get config values + if(config) { + if(config.moduleRegex) moduleRegex = new RegExp(config.moduleRegex); + if(config.childSpecRegex) childSpecRegex = new RegExp(config.childSpecRegex); + } + + // Grab the spec module id, *or comma separated list of spec module ids* + // Split in case it's a comma separated list of spec ids + specIds = resourceId.split(splitSpecsRegex); + + return when.map(specIds, function(specId) { + return processSpec(specId); + }).then(write, io.error); + + // For each spec id, add the spec itself as a dependency, and then + // scan the spec contents to find all modules that it needs (e.g. + // "module" and "create") + function processSpec(specId) { + var dependencies, ids; + + dependencies = []; + ids = [specId]; + + addDep(wireId); + + return unfold(fetchNextSpec, endOfList, scanSpec, ids) + .then(function() { + return generateDefine(specId, dependencies); + } + ); + + + function fetchNextSpec() { + var id, dfd; + + id = ids.shift(); + dfd = when.defer(); + + require( + [id], + function(spec) { dfd.resolve([spec, ids]); }, + dfd.reject + ); + + return dfd.promise; + } + + function scanSpec(spec) { + scanPlugins(spec); + scanObj(spec); + } + + function scanObj(obj, path) { + // Scan all keys. This might be the spec itself, or any sub-object-literal + // in the spec. + for (var name in obj) { + scanItem(obj[name], createPath(path, name)); + } + } + + function scanItem(it, path) { + // Determine the kind of thing we're looking at + // 1. If it's a string, and the key is module or create, then assume it + // is a moduleId, and add it as a dependency. + // 2. If it's an object or an array, scan it recursively + // 3. If it's a wire spec, add it to the list of spec ids + if (isSpec(path) && typeof it === 'string') { + addSpec(it); + + } else if (isDep(path) && typeof it === 'string') { + // Get module def + addDep(it); + + } else if (isStrictlyObject(it)) { + // Descend into subscope + scanObj(it, path); + + } else if (Array.isArray(it)) { + // Descend into array + var arrayPath = path + '[]'; + it.forEach(function(arrayItem) { + scanItem(arrayItem, arrayPath); + }); + } + } + + function scanPlugins(spec) { + var plugins = spec.$plugins || spec.plugins; + + if(Array.isArray(plugins)) { + plugins.forEach(addPlugin); + } else if(typeof plugins === 'object') { + Object.keys(plugins).forEach(function(key) { + addPlugin(plugins[key]); + }); + } + } + + function addPlugin(plugin) { + if(typeof plugin === 'string') { + addDep(plugin); + } else if(typeof plugin === 'object' && plugin.module) { + addDep(plugin.module); + } + } + + function addDep(moduleId) { + if(!(moduleId in seenModules)) { + dependencies.push(moduleId); + seenModules[moduleId] = moduleId; + } + } + + function addSpec(specId) { + if(!(specId in seenModules)) { + ids.push(specId); + } + addDep(specId); + } + } + + function generateDefine(specId, dependencies) { + var dfd, buffer; + + dfd = when.defer(); + + io.read(ensureExtension(specId, 'js'), function(specText) { + buffer = parser.injectIds(specText, specId, dependencies); + + defines.push(buffer); + dfd.resolve(); + + }, dfd.reject); + + return dfd.promise; + } + + function write() { + // protect against prior code that may have omitted a semi-colon + io.write('\n;' + defines.join('\n')); + } + + function isDep(path) { + return moduleRegex.test(path); + } + + function isSpec(path) { + return childSpecRegex.test(path); + } + } + + function createPath(path, name) { + return path ? (path + '.' + name) : name + } + + function isStrictlyObject(it) { + return (it && Object.prototype.toString.call(it) == '[object Object]'); + } + + function ensureExtension(id, ext) { + return id.lastIndexOf('.') <= id.lastIndexOf('/') + ? id + '.' + ext + : id; + } + + function endOfList(ids) { + return !ids.length; + } + +}); +}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); diff --git a/builder/cram/parser.js b/builder/cram/parser.js new file mode 100644 index 0000000..caa22b9 --- /dev/null +++ b/builder/cram/parser.js @@ -0,0 +1,49 @@ +/** @license MIT License (c) copyright F. Matrat */ + +/** + * TODO Fabrice. + * https://github.com/cujojs/cram + * + * wire is part of the cujo.js family of libraries (http://cujojs.com/) + * + * Licensed under the MIT License at: + * http://www.opensource.org/licenses/mit-license.php + */ +(function(define) { +define(function(require) { + + var replaceIdsRx, removeCommentsRx; + + // adapted from cram's scan function: + //replaceIdsRegex = /(define)\s*\(\s*(?:\s*["']([^"']*)["']\s*,)?(?:\s*\[([^\]]+)\]\s*,)?\s*(function)?\s*(?:\(([^)]*)\))?/g; + replaceIdsRx = /(define)\s*\(\s*(?:\s*["']([^"']*)["']\s*,)?(?:\s*\[([^]]*)]\s*,)?/; + removeCommentsRx = /\/\*[\s\S]*?\*\/|\/\/.*?[\n\r]/g; + //removeCommentsRx = /define\(([\s*\/\/|\/\*].*)[\s\S]*\{/g; + //removeCommentsRx = /define\([\{\}\/\w\S\s]*\*\/\n\r\{/g; + //removeCommentsRx = /define\([\{\}\/\w\S\s]*\*\/\n\{/g; + return { + injectIds: injectIds + }; + + function injectIds(moduleText, absId, moduleIds) { + // note: replaceIdsRx removes commas, parens, and brackets + // return moduleText.replace(removeCommentsRx, 'define({') + return moduleText.replace(removeCommentsRx, '') + .replace(replaceIdsRx, function(m, def, mid, depIds) { + // merge deps, but not args since they're not referenced in module + if (depIds) moduleIds = moduleIds.concat(depIds); + + moduleIds = moduleIds.map(quoted).join(', '); + if (moduleIds) moduleIds = '[' + moduleIds + '], '; + + return def + '(' + quoted(absId) + ', ' + moduleIds; + } + ); + } + + function quoted(id) { + return '"' + id + '"'; + } + +}); +}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); diff --git a/test/node/builder/cram/parser-test.js b/test/node/builder/cram/parser-test.js new file mode 100644 index 0000000..cc55ff1 --- /dev/null +++ b/test/node/builder/cram/parser-test.js @@ -0,0 +1,72 @@ +var buster, assert, refute, fail, builder, forEach; + +buster = require('buster'); +assert = buster.assert; +refute = buster.refute; +fail = buster.assertions.fail; + +parser = require('../../../../builder/cram/parser'); + +forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); + +buster.testCase('wire/builder/cram/parser', { + 'should write a named define': function() { + var spec, test; + test = "test" + spec ="{\n\ra: { test: 'a' }\n\r}"; + assert.equals(parser.injectIds(specObjectToModule(spec), test, []), specObjectToModuleWithIds(spec, test)); + }, + 'should write a named define even with a one line comment': function() { + var spec, test, result; + test = "test" + spec = "{\n\r"; + result = "{\n\r"; + spec += "//This is my comment\n\r"; + spec += "a: { test: 'a' }}"; + result += "a: { test: 'a' }}"; + assert.equals(parser.injectIds(specObjectToModule(spec), test, []).replace(/(\n|\r)/g,''), specObjectToModuleWithIds(result, test).replace(/(\n|\r)/g,'')); + }, + 'should write a named define even with a comment at the end of an existing line': function() { + var spec, test, result; + test = "test" + spec = "{\n\r"; + result = "{\n\r"; + spec += "a: { test: 'a' }//This is my comment\n\r}"; + result += "a: { test: 'a' }}"; + assert.equals(parser.injectIds(specObjectToModule(spec), test, []).replace(/(\n|\r)/g,''), specObjectToModuleWithIds(result, test).replace(/(\n|\r)/g,'')); + }, + 'should write a named define even with a comment at the end of the first line': function() { + var spec, test, result; + test = "test" + spec = "{//This is my comment\n\r"; + result = "{\n\r"; + spec += "a: { test: 'a' }}"; + result += "a: { test: 'a' }}"; + assert.equals(parser.injectIds(specObjectToModule(spec), test, []).replace(/(\n|\r)/g,''), specObjectToModuleWithIds(result, test).replace(/(\n|\r)/g,'')); + }, + 'should write a named define even with a comment inbetween': function() { + var spec, test, result; + test = "test" + spec = "/*This is my comment*/ {\n\r"; + result = "{\n\r"; + spec += "a: { test: 'a' }}"; + result += "a: { test: 'a' }}"; + assert.equals(parser.injectIds(specObjectToModule(spec), test, []).replace(/(\n|\r)/g,''), specObjectToModuleWithIds(result, test).replace(/(\n|\r)/g,'')); + }, + 'should write a named define even if one value contains a comment like but is not a comment': function() { + var spec, test, result; + test = "test" + spec = "/*Comment to be removed*/ {\n\r"; + result = "{\n\r"; + spec += "a: { test: 'http://example.com' }\n\r}"; + result += "a: { test: 'http://example.com' }\n\r}"; + assert.equals(parser.injectIds(specObjectToModule(spec), test, []).replace(/(\n|\r)/g,''), specObjectToModuleWithIds(result, test).replace(/(\n|\r)/g,'')); + } +}); + +function specObjectToModule(spec) { + return 'define(' + spec + ');'; +} +function specObjectToModuleWithIds(spec, id) { + return 'define("' + id + '", ' + spec + ');'; +} diff --git a/test/util b/test/util index 7752157..9e1dc62 160000 --- a/test/util +++ b/test/util @@ -1 +1 @@ -Subproject commit 77521579aa62f945d42c60ec0d100e3fc21e3078 +Subproject commit 9e1dc62201b7cd06f1c052bae663882003cd3957