Giter Site home page Giter Site logo

Comments (14)

jrburke avatar jrburke commented on August 16, 2024

It is definitely just a limitation of almond, done to try to reduce file size, since the requirejs optimizer already sequences modules in dependency order.

I can do a branch that would allow out-of-order listing. It means resolution of dependencies is triggered by a setTimeout, but that is probably fine.

Note however that this still requires named modules. If your source modules are using anonymous define() calls (best for development), your concatenation process will need to name them.

I encourage doing a trial with the requirejs optimizer, to confirm it is too slow for your workflow. It is a single JS file, and as illustrated in almond's README, it can accept command line args. It runs quite fast in node, and it also allows using loader plugins to inline their resources in the built file. Particularly handy for text templates.

All that said though, I'm all for supporting other concatenation tools. If your workflow takes care of anonymous modules, I can do a branch of almond that shows how it could work for out of order modules.

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

I can do a branch that would allow out-of-order listing. It means resolution of dependencies is triggered by a setTimeout, but that is probably fine.

(bold emphasis mine). No, I don't want that. I want to trigger the module loads via calls to require(). Basically, the "main" for my JS does a single require() to pull in the root module. All the loads will be triggered off of the deps specified in define() (I only use "require", "exports", "module" :-) ), or other require() calls. I add that root require() call, as part of the build.

To accomodate this style, seems like it would be appropriate to call the almond runtime telling it this, somehow. Basically, during define(), don't run the factory function, but arrange to run it during the first require(). Say, running this, after loading the almond code:

define.dontLoadDuringDefine = true

You probably already have some kind of config object you use, which would be a more appropriate place to put this.

Note however that this still requires named modules. If your source modules are using anonymous define() calls (best for development), your concatenation process will need to name them.

Indeed. My source modules are CJS format, and then I AMD-ize them, then concat them, adding the module names.

I encourage doing a trial with the requirejs optimizer, to confirm it is too slow for your workflow.

I probably should, but I happy with my current toolset, which I completely understand, and my concat processing currently takes 0.04 seconds on my laptop. I'd prefer to not inject another tool into the story right now.

from almond.

jrburke avatar jrburke commented on August 16, 2024

Ah OK, so as long as you guarantee the top level call(s) to require() are at the end of the file, then that is workable too. A variation of that is how requirejs works internally.

I assumed that the file with the require() call could be concatenated in the middle of the files calling define, with some of the require() dependencies occurring after the require() call.

To clarify, how are you calling the top-level require:

require('main');

or

require(['main']);

I want to be sure I explicitly test for the right case.

Better yet, if you have a sample output that you would like to work, feel free to pass it to me. You can do it in a private channel if you wish. I am also jrburke on gmail.

So if that sounds good, I'll work up a branch today with that change: just hold on to define() calls until a top-level require is hit, then execute/resolve dependencies during that require() call.

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

Yes, the top-level/root/first call to require() is at the end of the file - all of the define() calls will have been executed before the first require(), at the end of the file, would be executed. Of course, further require() calls will be made within those defined()d modules which are now getting loaded as part of the require() calls.

I use

require('main')

but I guess the array version should work as well (guessing that it should work semantically)

I don't have a sample to give you just yet.

I should mention, and I guess that you may have guessed, that I'm looking at a work-flow of using CJS-formatted modules with almond as the loader, wrapping the CJS-formatted modules with a generic AMD wrapper. Assuming I actually get this to work, and I'll wait for the outcome this "rando-ordered module defintions" issue, I'll do a write-up as a blog post. I may have my slightly chunky real code out in the open by that time as well, otherwise, I'll just create a sample.

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

Let me write a little test case fer ya in a gist. In a little bit ...

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

test cases

https://gist.github.com/1444113 - modules NOT in pre-req order
https://gist.github.com/1444117 - modules in pre-req order

Currently, the 1st case fails, but should work

from almond.

jrburke avatar jrburke commented on August 16, 2024

Great, thanks, I'm starting the branch now, should have something later today.

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

Note on the semantics of define() and require(). While reading up on the AMD specs, on the https://github.com/amdjs/amdjs-api/wiki/require page, I noticed the following, under "require(String)":

It MUST throw an error if the module has not already been loaded and evaluated

This is what I was getting at, asking if define() is actually spec'd to load and evaluate the module. My take is that load means: "prepare the module so it can be accessed via require(), but don't run the factory function", and evaluate means: "run the factory function, which will be done the first time the module is either require()d or specified as a dependency of a module which is being evaluated".

It sounds like, given the spec, define() is expected to load and evaluate, but I'm used to thinking of doing the evaluate lazily.

If you don't believe in lazy evaluate, then the changes I'm asking for in define() &| require() are actually providing completely different semantics for AMD. Which makes things confusing.

Perhaps another method is required, to do a define() which will evaluate the factory function lazily.

from almond.

jrburke avatar jrburke commented on August 16, 2024

It sounds like, given the spec, define() is expected to load and evaluate, but I'm used to thinking of doing the evaluate lazily.

We could probably reword that phrasing in the require API doc to mean 'throw if the module exports is not synchronously available'. I'll ask on the amd-implement list about that.

Mainly we want to avoid the expectation that there is synchronous loading/fetching of the dependency. Ideally the loader will have already parsed out the dependencies in the function via Function.prototype.toString() but I think there should be enough implementation leeway to allow the feature request in this bug -- I want to avoid the code weight of that approach for almond.

However, the expectation is that AMD modules have their dependencies evaluated before their factory function is called. It is a declarative dependency model, not the extremely imperative one of CommonJS -- require([], function (){}) can be used for imperative require use.

Also, the expectation is that every define()'d module will have their factory function called at some point during script execution. There were some CommonJS wrapping proposals that wanted to just deliver code, but not execute the factory function until the first require('') call, but that leads to a wordier base module API spec, particularly if callback-style require([], function(){}) is not supported. I do not think this would affect your use case (no reason to deliver code if it is not going to execute) but just mentioning it since it deals with module execution mechanics.

So if that sounds good, I'll continue with the branch. I'll send a note to the amd-implement list too about updating that text in the require API doc.

from almond.

jrburke avatar jrburke commented on August 16, 2024

Here is the amd-implement thread for the require wording change

from almond.

jrburke avatar jrburke commented on August 16, 2024

Here is the branch, it includes support for the implicit require, exports, and module dependencies.

I took your suggestion of an API to enable this behavior, if you set define.unordered = true then this behavior is enabled. Here is the file used in the unit test.

This brings the closure compiled and gzipped size up to 838 bytes, about an 80 byte increase. I would like to decrease that, try to get it under 800 bytes, but I'm probably not going to be religious about it, as that amount will likely be lost in the noise once combined with a few modules.

Give a holler if this looks good, and I'll consider moving this to the master branch.

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

works great, thx. test case is perfect.

I'd be a happy camper, switching from my first almond-like loader, to almond, if you fold this in.

from almond.

pmuellr avatar pmuellr commented on August 16, 2024

One thing that would probably shrink almond would be if you didn't distinguish between ordered and unordered. Boils down to embracing "define() does not run the factory function". define() is only used to register the module. The factory function is only run when someone require()s the module, or it's in the pre-req chain of a module being loaded. If no module ever pre-reqs the module, the factory function would never get run.

Per previous questions, not sure if this is a breaks AMD semantics or not.

from almond.

jrburke avatar jrburke commented on August 16, 2024

I went ahead and pushed the changes into master and did a 0.0.3 release. Closing this out now.

Removing the toggle for unordered just saves about 10 bytes. I would like to get down to just one path of behavior, but right now there are some uses of almond that just bundle up some define calls, the require() call happens implicitly with the data-main on a script tag, so I would need to modify the optimizer to add in that require call after a build, but it would have to be an optional thing, and I'm not sure it is worth the extra config flags in the loader yet.

from almond.

Related Issues (20)

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.