From fd689303a65e71d26c231f9b7f41ae9316694163 Mon Sep 17 00:00:00 2001 From: Manuel Mujica Date: Mon, 8 May 2017 10:20:23 -0600 Subject: [PATCH] Fix substitution variation detection algorithm a) modules using plugins, e.g: "#{foo}.css!" b) substitution of folder names, e.g: "/foo/#{platform}/index.js" Closes #38 --- conditional.js | 88 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/conditional.js b/conditional.js index 03f849c..13b10a5 100644 --- a/conditional.js +++ b/conditional.js @@ -43,6 +43,31 @@ define(['module'], function(module) { return Promise.resolve(); } + /** + * Returns the bundle module name for a string substitution + * @param {string} nameWithConditional The module identifier including the condition + * @param {string} variation A match of the glob pattern + * @return {string} The bundle module name + */ + function getModuleName(nameWithConditional, variation) { + var modName; + var conditionIndex = nameWithConditional.search(conditionalRegEx); + + // look for any "/" after the condition + var lastSlashIndex = nameWithConditional.indexOf("/", + nameWithConditional.indexOf("}")); + + // substitution of a folder name + if (lastSlashIndex !== -1) { + modName = nameWithConditional.substr(0, conditionIndex) + variation; + } + else { + modName = nameWithConditional.replace(conditionalRegEx, variation); + } + + return modName; + } + loader.normalize = function(name, parentName, parentAddress, pluginNormalize) { var loader = this; @@ -71,8 +96,51 @@ define(['module'], function(module) { var handleConditionalBuild = function() {}; + /** + * Adds bundles needed to load conditional modules in production + * + * Conditional modules are loaded dynamically, these modules are + * not part of the dependency graph and steal-tools has to create + * individual bundles for each of them. + * + * ** The boolean syntax ** + * + * After removing the condition from the module identifier, the + * result is the identifier of the module that could be loaded + * during runtime, e.g: + * + * With an indentifier like: `es5-shim#?conditions.needs-es5shim`, + * removing everything after the condition gives us back `es5-shim` + * which is then configured to be made its own bundle by steal-tools. + * + * ** The substitution syntax ** + * + * This one is more involved, since there is no way statically to + * figure out all of the possible string substitution value. The + * following algorithm is used to detect them: + * + * 1) The condition is replaced by a placeholder in the module + * identifier; e.g: `jquery/#{browser.grade}` is turned into + * `jquery/__PLACEHOLDER__`. + * + * 2) The result of step 1 is normalised + * + * 3) The placeholder is replaced by `*` which results in a module + * name that looks like: `app@0.0.1#jquery/*` + * + * 4) The module name from step 3 is run through `locate` which + * returns the address where the variations are located. + * + * 5) The address is then turned into a glob pattern and used to + * match the files that could be loaded + * + * 6) The process is then reverted; the modules addresses are turned + * into module names which will be set to `loader.bundle` so + * bundles are created for each of them. + */ //!steal-remove-start handleConditionalBuild = function() { + var PLACEHOLDER = "__PLACEHOLDER__"; var setBundlesPromise = Promise.resolve(); // make sure loader.bundle is an array @@ -85,7 +153,7 @@ define(['module'], function(module) { // remove the conditional and the trailing slash var nameWithoutConditional = name - .replace(conditionalRegEx, "") + .replace(conditionalRegEx, PLACEHOLDER) .replace(/\/+$/, ""); setBundlesPromise = getGlob() @@ -105,16 +173,19 @@ define(['module'], function(module) { parentName, parentAddress, pluginNormalize); }) .then(function(normalized) { - return loader.locate({ name: normalized + "/*", metadata: {} }); + return loader.locate({ + metadata: {}, + name: normalized.replace(PLACEHOLDER, "*") + }); }) .then(function(address) { var path = address.replace("file:", ""); - var parts = path.split("/"); - var pattern = parts.pop(); + var cwd = path.substr(0, path.indexOf("*")); + var pattern = path.substring(path.indexOf("*")); return new Promise(function(resolve, reject) { var options = { - cwd: parts.join("/"), + cwd: cwd, dot: true, nobrace: true, noglobstar: true, noext: true, nodir: true }; @@ -162,8 +233,11 @@ define(['module'], function(module) { * for each variation when the app is built. */ for (var i = 0; i < variations.length; i += 1) { - var variation = variations[i].replace(".js", ""); - var modName = nameWithConditional.replace(conditionalRegEx, variation); + var variation = variations[i]; + + // remove the extension + variation = variation.substr(0, variation.lastIndexOf(".")); + var modName = getModuleName(nameWithConditional, variation); var promise = loader.normalize.call(loader, modName, parentName, parentAddress, pluginNormalize);