├── .travis.yml ├── .gitignore ├── tests ├── e.js ├── bower.json ├── f.js ├── shim.js ├── c.js ├── b.js ├── prologue.js ├── a.js ├── d.js ├── package.json ├── epilogue.js └── tests.js ├── .editorconfig ├── getJson.js ├── getReplacements.js ├── prologueRequire.js ├── prologueImport.js ├── package.json ├── prologueGlobals.js ├── getFiles.js ├── Globals.js ├── VarSet.js ├── yarn.lock ├── index.js ├── processFile.js ├── LICENSE └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - "7" 7 | - "8" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | .DS_Store 3 | coverage/ 4 | 5 | tests/out 6 | tests/test-module.js 7 | -------------------------------------------------------------------------------- /tests/e.js: -------------------------------------------------------------------------------- 1 | define(['./d'], function(d){ 2 | "use strict"; 3 | return function () { 4 | if (d() !== 'd') return false; 5 | return 'e'; 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /tests/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "prologue.js", 4 | "epilogue.js", 5 | "shim.js", 6 | "tests.js", 7 | "test-module.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | indent_size = 4 10 | 11 | [*.{json,yml,md}] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /tests/f.js: -------------------------------------------------------------------------------- 1 | define(["./b", "./c"], function(b, c){ 2 | "use strict"; 3 | window.heya = window.heya || {}; 4 | window.heya.F = function () { 5 | if (b() !== 'b') return false; 6 | if (c() !== 'c') return false; 7 | return 'f'; 8 | }; 9 | return null; 10 | }); 11 | -------------------------------------------------------------------------------- /tests/shim.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | return function () { 5 | return 'c'; 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /tests/c.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | return function () { 5 | return 'need shim'; 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /getJson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var fs = require('fs'); 5 | 6 | 7 | function getJson (name) { 8 | try { 9 | return JSON.parse(fs.readFileSync(name)); 10 | } catch (e) { 11 | console.warn('WARNING: can\'t use', name, '-', e.message); 12 | } 13 | return null; 14 | } 15 | 16 | 17 | module.exports = getJson; 18 | -------------------------------------------------------------------------------- /tests/b.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (["./c"], function(c){ 3 | "use strict"; 4 | return function () { 5 | if (c() !== 'c') return false; 6 | return 'b'; 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /tests/prologue.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // simulate the global 4 | var window = {}; 5 | 6 | // simulate external packages 7 | window.BOOM = function () { return 'boom'; }; 8 | window.BOOM['Hello-World'] = function () { return 'helo'; }; 9 | window['wham!'] = function () { return 'wham'; }; 10 | 11 | // begin: generated modules that use globals 12 | -------------------------------------------------------------------------------- /tests/a.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (["./b", "./c"], function(b, c){ 3 | "use strict"; 4 | return function () { 5 | if (b() !== 'b') return false; 6 | if (c() !== 'c') return false; 7 | return 'a'; 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /tests/d.js: -------------------------------------------------------------------------------- 1 | define(['module', 'boom', 'boom/Hello-World', 'wham!'], function(module, boom, helo, wham){ 2 | "use strict"; 3 | window.heya = window.heya || {}; 4 | window.heya.D = function () { 5 | if (boom() !== 'boom') return false; 6 | if (helo() !== 'helo') return false; 7 | if (wham() !== 'wham') return false; 8 | if (!/\bd\.js$/.test(module.id)) return false; 9 | return 'd'; 10 | }; 11 | return null; 12 | }); 13 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heya-globalize-test", 3 | "private": true, 4 | "version": "1.0.0", 5 | "author": "Eugene Lazutkin (http://www.lazutkin.com/)", 6 | "license": "BSD-3-Clause", 7 | "browserGlobals": { 8 | "!root": "heya.example", 9 | "!dist": "out", 10 | "boom": "BOOM", 11 | "./d": "!heya.D", 12 | "./f": "!heya.F" 13 | }, 14 | "browser": { 15 | "c.js": "shim.js" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/epilogue.js: -------------------------------------------------------------------------------- 1 | // end: generated modules that use globals 2 | 3 | // the main test that checks all modules 4 | module.exports = function () { 5 | if (window.heya.example.a() !== 'a') return false; 6 | if (window.heya.example.b() !== 'b') return false; 7 | if (window.heya.example.c() !== 'c') return false; 8 | if (window.heya.example.e() !== 'e') return false; 9 | if (window.heya.D() !== 'd') return false; 10 | if (window.heya.F() !== 'f') return false; 11 | return true; 12 | }; 13 | -------------------------------------------------------------------------------- /getReplacements.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = function (packageJson) { 5 | var replacements = {}; 6 | 7 | if (packageJson && packageJson.browser) { 8 | if (typeof packageJson.browser == 'string') { 9 | replacements[toRelativeName(packageJson.main || './index.js')] = toRelativeName(packageJson.browser); 10 | } else { 11 | replacements = packageJson.browser; 12 | } 13 | } 14 | 15 | return replacements; 16 | }; 17 | 18 | 19 | function toRelativeName (name) { 20 | return name.charAt(0) === '.' && name.charAt(1) === '/' ? name : './' + name; 21 | } 22 | -------------------------------------------------------------------------------- /prologueRequire.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | function prologueRequire (deps, mod, from) { 5 | var prologue = generatePrologue(deps); 6 | if (!prologue) { 7 | console.error('ERROR: a loader is detected in', from, '- but some dependencies are unknown - skipping.'); 8 | return null; 9 | } 10 | return prologue; 11 | } 12 | 13 | 14 | function generatePrologue (deps) { 15 | return '(function(_,f){module.exports=f(' + deps.map(function (name) { 16 | return name === 'module' ? name : 'require(' + JSON.stringify(name) + ')'; 17 | }).join(',') + ');})'; 18 | } 19 | 20 | 21 | module.exports = prologueRequire; 22 | -------------------------------------------------------------------------------- /prologueImport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | function prologueRequire (deps, mod, from) { 5 | var prologue = generatePrologue(deps); 6 | if (!prologue) { 7 | console.error('ERROR: a loader is detected in', from, '- but some dependencies are unknown - skipping.'); 8 | return null; 9 | } 10 | return prologue; 11 | } 12 | 13 | 14 | function generatePrologue (deps) { 15 | return deps.map(function (name, index) { 16 | return name === 'module' ? '' : 'import m' + index + ' from ' + JSON.stringify(name) + ';'; 17 | }).join('') + 'export default (function(_,f){return f(' + deps.map(function (_, index) { 18 | return 'm' + index; 19 | }).join(',') + ');})'; 20 | } 21 | 22 | 23 | module.exports = prologueRequire; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heya-globalize", 3 | "version": "1.2.1", 4 | "description": "Make a browser version of JS files using globals from a Heya-style UMD, or a simple AMD.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "cd tests && node ../index.js && node tests.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/heya/globalize.git" 12 | }, 13 | "keywords": [ 14 | "source transform", 15 | "UMD", 16 | "globals", 17 | "front-end", 18 | "browser" 19 | ], 20 | "author": "Eugene Lazutkin (http://www.lazutkin.com/)", 21 | "license": "BSD-3-Clause", 22 | "dependencies": { 23 | "glob": "^7.0.0", 24 | "minimist": "^1.2.0", 25 | "mkdirp": "^0.5.1" 26 | }, 27 | "browserGlobals": {} 28 | } 29 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var fs = require('fs'); 5 | 6 | 7 | // everything should be transformed at this point 8 | 9 | 10 | // concatenate all transformed files in a test module 11 | 12 | var writeable = fs.createWriteStream('test-module.js'); 13 | 14 | function dump (name, end) { 15 | return function () { 16 | var readable = fs.createReadStream(name); 17 | return new Promise(function (resolve, reject) { 18 | readable.on('end', function (error) { 19 | if (error) { 20 | reject(error); 21 | } else { 22 | resolve(true); 23 | } 24 | }); 25 | readable.pipe(writeable, {end: !!end}); 26 | }); 27 | }; 28 | } 29 | 30 | function sleep (ms) { 31 | return function () { 32 | return new Promise(function (resolve) { 33 | setTimeout(function () { 34 | resolve(ms); 35 | }, ms); 36 | }); 37 | }; 38 | } 39 | 40 | dump('prologue.js')(). 41 | then(dump('out/c.js')). 42 | then(dump('out/b.js')). 43 | then(dump('out/a.js')). 44 | then(dump('out/d.js')). 45 | then(dump('out/e.js')). 46 | then(dump('out/f.js')). 47 | then(dump('epilogue.js', true)). 48 | then(sleep(200)). 49 | then(function () { 50 | var test = require('./test-module'); 51 | process.exit(test() ? 0 : 2); 52 | }). 53 | catch(function (error) { 54 | console.error('ERROR:', error); 55 | process.exit(1); 56 | }); 57 | -------------------------------------------------------------------------------- /prologueGlobals.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var path = require('path'); 5 | 6 | var VarSet = require('./VarSet'); 7 | 8 | 9 | function prologueGlobals (globals) { 10 | return function (deps, mod, from) { 11 | var prologue = generatePrologue(deps, mod, from, globals); 12 | if (!prologue) { 13 | console.error('ERROR: a loader is detected in', from, '- but some dependencies are unknown - skipping.'); 14 | return null; 15 | } 16 | return prologue; 17 | }; 18 | } 19 | 20 | function generatePrologue (deps, mod, name, globals) { 21 | // normalize dependencies 22 | deps = deps.map(function (name) { 23 | if (name === 'module' || name.charAt(0) !== '.') { 24 | return name; 25 | } 26 | var norm = path.join(path.dirname(mod), name); 27 | if (!/^\.\//.test(norm)) { 28 | norm = './' + norm; 29 | } 30 | return norm; 31 | }); 32 | // prepare global variables 33 | var variables = new VarSet(), needModule = false, g; 34 | for (var i = 0; i < deps.length; ++i) { 35 | if (deps[i] !== 'module') { 36 | g = globals.getGlobalByModule(deps[i]); 37 | if (!g) { 38 | return ''; 39 | } 40 | variables.add(g); 41 | } else { 42 | needModule = true; 43 | } 44 | } 45 | 46 | g = globals.getGlobalByModule(mod); 47 | if (!g) { 48 | return ''; 49 | } 50 | 51 | var assignment = variables.buildSetter(g), 52 | modulePart = needModule ? 'm={};m.id=m.filename=' + JSON.stringify(name) + ';' : ''; 53 | 54 | var prologue = '(function(_,f'; 55 | if (/^g=/.test(assignment)) { 56 | prologue += ',g'; 57 | } 58 | if (modulePart) { 59 | prologue += ',m'; 60 | } 61 | return prologue + '){' + modulePart + assignment + 'f(' + 62 | deps.map(function (path) { 63 | return path === 'module' ? 'm' : 'window' + variables.buildGetter(globals.getGlobalByModule(path, true)); 64 | }).join(',') + ');})'; 65 | } 66 | 67 | 68 | module.exports = prologueGlobals; 69 | -------------------------------------------------------------------------------- /getFiles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var path = require('path'); 5 | 6 | var glob = require('glob'); 7 | 8 | 9 | function Dict () { 10 | this.dict = {}; 11 | this.size = 0; 12 | } 13 | 14 | Dict.prototype = { 15 | add: function (pattern) { 16 | var files = toFiles(pattern); 17 | files.forEach(function (name) { 18 | if (!this.dict.hasOwnProperty(name)) { 19 | this.dict[name] = 1; 20 | ++this.size; 21 | } 22 | }, this); 23 | }, 24 | remove: function (pattern) { 25 | if (this.size) { 26 | var files = toFiles(pattern); 27 | for (var i = 0; i < files.length; ++i) { 28 | var name = files[i]; 29 | if (this.dict.hasOwnProperty(name)) { 30 | delete this.dict[name]; 31 | if (!--this.size) { 32 | return; 33 | } 34 | } 35 | } 36 | } 37 | }, 38 | filter: function (files) { 39 | return this.size ? files.filter(function (name) { return this.dict.hasOwnProperty(name); }, this) : []; 40 | } 41 | }; 42 | 43 | function toFiles (pattern) { 44 | return pattern instanceof Array ? pattern : glob.sync(pattern, {follow: true}); 45 | } 46 | 47 | 48 | function normalizePattern (pattern) { 49 | if (pattern) { 50 | // pattern = (pattern.charAt(0) === '/' ? '**' : '**/') + pattern; 51 | pattern += (pattern.charAt(pattern.length - 1) === '/' ? '**' : '/**'); 52 | } 53 | return pattern; 54 | } 55 | 56 | 57 | module.exports = function (bowerJson, globals) { 58 | var dict = new Dict(), files = glob.sync(path.join(globals.getFrom(), '**/*.js')); 59 | 60 | dict.add(files); 61 | dict.remove(normalizePattern('node_modules')); 62 | dict.remove(normalizePattern('bower_components')); 63 | dict.remove(normalizePattern(globals.getDist())); 64 | 65 | if (bowerJson && bowerJson.ignore && bowerJson.ignore instanceof Array) { 66 | bowerJson.ignore.forEach(function (pattern) { 67 | if (pattern) { 68 | if (!/^\*\*\//.test(pattern) && pattern.charAt(0) !== '/') { 69 | pattern = '**/' + pattern; 70 | } 71 | dict.remove(pattern); 72 | if (!/\/\*\*$/.test(pattern)) { 73 | dict.remove(pattern + '/**'); 74 | } 75 | } 76 | }); 77 | } 78 | 79 | return dict.filter(files); 80 | }; 81 | -------------------------------------------------------------------------------- /Globals.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var path = require('path'); 5 | 6 | 7 | var CWD = process.cwd(); 8 | 9 | 10 | function Globals (browserGlobals, name) { 11 | this.globals = {}; 12 | 13 | if (browserGlobals) { 14 | this.globals = browserGlobals || {}; 15 | } 16 | 17 | if (!this.globals['!root']) { 18 | this.globals['!root'] = name; 19 | } 20 | 21 | if (!this.globals['!dist']) { 22 | this.globals['!dist'] = 'dist'; 23 | } 24 | 25 | if (!this.globals['!from']) { 26 | this.globals['!from'] = '.'; 27 | } 28 | 29 | this.cwd = path.join(CWD, this.globals['!from']); 30 | } 31 | 32 | Globals.prototype = { 33 | getRoot: function () { 34 | return this.globals['!root']; 35 | }, 36 | getDist: function () { 37 | return this.globals['!dist']; 38 | }, 39 | getFrom: function () { 40 | return this.globals['!from']; 41 | }, 42 | getGlobalByModule: function (name, silent) { 43 | if (name.charAt(0) === '.') { 44 | // local file 45 | var fullName = path.join(this.cwd, name); 46 | if (this.cwd !== fullName.slice(0, this.cwd.length) || this.cwd.charAt(this.cwd.length - 1) !== '/' && fullName.charAt(this.cwd.length) !== '/') { 47 | console.error('ERROR: local file', name, 'is outside of the root.'); 48 | return ''; 49 | } 50 | // normalize to use './' form 51 | var size = this.cwd.length; 52 | if (fullName.charAt(size) === '/') { 53 | ++size; 54 | } 55 | name = './' + fullName.slice(size); 56 | } 57 | 58 | // check for direct correspondence 59 | if (this.globals.hasOwnProperty(name)) { 60 | return this.globals[name]; 61 | } 62 | 63 | // check parents 64 | var parts = name.split('/'); 65 | if (parts[0] === '.') { 66 | // merge first dot with the first meaningful part 67 | parts.shift(); 68 | parts[0] = './' + parts[0]; 69 | } 70 | for (var i = parts.length - 1; i > 0; --i) { 71 | var parent = parts.slice(0, i).join('/'); 72 | if (this.globals.hasOwnProperty(parent)) { 73 | return this.globals[parent] + '.' + parts.slice(i).join('.'); 74 | } 75 | } 76 | 77 | // nothing was found => make up a new name, if possible 78 | if (parts[0].charAt(0) === '.') { 79 | // local file 80 | return this.getRoot() + '.' + parts.join('.').slice(2); 81 | } 82 | 83 | // undeclared module 84 | if (!silent) { 85 | console.warn('WARNING: external module', name, 'is undeclared!', parts[0], 'is used as a global variable of its package'); 86 | } 87 | return parts.join('.'); 88 | } 89 | }; 90 | 91 | 92 | module.exports = Globals; 93 | -------------------------------------------------------------------------------- /VarSet.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var isId = /^[a-zA-Z_]\w*$/; 5 | 6 | 7 | function VarSet () { 8 | this.root = {}; 9 | } 10 | 11 | VarSet.prototype = { 12 | add: function (path) { 13 | toParts(path).reduce(function (root, part, index) { 14 | if (!index && part.charAt(0) === '!') { 15 | part = part.slice(1); 16 | } 17 | if (!root.hasOwnProperty(part)) { 18 | root[part] = {}; 19 | } 20 | return root[part]; 21 | }, this.root); 22 | }, 23 | buildGetter: function (path) { 24 | return toParts(path).reduce(function (acc, part, index) { 25 | if (!index && part.charAt(0) === '!') { 26 | part = part.slice(1); 27 | } 28 | return acc + (isId.test(part) ? '.' + part : '[' + JSON.stringify(part) + ']'); 29 | }, ''); 30 | }, 31 | buildSetter: function (path) { 32 | var parts = toParts(path); 33 | 34 | // no assignment for '!' variables 35 | if (parts[0].charAt(0) === '!') { 36 | return ''; 37 | } 38 | 39 | // simple case: all paths are already created 40 | var prefixSize = this.getPrefixSize(parts); 41 | if (prefixSize + 1 >= parts.length) { 42 | if (prefixSize == parts.length) { 43 | console.warn('WARNING: about to assign to already assigned variable -', parts.join('.')); 44 | } 45 | return 'window' + this.buildGetter(parts) + '='; 46 | } 47 | 48 | // assign a common prefix (no need to create intermediate objects) 49 | var s = 'g=window' + this.buildGetter(parts.slice(0, prefixSize)) + ';'; 50 | 51 | // create objects for the rest but the last one 52 | s += parts.slice(prefixSize, parts.length - 1).reduce(function (acc, part) { 53 | var accessor = this.buildGetter(part); 54 | return acc + 'g=g' + accessor + '||(g' + accessor + '={});'; 55 | }.bind(this), ''); 56 | 57 | // assign to the last one 58 | return s + 'g' + this.buildGetter(parts[parts.length - 1]) + '='; 59 | }, 60 | getPrefixSize: function (path) { 61 | var parts = toParts(path); 62 | for (var i = 0, root = this.root; i < parts.length; ++i) { 63 | var part = parts[i]; 64 | if (!root.hasOwnProperty(part)) { 65 | return i; 66 | } 67 | root = root[part]; 68 | } 69 | return parts.length; 70 | } 71 | }; 72 | 73 | 74 | VarSet.getPrefixSize = function getPrefixSize (path1, path2) { 75 | var parts1 = toParts(path1), parts2 = toParts(path2), 76 | size = Math.min(parts1.length, parts2.length); 77 | for (var i = 0; i < size; ++i) { 78 | if (parts1[i] !== parts2[i]) { 79 | return i; 80 | } 81 | } 82 | return size; 83 | }; 84 | 85 | 86 | function toParts (path) { 87 | return path instanceof Array ? path : path.split('.'); 88 | } 89 | 90 | 91 | module.exports = VarSet; 92 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | balanced-match@^1.0.0: 6 | version "1.0.0" 7 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 8 | 9 | brace-expansion@^1.1.7: 10 | version "1.1.8" 11 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 12 | dependencies: 13 | balanced-match "^1.0.0" 14 | concat-map "0.0.1" 15 | 16 | concat-map@0.0.1: 17 | version "0.0.1" 18 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 19 | 20 | fs.realpath@^1.0.0: 21 | version "1.0.0" 22 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 23 | 24 | glob@^7.0.0: 25 | version "7.1.2" 26 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 27 | dependencies: 28 | fs.realpath "^1.0.0" 29 | inflight "^1.0.4" 30 | inherits "2" 31 | minimatch "^3.0.4" 32 | once "^1.3.0" 33 | path-is-absolute "^1.0.0" 34 | 35 | inflight@^1.0.4: 36 | version "1.0.6" 37 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 38 | dependencies: 39 | once "^1.3.0" 40 | wrappy "1" 41 | 42 | inherits@2: 43 | version "2.0.3" 44 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 45 | 46 | minimatch@^3.0.4: 47 | version "3.0.4" 48 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 49 | dependencies: 50 | brace-expansion "^1.1.7" 51 | 52 | minimist@0.0.8: 53 | version "0.0.8" 54 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 55 | 56 | minimist@^1.2.0: 57 | version "1.2.0" 58 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 59 | 60 | mkdirp@^0.5.1: 61 | version "0.5.1" 62 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 63 | dependencies: 64 | minimist "0.0.8" 65 | 66 | once@^1.3.0: 67 | version "1.4.0" 68 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 69 | dependencies: 70 | wrappy "1" 71 | 72 | path-is-absolute@^1.0.0: 73 | version "1.0.1" 74 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 75 | 76 | wrappy@1: 77 | version "1.0.2" 78 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 79 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var argv = require('minimist')(process.argv.slice(2)); 5 | 6 | // help 7 | if (argv.h || argv.help) { 8 | console.log('Use: node heya-globalize/index.js [--amd|--cjs|--es6] ...'); 9 | console.log(' Set module format:'); 10 | console.log(' --amd generates an AMD prologue.'); 11 | console.log(' --cjs generates an CommonJS prologue.'); 12 | console.log(' --es6 generates an ES6 module prologue.'); 13 | console.log(' Otherwise, a prologue based on browser globals is generated.'); 14 | console.log(' Override directories:'); 15 | console.log(' --source=src sets where to get files from.'); 16 | console.log(' --config=cfg sets where to get configuration files.'); 17 | console.log(' --target=trg sets where to save transformed files.'); 18 | process.exit(2); 19 | } 20 | 21 | 22 | var path = require('path'); 23 | 24 | var getReplacements = require('./getReplacements'); 25 | var getJson = require('./getJson'); 26 | var getFiles = require('./getFiles'); 27 | var Globals = require('./Globals'); 28 | var processFile = require('./processFile'); 29 | var prologueGlobals = require('./prologueGlobals'); 30 | var prologueRequire = require('./prologueRequire'); 31 | var prologueImport = require('./prologueImport'); 32 | 33 | 34 | // loaders 35 | var loaders = ['/* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))})']; 36 | 37 | // fetch JSON files 38 | var configDir = typeof argv.config == 'string' ? argv.config : '.', 39 | packageJson = getJson(path.join(configDir, 'package.json')), 40 | bowerJson = getJson(path.join(configDir, 'bower.json')); 41 | 42 | // prepare file replacements 43 | var replacements = getReplacements(packageJson); 44 | 45 | // prepare the globals map 46 | var globals = new Globals(packageJson.browserGlobals, packageJson.name); 47 | if (typeof argv.source == 'string') { 48 | globals.globals['!from'] = argv.source; 49 | } 50 | if (typeof argv.target == 'string') { 51 | globals.globals['!dist'] = argv.target; 52 | } 53 | 54 | // construct a list of files 55 | var files = getFiles(bowerJson, globals); 56 | 57 | // prepare a prologue generator 58 | var newLoader; 59 | if (argv.amd) { 60 | console.log('converting to AMD...'); 61 | newLoader = 'define'; 62 | } else if (argv.cjs) { 63 | console.log('converting to CommonJS...'); 64 | newLoader = prologueRequire; 65 | } else if (argv.es6) { 66 | console.log('converting to ES6 modules...'); 67 | newLoader = prologueImport; 68 | } else { 69 | console.log('converting to browser globals...'); 70 | newLoader = prologueGlobals(globals); 71 | } 72 | 73 | // go over files 74 | var pf = processFile(loaders, newLoader); 75 | files.forEach(function (name) { 76 | var relative = path.relative(globals.getFrom(), name), 77 | ext = path.extname(relative), 78 | mod = './' + (ext ? relative.slice(0, -ext.length) : relative), 79 | from = replacements.hasOwnProperty(relative) ? replacements[relative] : relative, 80 | to = path.join(globals.getDist(), relative); 81 | if (from) { 82 | from = path.join(globals.getFrom(), from); 83 | if (name === from) { 84 | console.log(name, '=>', to); 85 | } else { 86 | console.log(name, '=>', from, '=>', to); 87 | } 88 | pf(from, mod, to); 89 | } 90 | }); 91 | -------------------------------------------------------------------------------- /processFile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | 7 | var mkdirp = require('mkdirp'); 8 | 9 | 10 | // parsing constants 11 | 12 | var doubleQuotedString = /"((?:\\"|[^"])*)"/g, 13 | singleQuotedString = /'((?:\\'|[^'])*)'/g, 14 | startPattern = /\s*\(\s*\[\s*/g, 15 | middlePattern = /\s*,\s*/g, 16 | endPattern = /\s*\]/g, 17 | definePattern = /^\s*define\b\s*/g; 18 | 19 | 20 | function processFile (loaders, newLoader) { 21 | return function (from, mod, to) { 22 | var deps, prologue; 23 | 24 | // get source 25 | var text = fs.readFileSync(from, 'utf8').split(/\r?\n/); 26 | 27 | do { 28 | // check if it is a Heya UMD 29 | if (loaders.some(function (umd) { return umd === text[0]; })) { 30 | if (typeof newLoader == 'string') { 31 | text[0] = newLoader; 32 | break; 33 | } 34 | // process the second line 35 | deps = parseDependencies(text[1], 0); 36 | if (!deps) { 37 | console.error('ERROR: a loader is detected in', from, '- but we cannot parse the dependency list - skipping.'); 38 | return null; 39 | } 40 | // generate new prologue 41 | prologue = newLoader(deps, mod, from); 42 | if (!prologue) { 43 | return null; 44 | } 45 | text[0] = prologue; 46 | break; 47 | } 48 | 49 | // check if it is a simple define() 50 | definePattern.lastIndex = 0; 51 | if (definePattern.test(text[0])) { 52 | if (typeof newLoader == 'string') { 53 | text[0] = newLoader + '\n' + text[0].slice(definePattern.lastIndex); 54 | break; 55 | } 56 | // process the second line 57 | deps = parseDependencies(text[0], definePattern.lastIndex); 58 | if (!deps) { 59 | console.error('ERROR: a loader is detected in', from, '- but we cannot parse the dependency list - skipping.'); 60 | return null; 61 | } 62 | // generate new prologue 63 | prologue = newLoader(deps, mod, from); 64 | if (!prologue) { 65 | return null; 66 | } 67 | text[0] = prologue + '\n' + text[0].slice(definePattern.lastIndex); 68 | break; 69 | } 70 | 71 | console.warn('WARNING: no actionable prologue is detected in', from, '- skipping.'); 72 | return null; 73 | } while (false); 74 | 75 | if (to) { 76 | mkdirp.sync(path.dirname(to)); 77 | fs.writeFileSync(to, text.join('\n'), 'utf8'); 78 | } 79 | 80 | return text; 81 | }; 82 | } 83 | 84 | function match (pattern, text, index) { 85 | pattern.lastIndex = index; 86 | var result = pattern.exec(text); 87 | return result && result.index === index && result; // look strange? we want to return a result object, or a falsy value 88 | } 89 | 90 | function parseDependencies (text, index) { 91 | if (!match(startPattern, text, index)) { 92 | return null; 93 | } 94 | index = startPattern.lastIndex; 95 | var deps = [], expectedValue = true, expectedEnd = true, result; 96 | for(;;) { 97 | if (expectedEnd) { 98 | if (match(endPattern, text, index)) { 99 | break; 100 | } 101 | } 102 | if (expectedValue) { 103 | result = match(doubleQuotedString, text, index); 104 | if (result) { 105 | deps.push(result[1]); 106 | index = doubleQuotedString.lastIndex; 107 | expectedValue = false; 108 | expectedEnd = true; 109 | continue; 110 | } 111 | result = match(singleQuotedString, text, index); 112 | if (result) { 113 | deps.push(result[1]); 114 | index = singleQuotedString.lastIndex; 115 | expectedValue = false; 116 | expectedEnd = true; 117 | continue; 118 | } 119 | return null; 120 | } 121 | result = match(middlePattern, text, index); 122 | if (result) { 123 | index = middlePattern.lastIndex; 124 | expectedValue = true; 125 | expectedEnd = false; 126 | continue; 127 | } 128 | return null; 129 | } 130 | return deps; 131 | } 132 | 133 | 134 | module.exports = processFile; 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This library is available under *either* the terms of the modified BSD license 2 | *or* the Academic Free License version 2.1. As a recipient of this work, you 3 | may choose which license to receive this code under. No external contributions 4 | are allowed under licenses which are fundamentally incompatible with the AFL 5 | or BSD licenses that this library is distributed under. 6 | 7 | The text of the AFL and BSD licenses is reproduced below. 8 | 9 | ------------------------------------------------------------------------------- 10 | The "New" BSD License: 11 | ********************** 12 | 13 | Copyright (c) 2005-2012, Eugene Lazutkin 14 | All rights reserved. 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions are met: 18 | 19 | * Redistributions of source code must retain the above copyright notice, this 20 | list of conditions and the following disclaimer. 21 | * Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | * Neither the name of Eugene Lazutkin nor the names of other contributors 25 | may be used to endorse or promote products derived from this software 26 | without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 29 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 32 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 34 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 35 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 36 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | ------------------------------------------------------------------------------- 40 | The Academic Free License, v. 2.1: 41 | ********************************** 42 | 43 | This Academic Free License (the "License") applies to any original work of 44 | authorship (the "Original Work") whose owner (the "Licensor") has placed the 45 | following notice immediately following the copyright notice for the Original 46 | Work: 47 | 48 | Licensed under the Academic Free License version 2.1 49 | 50 | 1) Grant of Copyright License. Licensor hereby grants You a world-wide, 51 | royalty-free, non-exclusive, perpetual, sublicenseable license to do the 52 | following: 53 | 54 | a) to reproduce the Original Work in copies; 55 | 56 | b) to prepare derivative works ("Derivative Works") based upon the Original 57 | Work; 58 | 59 | c) to distribute copies of the Original Work and Derivative Works to the 60 | public; 61 | 62 | d) to perform the Original Work publicly; and 63 | 64 | e) to display the Original Work publicly. 65 | 66 | 2) Grant of Patent License. Licensor hereby grants You a world-wide, 67 | royalty-free, non-exclusive, perpetual, sublicenseable license, under patent 68 | claims owned or controlled by the Licensor that are embodied in the Original 69 | Work as furnished by the Licensor, to make, use, sell and offer for sale the 70 | Original Work and Derivative Works. 71 | 72 | 3) Grant of Source Code License. The term "Source Code" means the preferred 73 | form of the Original Work for making modifications to it and all available 74 | documentation describing how to modify the Original Work. Licensor hereby 75 | agrees to provide a machine-readable copy of the Source Code of the Original 76 | Work along with each copy of the Original Work that Licensor distributes. 77 | Licensor reserves the right to satisfy this obligation by placing a 78 | machine-readable copy of the Source Code in an information repository 79 | reasonably calculated to permit inexpensive and convenient access by You for as 80 | long as Licensor continues to distribute the Original Work, and by publishing 81 | the address of that information repository in a notice immediately following 82 | the copyright notice that applies to the Original Work. 83 | 84 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names 85 | of any contributors to the Original Work, nor any of their trademarks or 86 | service marks, may be used to endorse or promote products derived from this 87 | Original Work without express prior written permission of the Licensor. Nothing 88 | in this License shall be deemed to grant any rights to trademarks, copyrights, 89 | patents, trade secrets or any other intellectual property of Licensor except as 90 | expressly stated herein. No patent license is granted to make, use, sell or 91 | offer to sell embodiments of any patent claims other than the licensed claims 92 | defined in Section 2. No right is granted to the trademarks of Licensor even if 93 | such marks are included in the Original Work. Nothing in this License shall be 94 | interpreted to prohibit Licensor from licensing under different terms from this 95 | License any Original Work that Licensor otherwise would have a right to 96 | license. 97 | 98 | 5) This section intentionally omitted. 99 | 100 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative 101 | Works that You create, all copyright, patent or trademark notices from the 102 | Source Code of the Original Work, as well as any notices of licensing and any 103 | descriptive text identified therein as an "Attribution Notice." You must cause 104 | the Source Code for any Derivative Works that You create to carry a prominent 105 | Attribution Notice reasonably calculated to inform recipients that You have 106 | modified the Original Work. 107 | 108 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that 109 | the copyright in and to the Original Work and the patent rights granted herein 110 | by Licensor are owned by the Licensor or are sublicensed to You under the terms 111 | of this License with the permission of the contributor(s) of those copyrights 112 | and patent rights. Except as expressly stated in the immediately proceeding 113 | sentence, the Original Work is provided under this License on an "AS IS" BASIS 114 | and WITHOUT WARRANTY, either express or implied, including, without limitation, 115 | the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR 116 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. 117 | This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No 118 | license to Original Work is granted hereunder except under this disclaimer. 119 | 120 | 8) Limitation of Liability. Under no circumstances and under no legal theory, 121 | whether in tort (including negligence), contract, or otherwise, shall the 122 | Licensor be liable to any person for any direct, indirect, special, incidental, 123 | or consequential damages of any character arising as a result of this License 124 | or the use of the Original Work including, without limitation, damages for loss 125 | of goodwill, work stoppage, computer failure or malfunction, or any and all 126 | other commercial damages or losses. This limitation of liability shall not 127 | apply to liability for death or personal injury resulting from Licensor's 128 | negligence to the extent applicable law prohibits such limitation. Some 129 | jurisdictions do not allow the exclusion or limitation of incidental or 130 | consequential damages, so this exclusion and limitation may not apply to You. 131 | 132 | 9) Acceptance and Termination. If You distribute copies of the Original Work or 133 | a Derivative Work, You must make a reasonable effort under the circumstances to 134 | obtain the express assent of recipients to the terms of this License. Nothing 135 | else but this License (or another written agreement between Licensor and You) 136 | grants You permission to create Derivative Works based upon the Original Work 137 | or to exercise any of the rights granted in Section 1 herein, and any attempt 138 | to do so except under the terms of this License (or another written agreement 139 | between Licensor and You) is expressly prohibited by U.S. copyright law, the 140 | equivalent laws of other countries, and by international treaty. Therefore, by 141 | exercising any of the rights granted to You in Section 1 herein, You indicate 142 | Your acceptance of this License and all of its terms and conditions. 143 | 144 | 10) Termination for Patent Action. This License shall terminate automatically 145 | and You may no longer exercise any of the rights granted to You by this License 146 | as of the date You commence an action, including a cross-claim or counterclaim, 147 | against Licensor or any licensee alleging that the Original Work infringes a 148 | patent. This termination provision shall not apply for an action alleging 149 | patent infringement by combinations of the Original Work with other software or 150 | hardware. 151 | 152 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this 153 | License may be brought only in the courts of a jurisdiction wherein the 154 | Licensor resides or in which Licensor conducts its primary business, and under 155 | the laws of that jurisdiction excluding its conflict-of-law provisions. The 156 | application of the United Nations Convention on Contracts for the International 157 | Sale of Goods is expressly excluded. Any use of the Original Work outside the 158 | scope of this License or after its termination shall be subject to the 159 | requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et 160 | seq., the equivalent laws of other countries, and international treaty. This 161 | section shall survive the termination of this License. 162 | 163 | 12) Attorneys Fees. In any action to enforce the terms of this License or 164 | seeking damages relating thereto, the prevailing party shall be entitled to 165 | recover its costs and expenses, including, without limitation, reasonable 166 | attorneys' fees and costs incurred in connection with such action, including 167 | any appeal of such action. This section shall survive the termination of this 168 | License. 169 | 170 | 13) Miscellaneous. This License represents the complete agreement concerning 171 | the subject matter hereof. If any provision of this License is held to be 172 | unenforceable, such provision shall be reformed only to the extent necessary to 173 | make it enforceable. 174 | 175 | 14) Definition of "You" in This License. "You" throughout this License, whether 176 | in upper or lower case, means an individual or a legal entity exercising rights 177 | under, and complying with all of the terms of, this License. For legal 178 | entities, "You" includes any entity that controls, is controlled by, or is 179 | under common control with you. For purposes of this definition, "control" means 180 | (i) the power, direct or indirect, to cause the direction or management of such 181 | entity, whether by contract or otherwise, or (ii) ownership of fifty percent 182 | (50%) or more of the outstanding shares, or (iii) beneficial ownership of such 183 | entity. 184 | 185 | 15) Right to Use. You may use the Original Work in all ways not otherwise 186 | restricted or conditioned by this License or by law, and Licensor promises not 187 | to interfere with or be responsible for such uses by You. 188 | 189 | This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. 190 | Permission is hereby granted to copy and distribute this license without 191 | modification. This license may not be modified without the express written 192 | permission of its copyright owner. 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `heya-globalize` 2 | 3 | [![Build status][travis-image]][travis-url] 4 | [![Dependencies][deps-image]][deps-url] 5 | [![devDependencies][dev-deps-image]][dev-deps-url] 6 | [![NPM version][npm-image]][npm-url] 7 | 8 | 9 | This utility is a simple source transformer for JavaScript modules written using either a Heya-style UMD prologue, or a simple AMD prologue. 10 | It can produce JavaScript modules, which use browser globals as their dependences and exports. Such modules can be directly including into HTML with `