Giter VIP home page Giter VIP logo

curl's Introduction

curl (cujoJS resource loader)

All development for curl.js and cram.js has stopped. For the foreseeable future, we will continue to respond to issues on github, as well as in the #cujojs room on freenode. However, keep in mind that there will be no further development, so some issues might not be resolved fully.

Also, if you are interested in becoming the lead maintainer of curl.js and/or cram.js, please let us know on #cujojs!

What is curl.js?

curl.js is a small and very fast AMD-compliant asynchronous loader. Size: ~4KB (gzipped) using Google's Closure Compiler.

If you'd like to use curl.js for non-AMD modules (ordinary javascript files), you'll want to use the legacy loader.

curl.js, like all async loaders, cannot circumvent browsers' security restrictions when using the file: protocol. Therefore, you must use curl from a page served from a web server (i.e. using http: or https:). Trying to run curl.js from a page loaded from your local file system will not work correctly in all browsers.

What the heck is "cujoJS"? cujoJS is the JavaScript Architectural Toolkit. More info: cujojs.com

What is "cram"? cram (cujoJS resource assembler) is the build tool companion to curl.js. You use cram to compile all of your modules into a small number of javascript files which are loaded much faster into the browsers.


Features at a glance:

  • Loads AMD-formatted javascript modules in parallel
  • Loads CommonJS/node modules (v1.1 when wrapped in a define())
  • Loads CommonJS/node modules (unwrapped when using the cjsm11 loader)
  • Loads non-AMD javascript files in parallel, too.
  • Loads CSS files and text files in parallel
  • Waits for dependencies (js, css, text, etc) before executing javascript
  • Waits for domReady, if desired
  • Allows for virtually limitless combinations of files and dependencies
  • Tested with Safari 5+, IE6+, and recent Chrome, FF, Opera

More detailed information below and on the wiki.


How to get support

  1. Go to the issues section of the curl repo (https://github.com/cujojs/curl/issues) and search for an answer to your question or problem.
  2. If no answer exists, file a new ticket! Somebody will typically respond within a few hours.

It's that easy.

Got more in-depth questions? Browse the cujoJS discussion group or come chat with us on freenode @ #cujojs.

See the wiki for information about using curl.js with jQuery, dojo, or underscore.


What's New?

  • 0.8.12
    • Add deprecation notice.
    • Add links to Rave Starters.
    • Add a link to the migration guide.
  • 0.8.11
    • Improve instructions for custom build thanks to @azazel75.
    • Stop catching (and indirectly squelching) errors in callback functions. Fixes #281.
    • Add version number at the beginning. Thanks @szepeviktor!
    • Add note about config options that are not supported in embedded config.
    • Added img! plugin and test cases. Thanks @asilvas!
    • Recognize Windows absolute urls. C:, e:/, etc. Thanks @doom777!
    • Update to README.md to correct link to James Burke's site. Thanks @kryger!
  • 0.8.10
    • Add moduleType and main properties to bower.json and package.json.
  • 0.8.9
    • Protect more API functions in plugins and loaders from closure compiler's aggressive obfuscation.
    • Switch to newer //# sourceURL syntax.
    • Stop doubling the ".js" on the sourceURL.
    • Ensure that define is undefined when wrapping cjs/node modules.
  • 0.8.8
    • Stop double-appending .css extension in css! plugin. (thanks @stanislawosinski!)
  • 0.8.7
    • Fix botched release wherein some dummy forgot to bin/make-all.sh.
  • 0.8.6
    • Cram plugins: hide legacy plugin's define from cram parser.
  • 0.8.5
    • Cram plugins: plugins are now responsible for resolving url/filepath.
    • Cram plugins: legacy loader no longer fails to add a .js extension when there are dots in the file name.

API at a glance

At it's core, curl.js provides an AMD environment:

define(['dep1', 'dep2', 'dep3' /* etc */], factory);
define(['dep1', 'dep2', 'dep3' /* etc */], module);
define(module);
define(name, ['dep1', 'dep2', 'dep3' /* etc */], factory);
define(name, ['dep1', 'dep2', 'dep3' /* etc */], module);
define(name, module);

These all define a module per the AMD specification.

  • ['dep1', 'dep2', 'dep3']: Module names or plugin-prefixed resource files. Dependencies may be named 'require', 'exports', or 'module' and will behave as defined in the CommonJS Modules 1.1 proposal.
  • factory: Function called to define the module. Each dependency is passed as a parameter to the factory.
  • module: Any javascript object, function, constructor, or primitive
  • name: String used to name a module. This is not necessary nor is it recommended. "Named modules" are typically only created by build tools and AMD bundlers.

define(function (require, exports, module) {
	var dep1 = require('app/foo');
	exports.foo2 = function () { return foo() + 2; };
});

Defines a module using the AMD-wrapped-CommonJS format. If a factory function has parameters, but the dependency list is missing, this format is assumed. The exports and module parameters are optional, but when specified, must be in this exact order.


define(function (require) {
	var dep1 = require('app/foo');
	return function () { return foo() + 2; };
});

Another variation on AMD-wrapped-CommonJS that uses require() in the tidy CommonJS manner, but returns the exports as per typical AMD.


define(['require', 'exports', 'module'], function (require, exports, module) {
	var dep1 = require('app/foo');
	exports.foo2 = function () { return foo() + 2; };
});

Another way to gain access to the CommonJS-like variables, require, exports, module. When specified in the dependency list, these "pseudo-modules" are provided as arguments to the factory function.


var dep1 = require('app/foo');
exports.foo2 = function () { return foo() + 2; };

curl.js also supports unwrapped CommonJS modules (and node.js modules) via the cjsm11 module loader. To use this module loader for a package, say Backbone, you would provide it to a package config, like this:

curl.config({
	packages: [
		{
			name: 'backbone',
			location: 'bower_components/backbone',
			main: 'backbone.min.js',
			config: { moduleLoader: 'curl/loader/cjsm11' } /* <-- hey! */
		}
	]
});

Read the notes in the src/curl/loader folder and the cjsm11.js file for more information about loading CommonJS and node.js modules.


curl.js's global API is for bootstrapping an app. You would typically only use this API once in your application to load the main module of your application.

For a complete description, check out the wiki.

curl(['main', 'other', 'another' /* etc */], callback, errorback);

Loads a module named "main" along with two other modules and then executes callback, handing it the exported values of the modules as parameters.

  • ['main', 'other', 'another']: Module names or plugin-prefixed resource files.
  • callback: Function to receive modules or resources. This is where you'd typically start up your app. Optional.
  • errorback: Function to receive an exception parameter when loading fails. Optional.

curl(['main', 'other', 'another' /* etc */])
	.then(callback, errorback);

Promises-based API for executing callbacks.

  • ['main', 'other', 'another']: Module names or plugin-prefixed resource files.
  • callback: Function to receive modules or resources. Optional.
  • errorback: Function to call if an exception occurred while loading. Optional.
  • Note: curl.js's Promises are not compliant with ES6 or Promises/A+ so they may not behave as you'd expect. For full compliance, use when.js or any another ES6 Promise shim.
    • when(curl(['dep1'])).then(callback); or Promise.resolve(curl(['dep1'])).then(callback);

curl(config, ['main' /* etc */], callback, errorback);

Specify configuration options, load dependencies, and execute callback.

  • config: Object containing curl configuration options (paths, etc.)
  • ['main']: Module name(s).
  • callback: Function to receive modules or resources. Optional.
  • errorback: Function to call if an exception occurred while loading. Optional.

curl(['main', 'domReady!' /* etc */]).then(
	callback,
	errorback
);
curl(['main', 'domReady!' /* etc */], function (main) {
	// do some bootstrapping here
});

Executes the callback when the dom is ready for manipulation AND all dependencies have loaded.


curl(['domReady!', 'js!nonAMD.js!order', 'js!another.js!order']), function () {
	/* do something cool here */
});

Executes the function when the non-AMD javascript files are loaded and the dom is ready. The another.js file will wait for the nonAMD.js file before executing.

Note: Please use curl.js's new legacy loader for much more flexible and sensible loading of non-AMD scripts. Please read the docs in the src/curl/loader folder for more information.

Note: if a file supports AMD or CommonJS module formats, you can not use the js! plugin on that file.


curl(['js!nonAMD.js'])
	.next(['dep1', 'dep2', 'dep3'], function (dep1, dep2, dep3) {
		// do something before the dom is ready
	})
	.next(['domReady!'])
	.then(
		function () {
			// do something after the dom is ready
		},
		function (ex) {
			// show an error to the user
		}
	);

Executes callbacks in stages using .next(deps, callback).

Note: .next() does not load resources in parallel. Therefore, it is a last resort when other options do not satisfy your use case. You should use the preloads config option and/or the legacy loader whenever possible.


curl = {
	baseUrl: '/path/to/my/js',
	pluginPath: 'for/some/reason/plugins/r/here',
	paths: {
		curl: 'curl/src/curl',
		cssx: 'cssx/src/cssx',
		my: '../../my-lib/'
	},
	apiName: 'someOtherName'
};

If called before the <script> that loads curl.js, a global curl var will configure curl.js when it loads. All of the configuration parameters are optional. curl.js tries to do something sensible in their absence. :)

Some common configuration options:

  • baseUrl: the root folder to find all modules, default is the document's folder
  • paths: a mapping of module paths to relative paths (from baseUrl)
  • pluginPath: the place to find plugins when they are specified without a path (e.g. "css!myCssFile" vs. "cssx/css!myCssFile") and there is no paths mapping that applies.
  • apiName: an alternate name to curl and require for curl.js's global variable
  • apiContext: an object, rather than window, to place curl on when using apiName

A more complete list can be found on the wiki.


Very Simple Example

<script>

	// configure curl
	curl = {
		paths: {
			cssx: 'cssx/src/cssx/',
			stuff: 'my/stuff/'
		}
	};

</script>
<script src="../js/curl.js" type="text/javascript"></script>
<script type="text/javascript">

	curl(
		// fetch all of these resources ("dependencies")
		[
			'stuff/three', // an AMD module
			'cssx/css!stuff/base', // a css file
			'i18n!stuff/nls/strings', // a translation file
			'text!stuff/template.html', // an html template
			'domReady!'
		]
	)
	// when they are loaded
	.then(
		// execute this callback, passing all dependencies as params
		function (three, link, strings, template) {
			var body = document.body;
			if (body) {
				body.appendChild(document.createTextNode('three == ' + three.toString() + ' '));
				body.appendChild(document.createElement('br'));
				body.appendChild(document.createTextNode(strings.hello));
				body.appendChild(document.createElement('div')).innerHTML = template;
			}
		},
		// execute this callback if there was a problem
		function (ex) {
			var msg = 'OH SNAP: ' + ex.message;
			alert(msg);
		}
	);

</script>

The file structure for this example would look as follows:

js/
	curl/
		plugin/
			i18n.js
			text.js
		domReady.js
	cssx/
		src/
			cssx/
				css.js
	my/
		stuff/
			nls/
				strings.js
			base.css
			template.html
			three.js
	curl.js

What is an asynchronous loader?

Web apps, especially large ones, require many modules and resources. Most of these modules and resources need to be loaded at page load, but some may be loaded later, either in the background or "just in time". They also need to be loaded as quickly as possible.

The traditional way to load javascript modules is via a <SCRIPT> element in an HTML page. Similarly, CSS files are loaded via a <LINK> element, and text resources are either loaded in the page or via XHR calls.

The problem with <SCRIPT> and <LINK> elements is that a browser must execute them sequentially since it has no idea if one may depend on another. It just assumes the developer has placed them in the correct order and that there are dependencies. (The term "synchronous loading" is used to describe this process since the elements are executed in a single timeline.)

If there are no dependencies between two files, loading them sequentially is a waste of time. These files could be loaded and executed in parallel (i.e at the same time).

An asynchronous loader does just that: it loads javascript files (and other types of files) in parallel whenever possible.

curl.js has lots of company. Other async loaders include LABjs, Steal.js, yepnope.js, $script.js, the Backdraft loader (bdLoad), and RequireJS.

(a more complete list)


What is AMD?

Asynchronous Module Definition is a de facto standard for javascript modules that can be loaded by asynchronous loaders. It defines a simple API that developers can use to write their javascript modules so that they may be loaded by any AMD-compliant loader.

AMD Spec

The AMD proposal follows some parts of the CommonJS Modules proposal. Because of the way browsers load and evaluate scripts, AMD can't follow it completely without causing significant processing overhead. Instead, AMD allows us to place a lightweight wrapper around javascript modules to help work around the shortcomings.

Ultimately, both proposals (AMD and Modules 1.1) are in preparation for an official javascript modules specification and eventual implementation in browsers.

If you don't want to wait for official javascript modules, then don't. The future is now. AMD works now -- and it's awesome.

AMD's API focuses on one globally-available function: define() and some CommonJS-inspired variables, require(), exports, and module. require() specifies a list of dependent modules or resources that must be loaded before running a set of code. This code resides in a callback function that is executed asynchronously, i.e. it runs later, not in the current "thread". Specifically, it executes when all of the dependencies are loaded and ready.

The proposal does not mandate that require() be specified globally. In fact, at a global level, the concerns of a loader are about application bootstrapping and not about finding dependencies. To keep the confusion about these two concepts to a minimum, curl.js uses curl() for the public API. You may rename this API back to require() by supplying the apiName config param (apiName: "require"), but this is not recommended.

It's more important that the define() method be consistent. This is the method that tells the loader what modules have been loaded by a script. define() also specifies a list of dependencies and a callback function that defines and/or creates the resource when the dependencies are ready. Optionally, define() also takes a name parameter, but this is mainly for build tools and optimizers.

Inside the define(), the require() method acts like other AMD loaders.

AMD's API also helps code reuse by providing compatibility with CommonJS (server) modules. AMD-compliant loaders support the same require() syntax and argument signatures as server-side javascript (ssjs) modules.

The beauty of AMD loaders is their ability to remove the drudgery of manually managing dependencies. Since all dependencies are listed within the modules, the loader will ensure that everything is loaded into the browser -- and in the right order.


Can curl.js work with non-AMD and non-CommonJS javascript files?

Yes, but why would you? Once you start using AMD, you'll never go back! :)

You may use non-AMD javascript files by using the legacy loader like this:

curl.config({
	paths: {
		plainOldJsFile1: {
			location: 'js/plainOldJsFile1.js',
			config: { loader: 'curl/loader/legacy', exports: 'aGlobal' }
		},
		anotherPlainOldJsFile: {
			location: 'js/anotherPlainOldJsFile.js',
			config: {
				loader: 'curl/loader/legacy',
				exports: 'anotherGlobal',
				requires: [ 'plainOldJsFile1' ]
			}
		}
	}
});
curl(['anotherPlainOldJsFile']).then(
	function (anotherGlobal) {
		/* do something with your plain, boring javascript files */
	},
	function () {
		/* do something if any fail to load */
	}
);

Please read the docs in the src/curl/loader folder for more information.


Can curl.js load non-javascript files via plugins?

Yes, curl.js follows the CommonJS Loader Plugin specification, so you can use any compatible plugin. The following plugins are included:

js! -- loads non-AMD javascript files. more info on the wiki

text! -- loads text files

link! -- loads css files via a link element (simple, fast)

css! -- loads css files (lots of options)

domReady! -- resolves when the dom is ready for manipulation

async! -- resolves when a module signals it's ready

i18n! -- loads text strings and other locale-specific constants

Some plugin docs are on the wiki.

More documentation is available inside the source of these plugins!


How are modules loaded?

curl.js uses <script> element injection rather than XHR/eval for AMD modules. This allows curl.js to load cross-domain scripts as well as local scripts. CommonJS modules use XHR/eval so must be wrapped for "transport" if not on the same domain. Typically, you will bundle your modules before using them in production, anyways. Most bundlers will wrap CommonJS modules in AMD.

To find scripts and other resources, curl.js uses module names. A module name looks just like a file path, but typically without the file extension. If a module requires a plugin in order to load correctly, it will have a prefix delimited by a "!" and will also often have a file extension when a plugin may load different types of files.

Some examples of module names:

By default, curl.js will look in the same folder as the current document's location. For instance, if your web page is located at http://my-domain/apps/myApp.html, curl.js will look for the JsonRest module at http://my-domain/apps/dojo/store/JsonRest.js.

You can tell curl.js to find modules in other locations by specifying a baseUrl or individual packages for each of your libraries. For example, if you specify a baseUrl of /resources/ and the following paths:

packages: [
	{ name: "dojo", location: "third-party/dojo" },
	{ name: "css", location: "third-party/cssmojo/css" },
	{ name: "my", location: "my-cool-app-v1.3" },
	{ name: "my/lib/js", location: "old-js-libs" }
]

Then the modules listed above will be sought in the following locations:

  • /resources/third-party/dojo/store/JsonRest.js
  • /resources/my-cool-app-v1.3/lib/string/format.js
  • /resources/old-js-libs/plain-old-js.js
  • /resources/my-cool-app-v1.3/styles/reset.css
  • http://some-cdn/uber/module.js

Note: you will need to create a path to curl.js's plugins and other modules if the curl folder isn't directly under the same folder as your web page. curl.js uses the same mechanism to find its own modules.


What are AMD plugins?

AMD supports the notion of plugins. Plugins are AMD modules that can be used to load javascript modules -- or other types of resources. curl comes with several plugins already, including a text plugin (for templates or other text resources), two different css plugins, a dom-ready plugin, and several others.

Plugins are designated by a prefix on the name of the module or resource to be loaded. They are delineated by a ! symbol. The following example shows the use of some plugins:

define(
	[
		'text!myTemplate.html',
		'css!myCssFile'
	],
	function (templateString, cssLinkNode) {
		// do something with the template and css here
	}
);

Since plugins are just AMD modules, they would typically be referenced using their fully-pathed names. curl provides a pluginPath configuration option that allows you to specify the folder where [most of] your plugins reside so you don't have to specify their full paths. This also helps with compatibility with other AMD loaders.

If one or more of your plugins does not reside in the folder specified by the pluginPath config option, you can use its full path or you can specify a path for it in curl's paths config object.

// example of a fully-pathed plugin under the lib/cssx folder
define(['lib/cssx!myCssFile'], function (cssxDef) {
	// do some awesome css stuff here
});

Plugins can also have configuration options. Global options can be specified on curl's configuration object. Options can also be supplied to plugins via suffixes. Suffixes are also delineated by the ! symbol. Here's an example of a plugin using options:

// don't try to repair IE6-8 opacity issues in my css file
define(['css!myCssFile!ignore:opacity'], function (cssxDef) {
	// do some awesome css stuff here
});

How do I use curl.js?

  1. Learn about AMD-formatted javascript modules if you don't already know how.
  2. Clone or download curl to your local machine or server.
  3. Figure out the baseUrl and paths configuration that makes sense for your project.
  4. Check out the "API at a glance" section above to figure out which loading methodology you want to use.
  5. Study the "Very Simple Example" and some of the test files.
  6. Try it on your own files.

Too Many Modules!

I have dozens (or hundreds) of modules. Even with parallel loading, the performance sucks! What can I do about that?

True! No parallel loader can lessen the latency required to create an HTTP connection. If you have dozens or hundreds of files to download, it's going to take time to initiate each of the connections.

However, there are tools to that are designed to fix this problem! There are builders and compilers. dojo users are probably already familiar with dojo's build tool and optimizer. RequireJS comes with a build tool and Google's Closure compiler.

The build tool is used to concatenate several modules (and/or resources) into just a few files. It does this by following the dependency chain specified in the define() and require() calls. You can specify which top-level modules or resources are in each file and the build tool finds the rest.

After the build tool creates the concatenated files, the files can be passed into a compiler (also called a shrinker or compressor).

curl.js is compatible with RequireJS's build tool, r.js, but there's also cram. Cram is the cujoJS resource assembler.


Package Support

cujoJS supports packages. Packages are defined by the packages configuration parameter:

curl = {
	baseUrl: 'path/to/js',
	packages: {
		'my-package': {
			location: 'path/to/my-package',
			main: 'main/main-module-file',
			config: { /* package-specific configuration options */ }
		}
	}
};
curl = {
	baseUrl: 'path/to/js',
	packages: [
		{
			name: 'my-package',
			location: 'path/to/my-package',
			main: 'main/main-module-file',
			config: { /* package-specific configuration options */ }
		}
	]
};

The path property describes where to find the package in relation to the baseUrl parameter. The main and lib properties describe where to find modules inside the package. The main property gives the relative path to the package's main module.

In the example above, the main module of the package can be obtained as follows:

curl(['my-package'], callback);

and will be fetched from the following path:

path/to/js/path/to/my-package/main/main-module-file.js

Some other file in the package would be obtained as follows:

curl(['my-package/other-module'], callback);

and will be fetched from the following path:

path/to/js/path/to/my-package/other-module.js

Note that a package may also contain it's own embedded set of config options. Most, but not all, config options may be specified here. Specifically, you cannot specify any options that change the path or id of modules, such as paths, packages, plugins, or 'pluginPrefix.


What is cujoJS?

cujoJS is the JavaScript Architectural Toolkit. It employs MVC, IOC, AMD and lots of other TLAs. :) Our goal is to provide architectural tools and guidance. Find out more at cujoJS.com.

Kudos

Many thanks to Bryan Forbes (@bryanforbes) for helping to clean up my code and for making curl's domReady much more robust and help with dojo compatibility. More about Bryan: http://www.reigndropsfall.net/

Kudos also to James Burke (@jrburke) who instigated the AMD standard and paved the way to create AMD-style loaders. More about James: http://jrburke.com/about/

Shout out to Kris Zyp (@kriszyp) for excellent ideas and feedback and to Kyle Simpson (@getify) who is inarguably the godfather of javascript loading.

curl's People

Contributors

apetrushin avatar asilvas avatar briancavalier avatar bryanforbes avatar da-li avatar duzun avatar gehan avatar kkirsche avatar kryger avatar mmacaula avatar pgilad avatar phated avatar pmcelhaney avatar ryanflorence avatar scothis avatar strathmeyer avatar szepeviktor avatar unscriptable avatar webpro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

curl's Issues

"define" dependencies with absolute paths not working -- paths override makes no difference

Same issue with relative paths (Issue 21).

This is proving to make development of new modules challenging, as the framework is "live", but modules in development still need to point to this framework. I don't seem to have a good way to override dependencies without pushing them to the live environment too.

More importantly, here's some code to bring this situation to light:

<script>

    curl: { paths: {
        "css!": "//css.mycdn/", 
        "shared/indev-module": "//localhost/indev-module/indev-module", // this works OK -- though would be even better if relative paths also worked (Issue 21)
        "css!shared/indev-module/indev-module.css": "css!//localhost/indev-module/indev-module.css" // absolute nor relative works. this property effectively is being ignored
    }
    };

</script>
<script src="http://mycdn/shared/curl/v0.5.3/curl.js"></script>
<script>
    require(["shared/indev-module"]);
</script>

As for the contents of "indev-module.js":

define("shared/indev-module", ["css!shared/indev-module/indev-module.css"], function () {

 // define my module

});
  1. I'd expect my path setting to replace "css!shared/indev-module/indev-module.css" with "css!//localhost/indev-module/indev-module.css", which it does not. It's like the path setting is being ignored entirely. I suspect the part about the path being ignored is the result of having a default "css!" path.
  2. As a work-around (keeping in mind this is for in-development modules), I provided the absolute path of the dependency. So instead of depending on "css!shared/indev-module/indev-module.css" in the "define" call, I provided "css!//localhost/indev-module/indev-module.css". To my suprise, not even this worked -- instead, it tried to add the path to the baseUrl. Absolute paths should always take priority and ignore the baseUrl. I'd expect relative ("./" or "../") pathing to work here too (Issue 21), but that isn't as critical of an issue.

As to the 1st issue, this is likely due to using "paths" even for plugins, instead of allowing plugin-based baseUrl's to reside external to "paths" -- Making this change would allow both absolute and relative paths to take priority over plugin baseUrl's. This would be consistent with how plugins now take priority over the baseUrl.

The 2nd issue, from best I can tell, is simply a bug.

Jquery 1.7.1 define() not found or duplicates found

Hi,

I just tried curl.js.
I change the apiName to 'require' in order to apply it on my requireJs project.

Unfortunately I have a problem with jQuery 1.7.1, which works fine with require.js

Actually I have this error :

Uncaught Error: define() not found or duplicates found: jquery-1.7.1.min.js

You can reproduce it easily with this gist

https://gist.github.com/1394883

Am I doing something wrong ?

Cheers
Romain

curl downloads `preload` modules even if they're built-in to curl.js file

This happens when using one of the dist/ versions of curl and the new preload feature at the same time. curl starts processing the modules specified in the preload array before the browser finishes executing the curl.js file. Therefore, it starts downloading the preload modules before the define() calls for those same modules are executed in the curl.js file.

This only happens when using curl = { /* config with preloads */ };, not when using curl({ /* config with preloads */ }, deps, cb);.

underscore shim not working on dev branch

I am running the following test to check if the underscore shim solves the problems it should be solving:

  require(['curl/shim/underscore'], function() {
      console.log('loaded!');
      require(['underscore'], function(_) {
        console.log(_.template);
      });
    });

The reason why I'm checking for template is because it's the last thing to be defined on underscore (this is 1.2.2 btw).

After taking a look at the shim code and poking it for a while, it looks like when it tries to get core['_define'], it's still not defined, so it might get overwritten later. I've tried a few setTimeouts but nothing seemed to solve the issue.

Latest curl on "dev" branch not firing callbacks

No code changes on my side, just getting the latest (unpackaged, unminified) curl.js is not firing my modules callback.

curl(["js!jquery.js"], function() { }); // loads, and performs callback OK
curl(["module1"], function() { }); // loads, but does NOT callback

// module1.js
define("module1", ["js!jquery.js", "link!module1.css"], function() { }); // all resources load, but does NOT callback

Being everything is loading, just not calling back, hard to say if the issue is in curl or the "js!" plugin. I removed the "link!" dependency temporarily to verify it was not the culprit.

Same-domain relative paths do not work

<script>
  curl = {
    baseUrl: "//cdn1/"
  };
</script>
<script src="//cdn2/curl.js"></script>
<script>
  curl("module1_from_cdn"); // works as expected
  curl("./module2_from_same_app"); // tries to hit baseUrl instead of same domain
  curl("js!../js_from_same_app.js"); // tries to hit baseUrl instead of same domain
</script>

It's my understanding that only forward-relative paths should be hitting any pathing configurations, otherwise you have no way to load from same-domain without using an absolute path (i.e. "js!/abs_path/file.js"). We should be able to prefix paths with "./" or "../" to avoid pathing. I suppose it's possible for some situations that desire backward-relative paths from the baseUrl (or other "paths"), but that could be a pain in some situations, especially local test pages.

Support for multiple "baseUrl" endpoints based on plugin or extension?

I misunderstood the capabilities of the "path" configuration, as I thought it could be used to specify different end points based on the plugin (i.e. "js" goes to "js.cdn.com", "css" goes to "css.cdn.com", etc). Not being able to use different endpoints for all file types is a big problem. Obviously specifying the full domain/path for every resource is not a realistic option. Without this some apps would be slower than they are without the loader, negating most of the purpose of using it to begin with.

Am I missing something, or is it currently not possible to do this?

Loading "nested" plugins does not respect pluginPath

My app is layed out like this:

  • /index.html
  • /js
    • /lib
      • /curl-0.5.2.js
      • /curl-plugins
        • /domReady
        • /js.js
      • /jquery-1.6.2.js
      • /jquery.tablesorter-2.0.5

index.html:

<!doctype html>
<html lang="en">
<head>

    <script>
        // Configure curl.js
        curl = {
            pluginPath: "lib/curl-plugins",
            paths: {
                lib: 'js/lib'
            },
            apiName: 'require'
        };
    </script>
    <script src="js/lib/curl-0.5.2.js"></script>
    <script>
        require(['js!lib/jquery-1.6.2.js!order'])
            .next(['js!lib/jquery.tablesorter-2.0.5!order'])
            .then(function() {
                require(['domReady!'], function() {
                    $('body').text('loaded');
                });
            });
    </script>

</head>
<body>

</body>
</html>

Now the js! plugin loads finde from 'js/lib/curl-plugins'. The call to the domReady! plugin fails however, curl tries to load it from 'curl/domReady.js' (which seems to be the default plugin location) instead of respecting my pluginPath setting.

Can't load named modules asynchronously

The error

The module at util/foo.

define('foo', { foo: 'bar' })

The loading:

require(['util/foo'], function (f) { console.log(f) });

Gives this error: Error: define() not found or duplicates found: /path/util/foo.js

RequireJS loads it just fine.

Why would we do this?!

We're migrating our codebase to AMD, but its far too large to do all at once, so we're dropping in curl.js or require.js and using it for new development, and will go back and convert old code over time.

The end goal is to use the require.js optimizer and then curl.js in the browser, but not until everything is converted to AMD. So, for the time being, we are still using our old build process to concat all our JS together with Jammit and are naming our modules by hand.

However, for our specs / development of new stuff we'll be loading things asynchronously so we need it to be able to load named modules as if they were unnamed.

tl;dr

js! plugin is broken without !order

Review the following code:

<!doctype html>
<html lang="en">
<head>
    <script src="js/curl-0.5.2.js"></script>
    <script>
        curl(['js!js/jquery-1.6.2.js'], function() {
                $('body').text('loaded');
            });
    </script>
</head>
<body>

</body>
</html>

Curl tries to load jQuery from / for some reason. Adding !order to the jQuery load fixes this. But according to the doc of the js! plugin !order is optional.

dev055 branch returning "undefined" for all plugin-related paths

Not sure if I'm doing something wrong, but I cannot get curl.js (I build the plugins into it using an already established make process) under the dev055 branch to work at all. When requesting:

  curl("js!http://domain.tld/test.js");

All this does is request "undefined". Was this branch not ready for testing?

Mixed mode issues with Packages & Modules (JMH: we need application.json)

First of all, I want to clarify, "package" refers to a single javascript file which contains a compilation of two or more modules, for optimization purposes.

So lets just dig into an example, and it should be clear what the issue is in this situation:

<script src="curl.js"></script>
<script> // preloads!
curl("my-super-package"); // contains module1, 2, and 3
</script>

<!-- 
in a completely separate (and dynamic) part of my application, I'm going to dynamically inject content and load the individual modules required by the content
-->


<script>
curl("module2", function() { // use module2
});
</script>


<script>
curl("module4", function() { // use module4
});
</script>

OK. So the problem should already be clear, but lets talk this out. We've got a race condition, with module 2 (as it should not be downloading "module2" since it is already inside of the package). So why would we have code like this? You've got a huge application, where not all parts talk with one another, but you've got some form of "master" (potentially a few) where your common work takes place (such as including curl, and pre-loading common files such as this "package"). Ideally, we want to allow these types of disconnects between the master and the child pages, as it can be very difficult to keep everything in sync otherwise.

Of course, I could simply remove the inclusion of the "package", and all is well again. In a perfect world, developers can throw in their "curl" calls left and right, for each and every requirement they have, without concern over what has already been included, or what packages may have been included. And as "common" files change, we swap out with different packages in the header. The potential here is some substantial optimization's in the "master" with no impact on the remainder of the application -- if it worked, that is.

I have some ideas on how this could be addressed, but wanted to first throw the problem out there. I'm not sure if it is a limitation of AMD, or a misuse of it, or otherwise.

"!order" breaks with module loaded outside require()

I'm porting a non-AMD app from script.js to curl.js, converting to AMD modules as I go along.

I figured I could start off by wrapping some non-AMD libs in define(), starting with jQueryUI, then ran into what I believe is a curl.js bug, but could also be idiocy on my part.

I'll summarise the scenario as follows:

<script type="text/javascript" src="curl.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script language="javascript">
   define("ui", ["jquery!order", "js!jquery-ui.js!order"], function(){});

   ... some other code which uses the new "ui" module...
</script>

The result is:

Uncaught TypeError: Object function (a,b){return new e.fn.init(a,b,h)} has no method 'load'    curl.js:564

Now, removing the "!order" tag makes the problem go away, but it doesn't seem like the Right Thing To Do. I guess that in the above code, jquery isn't getting loaded via require, so perhaps that confuses the order plugin.

-Steve

404 Not Found on domReady.js, other plugins

Using the 0.5 release, with "dist/curl-kitchen-sink" or "dist/curl-with-js-and-domReady", the following code will result in an unexpected request for domReady.js file... My understanding is that the "domReady" plugin is part of these distros, so is this a bug or am I missing something?

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
    <script src="scripts/curl.js"" type="text/javascript"></script>
</head>
<body>

<script>
    curl(["js!scripts/test.js"])
    .then(function () {
        console.log('test1 done');
    });
</script>


</body>
</html>

Debug version of curl worth it to remove logging from release version?

I noticed there is somewhere between 500B and 1KB of console-logging-related functionality in the minified loader (kitchen sink distro). Just noticed it by chance, so thought I'd mention it since you put so much effort into shrinking the loader. I wouldn't want to enable logging in a release loader anyway.

issue with multiple .next statements

I may not understand the intended meaning of the .next statement. However, I am trying to do the following: stage loading of files in tranches before the main code executes.

File testNext.html:

<head>
    <title></title>
    <script type="text/javascript" id="curlConfig">
        curl = {
            paths: {
                curl: '/sharedJs/unscriptable-curl-5ac0fcd/src/curl/',
            },
            jsPrefetch: true
        };
    </script>
    <script src="/sharedJs/unscriptable-curl-5ac0fcd/src/curl.js" type="text/javascript"> </script>
    <script type="text/javascript">
        var steps = 0;
        curl([ 'js!sharedJs/jquery.js' ])
        .next([ 'js!stepOne.js' ], function(){ steps = steps+1; })
        .next([ 'js!stepTwo.js' ], function(){ steps = steps+2; })
        .then( function()
                {
                    $(document).ready( function(){
                                                    alert( steps );
                                                    testOne();
                                                    testTwo();
                                                 });
                }
                ,function(){alert("something wrong")});

    </script>
</head>
<body>
</body>

stepOne.js:
var testOne;

$(document).ready(function()
{
testOne = function() { $( 'body' ).append("<p>Test One!</p>") }
});

stepTwo.js:
var testTwo;

$(document).ready(function()
{
testTwo = function() { $( 'body' ).append("<p>Test Two!</p>") }
});

On loading the page, an alert box appears saying "2". The console shows "testTwo is not defined". Inspecting the page shows that a script element has been made for stepOne.js, but not for stepTwo.js. So it looks as if the requirement for the first .next statement has been fulfilled, but its callback is NOT executed; and the requirement for the second .next statement has not been fulfilled, but its callback has been executed.
Thanks,
Joop

i18n plugin

Create the i18n plugin. It should initially behave like dojo's and RequireJS's i18n plugin. (Later, we'll likely add a feature to prefetch / inject strings that may be stored and/or retrieved by an alternative mechanism. For instance, a project could use this feature to preload a customized bundle for a particular client or OEM.)

Attempt to load an empty CSS-file leads to endless call of ssWatcher

Hello John,

I’ve found a bug in css plugin: an attempt to load an empty CSS-file as a dependency leads to endless call of ssWatcher function. Moreover, in Chrome such dependency can’t be resolved or rejected. You can use the following code as a test case:

curl(["css!empty.css"], function() {
    alert("loaded");
});

Best regards.

Double loading of JS-file with and without !order option

Hello,

First of all I would like to thank you for the great loader. It is very useful.
I found a bug in curl.js that leads to problems when a JS file is used as a dependency with and without !order option. Namely, if one module depends on "someFile.js" and another module depends on "someFile.js!order", then this situation causes double loading of "someFile.js". As a result some strange and difficult to find bugs can occur in an application. The problem has its roots in the following part of fetchDep function (lines 541 and 542 in curl.js):

name = prefix + '!' + resName;
normalizedDef = cache[name];

As you can see there is no test for options in "resName".

Best regards.

apiName config does not allow namespacing

The following will not work (note: 1 script tag):

<script>
var $namespace = {}; // I don't expect curl to auto-create the parent objects
curl = { apiName: "$namespace.require" };
/* include curl */
</script>

$namespace.require should work, but does not. Instead, I need to require an additional script block following the include, something like (note: 2 script tags):

<script>
 curl = {}; // my config stuff
/* include curl */
</script>
<script>
  $namespace.require = curl;
</script>

Not a huge deal, but for a snippet of code that will be copy/pasted a lot, it'd be nice to remove excess clutter (providing a single script block), since the feature "apiName" already exists for this purpose.

Detection of extension is invalid, not accounting for querystring

Extension detection is important as it determines if the '.js' extension should be appended to the module or not. But if the file happens to utilize a querystring (i.e. "dynamic_scripts/script1.js?do=this"), it will instead try to request "dynamic_scripts/script1.js?do=this.js". This is pretty minor, as I can get around this by doing something like ""dynamic_scripts/script1.js?do=this&test" (resulting in ""dynamic_scripts/script1.js?do=this&test.js"), but still a bug so wanted to make you aware.

Relative paths of plugins and passed to plugins don't work

I have the following structure:

eventd/
|_ curl/
|   |_ curl loader source
|_ test/
    |_ foo.js
    |_ plugin.js
    |_ test.js
    |_ test.html

The files look like the following:

foo.js

define([], function(){
    return {
        foo: "bar"
    };
});

plugin.js

define([], function(){
    return {
        load: function(id, require, load){
            require([id], load);
        }
    };
});

test.js

define(["./plugin!./foo"], function(foo){
    console.log(foo);

    return foo;
});

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Test Page</title>

    <script>
        var curl = { baseUrl: '..' };
    </script>
    <script src="../curl/src/curl.js"></script>
    <script>
        curl(['test/test'], function(test){
            console.log(test);
        });
    </script>
</head>
<body>
</body>
</html>

When I load test.html, I get the following error:

GET http://projects.local/eventd/plugin.js 404 (Not Found)
curl.js:705 Uncaught Error: Syntax error or http error: .././plugin.js

It seems that curl doesn't accept a relative path to a plugin module, which makes it very hard to write a library. There seems to be another problem as well: if I change the reference from ./foo! to test/foo, I get the following error:

GET http://projects.local/foo.js 404 (Not Found)
curl.js:705 Uncaught Error: Syntax error or http error: /foo.js

It looks like it's looking for the foo module at the root of the server. In fact, the id passed to load is /foo. Is this right?

Can't set two paths to the same file

I'm passing a config that looks like this

...
'paths': {
...
'eaf.util': 'my/eaf/core',
'eaf.core':'my/eaf/core'
...
}

core.js defines both 'eaf.util' and 'eaf.core' by name.

I also have a module that is like this:

define('domain.base', [..., 'eaf.util', 'eaf.core'...], function (...

Trying to load this last module with curl results in "Promise already completed." To get around this, I replaced the throw with a return. Not sure of all the dangers, but I'm just trying to get curl working and am purty frustrated at this point, I have to say. fyi, I'm trying to migrate from http://jsclass.jcoglan.com/ loader pattern, which is why it seems paths are the shortest path to success.

"Promise Already Completed" when trying to use underscore as a module

Underscore.js is, according to the documentation, supposed to work in requirejs. When I try to use it together with curl, I get the following error and callstack:

Uncaught Error: Promise already completed.
b curl.js:4
b curl.js:4
b curl.js:7
K curl.js:9
R curl.js:7
b curl.js:11
(anonymous function)underscore.js:11
(anonymous function)

But the strange thing is that despite the error, underscore is available. I used the same approach as for loading jquery, i.e. added underscore to the paths option:

      curl = {
        baseUrl:'/js',
        paths:{
          shaders:'../shaders',
          underscore:'lib/underscore'
        },
        apiName:'require'
      };

Then in the modules that depend on underscore:

define(['underscore'], function(_){ ... });

Any ideas why this error is happening?

Protocol-relative URL's (i.e. "//mydomain/path") do not work

This is a non-issue for me as I am not using this feature, but wanted to make you aware since I caught it.

<script>
curl = {
   baseUrl: "//cdn1.mydomain.com/root/",  // WONT WORK
   pluginPath: "//cdn2.mydomain.com/root/curl/plugin",  // WONT WORK
   paths: {
      js: "//cdn3.mydomain.com/root/curl/plugin/js" // WONT WORK
   }
}
</script>
<script src="cdn1.mydomain.com/root/curl/curl.js"></script>

The above code will not work. I believe what it is doing is detecting a prefix of a single-forward slash "/" to detect for an absolute path, and ignoring the fact that double-forward means something entirely different.

"css!" plugin bug with absolute paths (v0.5.3)

Not sure how I've gotten away without discovering this bug until now, but it has proven troublesome with existing prod code. Here's the issue in a nutshell:

curl = {
  baseUrl: "//cdn1/", // default path
  paths: {
    "css!": "//cdn2/" // default path for css includes
  }
};

The above script (a variation thereof anyway) is built into our master curl include. Now performing the following in the App:

require("css!/css/test.css"); // instead of hitting the absolute path (instead of the path override since it is absolute), it is hitting //cdn2//css/test.css

It is entirely possible the issue I noted above is just a variation of the same problems in some of the other 0.5.3 issues reported, and may not even be specific to the "css!" plugin (though I was unable to repro with the "js!" plugin, but wanted to make aware just in case.

The only temporary workaround I could come up with for now (that is working so far) is:

curl = {
  baseUrl: "//cdn1/", // default path
  paths: {
    "css!": "//cdn2/" // default path for css includes
    , "css!/": "/" // if prefixed with "/" use that instead
    , "css!//": "//"
    , "css!http:": "http:"
    , "css!https:": "https:"
  }
};

Messy, but it gets the job done.

YepNope Syntax

Hi,

Something I found useful in requireJs and I don't find in curl.js is the YepNope syntax.

For exemple, if I depend on polyfill before loading my app, I can do something like this with RequireJs :

require([
  window.fn1 ? '' : 'js!fn1.js',
  JSON.parse ? '' : 'js!fn2.js'
], function(){
  console.log('Loaded');
})

The empty string and the null value does not work well with curl.js

Supporting CommonJS Module Loader plugin extensnion (or improve it to fit your needs)

We start to have a lot of AMD implementation, and that's great. BTW, each implementation has it's own way to handle loading plugins, and it's not a good idea.

Making loader plugin a standard provided by Curl will improve the number of total plugin available for it and in general for AMD, helping to its diffusion as a standard for loading modules (at least on the browser side).

If the spec isn't satisfying, it could be a good idea to contribute to it also ;)

Support for AutoHttps

It would be very useful for the loader to automatically rewrite all http uri's to be https when executing under an https application. Optionally, you could add a "AutoHttps" setting to the config (default: true) if you prefer, but for any real-world use you always use https requests for https applications. Naturally, it if not running under an https app, there is no difference, requests continue through http.

This simplifies complex applications from having to remember to support both cases for every dependency. There are some cases where the caller may want to control both their http and https url's, which will also work, as you do never rewrite already https uri's even on an http application.

Middleware for curl?

Hello,

I was thinking that something that would be really cool and add a lot of new possibilities would be to implement some kind of middleware engine into curl.

A middleware could be similar to what we are used to in frameworks such as connect:

function cacheMiddleware(deps, callback, next){
  var result = {}
  var newDeps = []
  for(var i=0; deps.length;i++){
    if(deps[i] in resourceCache){
      newDeps.push(deps[i])
      result[deps[i]] = resourceCache[deps[i]]
    }
  }
  next(newDeps)
  callback(result)
}

The middleware takes as input an array of dependencies urls, will do something with them (for example check them in a cache, transforms them in any way etc), when finished it will call next which will in fact call the next middleware in the chain. Since everything is asynchronous we should not return anything, instead call a callback with the result of the middleware (which should be the resolved urls into strings with data). The current fetcher and caching code could be implemented as middlewares as well.

A solution like this could for example be used for amazing things such as:

  • Use localStorage/sessionStorage for caching modules.
  • Using other transport protocols for fetching modules from a server, for example socket.io
  • Having a server component that co-works with the loader, so that for example you can request several modules
    at the same time to reduce the amount of roadtrips.

What do you think?

Compiling curl.js

Hello,

This isn't really an issue with curl itself, more a problem with working out how to correctly compile it using closure.

I'm attempting to build a file with curl and the domReady and script plugins. Are you able to tell me the correct cmd line?

This has been my best effort so far...

java -jar compiler/compiler.jar --js=curl.js --js=curl/domReady.js --js=curl/plugin/domReady.js --js=curl/plugin/js.js --js_output_file=curl.min.js

Thanks,
Tom.

Why's the "kitchen sink" build still looking for plugins externally?

This is more of a stupid question than a bug, but it's something I haven't been able to figure out.

According to my understanding of the documentation and READMEs, if I bring in a build of curl that includes the link! plugin--such as the one labeled "kitchen sink"--I shouldn't ever need to tell curl where to find said link! plugin. Nevertheless, as soon as I try to get curl to load something which asks for the link! plugin, it fires off an HTTP request for link.js in the default pluginPath which obviously 404s.

I'm clearly doing something very wrong, but am at a loss as to what that might be.

plugin modules at top level require trickery to be loaded

Any plugins that reside at the top level (i.e. at baseUrl) are basically "naked" as defined in the wiki (https://github.com/unscriptable/curl/wiki/Plugins). Therefore, the loader prepends the pluginPath onto them. This causes the loader to look in the curl/plugin folder rather than at the top level.

There is a work-around for this issue: include an entry in the paths config like this:

paths: {
    myplugin: './myplugin'
}

The existence of the slash is enough for the loader to no longer consider the plugin id to be "naked".

domReady plugin fires much later than it should (in FF/Chrome/Safari)

During evaluation I determined the "domReady!" callback seems to fire after all initial calls of "curl" have loaded. A clear case of this is hitting a dynamic endpoint mimicking that of a js file, but with long sleeps to expose the vulnerability.

The easiest comparison is with jQuery.ready, which fires when the dom is actually available, rather than after all resources have finished loading. It does work as expected in IE9 (have not tested older).

let devs pick which execution timing to use (don't export too early)

curl's algorithm for cascading module dependencies is aggressive. It doesn't wait for the "next turn" before cascading a module's exports to dependent modules and can therefore, call the cascading define() calls before the exports are fully defined.

While we (@cujojs) don't consider this to be a bug in curl.js (it decreases load time significantly), we believe we should have a better way to accommodate naively-coded AMD modules that export too early.

This could be a config property: waitForNextTurn: true

or the converse: cascadeAsap: true

module-v1.0 is not auto-adding .js -- bug or by design?

Using:

curl(['module-v1_0']);

Will fetch 'module-v1_0.js' automatically. But using:

curl(['module-v1.0']);
// or
curl(['module-v1.0.html']); // cross-domain html file include

Will fetch 'module-v1.0', using '.0' as the extension (or 'module-v1.0.html' with '.html' as the extension).

While this may appear to be a harmless distinction, it provides inconsistency, which may be unnecessarily confusing in implementation on a large scale. Isn't it true that if you do not use a plugin ('plugin!filename') that it is in fact a module? I'd say requiring ".js" on the end of every module is a reasonable expectation, as it already assumes this for no extension.

If for whatever reason, someone wanted to include modules with a different extension, this could possibly be wrapped in a small plugin such as "mod!mymodule.mod".

Thoughts?

"CSS Light" plugin request

The current "css" plugin is pretty hefty, and still does not fully work for all browsers across domains. Have you considered a simpler plugin, that simply does not support callback AFTER load (same behavior as current "css!myfile!nowait") since it takes very little code to achieve a link insert? Trying to avoid having to define css dependencies elsewhere, outside of curl, as it's clunky, especially with dynamically injected modules.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.