mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Selector: Re-introduce selector-native.js
Re-introduce the `selector-native` similar to the one on the `3.x-stable` branch. One difference is since the `main` branch inlined Sizzle, some selector utils can be shared between the main `selector` module and `selector-native`. The main `selector` module can be disabled in favor of `selector-native` via: grunt custom:-selector Other changes: * Tests: Fix Safari detection - Chrome Headless has a different user agent than Safari and a browser check in selector tests didn't take that into account. * Tests: Run selector-native tests in `npm test` * Selector: Fix querying on document fragments Ref gh-4395 Closes gh-5085
This commit is contained in:
parent
f62d8e2159
commit
4c1171f2ed
4
.github/workflows/node.js.yml
vendored
4
.github/workflows/node.js.yml
vendored
@ -25,6 +25,10 @@ jobs:
|
|||||||
NODE_VERSION: "16.x"
|
NODE_VERSION: "16.x"
|
||||||
NPM_SCRIPT: "test:no-deprecated"
|
NPM_SCRIPT: "test:no-deprecated"
|
||||||
BROWSERS: "ChromeHeadless"
|
BROWSERS: "ChromeHeadless"
|
||||||
|
- NAME: "Browser tests: selector-native build, Chrome stable"
|
||||||
|
NODE_VERSION: "16.x"
|
||||||
|
NPM_SCRIPT: "test:selector-native"
|
||||||
|
BROWSERS: "ChromeHeadless"
|
||||||
- NAME: "Browser tests: ES modules build, Chrome stable"
|
- NAME: "Browser tests: ES modules build, Chrome stable"
|
||||||
NODE_VERSION: "16.x"
|
NODE_VERSION: "16.x"
|
||||||
NPM_SCRIPT: "test:esmodules"
|
NPM_SCRIPT: "test:esmodules"
|
||||||
|
@ -61,8 +61,7 @@ module.exports = function( grunt ) {
|
|||||||
all: {
|
all: {
|
||||||
dest: "dist/jquery.js",
|
dest: "dist/jquery.js",
|
||||||
minimum: [
|
minimum: [
|
||||||
"core",
|
"core"
|
||||||
"selector"
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// Exclude specified modules if the module matching the key is removed
|
// Exclude specified modules if the module matching the key is removed
|
||||||
@ -75,7 +74,8 @@ module.exports = function( grunt ) {
|
|||||||
remove: [ "ajax", "effects", "queue", "core/ready" ],
|
remove: [ "ajax", "effects", "queue", "core/ready" ],
|
||||||
include: [ "core/ready-no-deferred" ]
|
include: [ "core/ready-no-deferred" ]
|
||||||
},
|
},
|
||||||
event: [ "deprecated/ajax-event-alias", "deprecated/event" ]
|
event: [ "deprecated/ajax-event-alias", "deprecated/event" ],
|
||||||
|
selector: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,12 @@ Some example modules that can be excluded are:
|
|||||||
- **exports/global**: Exclude the attachment of global jQuery variables ($ and jQuery) to the window.
|
- **exports/global**: Exclude the attachment of global jQuery variables ($ and jQuery) to the window.
|
||||||
- **exports/amd**: Exclude the AMD definition.
|
- **exports/amd**: Exclude the AMD definition.
|
||||||
|
|
||||||
|
As a special case, you may also replace the full jQuery `selector` module by using a special flag `grunt custom:-selector`.
|
||||||
|
|
||||||
|
- **selector**: The full jQuery selector engine. When this module is excluded, it is replaced by a rudimentary selector engine based on the browser's `querySelectorAll` method that does not support jQuery selector extensions or enhanced semantics. See the [selector-native.js](https://github.com/jquery/jquery/blob/main/src/selector-native.js) file for details.
|
||||||
|
|
||||||
|
*Note*: Excluding the full `selector` module will also exclude all jQuery selector extensions (such as `effects/animatedSelector` and `css/hiddenVisibleSelectors`).
|
||||||
|
|
||||||
The build process shows a message for each dependent module it excludes or includes.
|
The build process shows a message for each dependent module it excludes or includes.
|
||||||
|
|
||||||
##### AMD name
|
##### AMD name
|
||||||
|
@ -128,11 +128,11 @@ module.exports = function( grunt ) {
|
|||||||
* Adds the specified module to the excluded or included list, depending on the flag
|
* Adds the specified module to the excluded or included list, depending on the flag
|
||||||
* @param {String} flag A module path relative to
|
* @param {String} flag A module path relative to
|
||||||
* the src directory starting with + or - to indicate
|
* the src directory starting with + or - to indicate
|
||||||
* whether it should included or excluded
|
* whether it should be included or excluded
|
||||||
*/
|
*/
|
||||||
const excluder = flag => {
|
const excluder = flag => {
|
||||||
let additional;
|
let additional;
|
||||||
const m = /^(\+|\-|)([\w\/-]+)$/.exec( flag );
|
const m = /^(\+|-|)([\w\/-]+)$/.exec( flag );
|
||||||
const exclude = m[ 1 ] === "-";
|
const exclude = m[ 1 ] === "-";
|
||||||
const module = m[ 2 ];
|
const module = m[ 2 ];
|
||||||
|
|
||||||
@ -150,10 +150,16 @@ module.exports = function( grunt ) {
|
|||||||
// These are the removable dependencies
|
// These are the removable dependencies
|
||||||
// It's fine if the directory is not there
|
// It's fine if the directory is not there
|
||||||
try {
|
try {
|
||||||
excludeList(
|
|
||||||
fs.readdirSync( `${ srcFolder }/${ module }` ),
|
// `selector` is a special case as we don't just remove
|
||||||
module
|
// the module, but we replace it with `selector-native`
|
||||||
);
|
// which re-uses parts of the `src/selector` folder.
|
||||||
|
if ( module !== "selector" ) {
|
||||||
|
excludeList(
|
||||||
|
fs.readdirSync( `${ srcFolder }/${ module }` ),
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
grunt.verbose.writeln( e );
|
grunt.verbose.writeln( e );
|
||||||
}
|
}
|
||||||
@ -232,14 +238,14 @@ module.exports = function( grunt ) {
|
|||||||
// Remove the comma for anonymous defines
|
// Remove the comma for anonymous defines
|
||||||
setOverride( `${ srcFolder }/exports/amd.js`,
|
setOverride( `${ srcFolder }/exports/amd.js`,
|
||||||
read( "exports/amd.js" )
|
read( "exports/amd.js" )
|
||||||
.replace( /(\s*)"jquery"(\,\s*)/,
|
.replace( /(\s*)"jquery"(,\s*)/,
|
||||||
amdName ? "$1\"" + amdName + "\"$2" : "" ) );
|
amdName ? "$1\"" + amdName + "\"$2" : "" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
grunt.verbose.writeflags( excluded, "Excluded" );
|
grunt.verbose.writeflags( excluded, "Excluded" );
|
||||||
grunt.verbose.writeflags( included, "Included" );
|
grunt.verbose.writeflags( included, "Included" );
|
||||||
|
|
||||||
// Indicate a Slim build without listing all of the exclusions
|
// Indicate a Slim build without listing all the exclusions
|
||||||
// to save space.
|
// to save space.
|
||||||
if ( isPureSlim ) {
|
if ( isPureSlim ) {
|
||||||
version += " slim";
|
version += " slim";
|
||||||
@ -260,7 +266,13 @@ module.exports = function( grunt ) {
|
|||||||
|
|
||||||
// Replace excluded modules with empty sources.
|
// Replace excluded modules with empty sources.
|
||||||
for ( const module of excluded ) {
|
for ( const module of excluded ) {
|
||||||
setOverride( `${ srcFolder }/${ module }.js`, "" );
|
setOverride(
|
||||||
|
`${ srcFolder }/${ module }.js`,
|
||||||
|
|
||||||
|
// The `selector` module is not removed, but replaced
|
||||||
|
// with `selector-native`.
|
||||||
|
module === "selector" ? read( "selector-native.js" ) : ""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,8 +77,9 @@
|
|||||||
"test:esmodules": "grunt && grunt karma:esmodules",
|
"test:esmodules": "grunt && grunt karma:esmodules",
|
||||||
"test:amd": "grunt && grunt karma:amd",
|
"test:amd": "grunt && grunt karma:amd",
|
||||||
"test:no-deprecated": "grunt test:prepare && grunt custom:-deprecated && grunt karma:main",
|
"test:no-deprecated": "grunt test:prepare && grunt custom:-deprecated && grunt karma:main",
|
||||||
|
"test:selector-native": "grunt test:prepare && grunt custom:-selector && grunt karma:main",
|
||||||
"test:slim": "grunt test:prepare && grunt custom:slim && grunt karma:main",
|
"test:slim": "grunt test:prepare && grunt custom:slim && grunt karma:main",
|
||||||
"test": "npm run test:slim && npm run test:no-deprecated && grunt && grunt test:slow && grunt karma:main && grunt karma:esmodules && grunt karma:amd",
|
"test": "npm run test:slim && npm run test:no-deprecated && npm run test:selector-native && grunt && grunt test:slow && grunt karma:main && grunt karma:esmodules && grunt karma:amd",
|
||||||
"jenkins": "npm run test:browserless"
|
"jenkins": "npm run test:browserless"
|
||||||
},
|
},
|
||||||
"commitplease": {
|
"commitplease": {
|
||||||
|
88
src/selector-native.js
Normal file
88
src/selector-native.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Optional limited selector module for custom builds.
|
||||||
|
*
|
||||||
|
* Note that this DOES NOT SUPPORT many documented jQuery
|
||||||
|
* features in exchange for its smaller size:
|
||||||
|
*
|
||||||
|
* * Attribute not equal selector (!=)
|
||||||
|
* * Positional selectors (:first; :eq(n); :odd; etc.)
|
||||||
|
* * Type selectors (:input; :checkbox; :button; etc.)
|
||||||
|
* * State-based selectors (:animated; :visible; :hidden; etc.)
|
||||||
|
* * :has(selector)
|
||||||
|
* * :not(complex selector)
|
||||||
|
* * custom selectors via jQuery extensions
|
||||||
|
* * Leading combinators (e.g., $collection.find("> *"))
|
||||||
|
* * Reliable functionality on XML fragments
|
||||||
|
* * Requiring all parts of a selector to match elements under context
|
||||||
|
* (e.g., $div.find("div > *") now matches children of $div)
|
||||||
|
* * Matching against non-elements
|
||||||
|
* * Reliable sorting of disconnected nodes
|
||||||
|
* * querySelectorAll bug fixes (e.g., unreliable :focus on WebKit)
|
||||||
|
*
|
||||||
|
* If any of these are unacceptable tradeoffs, either use the full
|
||||||
|
* selector engine or customize this stub for the project's specific
|
||||||
|
* needs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jQuery from "./core.js";
|
||||||
|
import document from "./var/document.js";
|
||||||
|
import documentElement from "./var/documentElement.js";
|
||||||
|
import whitespace from "./var/whitespace.js";
|
||||||
|
|
||||||
|
// The following utils are attached directly to the jQuery object.
|
||||||
|
import "./selector/contains.js";
|
||||||
|
import "./selector/escapeSelector.js";
|
||||||
|
import "./selector/uniqueSort.js";
|
||||||
|
|
||||||
|
// Support: IE 9 - 11+
|
||||||
|
// IE requires a prefix.
|
||||||
|
var matches = documentElement.matches || documentElement.msMatchesSelector;
|
||||||
|
|
||||||
|
jQuery.extend( {
|
||||||
|
find: function( selector, context, results, seed ) {
|
||||||
|
var elem, nodeType,
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
results = results || [];
|
||||||
|
context = context || document;
|
||||||
|
|
||||||
|
// Same basic safeguard as in the full selector module
|
||||||
|
if ( !selector || typeof selector !== "string" ) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Early return if context is not an element, document or document fragment
|
||||||
|
if ( ( nodeType = context.nodeType ) !== 1 && nodeType !== 9 && nodeType !== 11 ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( seed ) {
|
||||||
|
while ( ( elem = seed[ i++ ] ) ) {
|
||||||
|
if ( jQuery.find.matchesSelector( elem, selector ) ) {
|
||||||
|
results.push( elem );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
jQuery.merge( results, context.querySelectorAll( selector ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
},
|
||||||
|
expr: {
|
||||||
|
attrHandle: {},
|
||||||
|
match: {
|
||||||
|
bool: new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" +
|
||||||
|
"|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ),
|
||||||
|
needsContext: new RegExp( "^" + whitespace + "*[>+~]" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
jQuery.extend( jQuery.find, {
|
||||||
|
matches: function( expr, elements ) {
|
||||||
|
return jQuery.find( expr, null, null, elements );
|
||||||
|
},
|
||||||
|
matchesSelector: function( elem, expr ) {
|
||||||
|
return matches.call( elem, expr );
|
||||||
|
}
|
||||||
|
} );
|
@ -301,17 +301,6 @@ if ( !window.__karma__ ) {
|
|||||||
QUnit.isSwarm = ( QUnit.urlParams.swarmURL + "" ).indexOf( "http" ) === 0;
|
QUnit.isSwarm = ( QUnit.urlParams.swarmURL + "" ).indexOf( "http" ) === 0;
|
||||||
QUnit.basicTests = ( QUnit.urlParams.module + "" ) === "basic";
|
QUnit.basicTests = ( QUnit.urlParams.module + "" ) === "basic";
|
||||||
|
|
||||||
// Says whether jQuery positional selector extensions are supported.
|
|
||||||
// A full selector engine is required to support them as they need to be evaluated
|
|
||||||
// left-to-right. Remove that property when support for positional selectors is dropped.
|
|
||||||
QUnit.jQuerySelectorsPos = true;
|
|
||||||
|
|
||||||
// Says whether jQuery selector extensions are supported. Change that to `false`
|
|
||||||
// if your custom jQuery versions relies more on native qSA.
|
|
||||||
// This doesn't include support for positional selectors (see above).
|
|
||||||
// TODO do we want to keep this or just assume support for jQuery extensions?
|
|
||||||
QUnit.jQuerySelectors = true;
|
|
||||||
|
|
||||||
// Support: IE 11+
|
// Support: IE 11+
|
||||||
// A variable to make it easier to skip specific tests in IE, mostly
|
// A variable to make it easier to skip specific tests in IE, mostly
|
||||||
// testing integrations with newer Web features not supported by it.
|
// testing integrations with newer Web features not supported by it.
|
||||||
@ -388,6 +377,18 @@ this.loadTests = function() {
|
|||||||
|
|
||||||
// Get testSubproject from testrunner first
|
// Get testSubproject from testrunner first
|
||||||
require( [ parentUrl + "test/data/testrunner.js" ], function() {
|
require( [ parentUrl + "test/data/testrunner.js" ], function() {
|
||||||
|
|
||||||
|
// Says whether jQuery positional selector extensions are supported.
|
||||||
|
// A full selector engine is required to support them as they need to
|
||||||
|
// be evaluated left-to-right. Remove that property when support for
|
||||||
|
// positional selectors is dropped.
|
||||||
|
QUnit.jQuerySelectorsPos = includesModule( "selector" );
|
||||||
|
|
||||||
|
// Says whether jQuery selector extensions are supported. Change that
|
||||||
|
// to `false` if your custom jQuery versions relies more on native qSA.
|
||||||
|
// This doesn't include support for positional selectors (see above).
|
||||||
|
QUnit.jQuerySelectors = includesModule( "selector" );
|
||||||
|
|
||||||
var i = 0,
|
var i = 0,
|
||||||
tests = [
|
tests = [
|
||||||
// A special module with basic tests, meant for not fully
|
// A special module with basic tests, meant for not fully
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
QUnit.module( "selector", {
|
QUnit.module( "selector", {
|
||||||
beforeEach: function() {
|
beforeEach: function() {
|
||||||
this.safari = /\bsafari\b/i.test( navigator.userAgent ) &&
|
this.safari = /\bsafari\b/i.test( navigator.userAgent ) &&
|
||||||
!/\bchrome\b/i.test( navigator.userAgent );
|
!/\b(?:headless)?chrome\b/i.test( navigator.userAgent );
|
||||||
},
|
},
|
||||||
afterEach: moduleTeardown
|
afterEach: moduleTeardown
|
||||||
} );
|
} );
|
||||||
@ -1908,6 +1908,21 @@ QUnit.testUnlessIE( "jQuery.contains within <template/> doesn't throw (gh-5147)"
|
|||||||
assert.ok( true, "Didn't throw" );
|
assert.ok( true, "Didn't throw" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
QUnit.test( "find in document fragments", function( assert ) {
|
||||||
|
assert.expect( 1 );
|
||||||
|
|
||||||
|
var elem,
|
||||||
|
nonnodes = jQuery( "#nonnodes" ).contents(),
|
||||||
|
fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
nonnodes.each( function() {
|
||||||
|
fragment.appendChild( this );
|
||||||
|
} );
|
||||||
|
|
||||||
|
elem = jQuery( fragment ).find( "#nonnodesElement" );
|
||||||
|
assert.strictEqual( elem.length, 1, "Selection works" );
|
||||||
|
} );
|
||||||
|
|
||||||
QUnit.test( "jQuery.uniqueSort", function( assert ) {
|
QUnit.test( "jQuery.uniqueSort", function( assert ) {
|
||||||
assert.expect( 14 );
|
assert.expect( 14 );
|
||||||
|
|
||||||
@ -2156,7 +2171,7 @@ QUnit.test( "jQuery.escapeSelector", function( assert ) {
|
|||||||
assert.equal( jQuery.escapeSelector( "\uD834" ), "\uD834", "Doesn't escape lone high surrogate" );
|
assert.equal( jQuery.escapeSelector( "\uD834" ), "\uD834", "Doesn't escape lone high surrogate" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "custom pseudos", function( assert ) {
|
QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "custom pseudos", function( assert ) {
|
||||||
assert.expect( 6 );
|
assert.expect( 6 );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -55,7 +55,7 @@ testIframe(
|
|||||||
);
|
);
|
||||||
|
|
||||||
( function() {
|
( function() {
|
||||||
var expected,
|
var expected, browserKey,
|
||||||
userAgent = window.navigator.userAgent,
|
userAgent = window.navigator.userAgent,
|
||||||
expectedMap = {
|
expectedMap = {
|
||||||
ie_11: {
|
ie_11: {
|
||||||
@ -80,6 +80,13 @@ testIframe(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Make the selector-native build pass tests.
|
||||||
|
for ( browserKey in expectedMap ) {
|
||||||
|
if ( !includesModule( "selector" ) ) {
|
||||||
|
delete expectedMap[ browserKey ].cssSupportsSelector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( document.documentMode ) {
|
if ( document.documentMode ) {
|
||||||
expected = expectedMap.ie_11;
|
expected = expectedMap.ie_11;
|
||||||
} else if ( /chrome/i.test( userAgent ) ) {
|
} else if ( /chrome/i.test( userAgent ) ) {
|
||||||
|
Loading…
Reference in New Issue
Block a user