Skip to content

Commit

Permalink
Core: Support selectively disabling Migrate patches
Browse files Browse the repository at this point in the history
This adds three new APIs:
* `jQuery.migrateDisablePatches`
* `jQuery.migrateEnablePatches`
* `jQuery.migrateIsPatchEnabled`

allowing to selectively disable/re-enable patches in runtime. Source code is
refactored to avoid manually overwriting jQuery methods in favor of using
`migratePatchAndWarnFunc` or `migratePatchFunc` which cooperate with above
methods.

The `jQuery.UNSAFE_restoreLegacyHtmlPrefilter` API, introduced in Migrate 3.2.0,
is now deprecated in favor of calling:
```js
jQuery.migrateEnablePatches( "self-closed-tags" );
```

The commit also reorganizes test code a bit, grouping modules testing patches
to jQuery modules in a separate directory and extracting tests of Migrate APIs
out of `core.js` to a separate `migrate.js` file. Helper files are now put in
`test/data/` and unit tests in `test/unit/`; jQuery patch tests are in
`test/unit/jquery/`.

Fixes gh-449
Closes gh-450
  • Loading branch information
mgol authored Mar 14, 2022
1 parent c6d3ca2 commit 5ee8f69
Show file tree
Hide file tree
Showing 41 changed files with 557 additions and 361 deletions.
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

0 comments on commit 5ee8f69

Please sign in to comment.