开发者

requireJS an entire folder

Is it possible to "require" an entire folder using requireJS.

For example, I have a behaviors folder with a ton of behavior 开发者_如何学Cjs files. I'd really like to be able to simply use require(['behaviors/*'], function() {...}); to load everything in that folder rather than having to keep that list up to date. Once compressed and optimized I'd have all those files lump together, but for development it's easier to work with them individually.


javascript in browser has no filesystem access and so it can't scan a directory for files. If you are building your app in a scripting language like php or ruby you could write a script that scans the directory and adds the file names to the require() call.


I don't know if I can recommend this approach anymore. I think the more explicit way to do this is by manually "requiring"/"exporting" the functionality you need. The exception I think is if you have a "namespace" of files that you want exported see below "Babel and ES6 Module Import Declarations (export-namespace-from) or see below "Babel and ES6 Module Import Declarations.

These solutions also assume that you have a meaningful file structure - where file names become part of that "require" * definition.

However, if you still need to do this there are a few existing tools and methods that might provide the behavior that you're looking for.


Possible Solutions

Babel and ES6 Module Import Declarations (plugin-export-namespace-from)

  1. Have a setup that is ES6 compliant.
  2. You need to update your .babelrc file to include babel-plugin-proposal-export-namespace-from.
  3. Use export namespace plugin by writing syntax like the following:

common/index.js

export * from './common/a'; // export const a = false;
export * from './common/b'; // export const b = true;

main.js

import { a, b } from './common';

console.log(a); // false
console.log(b); // true

Babel and ES6 Module Import Declarations (plugin-wildcard)

  1. Have a setup that is ES6 compliant.
  2. You need to update your .babelrc file to include babel-plugin-wildcard.
  3. Use wildcard namespace plugin by writing syntax like the following:

main.js

import { a, b } from './common/*'; // imports './common/a.js' and './common/b.js'

console.log(a); // false
console.log(b); // true

RequireJS (Now Outdated)

  1. Download and install require-wild npm install require-wild
  2. Configure the declaration as follows
    grunt.initConfig({
        requireWild: {
            app: {
                // Input files to look for wildcards (require|define)
                src: ["./**/*.js"], 

                // Output file contains generated namespace modules
                dest: "./namespaces.js", 

                // Load your require config file used to find baseUrl - optional
                options: { requireConfigFile: "./main.js" }
            }
        }
    }); 
    
    grunt.loadNpmTasks("require-wild");
    
    grunt.registerTask('default', ['requireWild']);
  1. Then run the grunt task. Your file will be generated. Modify your setup to load namespaces.js

require(['namespaces'], function () { ... });

  1. This now allows modules under src to use dependencies glob pattern matching.

require(['behaviors/**/*'], function (behaviors) { }


I know this is old, but I'd like to share my solution:

For this solution you need JQuery

1) Create a bash script that will list all the js files in "MyDirectory/", and save it to "directoryContents.txt":

#!/bin/bash
  #Find all the files in that directory...
  for file in $( find MyDirectory/ -type f -name "*.js" )
        do
          fileClean=${file%.js} #Must remove .js from the end!
          echo -n "$fileClean " >> MyDirectory/directoryContents.txt
        done
  • File will look like this:

MyDirectory/FirstJavascriptFile MyDirectory/SecondJavascriptFile MyDirectory/ThirdJavascriptFile

  • Problem with my script! Puts an extra " " at the end, that messes things up! Make sure to remove the excess space at the end of directoryContents.txt

2) Then in your Client side JS code:

  • do a "GET" request to retrieve the text file
  • For each entry (split by the space), 'require' that file:

.

$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
    var allJsFilesInFolder = data.split(" ");
    for(var a=0; a<allJsFilesInFolder.length; a++)
    {
        require([allJsFilesInFolder[a]], function(jsConfig) 
        {
            //Done loading this one file
        });
    }
}, "text");

I was having a problem with this code not finishing before my other code, so Here's my extended answer:

define([''], function() {

return {

    createTestMenu: function() 
    {
        this.loadAllJSFiles(function(){
            //Here ALL those files you need are loaded!
        });
    },

    loadAllJSFiles: function(callback)
    {   
        $.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
            var allJsFilesInFolder = data.split(" ");
            var currentFileNum = 0;
            for(var a=0; a<allJsFilesInFolder.length; a++)
            {
                require([allJsFilesInFolder[a]], function(jsConfig) 
                {
                    currentFileNum++;
                    //If it's the last file that needs to be loaded, run the callback.
                    if (currentFileNum==allJsFilesInFolder.length)
                    {
                        console.log("Done loading all configuration files.");
                        if (typeof callback != "undefined"){callback();}
                    }
                });
            }
        }, "text");
    }
}
});

What I ended up doing was everytime my Node server boots, it will run the bash script, populating directoryContents.txt. Then My client side just reads directoryContents.txt for the list of files, and requires each in that list.

Hope this helps!


There isn't really a way to do this conceptually on the fly (that I know of).

There's a few work arounds though:

Use grunt and concat and then just require that behemoth...I know, kinda sucky.

What I think is a better solution... use a require hierarchy like so:

require('/js/controllers/init', function(ctrls){
    ctrls(app, globals);
});

// /js/controllers/init.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
    return function protagonist(app, globals){
        var indexModule = index(app, globals);
        var indexModule = posts(app, globals);

        return app || someModule;
    };
});

// /js/controllers/index.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
    return function protagonist(app, globals){
        function method1(){}
        function method2(){}

        return {
           m1: method1,
           m2: method2
        };
    };
});

Note that "protagonist" function. That allows you to initialize modules before their use, so now you can pass in a 'sandbox' -- in this case app and globals.

Realistically, you wouldn't have /js/controllers/index.js... It should probably be something like /js/controllers/index/main.js or /js/controllers/index/init.js so that there is a directory adjacent to (sibling of) /js/controllers/init.js called "index". This will make your modules scalable to a given interface -- you can simply swap modules out and keep your interface the same.

Hope this helps! Happy coding!


I wrote a library to solve this problem. Eventually someone else came along and improved my library, here it is:

https://github.com/smartprocure/directory-metagen

You can use my lib with Gulp or whatever - it generates metadata for your project and RequireJS can use that metadata to require the desired files from the filesystem.

Using this lib will produce a RequireJS module that looks something like this:

define(
    [
        "text!app/templates/dashboardTemplate.ejs",
        "text!app/templates/fluxCartTemplate.ejs",
        "text!app/templates/footerTemplate.ejs",
        "text!app/templates/getAllTemplate.ejs",
        "text!app/templates/headerTemplate.ejs",
        "text!app/templates/homeTemplate.ejs",
        "text!app/templates/indexTemplate.ejs",
        "text!app/templates/jobsTemplate.ejs",
        "text!app/templates/loginTemplate.ejs",
        "text!app/templates/overviewTemplate.ejs",
        "text!app/templates/pictureTemplate.ejs",
        "text!app/templates/portalTemplate.ejs",
        "text!app/templates/registeredUsersTemplate.ejs",
        "text!app/templates/userProfileTemplate.ejs"
    ],
    function(){

        return {

            "templates/dashboardTemplate.ejs": arguments[0],
            "templates/fluxCartTemplate.ejs": arguments[1],
            "templates/footerTemplate.ejs": arguments[2],
            "templates/getAllTemplate.ejs": arguments[3],
            "templates/headerTemplate.ejs": arguments[4],
            "templates/homeTemplate.ejs": arguments[5],
            "templates/indexTemplate.ejs": arguments[6],
            "templates/jobsTemplate.ejs": arguments[7],
            "templates/loginTemplate.ejs": arguments[8],
            "templates/overviewTemplate.ejs": arguments[9],
            "templates/pictureTemplate.ejs": arguments[10],
            "templates/portalTemplate.ejs": arguments[11],
            "templates/registeredUsersTemplate.ejs": arguments[12],
            "templates/userProfileTemplate.ejs": arguments[13]
        }
    });

You can then require modules in your front-end like so:

var footerView = require("app/js/jsx/standardViews/footerView");

however, as you can see this is too verbose, so the magic way is like so:

name the dependency above as allViews!

now you can do:

var allViews = require('allViews');
var footerView = allViews['standardViews/footerView'];

There are two advantages to requiring directories whole:

(1) in production, with the r.js optimizer, you can point to one dependency (module A) and it can then easily trace all of A's dependencies that represent a entire directory

(2) in development, you can require whole directories up front and then use synchronous syntax to require dependencies because you know they have already been loaded

enjoy "RequireJS-Metagen"

https://github.com/smartprocure/directory-metagen

https://www.npmjs.com/package/requirejs-metagen

https://github.com/ORESoftware/requirejs-metagen

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜