Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jQuery's AMDness and require() #1066

Closed
IngwiePhoenix opened this issue May 15, 2015 · 6 comments
Closed

jQuery's AMDness and require() #1066

IngwiePhoenix opened this issue May 15, 2015 · 6 comments

Comments

@IngwiePhoenix
Copy link

While tracing down some biggies within my pack, I realized that jQuery is about 200kb. Ouch.

So in order to attempt to slim it down, I began to read into its source code, and it turned out that jQuery was using AMD style declaration. But when I tried to do the following, it didn't work at all!

define([
    "jquery.js/core",
    "jquery.js/sizzle/dist/sizzle",
    "jquery.js/event",
    "jquery.js/event/alias",
    "jquery.js/traversing",
    "jquery.js/callbacks",
    "jquery.js/deferred",
    "jquery.js/core/ready",
    "jquery.js/data",
    "jquery.js/queue",
    "jquery.js/queue/delay",
    "jquery.js/attributes",
    "jquery.js/manipulation",
    "jquery.js/manipulation/_evalUrl",
    "jquery.js/wrap",
    "jquery.js/css",
    "jquery.js/css/hiddenVisibleSelectors",
    "jquery.js/serialize",
    "jquery.js/ajax",
    "jquery.js/ajax/xhr",
    "jquery.js/ajax/script",
    "jquery.js/ajax/jsonp",
    "jquery.js/ajax/load",
    "jquery.js/event/ajax",
    "jquery.js/effects",
    "jquery.js/effects/animatedSelector",
    "jquery.js/offset",
    "jquery.js/dimensions",
    "jquery.js/deprecated"
], function( jQuery, Sizzle ) {

// Hotfix Sizzle
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;

return jQuery;

});

jquery.js is essentially an alias to bower_components/jquery/src. When I load that in the browser, I get a variety of odd errors.

What is the best way to use an AMD module via require()?

@jhnns
Copy link
Member

jhnns commented May 15, 2015

You don't need to use AMD to require AMD modules. You can just use CommonJS. Could you also post your webpack.config.js and the error messages you get.

BTW: jQuery is 84kb minified

@IngwiePhoenix
Copy link
Author

Sure, here you go. It seems that something is undefined, although I used the code snippet from above. It seems that jQuery is not getting what it really needs...

Error:

a115bca0ad522dfd05ea-main.js:11566 Uncaught TypeError: Cannot read property 'match' of undefined

Failing line:

/* 96 */
/***/ function(module, exports, __webpack_require__) {

    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
        __webpack_require__(18),
        __webpack_require__(82)
    ], __WEBPACK_AMD_DEFINE_RESULT__ = function( jQuery ) {
        ---> return jQuery.expr.match.needsContext;
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ },

Webpack config:

// core
var path = require("path");

// Paths
var cdn = path.join(__dirname,"..","cdn");
var app = path.join(cdn,"app");
var theme = path.join(__dirname,"..","themes","dragonsinn");
var cache = path.join(__dirname,"..","cache");

// Config stuff
if(typeof global.config == "undefined") {
    var ini = require("multilevel-ini");
    var me = require("package")(path.join(__dirname,".."));
    var config = ini.getSync(path.join(__dirname, "..", "config/BIRD3.ini"));
    config.base = path.join(__dirname,"..");
    config.version = me.version;
    config.package = me;
} else {
    var config = global.config;
}
config.maxFileSize = 1024*10;

var __debug = global.__debug || false;
//var _jquery = "jquip/dist/jquip.all"; // 154 kb
//var _jquery = "zepto/src/zepto"; // 135 kb
//var _jquery = "cash/dist/cash.js"; // 129 kb
//var _jquery = "jquery"; // 207 kb
var _jquery = path.join(config.base, "web-lib/jquery.js");

// Webpack: Load plugins
var webpack = require("webpack");
var wpConf = require("./webpack.config");
var extractText = require("extract-text-webpack-plugin");
var HashPlugin = require('hash-webpack-plugin');
var bowerwp = require("bower-webpack-plugin");
var stripper = require("strip-loader").loader;

// Webpack: make instances
// Generate the general webpack file - make it a lil' lib.
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin(
    "[hash]-libwebpack.js",
    ["main"]
);
// Constants that should be distributed namelessly to the front-end
var defines = new webpack.DefinePlugin({
    "__DEV__": JSON.stringify(__debug),
    "__PRERELEASE__": JSON.stringify(true),
    "__VERSION__": JSON.stringify(config.version),
    "__CDN__": JSON.stringify(cdn),
    "__APP__": JSON.stringify(app),
    "__TITLE__": JSON.stringify(config.BIRD3.name),
});
// Bower integration. Mind the excludes!
var bowerProvider = new webpack.ResolverPlugin([
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"]),
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin(".bower.json", ["main"])
], ["normal", "loader"]);
// This file is read by php_handler.js and given as userData.webpackHash
// Required to properly inject generated sources, enables cache busting.
// I <3 my NodeJS+PHP system!
var assetsp = new HashPlugin({
    path: path.join(config.base, "cache"),
    fileName: "webpack-hash.txt"
});
// The usual jQuery madness.
var provider = new webpack.ProvidePlugin({
    $: _jquery,
    jQuery: _jquery,
    "window.jQuery": _jquery,
    "window.$": _jquery
});
// Generate bundled CSS. (id, fileName)
var extractor = new extractText("style","[hash]-[name].css");
// Compress and press down our JS.
// FIXME: Learn UglyfyJS
var uglify = new webpack.optimize.UglifyJsPlugin({
    compress: {
        warnings: false,
        properties: true,
        sequences: true,
        dead_code: true,
        conditionals: true,
        comparisons: true,
        evaluate: true,
        booleans: true,
        unused: true,
        loops: true,
        hoist_funs: true,
        cascade: true,
        if_return: true,
        join_vars: true,
        //drop_console: true,
        drop_debugger: true,
        negate_iife: true,
        unsafe: true,
        hoist_vars: true,
        negate_iife: true,
        //side_effects: true
    },
    sourceMap: true,
    /*mangle: {
        //toplevel: true,
        sort: true,
        eval: true
    }*/
});
// Querystring for the CSS Loader
var cssq = [
    "keepSpecialComments=0",
    "processImport=true",
    "rebase=true",
    "relativeTo="+config.base,
    "shorthandCompacting=true",
    "target="+app,
    "sourceMap"
].join("&");
// Configure SASS
var sassq = [
    "includePaths[]="+path.join(
        config.base, "bower_components",
        "bootstrap-sass/assets/stylesheets"
    ),
    "includePaths[]="+path.join(config.base, "bower_components"),
    "includePaths[]="+path.join(config.base, "node_modules"),
    "includePaths[]="+path.join(config.base, "themes"),
    "sourceMap"
].join("&");
// Progress output
var logger = require(config.base+"/node-lib/logger")(config.base);
var progress = new webpack.ProgressPlugin(function(p, msg){
    if(p===0) msg = "Starting compilation...";
    if(p===1) msg = "Done!";
    logger.update("WebPack => [%s%%]: %s", p.toFixed(2)*100, msg);
});
// Try to press down further
var dedupe = new webpack.optimize.DedupePlugin();

// Return the config
module.exports = {
    context: config.base,
    cache: true,
    debug: __debug,
    watchDelay: 1000*5,
    devtool: "#source-map",
    entry: {
        main: path.join(__dirname, "../web-lib/main.oj")
    },
    output: {
        // to cdn/app/
        path: app,
        filename: "[hash]-[name].js",
        //chunkFilename: "[name].[id].js",
        sourceMapFilename: "[hash]-[name].[id].map",
        publicPath: "/cdn/app/",
        sourcePrefix: "    "
    },
    resolve: {
        extensions: [
            "", // Support supplied extensions.
            ".js", ".oj", // JavaScript
            ".json", // Structured data
            ".css", ".scss" // Styles
        ],
        root: [
            config.base,
            // Yii
            path.join(config.base,"protected/modules"),
            path.join(config.base,"protected/extensions"),
            path.join(config.base,"themes")
        ],
        modulesDirectories: [
            // NPM, Bower
            'bower_components',
            'node_modules',
        ],
        alias: {
            // Ensure compatibility to original bootstrap
            "bootstrap.js": path.join(
                config.base,
                "bower_components",
                "bootstrap-sass/assets/javascripts/bootstrap"
            ),
            bootstrap: path.join(
                config.base,
                "web-lib/bootstrapper.js"
            ),
            "a11y.bs": path.join(
                config.base,
                "bower_components",
                "bootstrapaccessibilityplugin/src"
            ),
            jquery: _jquery,
            "jquery.js": path.join(
                config.base,
                "bower_components",
                "jquery/src"
            ),
            ws: "ws/lib/browser"
        }
    },
    module: {
        loaders: [
            { // Extract CSS
                test: /\.css$/,
                loader: extractText.extract(
                    "style",
                    "css?"+cssq
                )
            },{ // Extract Sassy CSS
                test: /\.scss$/,
                loader: extractText.extract(
                    "style",
                    "css?"+cssq+"!sass?"+sassq
                )
            },{ // WingStyle -> CSS
                test: /\.ws\.php$/,
                loader: extractText.extract(
                    "style",
                    [
                        "css?"+cssq,
                        path.join(__dirname,"wingstyle-loader.js")
                    ].join("!")
                )
            },{ // Images
                test: /\.(png|jpg|jpeg|gif|svg)/i,
                loader: [
                    // optimize image
                    "img?minimize=true&optimizationLevel=7",
                    // 50kb or smaller
                    "file",
                ].join("!")
            },{ // Fonts
                test: /\.(eot|woff|woff2|ttf|otf|mp3|wav|ogg)/,
                loader: "file"
            },{ // Markdown
                test: /\.md$/,
                loader: "markdown"
            },{ // HTML
                test: /\.html$/,
                loader: "html"
            },{ // Embedded JS templates
                test: /\.ejs$/,
                loader: "ejs-compiled?delimiter=?&+rmWhitespaces"
            },{ // OJ -> JS
                test: /\.oj$/,
                loader: "oj"
            }
        ],
        noParse: [
            /node_modules\/socket\.io-client\/socket\.io\.js$/,
            /\.(min|bundle|pack)\.js$/,
        ]
    },
    plugins: [
        progress,
        dedupe,
        commonsPlugin,
        defines,
        provider,
        bowerProvider,
        extractor,
        assetsp,
        //uglify
    ]
};

@AlexanderOMara
Copy link

The error you are getting is/was an issue with jQuery.

jQuery uses RequireJS, and the RequireJS implementation of AMD differs from the AMD spec, while Webpack adheres to it.

#926

I previously submitted a now-merged pull request to jQuery, when I encountered the exact same problem you are, to patch the faulty code that relies upon RequireJS's quirk.

jquery/jquery#2179

However, this fix will not be available until jQuery 3, so I also developed a Webpack loader to patch these problem files on the fly.

https://github.com/AlexanderOMara/amd-define-factory-patcher-loader

I've also written a blog post about how to do this with jQuery.

http://alexomara.com/blog/webpack-and-jquery-include-only-the-parts-you-need/

@shama
Copy link
Member

shama commented Jul 4, 2015

@sokra This seems like an error with jQuery that was resolved (thanks @AlexanderOMara) so can now be closed, right?

@sokra sokra closed this as completed Jul 5, 2015
@andrewwakeling
Copy link

Thank you @AlexanderOMara for such a complete and comprehensive reply and blog post around these issues.

@hzzhujf
Copy link

hzzhujf commented Sep 19, 2015

@AlexanderOMara your situation is similar to mine. I will try. Thanks anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants