/* * Meta Extension * * Sets default metadata on a load record (load.metadata) from * loader.metadata via SystemJS.meta function. * * * Also provides an inline meta syntax for module meta in source. * * Eg: * * loader.meta({ * 'my/module': { deps: ['jquery'] } * 'my/*': { format: 'amd' } * }); * * Which in turn populates loader.metadata. * * load.metadata.deps and load.metadata.format will then be set * for 'my/module' * * The same meta could be set with a my/module.js file containing: * * my/module.js * "format amd"; * "deps[] jquery"; * "globals.some value" * console.log('this is my/module'); * * Configuration meta always takes preference to inline meta. * * Multiple matches in wildcards are supported and ammend the meta. * * * The benefits of the function form is that paths are URL-normalized * supporting say * * loader.meta({ './app': { format: 'cjs' } }); * * Instead of needing to set against the absolute URL (https://site.com/app.js) * */ (function() { hookConstructor(function(constructor) { return function() { this.meta = {}; constructor.call(this); }; }); hook('locate', function(locate) { return function(load) { var meta = this.meta; var name = load.name; // NB for perf, maybe introduce a fast-path wildcard lookup cache here // which is checked first // apply wildcard metas var bestDepth = 0; var wildcardIndex; for (var module in meta) { wildcardIndex = module.indexOf('*'); if (wildcardIndex === -1) continue; if (module.substr(0, wildcardIndex) === name.substr(0, wildcardIndex) && module.substr(wildcardIndex + 1) === name.substr(name.length - module.length + wildcardIndex + 1)) { var depth = module.split('/').length; if (depth > bestDepth) bestDepth = depth; extendMeta(load.metadata, meta[module], bestDepth != depth); } } // apply exact meta if (meta[name]) extendMeta(load.metadata, meta[name]); return locate.call(this, load); }; }); // detect any meta header syntax // only set if not already set var metaRegEx = /^(\s*\/\*[^\*]*(\*(?!\/)[^\*]*)*\*\/|\s*\/\/[^\n]*|\s*"[^"]+"\s*;?|\s*'[^']+'\s*;?)+/; var metaPartRegEx = /\/\*[^\*]*(\*(?!\/)[^\*]*)*\*\/|\/\/[^\n]*|"[^"]+"\s*;?|'[^']+'\s*;?/g; function setMetaProperty(target, p, value) { var pParts = p.split('.'); var curPart; while (pParts.length > 1) { curPart = pParts.shift(); target = target[curPart] = target[curPart] || {}; } curPart = pParts.shift(); if (!(curPart in target)) target[curPart] = value; } hook('translate', function(translate) { return function(load) { // shortpath for bundled if (load.metadata.format == 'defined') { load.metadata.deps = load.metadata.deps || []; return Promise.resolve(load.source); } // NB meta will be post-translate pending transpiler conversion to plugins var meta = load.source.match(metaRegEx); if (meta) { var metaParts = meta[0].match(metaPartRegEx); for (var i = 0; i < metaParts.length; i++) { var curPart = metaParts[i]; var len = curPart.length; var firstChar = curPart.substr(0, 1); if (curPart.substr(len - 1, 1) == ';') len--; if (firstChar != '"' && firstChar != "'") continue; var metaString = curPart.substr(1, curPart.length - 3); var metaName = metaString.substr(0, metaString.indexOf(' ')); if (metaName) { var metaValue = metaString.substr(metaName.length + 1, metaString.length - metaName.length - 1); if (metaName.substr(metaName.length - 2, 2) == '[]') { metaName = metaName.substr(0, metaName.length - 2); load.metadata[metaName] = load.metadata[metaName] || []; load.metadata[metaName].push(metaValue); } else if (load.metadata[metaName] instanceof Array) { // temporary backwards compat for previous "deps" syntax warn.call(this, 'Module ' + load.name + ' contains deprecated "deps ' + metaValue + '" meta syntax.\nThis should be updated to "deps[] ' + metaValue + '" for pushing to array meta.'); load.metadata[metaName].push(metaValue); } else { setMetaProperty(load.metadata, metaName, metaValue); } } else { load.metadata[metaString] = true; } } } return translate.apply(this, arguments); }; }); })();