Skip to content

Core: Support selectively disabling Migrate patches #450

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

Merged
merged 1 commit into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ module.exports = function( grunt ) {
"dist/jquery-migrate.min.js",
"test/data/compareVersions.js",

"test/testinit.js",
"test/migrate.js",
"test/core.js",
"test/ajax.js",
"test/attributes.js",
"test/css.js",
"test/data.js",
"test/deferred.js",
"test/effects.js",
"test/event.js",
"test/manipulation.js",
"test/offset.js",
"test/serialize.js",
"test/traversing.js",
"test/data/testinit.js",
"test/data/test-utils.js",
"test/unit/migrate.js",
"test/unit/jquery/core.js",
"test/unit/jquery/ajax.js",
"test/unit/jquery/attributes.js",
"test/unit/jquery/css.js",
"test/unit/jquery/data.js",
"test/unit/jquery/deferred.js",
"test/unit/jquery/effects.js",
"test/unit/jquery/event.js",
"test/unit/jquery/manipulation.js",
"test/unit/jquery/offset.js",
"test/unit/jquery/serialize.js",
"test/unit/jquery/traversing.js",

{ pattern: "dist/jquery-migrate.js", included: false, served: true },
{ pattern: "test/**/*.@(js|json|css|jpg|html|xml)", included: false, served: true }
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ This plugin adds some properties to the `jQuery` object that can be used to prog

`jQuery.migrateDeduplicateWarnings`: By default, Migrate only gives a specific warning once. If you set this property to `false` it will give a warning for every occurrence each time it happens. Note that this can generate a lot of output, for example when a warning occurs in a loop.

`jQuery.migrateDisablePatches`: Disables patches by their codes. You can find a code for each patch in square brackets in [warnings.md](https://github.com/jquery/jquery-migrate/blob/main/warnings.md). A limited number of warnings doesn't have codes defined and cannot be disabled. These are mostly setup issues like using an incorrect version of jQuery or loading Migrate multiple times.

`jQuery.migrateDisablePatches`: Disables patches by their codes.

`jQuery.migrateIsPatchEnabled`: Returns `true` if a patch of a provided code is enabled and `false` otherwise.

`jQuery.UNSAFE_restoreLegacyHtmlPrefilter`: A deprecated alias of `jQuery.migrateEnablePatches( "self-closed-tags" )`

## Reporting problems

Bugs that only occur when the jQuery Migrate plugin is used should be reported in the [jQuery Migrate Issue Tracker](https://github.com/jquery/jquery-migrate/issues) and should be accompanied by an executable test case that demonstrates the bug. The easiest way to do this is via an online test tool such as [jsFiddle.net](https://jsFiddle.net/) or [jsbin.com](https://jsbin.com). Use the development version when you are reporting bugs.
Expand Down
33 changes: 33 additions & 0 deletions src/disablePatches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// A map from disabled patch codes to `true`. This should really
// be a `Set` but those are unsupported in IE.
var disabledPatches = Object.create( null );

// Don't apply patches for specified codes. Helpful for code bases
// where some Migrate warnings have been addressed and it's desirable
// to avoid needless patches or false positives.
jQuery.migrateDisablePatches = function() {
var i;
for ( i = 0; i < arguments.length; i++ ) {
disabledPatches[ arguments[ i ] ] = true;
}
};

// Allow enabling patches disabled via `jQuery.migrateDisablePatches`.
// Helpful if you want to disable a patch only for some code that won't
// be updated soon to be able to focus on other warnings - and enable it
// immediately after such a call:
// ```js
// jQuery.migrateDisablePatches( "workaroundA" );
// elem.pluginViolatingWarningA( "pluginMethod" );
// jQuery.migrateEnablePatches( "workaroundA" );
// ```
jQuery.migrateEnablePatches = function() {
var i;
for ( i = 0; i < arguments.length; i++ ) {
delete disabledPatches[ arguments[ i ] ];
}
};

jQuery.migrateIsPatchEnabled = function( patchCode ) {
return !disabledPatches[ patchCode ];
};
14 changes: 7 additions & 7 deletions src/jquery/ajax.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { jQueryVersionSince } from "../compareVersions.js";
import { migrateWarn, migrateWarnFunc } from "../main.js";
import { migrateWarn, migratePatchAndWarnFunc, migratePatchFunc } from "../main.js";

// Support jQuery slim which excludes the ajax module
if ( jQuery.ajax ) {

var oldAjax = jQuery.ajax,
rjsonp = /(=)\?(?=&|$)|\?\?/;

jQuery.ajax = function( ) {
migratePatchFunc( jQuery, "ajax", function() {
var jQXHR = oldAjax.apply( this, arguments );

// Be sure we got a jQXHR (e.g., not sync)
if ( jQXHR.promise ) {
migrateWarnFunc( jQXHR, "success", jQXHR.done,
migratePatchAndWarnFunc( jQXHR, "success", jQXHR.done, "jqXHR-methods",
"jQXHR.success is deprecated and removed" );
migrateWarnFunc( jQXHR, "error", jQXHR.fail,
migratePatchAndWarnFunc( jQXHR, "error", jQXHR.fail, "jqXHR-methods",
"jQXHR.error is deprecated and removed" );
migrateWarnFunc( jQXHR, "complete", jQXHR.always,
migratePatchAndWarnFunc( jQXHR, "complete", jQXHR.always, "jqXHR-methods",
"jQXHR.complete is deprecated and removed" );
}

return jQXHR;
};
}, "jqXHR-methods" );

// Only trigger the logic in jQuery <4 as the JSON-to-JSONP auto-promotion
// behavior is gone in jQuery 4.0 and as it has security implications, we don't
Expand All @@ -40,7 +40,7 @@ if ( !jQueryVersionSince( "4.0.0" ) ) {
.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
rjsonp.test( s.data )
) ) {
migrateWarn( "JSON-to-JSONP auto-promotion is deprecated" );
migrateWarn( "jsonp-promotion", "JSON-to-JSONP auto-promotion is deprecated" );
}
} );
}
Expand Down
16 changes: 9 additions & 7 deletions src/jquery/attributes.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
import { migrateWarn } from "../main.js";
import { migratePatchFunc, migrateWarn } from "../main.js";

var oldRemoveAttr = jQuery.fn.removeAttr,
oldToggleClass = jQuery.fn.toggleClass,
rmatchNonSpace = /\S+/g;

jQuery.fn.removeAttr = function( name ) {
migratePatchFunc( jQuery.fn, "removeAttr", function( name ) {
var self = this;

jQuery.each( name.match( rmatchNonSpace ), function( _i, attr ) {
if ( jQuery.expr.match.bool.test( attr ) ) {
migrateWarn( "jQuery.fn.removeAttr no longer sets boolean properties: " + attr );
migrateWarn( "removeAttr-bool",
"jQuery.fn.removeAttr no longer sets boolean properties: " + attr );
self.prop( attr, false );
}
} );

return oldRemoveAttr.apply( this, arguments );
};
}, "removeAttr-bool" );

jQuery.fn.toggleClass = function( state ) {
migratePatchFunc( jQuery.fn, "toggleClass", function( state ) {

// Only deprecating no-args or single boolean arg
if ( state !== undefined && typeof state !== "boolean" ) {

return oldToggleClass.apply( this, arguments );
}

migrateWarn( "jQuery.fn.toggleClass( boolean ) is deprecated" );
migrateWarn( "toggleClass-bool", "jQuery.fn.toggleClass( boolean ) is deprecated" );

// Toggle entire class name of each element
return this.each( function() {
Expand All @@ -46,4 +48,4 @@ jQuery.fn.toggleClass = function( state ) {
);
}
} );
};
}, "toggleClass-bool" );
80 changes: 47 additions & 33 deletions src/jquery/core.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { jQueryVersionSince } from "../compareVersions.js";
import { migrateWarn, migrateWarnFunc, migrateWarnProp } from "../main.js";
import {
migratePatchFunc,
migrateWarn,
migratePatchAndWarnFunc,
migrateWarnProp
} from "../main.js";
import "../disablePatches.js";

var findProp,
class2type = {},
Expand All @@ -13,21 +19,27 @@ var findProp,
// Make sure we trim BOM and NBSP
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

jQuery.fn.init = function( arg1 ) {
migratePatchFunc( jQuery.fn, "init", function( arg1 ) {
var args = Array.prototype.slice.call( arguments );

if ( typeof arg1 === "string" && arg1 === "#" ) {
if ( jQuery.migrateIsPatchEnabled( "selector-empty-id" ) &&
typeof arg1 === "string" && arg1 === "#" ) {

// JQuery( "#" ) is a bogus ID selector, but it returned an empty set before jQuery 3.0
migrateWarn( "jQuery( '#' ) is not a valid selector" );
// JQuery( "#" ) is a bogus ID selector, but it returned an empty set
// before jQuery 3.0
migrateWarn( "selector-empty-id", "jQuery( '#' ) is not a valid selector" );
args[ 0 ] = [];
}

return oldInit.apply( this, args );
};
}, "selector-empty-id" );

// This is already done in Core but the above patch will lose this assignment
// so we need to redo it. It doesn't matter whether the patch is enabled or not
// as the method is always going to be a Migrate-created wrapper.
jQuery.fn.init.prototype = jQuery.fn;

jQuery.find = function( selector ) {
migratePatchFunc( jQuery, "find", function( selector ) {
var args = Array.prototype.slice.call( arguments );

// Support: PhantomJS 1.x
Expand All @@ -49,16 +61,18 @@ jQuery.find = function( selector ) {
// Note that there may be false alarms if selector uses jQuery extensions
try {
window.document.querySelector( selector );
migrateWarn( "Attribute selector with '#' must be quoted: " + args[ 0 ] );
migrateWarn( "selector-hash",
"Attribute selector with '#' must be quoted: " + args[ 0 ] );
args[ 0 ] = selector;
} catch ( err2 ) {
migrateWarn( "Attribute selector with '#' was not fixed: " + args[ 0 ] );
migrateWarn( "selector-hash",
"Attribute selector with '#' was not fixed: " + args[ 0 ] );
}
}
}

return oldFind.apply( this, args );
};
}, "selector-hash" );

// Copy properties attached to original jQuery.find method (e.g. .attr, .isXML)
for ( findProp in oldFind ) {
Expand All @@ -68,53 +82,53 @@ for ( findProp in oldFind ) {
}

// The number of elements contained in the matched element set
migrateWarnFunc( jQuery.fn, "size", function() {
migratePatchAndWarnFunc( jQuery.fn, "size", function() {
return this.length;
},
}, "size",
"jQuery.fn.size() is deprecated and removed; use the .length property" );

migrateWarnFunc( jQuery, "parseJSON", function() {
migratePatchAndWarnFunc( jQuery, "parseJSON", function() {
return JSON.parse.apply( null, arguments );
},
}, "parseJSON",
"jQuery.parseJSON is deprecated; use JSON.parse" );

migrateWarnFunc( jQuery, "holdReady", jQuery.holdReady,
"jQuery.holdReady is deprecated" );
migratePatchAndWarnFunc( jQuery, "holdReady", jQuery.holdReady,
"holdReady", "jQuery.holdReady is deprecated" );

migrateWarnFunc( jQuery, "unique", jQuery.uniqueSort,
"jQuery.unique is deprecated; use jQuery.uniqueSort" );
migratePatchAndWarnFunc( jQuery, "unique", jQuery.uniqueSort,
"unique", "jQuery.unique is deprecated; use jQuery.uniqueSort" );

// Now jQuery.expr.pseudos is the standard incantation
migrateWarnProp( jQuery.expr, "filters", jQuery.expr.pseudos,
migrateWarnProp( jQuery.expr, "filters", jQuery.expr.pseudos, "expr-pre-pseudos",
"jQuery.expr.filters is deprecated; use jQuery.expr.pseudos" );
migrateWarnProp( jQuery.expr, ":", jQuery.expr.pseudos,
migrateWarnProp( jQuery.expr, ":", jQuery.expr.pseudos, "expr-pre-pseudos",
"jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos" );

// Prior to jQuery 3.1.1 there were internal refs so we don't warn there
if ( jQueryVersionSince( "3.1.1" ) ) {
migrateWarnFunc( jQuery, "trim", function( text ) {
migratePatchAndWarnFunc( jQuery, "trim", function( text ) {
return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
},
}, "trim",
"jQuery.trim is deprecated; use String.prototype.trim" );
}

// Prior to jQuery 3.2 there were internal refs so we don't warn there
if ( jQueryVersionSince( "3.2.0" ) ) {
migrateWarnFunc( jQuery, "nodeName", function( elem, name ) {
migratePatchAndWarnFunc( jQuery, "nodeName", function( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
}, "nodeName",
"jQuery.nodeName is deprecated" );

migrateWarnFunc( jQuery, "isArray", Array.isArray,
migratePatchAndWarnFunc( jQuery, "isArray", Array.isArray, "isArray",
"jQuery.isArray is deprecated; use Array.isArray"
);
}

if ( jQueryVersionSince( "3.3.0" ) ) {

migrateWarnFunc( jQuery, "isNumeric", function( obj ) {
migratePatchAndWarnFunc( jQuery, "isNumeric", function( obj ) {

// As of jQuery 3.0, isNumeric is limited to
// strings and numbers (primitives or objects)
Expand All @@ -126,7 +140,7 @@ if ( jQueryVersionSince( "3.3.0" ) ) {
// ...but misinterprets leading-number strings, e.g. hex literals ("0x...")
// subtraction forces infinities to NaN
!isNaN( obj - parseFloat( obj ) );
},
}, "isNumeric",
"jQuery.isNumeric() is deprecated"
);

Expand All @@ -137,7 +151,7 @@ if ( jQueryVersionSince( "3.3.0" ) ) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );

migrateWarnFunc( jQuery, "type", function( obj ) {
migratePatchAndWarnFunc( jQuery, "type", function( obj ) {
if ( obj == null ) {
return obj + "";
}
Expand All @@ -146,19 +160,19 @@ if ( jQueryVersionSince( "3.3.0" ) ) {
return typeof obj === "object" || typeof obj === "function" ?
class2type[ Object.prototype.toString.call( obj ) ] || "object" :
typeof obj;
},
}, "type",
"jQuery.type is deprecated" );

migrateWarnFunc( jQuery, "isFunction",
migratePatchAndWarnFunc( jQuery, "isFunction",
function( obj ) {
return typeof obj === "function";
},
}, "isFunction",
"jQuery.isFunction() is deprecated" );

migrateWarnFunc( jQuery, "isWindow",
migratePatchAndWarnFunc( jQuery, "isWindow",
function( obj ) {
return obj != null && obj === obj.window;
},
}, "isWindow",
"jQuery.isWindow() is deprecated"
);
}
Loading