Requirejs can't compile my dojo-dependent modu

2019-05-01 04:47发布

问题:

UPDATE: I tried Jeff Barczewski's answer below, and though I no longer get the error below for plugins, I am now getting a different error:

Error: TypeError: Cannot read property 'normalize' of undefined
In module tree:
    mymodule/core

    at Object.<anonymous> (/usr/local/lib/node_modules/requirejs/bin/r.js:1193:35)

UPDATE 2: Since Jeff is correct that Dojo's plugins are not compatible with RequireJS, I decided to switch to using grunt-dojo instead for building dojo. I still use RequireJS for my own code and simply override the dojo dependencies to be ignored.

Original Post:

I'm trying to use grunt to compile a single JS file to lower the amount of HTTP requests browsers need to make. Since Dojo 1.9 is AMD-compliant, I figured I'd use Grunt's requirejs plugin to optimize my code. However, I am receiving the following error, both when using the Grunt plugin and when using r.js directly:

>> Tracing dependencies for: mymodule/core
>> TypeError: Cannot call method 'createElement' of undefined
>> In module tree:
>>     mymodule/core
>>       dojo/behavior
>>         dojo/query
>>           dojo/selector/_loader
{ [Error: TypeError: Cannot call method 'createElement' of undefined
In module tree:
    mymodule/core
      dojo/behavior
        dojo/query
          dojo/selector/_loader

    at eval (eval at <anonymous> (/Users/EugeneZ/Workspace/presentment/web/js/node_modules/grunt-contrib-requirejs/node_modules/requirejs/bin/r.js:23690:38), <anonymous>:6:24)
]
  originalError: 
   { [TypeError: Cannot call method 'createElement' of undefined]
     moduleTree: 
      [ 'dojo/selector/_loader',
        'dojo/query',
        'dojo/behavior',
        'mymodule/core' ],
     fileName: '/Users/EugeneZ/Workspace/presentment/web/js/dojo_release/dojo/selector/_loader.js' } }

Looking at the code for the _loader Dojo module, it's assuming it's running in a browser and relying on the document global:

var document;
var testDiv = document.createElement("div");

But why does requirejs not allow this? I've searched their documentation and can't find any way to turn this check off. I'm assuming I'm misunderstanding something or doing something wrong, but can't figure it out.

Here is the requirejs-relevant portion of my Gruntfile.js:

    requirejs: {
        compile: {
            options: {
                'baseUrl': './',
                'paths': {
                    'dojo': 'dojo_release/dojo',
                    'dojox': 'dojo_release/dojox',
                    'dijit': 'dojo_release/dijit',
                    'mymodule' : 'core/mymodule',
                    'osi': 'osi',
                    'demo': 'demo',
                    'slick': 'core/slick'
                },
                'name': 'mymodule/core',
                'out': './mymodule.js'
            }
        }
    }

回答1:

Dojo has several plugins that it uses that are not compatible with the r.js builder/optimizer. The best thing to do is to log an issue on https://bugs.dojotoolkit.org to have someone add the necessary plugin hooks so this can be resolved.

An alternative is to switch over to using the dojo builder, but it doesn't appear to create code that requirejs can use, so you would need to use their loader too, not requirejs. (dojo builder uses some proprietary? {cache:...} option for doing its dependencies rather than just inlining defines, so I could not find a way to build and load with requirejs).

Another work around (until dojo fixes the plugins to be compatible) if you want to stay with requirejs (like I did), you can exclude files that use these dojo plugins from the optimization and just have those files loaded separately unoptimized. So most of your files can be optimized except for ones using these plugins. Its not perfect but it gets you closer. Requirejs will simply load most of the files in optimized fashion and then just fetch those excluded ones individually at runtime.

To do this, add to your r.js build.js exclusions for specific files that use plugins which error out. So after running the build and you get that error, add to your paths the file that is using the plugin, ie. the second to last in the stack trace.

So add to your r.js build options

paths: { 
  'dojo/query': 'empty:', // this will exclude it from the build

Then run your build again and repeat until you have gotten all the other files that error.

When I was trying to build dojo's dgrid, I ended up with the following exclusions:

paths: {
    // r.js having issues building these, the plugins are not
    // compatible so let them load normally unoptimized
    'dgrid/extensions/ColumnHider': 'empty:',
    'put-selector/put': 'empty:'
    'dojo/i18n': 'empty:',
    'dojo/selector/_loader': 'empty:',
    'dojo/query': 'empty:',
    'dgrid/extensions/ColumnResizer': 'empty:',
    'dgrid/List': 'empty:',
    'xstyle/css': 'empty:'

Your list may vary based on what you are using.

Then just have these files (and their dependencies) available when running and requirejs will load them as it does in development. So at least the majority of your files will be loaded optimized from one file, then these will load after.

Note: Asking dojo to make the plugins r.js builder/optimizer compatible is the ultimate solution so we wouldn't need to do this hack. So even if you use this work around, please add an issue for dojo so this can get resolved once and for all. Mention all of the files that you ended up having to exclude to help the developers know what to fix. Then post a comment here for others to +1.