├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── require.js └── test ├── a.js ├── b.js ├── c └── c.js ├── d.js ├── lib └── jade.js ├── page.jade ├── riot ├── compiler.js ├── riot.js ├── test.riot.html ├── todo.css └── todo.tag ├── style.css ├── test.html └── text.txt /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.0.2 2 | ===== 3 | 4 | * added working tests for jade, riot, css 5 | 6 | 1.01 7 | ==== 8 | 9 | * Added compiler handler based on extension -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 weepy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # require 2 | 3 | Dropin `require` polyfill for the browser 4 | 5 | Really useful in development for when you don't want to crank up browserify. 6 | 7 | Super simple. 8 | 9 | Fully swappable with browserify and friends in production. 10 | 11 | # Installation 12 | 13 | 1) Include the script and serve it (it won't work direct from the filesystem due to browser security) 14 | 15 | 16 | ``` 17 | 18 | ``` 19 | 20 | or use it direct from rawgit 21 | 22 | ``` 23 | 24 | ``` 25 | 26 | 27 | 2) Code like a boss 28 | 29 | 3) You can optionally include the PARSEJS library which will help point point any syntax errors in your code whilst 30 | 31 | # Compilers 32 | 33 | You can add compilers for coffeescript, css, JSX or whatever in very simply by registering a handler like 34 | 35 | ``` 36 | require.compilers.css = function(text) { 37 | 38 | var style = document.createElement('style'); 39 | style.appendChild(document.createTextNode(text)); 40 | document.head.appendChild(style); 41 | } 42 | ``` 43 | 44 | This will allow you to include css on page like : 45 | 46 | ``` 47 | require("./style.css") 48 | ``` 49 | 50 | 51 | # Production 52 | 53 | It uses Sync XHR which is great for development, but not so much in production. 54 | 55 | Forunately, you can swap out this library with browserify or similar in production. 56 | 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brequire", 3 | "version": "1.0.2", 4 | "repo": "https://github.com/weepy/brequire", 5 | "description": "Dropin require polyfill for the browser. Also supports compilation, e.g. babel, css, JSX", 6 | "main": "require.js", 7 | "author": "weepy", 8 | "license": "MIT" 9 | } 10 | -------------------------------------------------------------------------------- /require.js: -------------------------------------------------------------------------------- 1 | 2 | ;(function(root) { 3 | 4 | function require(p) { 5 | var path = require.resolve(p) 6 | , mod = require.modules[path]; 7 | 8 | // weepy: following line added 9 | if (!mod) mod = require.load(path); 10 | 11 | if (!mod) throw new Error('failed to require "' + p + '"'); 12 | 13 | if (!mod.exports) { 14 | mod.exports = {}; 15 | 16 | // weepy: added __filename and __dirname 17 | var bits = path.split('/') 18 | , __filename = bits.pop() + '.js' 19 | , __dirname = bits.join('/') 20 | 21 | 22 | mod.call(null, mod, mod.exports, require.relative(path), __filename, __dirname); 23 | } 24 | return mod.exports; 25 | } 26 | 27 | require.modules = {}; 28 | 29 | require.prefix = "" 30 | 31 | require.resolve = function (path){ 32 | path = require.prefix + path 33 | var orig = path 34 | , reg = path + '.js' 35 | , index = path + '/index.js'; 36 | return require.modules[reg] && reg 37 | || require.modules[index] && index 38 | || orig; 39 | }; 40 | 41 | require.register = function (path, fn){ 42 | require.modules[path] = fn; 43 | }; 44 | 45 | require.relative = function (parent) { 46 | return function(p){ 47 | if ('.' != p.charAt(0)) return require(p); 48 | 49 | var path = parent.split('/') 50 | , segs = p.split('/'); 51 | path.pop(); 52 | 53 | 54 | var relative_path = path.concat(segs) 55 | var normalized = [] 56 | 57 | for (var i = 0; i < relative_path.length; i++) { 58 | var seg = relative_path[i]; 59 | 60 | if ('..' == seg && normalized.length && normalized[normalized.length-1].charAt(0) != ".") { 61 | normalized.pop(); 62 | } 63 | else if('.' == seg && normalized.length) { 64 | // 65 | } 66 | else 67 | normalized.push(seg) 68 | } 69 | 70 | return require(normalized.join('/')); 71 | }; 72 | }; 73 | 74 | 75 | var compilers = require.compilers = { 76 | js: function(text) { 77 | return text 78 | }, 79 | json: function(text) { 80 | try { 81 | JSON.parse(text) 82 | } catch(e) { 83 | console.error("BAD JSON @ " + text, e) 84 | return 85 | } 86 | return "module.exports = " + text 87 | }, 88 | txt: function(text) { 89 | return "module.exports = " + JSON.stringify(text) 90 | } 91 | } 92 | 93 | // weepy: following added 94 | require.load = function( path ) { 95 | 96 | var orig_path = path 97 | var request = new XMLHttpRequest(); 98 | 99 | if(path.match(/\.[^./]+$/)) { 100 | // has extension 101 | 102 | } else { 103 | path += ".js" 104 | } 105 | 106 | var ext = path.split(".").pop() 107 | 108 | request.open('GET', path, false); 109 | request.send(); 110 | var data = request.responseText; 111 | 112 | if(!request.responseText || !request.responseText.length) 113 | console.log("FAILED to load ", path) 114 | 115 | console.log("xhr: ", path, " loaded ", request.responseText.length, " bytes") 116 | 117 | var compiler = compilers[ext] || compilers.txt 118 | 119 | var output = compiler(data) || "" 120 | 121 | 122 | var text = "require.register('" + orig_path + "', \ 123 | function(module, exports, require, __filename, __dirname) {\n\n" 124 | + output+ "\n\n\ 125 | }); //@ sourceURL=" + path; 126 | 127 | try { 128 | (window.execScript || function(text) { 129 | return window["eval"].call(window, text) 130 | })(text) 131 | 132 | } 133 | catch(e) { 134 | if(root.PARSEJS) { 135 | try { 136 | root.PARSEJS.parse(text) 137 | } catch(e) { 138 | console.error('Syntax error in file ' + path + ' at line:' + (e.line-2) + ', col:' + e.col + '. ' + e.message) //, e.stack) //, e) 139 | } 140 | } else { 141 | console.error( "Syntax Error in file " + path + ": " + e.toString() ) 142 | } 143 | } 144 | 145 | 146 | 147 | 148 | 149 | return require.modules[ orig_path ]; 150 | } 151 | 152 | root.require = require; 153 | 154 | })(window); 155 | -------------------------------------------------------------------------------- /test/a.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | hello: "world", 3 | boo: require('./c/c') 4 | } -------------------------------------------------------------------------------- /test/b.js: -------------------------------------------------------------------------------- 1 | require("./a") 2 | 3 | module.exports = require('./a') -------------------------------------------------------------------------------- /test/c/c.js: -------------------------------------------------------------------------------- 1 | exports.boo = "boo" -------------------------------------------------------------------------------- /test/d.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | a: 1 3 | } -------------------------------------------------------------------------------- /test/page.jade: -------------------------------------------------------------------------------- 1 | h1 2 | | jade 3 | b FTW -------------------------------------------------------------------------------- /test/riot/compiler.js: -------------------------------------------------------------------------------- 1 | ;(function(window) { 2 | riot.parsers = { 3 | html: {}, 4 | css: {}, 5 | js: { 6 | coffee: function(js) { 7 | return CoffeeScript.compile(js, { bare: true }) 8 | }, 9 | es6: function(js) { 10 | return babel.transform(js, { blacklist: ['useStrict'] }).code 11 | }, 12 | none: function(js) { 13 | return js 14 | } 15 | } 16 | } 17 | // 4 the nostalgics 18 | riot.parsers.js.coffeescript = riot.parsers.js.coffee 19 | 20 | 21 | var BOOL_ATTR = ('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,'+ 22 | 'defaultchecked,defaultmuted,defaultselected,defer,disabled,draggable,enabled,formnovalidate,hidden,'+ 23 | 'indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,'+ 24 | 'pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,spellcheck,translate,truespeed,'+ 25 | 'typemustmatch,visible').split(','), 26 | // these cannot be auto-closed 27 | VOID_TAGS = 'area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr'.split(','), 28 | /* 29 | Following attributes give error when parsed on browser with { exrp_values } 30 | 31 | 'd' describes the SVG , Chrome gives error if the value is not valid format 32 | https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d 33 | */ 34 | PREFIX_ATTR = ['style', 'src', 'd'], 35 | 36 | LINE_TAG = /^<([\w\-]+)>(.*)<\/\1>/gim, 37 | QUOTE = /=({[^}]+})([\s\/\>])/g, 38 | SET_ATTR = /([\w\-]+)=(["'])([^\2]+?)\2/g, 39 | EXPR = /{\s*([^}]+)\s*}/g, 40 | // (tagname) (html) (javascript) endtag 41 | CUSTOM_TAG = /^<([\w\-]+)\s?([^>]*)>([^\x00]*[\w\/}"']>$)?([^\x00]*?)^<\/\1>/gim, 42 | SCRIPT = /'"]+)['"]?)?>([^\x00]*?)<\/script>/gm, 43 | STYLE = /'"]+)['"]?|\s+scoped)?>([^\x00]*?)<\/style>/gm, 44 | CSS_SELECTOR = /(^|\}|\{)\s*([^\{\}]+)\s*(?=\{)/g, 45 | CSS_COMMENT = /\/\*[^\x00]*?\*\//gm, 46 | HTML_COMMENT = //g, 47 | CLOSED_TAG = /<([\w\-]+)([^>]*)\/\s*>/g, 48 | LINE_COMMENT = /^\s*\/\/.*$/gm, 49 | JS_COMMENT = /\/\*[^\x00]*?\*\//gm, 50 | INPUT_NUMBER = /(]*?)type=['"]number['"]/gm 51 | 52 | function mktag(name, html, css, attrs, js) { 53 | return 'riot.tag(\'' 54 | + name + '\', \'' 55 | + html + '\'' 56 | + (css ? ', \'' + css + '\'' : '') 57 | + (attrs ? ', \'' + attrs.replace(/'/g, "\\'") + '\'' : '') 58 | + ', function(opts) {' + js + '\n});' 59 | } 60 | 61 | function compileHTML(html, opts, type) { 62 | 63 | var brackets = riot.util.brackets 64 | 65 | // foo={ bar } --> foo="{ bar }" 66 | html = html.replace(brackets(QUOTE), '="$1"$2') 67 | 68 | // whitespace 69 | html = opts.whitespace ? html.replace(/\n/g, '\\n') : html.replace(/\s+/g, ' ') 70 | 71 | // strip comments 72 | html = html.trim().replace(HTML_COMMENT, '') 73 | 74 | // input type=numbr 75 | html = html.replace(INPUT_NUMBER, '$1riot-type='+brackets(0)+'"number"'+brackets(1)) // fake expression 76 | 77 | // alter special attribute names 78 | html = html.replace(SET_ATTR, function(full, name, _, expr) { 79 | if (expr.indexOf(brackets(0)) >= 0) { 80 | name = name.toLowerCase() 81 | 82 | if (PREFIX_ATTR.indexOf(name) >= 0) name = 'riot-' + name 83 | 84 | // IE8 looses boolean attr values: `checked={ expr }` --> `__checked={ expr }` 85 | else if (BOOL_ATTR.indexOf(name) >= 0) name = '__' + name 86 | } 87 | 88 | return name + '="' + expr + '"' 89 | }) 90 | 91 | // run expressions trough parser 92 | if (opts.expr) { 93 | html = html.replace(brackets(EXPR), function(_, expr) { 94 | var ret = compileJS(expr, opts, type).trim().replace(/\r?\n|\r/g, '').trim() 95 | if (ret.slice(-1) == ';') ret = ret.slice(0, -1) 96 | return brackets(0) + ret + brackets(1) 97 | }) 98 | } 99 | 100 | // -> 101 | html = html.replace(CLOSED_TAG, function(_, name, attr) { 102 | var tag = '<' + name + (attr ? ' ' + attr.trim() : '') + '>' 103 | 104 | // Do not self-close HTML5 void tags 105 | if (VOID_TAGS.indexOf(name.toLowerCase()) == -1) tag += '' 106 | return tag 107 | }) 108 | 109 | // escape single quotes 110 | html = html.replace(/'/g, "\\'") 111 | 112 | // \{ jotain \} --> \\{ jotain \\} 113 | html = html.replace(brackets(/\\{|\\}/g), '\\$&') 114 | 115 | // compact: no whitespace between tags 116 | if (opts.compact) html = html.replace(/> <') 117 | 118 | return html 119 | 120 | } 121 | 122 | 123 | function riotjs(js) { 124 | 125 | // strip comments 126 | js = js.replace(LINE_COMMENT, '').replace(JS_COMMENT, '') 127 | 128 | // ES6 method signatures 129 | var lines = js.split('\n'), 130 | es6Ident = '' 131 | 132 | lines.forEach(function(line, i) { 133 | var l = line.trim() 134 | 135 | // method start 136 | if (l[0] != '}' && l.indexOf('(') > 0 && l.indexOf('function') == -1) { 137 | var end = /[{}]/.exec(l.slice(-1)), 138 | m = end && /(\s+)([\w]+)\s*\(([\w,\s]*)\)\s*\{/.exec(line) 139 | 140 | if (m && !/^(if|while|switch|for|catch)$/.test(m[2])) { 141 | lines[i] = m[1] + 'this.' + m[2] + ' = function(' + m[3] + ') {' 142 | 143 | // foo() { } 144 | if (end[0] == '}') { 145 | lines[i] += ' ' + l.slice(m[0].length - 1, -1) + '}.bind(this)' 146 | 147 | } else { 148 | es6Ident = m[1] 149 | } 150 | } 151 | 152 | } 153 | 154 | // method end 155 | if (line.slice(0, es6Ident.length + 1) == es6Ident + '}') { 156 | lines[i] = es6Ident + '}.bind(this);' 157 | es6Ident = '' 158 | } 159 | 160 | }) 161 | 162 | return lines.join('\n') 163 | 164 | } 165 | 166 | function scopedCSS (tag, style, type) { 167 | return style.replace(CSS_COMMENT, '').replace(CSS_SELECTOR, function (m, p1, p2) { 168 | return p1 + ' ' + p2.split(/\s*,\s*/g).map(function(sel) { 169 | var s = sel.trim().replace(/:scope\s*/, '') 170 | return s[0] == '@' || s == 'from' || s == 'to' || /%$/.test(s) ? s : 171 | tag + ' ' + s + ', [riot-tag="' + tag + '"] ' + s 172 | }).join(',') 173 | }).trim() 174 | } 175 | 176 | function compileJS(js, opts, type) { 177 | var parser = opts.parser || (type ? riot.parsers.js[type] : riotjs) 178 | if (!parser) throw new Error('Parser not found "' + type + '"') 179 | return parser(js, opts) 180 | } 181 | 182 | function compileTemplate(lang, html) { 183 | var parser = riot.parsers.html[lang] 184 | if (!parser) throw new Error('Template parser not found "' + lang + '"') 185 | return parser(html) 186 | } 187 | 188 | function compileCSS(style, tag, type) { 189 | if (type == 'scoped-css') style = scopedCSS(tag, style) 190 | else if (riot.parsers.css[type]) style = riot.parsers.css[type](tag, style) 191 | return style.replace(/\s+/g, ' ').replace(/\\/g, '\\\\').replace(/'/g, "\\'").trim() 192 | } 193 | 194 | function compile(src, opts) { 195 | 196 | opts = opts || {} 197 | 198 | if (opts.brackets) riot.settings.brackets = opts.brackets 199 | 200 | if (opts.template) src = compileTemplate(opts.template, src) 201 | 202 | src = src.replace(LINE_TAG, function(_, tagName, html) { 203 | return mktag(tagName, compileHTML(html, opts), '', '', '') 204 | }) 205 | 206 | return src.replace(CUSTOM_TAG, function(_, tagName, attrs, html, js) { 207 | 208 | html = html || '' 209 | 210 | // js wrapped inside 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/riot/todo.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'myriad pro', sans-serif; 3 | font-size: 20px; 4 | border: 0; 5 | } 6 | 7 | todo { 8 | display: block; 9 | max-width: 400px; 10 | margin: 5% auto; 11 | } 12 | 13 | form input { 14 | font-size: 100%; 15 | padding: .6em; 16 | border: 1px solid #ccc; 17 | border-radius: 3px; 18 | } 19 | 20 | button { 21 | background-color: #1FADC5; 22 | border: 1px solid rgba(0,0,0,.2); 23 | font-size: 100%; 24 | color: #fff; 25 | padding: .6em 1.2em; 26 | border-radius: 3em; 27 | cursor: pointer; 28 | margin: 0 .3em; 29 | outline: none; 30 | } 31 | 32 | button[disabled] { 33 | background-color: #ddd; 34 | color: #aaa; 35 | } 36 | 37 | ul { 38 | padding: 0; 39 | } 40 | 41 | li { 42 | list-style-type: none; 43 | padding: .2em 0; 44 | } 45 | 46 | .completed { 47 | text-decoration: line-through; 48 | color: #ccc; 49 | } 50 | 51 | label { 52 | cursor: pointer; 53 | } 54 | -------------------------------------------------------------------------------- /test/riot/todo.tag: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{ opts.title }

4 | 5 |
    6 |
  • 7 | 10 |
  • 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 | 40 | 41 |
42 | -------------------------------------------------------------------------------- /test/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background : red; 3 | } -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | -------------------------------------------------------------------------------- /test/text.txt: -------------------------------------------------------------------------------- 1 | HELLO! --------------------------------------------------------------------------------