├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE-MIT ├── README.md ├── example ├── example.js ├── files │ ├── file&2.t-t │ └── file1.txt ├── index.html ├── index.js └── package.json ├── folderify.sublime-project ├── lib └── folderify.js ├── package.json └── test ├── .eslintrc ├── fixtures ├── expected │ ├── bundle-with-json.js │ ├── bundle.js │ ├── include-folder-default.custom-js │ ├── include-folder-default.js │ ├── include-folder-filenames.js │ ├── include-folder-regex.js │ └── unrequire-include-folder.js ├── files │ ├── .hiddenfile │ ├── file-3-other&file.txt │ ├── file1.check │ └── file1.txt └── source │ ├── bundle-with-json.js │ ├── bundle.js │ ├── data.json │ ├── include-folder-default.custom-js │ ├── include-folder-default.js │ ├── include-folder-filenames.js │ ├── include-folder-regex.js │ └── unrequire-include-folder.js └── folderify_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /**/config.json 3 | *.sublime-workspace 4 | *.log 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | private 3 | example 4 | test 5 | .travis.yml 6 | .gitignore 7 | *.log 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | 5 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Andrea Parodi 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # folderify 4 | [![Build Status](https://img.shields.io/travis/parro-it/folderify.svg)](http://travis-ci.org/parro-it/folderify) 5 | [![Npm module](https://img.shields.io/npm/dt/folderify.svg)](https://npmjs.org/package/folderify) 6 | [![Code Climate](https://img.shields.io/codeclimate/github/parro-it/folderify.svg)](https://codeclimate.com/github/parro-it/folderify) 7 | [![Coverage](https://img.shields.io/codeclimate/coverage/github/parro-it/folderify.svg)](https://codeclimate.com/github/parro-it/folderify) 8 | [![Dependencies](https://img.shields.io/versioneye/d/parro-it/folderify.svg)](https://codeclimate.com/github/parro-it/folderify) 9 | 10 | 11 | 12 | 13 | 14 | browserify call to [includeFolder](https://github.com/parro-it/include-folder) 15 | 16 | 17 | This module is a plugin for [browserify](http://browserify.org) to parse the AST 18 | for `includeFolder()` calls so that you can inline folder contents into your 19 | bundles. 20 | 21 | Even though this module is intended for use with browserify, nothing about it is 22 | particularly specific to browserify so it should be generally useful in other 23 | projects. 24 | 25 | ## Getting Started 26 | Install the module with: `npm install folderify --save` 27 | 28 | then, for a main.js: 29 | 30 | ``` js 31 | var includeFolder = require('include-folder'), 32 | folder = includeFolder("./aFolder"); 33 | ``` 34 | 35 | and a [aFolder like this](https://github.com/parro-it/include-folder/tree/master/test/files): 36 | 37 | 38 | when you run the browserify command: 39 | 40 | ``` 41 | $ browserify -t folderify main.js > bundle.js 42 | ``` 43 | 44 | now in the bundle output file you get, 45 | 46 | ``` js 47 | var includeFolder = undefined, 48 | folder = { 49 | file3OtherFile: 'this is file3OtherContent content', 50 | file1: 'this is file1 content', 51 | file1_1: 'this is file1_1 content' 52 | }; 53 | ``` 54 | 55 | 56 | ## or with the api 57 | 58 | ``` js 59 | var browserify = require('browserify'); 60 | var fs = require('fs'); 61 | 62 | var b = browserify('example/main.js'); 63 | b.transform('folderify'); 64 | 65 | b.bundle().pipe(fs.createWriteStream('bundle.js')); 66 | ``` 67 | 68 | 69 | 70 | 71 | ##How it works 72 | 73 | Folderify inline a whole directory content in browserify results. 74 | 75 | 1. It uses falafel to intercepts calls to [include-folder](https://github.com/parro-it/include-folder) 76 | 2. use include-folder to generate source code of a function with a fs.readFileSync call for each file in directory 77 | 3. feed brfs stream with generated source code 78 | 4. replace include-folder call with brfs output 79 | 80 | 81 | ##Use cases 82 | 83 | I use it to inline my HTML templates folder when I browserify 84 | sites, but I guess it could be useful in many situations... 85 | 86 | ##Custom file extensions 87 | 88 | By default, supported file extensions are: 89 | - `.es` 90 | - `.es6` 91 | - `.js` 92 | - `.jsx` 93 | 94 | The list is exposed as a property `validExtensions` on the folderify function and can be easily extended: 95 | ```js 96 | var browserify = require('browserify'); 97 | var folderify = require('folderify'); 98 | folderify.validExtensions.push('.custom-js'); 99 | 100 | var b = browserify('example/main.js'); 101 | b.transform(folderify); 102 | ``` 103 | 104 | 105 | ## Contributing 106 | 107 | In lieu of a formal styleguide, take care to maintain the existing coding style. 108 | Add unit tests for any new or changed functionality. 109 | 110 | 111 | ## License 112 | 113 | Copyright (c) 2013 Andrea Parodi 114 | 115 | Licensed under the MIT license. 116 | 117 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 2 | folderify example 3 | 4 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var includeFolder = require('include-folder'); 4 | 5 | var folder = includeFolder(__dirname + '/files'); 6 | 7 | console.log(folder.file1, folder.file2); 8 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "folderify-example", 3 | "version": "1.0.0", 4 | "description": "an example project for folderify", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "browserify index.js -t folderify > example.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/parro-it/folderify" 13 | }, 14 | "author": "andrea@parro.it", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/parro-it/folderify/issues" 18 | }, 19 | "homepage": "https://github.com/parro-it/folderify", 20 | "dependencies": { 21 | "include-folder": "^0.9.0" 22 | }, 23 | "devDependencies": { 24 | "browserify": "^11.0.0", 25 | "folderify": "*" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /folderify.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "." 6 | }, 7 | { 8 | "path": "/home/parro-it/Desktop/repos/include-folder" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /lib/folderify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var brfs = require('brfs'); 4 | var concat = require('concat-stream'); 5 | var includeFolder = require('include-folder'); 6 | var falafel = require('falafel'); 7 | var through = require('through'); 8 | var path = require('path'); 9 | var fs = require('fs'); 10 | 11 | function folderify (file) { 12 | var data; 13 | var pending; 14 | var ifNames; 15 | var tr; 16 | var itsDirName; 17 | var itsFileName; 18 | 19 | function isIF (node) { 20 | var c = node.callee; 21 | 22 | return c && 23 | node.type === 'CallExpression' && 24 | node.arguments && 25 | node.arguments.length && 26 | node.arguments[0].value === 'include-folder' && 27 | 28 | c.type === 'Identifier' && 29 | c.name === 'require'; 30 | } 31 | 32 | function isParsableFileName (filename) { 33 | return folderify.validExtensions.indexOf(path.extname(filename)) >= 0; 34 | } 35 | 36 | function write (buf) { 37 | data += buf; 38 | } 39 | 40 | function finish (output) { 41 | tr.queue(String(output)); 42 | tr.queue(null); 43 | } 44 | 45 | function isVarDecl (node) { 46 | return isIF(node) && 47 | node.parent.type === 'VariableDeclarator' && 48 | node.parent.id.type === 'Identifier'; 49 | } 50 | 51 | function isVarAssign (node) { 52 | return isIF(node) && 53 | node.parent.type === 'AssignmentExpression' && 54 | node.parent.left.type === 'Identifier' 55 | 56 | ; 57 | } 58 | 59 | function unrequireIF (node) { 60 | function unrequire (n) { 61 | n.update('undefined'); 62 | } 63 | if (isVarDecl(node)) { 64 | ifNames[node.parent.id.name] = true; 65 | 66 | unrequire(node.parent.init); 67 | } else if (isVarAssign(node)) { 68 | ifNames[node.parent.left.name] = true; 69 | 70 | unrequire(node.parent.right); 71 | } 72 | } 73 | 74 | function buildOriginalSource (folder, filter, options, stream) { 75 | if (!filter) { 76 | filter = /^[^.].*$/; 77 | } 78 | 79 | if (typeof options !== 'object') { 80 | options = {}; 81 | } 82 | 83 | // Emit file events for each file in this folder. 84 | // Used by a file-watcher like watchify. 85 | fs.readdirSync(folder) 86 | .filter(filter.test.bind(filter)) 87 | .map(function (file) { 88 | stream.emit('file', path.join(folder, file)); 89 | }); 90 | 91 | var fnBody = includeFolder.buildSource(folder, filter, options); 92 | 93 | return '(function(){' + 94 | fnBody + 95 | '})()'; 96 | } 97 | 98 | function parse (stream) { 99 | if (!isParsableFileName(itsFileName)) { 100 | finish(data); 101 | return; 102 | } 103 | 104 | var output = falafel(data, function (node) { 105 | unrequireIF(node); 106 | 107 | if (node.type === 'CallExpression' && node.callee && ifNames[node.callee.name] === true) { 108 | var folderSourceCode = node.arguments[0].source(); 109 | 110 | folderSourceCode = folderSourceCode 111 | .replace(/__dirname/g, '"' + itsDirName + '"') 112 | .replace(/__filename/g, '"' + itsFileName + '"') 113 | .replace(/\\/g, '/'); 114 | 115 | var folder = eval(folderSourceCode); // eslint-disable-line no-eval 116 | 117 | var filesFilter; 118 | 119 | if (node.arguments.length > 1) { 120 | filesFilter = eval(node.arguments[1].source()); // eslint-disable-line no-eval 121 | } 122 | 123 | var options; 124 | 125 | if (node.arguments.length > 2) { 126 | options = eval('(' + node.arguments[2].source() + ')'); // eslint-disable-line no-eval 127 | } 128 | 129 | var originalSource; 130 | 131 | originalSource = buildOriginalSource(folder, filesFilter, options, stream); 132 | 133 | var brfsStream = brfs(folder + 'bogus.txt'); 134 | 135 | var brfsResult = concat({encoding: 'string'}, function (result) { 136 | node.update(result); 137 | pending--; 138 | if (pending === 0) { 139 | finish(output); 140 | } 141 | }); 142 | 143 | brfsStream.on('error', function (err) { 144 | this.emit('error', err); 145 | }); 146 | 147 | brfsStream.pipe(brfsResult); 148 | 149 | brfsStream.write(originalSource); 150 | brfsStream.end(); 151 | 152 | pending++; 153 | } 154 | }); 155 | 156 | if (pending === 0) { 157 | finish(output); 158 | } 159 | } 160 | 161 | function end () { 162 | try { 163 | parse(this); 164 | } catch (err) { 165 | this.emit('error', err); 166 | } 167 | } 168 | 169 | itsDirName = path.dirname(file); 170 | itsFileName = file; 171 | 172 | data = ''; 173 | pending = 0; 174 | ifNames = {}; 175 | tr = through(write, end); 176 | 177 | return tr; 178 | } 179 | 180 | folderify.validExtensions = [ 181 | '.es', 182 | '.es6', 183 | '.js', 184 | '.jsx' 185 | ]; 186 | 187 | module.exports = folderify; 188 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "folderify", 3 | "description": "Expose the content of each file in a folder as an object property.", 4 | "version": "1.2.1", 5 | "homepage": "https://github.com/parro-it/folderify", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Andrea Parodi", 9 | "email": "andrea@parro.it" 10 | }, 11 | "eslintConfig": { 12 | "extends": "eslint-config-semistandard" 13 | }, 14 | "repository": "parro-it/folderify.git", 15 | "licenses": "MIT", 16 | "main": "lib/folderify", 17 | "engines": { 18 | "node": ">= 0.10.0" 19 | }, 20 | "scripts": { 21 | "test": "eslint lib test/folderify_test.js && node test/folderify_test.js | faucet" 22 | }, 23 | "devDependencies": { 24 | "browserify": "^11.0.0", 25 | "eslint": "^1.10.3", 26 | "eslint-plugin-standard": "^1.3.1", 27 | "eslint-config-semistandard": "^5.0.0", 28 | "eslint-config-standard": "^4.4.0", 29 | "expect.js": "^0.3.1", 30 | "faucet": "0.0.1", 31 | "tape": "^4.4.0" 32 | }, 33 | "keywords": [ 34 | "folder", 35 | "content", 36 | "file", 37 | "require", 38 | "readFileSync", 39 | "browserify", 40 | "browserify-plugin", 41 | "browserify-transform" 42 | ], 43 | "dependencies": { 44 | "brfs": "~1.0.2", 45 | "concat-stream": "^1.5.0", 46 | "falafel": "^1.2.0", 47 | "include-folder": "^1.0.0", 48 | "through": "^2.3.8" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "mocha": true 5 | }, 6 | "extends": "eslint-config-semistandard" 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/expected/bundle-with-json.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o