├── example
├── files
│ ├── file1.txt
│ └── file&2.t-t
├── index.html
├── index.js
├── package.json
└── example.js
├── test
├── fixtures
│ ├── files
│ │ ├── .hiddenfile
│ │ ├── file1.check
│ │ ├── file1.txt
│ │ └── file-3-other&file.txt
│ ├── source
│ │ ├── data.json
│ │ ├── include-folder-default.js
│ │ ├── include-folder-default.custom-js
│ │ ├── include-folder-regex.js
│ │ ├── bundle.js
│ │ ├── include-folder-filenames.js
│ │ ├── bundle-with-json.js
│ │ └── unrequire-include-folder.js
│ └── expected
│ │ ├── unrequire-include-folder.js
│ │ ├── include-folder-default.js
│ │ ├── include-folder-default.custom-js
│ │ ├── include-folder-filenames.js
│ │ ├── include-folder-regex.js
│ │ ├── bundle.js
│ │ └── bundle-with-json.js
├── .eslintrc
└── folderify_test.js
├── .travis.yml
├── .gitignore
├── .npmignore
├── folderify.sublime-project
├── LICENSE-MIT
├── package.json
├── README.md
└── lib
└── folderify.js
/example/files/file1.txt:
--------------------------------------------------------------------------------
1 | hello
--------------------------------------------------------------------------------
/example/files/file&2.t-t:
--------------------------------------------------------------------------------
1 | world
--------------------------------------------------------------------------------
/test/fixtures/files/.hiddenfile:
--------------------------------------------------------------------------------
1 | ciao
--------------------------------------------------------------------------------
/test/fixtures/files/file1.check:
--------------------------------------------------------------------------------
1 | this is file1 content
--------------------------------------------------------------------------------
/test/fixtures/files/file1.txt:
--------------------------------------------------------------------------------
1 | this is file1_1 content
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 |
5 |
--------------------------------------------------------------------------------
/test/fixtures/files/file-3-other&file.txt:
--------------------------------------------------------------------------------
1 | this is file3OtherContent content
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /**/config.json
3 | *.sublime-workspace
4 | *.log
5 |
--------------------------------------------------------------------------------
/test/fixtures/source/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": "bar",
3 | "beep": 3,
4 | "boop": true
5 | }
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *.sublime-*
2 | private
3 | example
4 | test
5 | .travis.yml
6 | .gitignore
7 | *.log
8 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
folderify example
3 |
4 |
--------------------------------------------------------------------------------
/test/fixtures/source/include-folder-default.js:
--------------------------------------------------------------------------------
1 | var iF = require("include-folder");
2 | var files = iF("./test/fixtures/files");
3 |
--------------------------------------------------------------------------------
/test/fixtures/source/include-folder-default.custom-js:
--------------------------------------------------------------------------------
1 | var iF = require("include-folder");
2 | var files = iF("./test/fixtures/files");
3 |
--------------------------------------------------------------------------------
/test/fixtures/source/include-folder-regex.js:
--------------------------------------------------------------------------------
1 | var iF = require("include-folder");
2 | var files = iF("./test/fixtures/files",/(.*)/);
3 |
--------------------------------------------------------------------------------
/test/fixtures/source/bundle.js:
--------------------------------------------------------------------------------
1 | var iF = require('include-folder');
2 | var files = iF(__dirname + '/../files',/(.*)/);
3 | console.dir(files);
4 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha": true
5 | },
6 | "extends": "eslint-config-semistandard"
7 | }
8 |
--------------------------------------------------------------------------------
/test/fixtures/source/include-folder-filenames.js:
--------------------------------------------------------------------------------
1 | var iF = require("include-folder");
2 | var files = iF("./test/fixtures/files",null,{preserveFilenames:true});
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/test/fixtures/source/bundle-with-json.js:
--------------------------------------------------------------------------------
1 | var iF = require("include-folder");
2 | var json = require("./data.json");
3 | var files = iF(__dirname + "/../files",/(.*)/);
4 | console.dir(files);
5 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/test/fixtures/expected/unrequire-include-folder.js:
--------------------------------------------------------------------------------
1 | var iF = undefined;
2 | console.log("anything");
3 | var another = undefined;
4 | console.log("anything");
5 | var ciao=iF;
6 | var b;
7 | console.log(b = another);
8 | console.log(b = undefined);
9 |
--------------------------------------------------------------------------------
/test/fixtures/source/unrequire-include-folder.js:
--------------------------------------------------------------------------------
1 | var iF = require("include-folder");
2 | console.log("anything");
3 | var another = require("include-folder");
4 | console.log("anything");
5 | var ciao=iF;
6 | var b;
7 | console.log(b = another);
8 | console.log(b = require("include-folder"));
9 |
--------------------------------------------------------------------------------
/test/fixtures/expected/include-folder-default.js:
--------------------------------------------------------------------------------
1 | var iF = undefined;
2 | var files = (function(){var self={},fs = require("fs");
3 | self["file3OtherFile"] = "this is file3OtherContent content";
4 | self["file1"] = "this is file1 content";
5 | self["file1_1"] = "this is file1_1 content";
6 | return self})();
7 |
--------------------------------------------------------------------------------
/test/fixtures/expected/include-folder-default.custom-js:
--------------------------------------------------------------------------------
1 | var iF = undefined;
2 | var files = (function(){var self={},fs = require("fs");
3 | self["file3OtherFile"] = "this is file3OtherContent content";
4 | self["file1"] = "this is file1 content";
5 | self["file1_1"] = "this is file1_1 content";
6 | return self})();
7 |
--------------------------------------------------------------------------------
/test/fixtures/expected/include-folder-filenames.js:
--------------------------------------------------------------------------------
1 | var iF = undefined;
2 | var files = (function(){var self={},fs = require("fs");
3 | self["file-3-other&file.txt"] = "this is file3OtherContent content";
4 | self["file1.check"] = "this is file1 content";
5 | self["file1.txt"] = "this is file1_1 content";
6 | return self})();
7 |
--------------------------------------------------------------------------------
/test/fixtures/expected/include-folder-regex.js:
--------------------------------------------------------------------------------
1 | var iF = undefined;
2 | var files = (function(){var self={},fs = require("fs");
3 | self["hiddenfile"] = "ciao";
4 | self["file3OtherFile"] = "this is file3OtherContent content";
5 | self["file1"] = "this is file1 content";
6 | self["file1_1"] = "this is file1_1 content";
7 | return self})();
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 |
--------------------------------------------------------------------------------
/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= 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # folderify
4 | [](http://travis-ci.org/parro-it/folderify)
5 | [](https://npmjs.org/package/folderify)
6 | [](https://codeclimate.com/github/parro-it/folderify)
7 | [](https://codeclimate.com/github/parro-it/folderify)
8 | [](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 |
--------------------------------------------------------------------------------
/test/folderify_test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var expect = require('expect.js');
4 | var concat = require('concat-stream');
5 | var test = require('tape');
6 | var fs = require('fs');
7 | var path = require('path');
8 | var folderify = require('../lib/folderify');
9 | var browserify = require('browserify');
10 |
11 | function rf (file) {
12 | return fs.readFileSync(path.join(__dirname, file), 'utf8');
13 | }
14 |
15 | function checkTransform (sourcefile, expectedfile, t) {
16 | var source = rf(sourcefile);
17 | var expected = rf(expectedfile);
18 | var result = concat(function (data) {
19 | expect(data).to.be.equal(expected);
20 | t.end();
21 | });
22 | var stream = folderify(sourcefile);
23 | stream.pipe(result);
24 | stream.write(source);
25 | stream.end();
26 | }
27 |
28 | test('folderify exports a function', function (t) {
29 | expect(folderify).to.be.an('function');
30 | t.end();
31 | });
32 |
33 | test('return a through stream', function (t) {
34 | expect(folderify().constructor.name).to.be.equal('Stream');
35 | t.end();
36 | });
37 |
38 | test('un-requires include-folder', function (t) {
39 | checkTransform(
40 | 'fixtures/source/unrequire-include-folder.js',
41 | 'fixtures/expected/unrequire-include-folder.js',
42 | t
43 | );
44 | });
45 |
46 | test('replaces includeFolder call with `files` array', function (t) {
47 | checkTransform(
48 | 'fixtures/source/include-folder-default.js',
49 | 'fixtures/expected/include-folder-default.js',
50 | t
51 | );
52 | });
53 |
54 | test('respects includeFolder pattern-match arguments', function (t) {
55 | checkTransform(
56 | 'fixtures/source/include-folder-regex.js',
57 | 'fixtures/expected/include-folder-regex.js',
58 | t
59 | );
60 | });
61 |
62 | test('preserves filenames when option is set', function (t) {
63 | checkTransform(
64 | 'fixtures/source/include-folder-filenames.js',
65 | 'fixtures/expected/include-folder-filenames.js',
66 | t
67 | );
68 | });
69 |
70 | test('as a browserify transform', function (s) {
71 | var expectedBundle = rf('fixtures/expected/bundle.js');
72 | var expectedBundleWithJson = rf('fixtures/expected/bundle-with-json.js');
73 |
74 | s.test('doesn\'t require brfs', function (t) {
75 | var b = browserify(path.join(__dirname, 'fixtures/source/bundle.js'));
76 | b.transform(folderify);
77 |
78 | b
79 | .bundle()
80 | .on('error', t.end.bind(t))
81 | .pipe(concat(function (data) {
82 | expect(data.toString('utf8')).to.be.equal(expectedBundle);
83 | t.end();
84 | }));
85 | });
86 |
87 | s.test('supports brfs running after folderify', function (t) {
88 | var b = browserify(path.join(__dirname, 'fixtures/source/bundle.js'));
89 | b.transform(folderify);
90 | b.transform('brfs');
91 |
92 | b
93 | .bundle()
94 | .on('error', t.end.bind(t))
95 | .pipe(concat(function (data) {
96 | expect(data.toString('utf8')).to.be.equal(expectedBundle);
97 | t.end();
98 | }));
99 | });
100 |
101 | s.test('support brfs running before folderify', function (t) {
102 | var b = browserify(__dirname + '/fixtures/source/bundle.js');
103 | b.transform('brfs');
104 | b.transform(folderify);
105 |
106 | b
107 | .bundle()
108 | .on('error', t.end.bind(t))
109 | .pipe(concat(function (data) {
110 | expect(data.toString('utf8')).to.be.equal(expectedBundle);
111 | t.end();
112 | }));
113 | });
114 |
115 | s.test('support bundles with non JavaScript files', function (t) {
116 | var b = browserify(__dirname + '/fixtures/source/bundle-with-json.js');
117 | b.transform('brfs');
118 | b.transform(folderify);
119 |
120 | b
121 | .bundle()
122 | .on('error', t.end.bind(t))
123 | .pipe(concat(function (data) {
124 | expect(data.toString('utf8')).to.be.equal(expectedBundleWithJson);
125 | t.end();
126 | }));
127 | });
128 | });
129 |
130 | test('Custom extensions', function (s) {
131 | s.test('expose validExtensions', function (t) {
132 | expect(folderify.validExtensions).to.be.an('array');
133 | t.end();
134 | });
135 |
136 | s.test('can modify validExtensions for allowing custom extensions', function (t) {
137 | var origin = folderify.validExtensions;
138 | folderify.validExtensions = ['.custom-js'];
139 | checkTransform(
140 | 'fixtures/source/include-folder-default.custom-js',
141 | 'fixtures/expected/include-folder-default.custom-js',
142 | {
143 | end: function (err) {
144 | folderify.validExtensions = origin;
145 | t.end(err);
146 | }
147 | }
148 | );
149 | });
150 | });
151 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------