@asamuzakjp/dom-selector 4.2.2 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/js/parser.js CHANGED
@@ -8,7 +8,7 @@ import { findAll, parse, toPlainObject, walk } from 'css-tree';
8
8
  /* constants */
9
9
  import {
10
10
  BIT_01, BIT_02, BIT_04, BIT_08, BIT_16, BIT_32, BIT_FFFF, BIT_HYPHEN,
11
- DUO, EMPTY, HEX, REG_LOGICAL_PSEUDO, REG_SHADOW_PSEUDO, SELECTOR,
11
+ COMBINATOR, DUO, EMPTY, HEX, REG_LOGICAL_PSEUDO, REG_SHADOW_PSEUDO, SELECTOR,
12
12
  SELECTOR_ATTR, SELECTOR_CLASS, SELECTOR_ID, SELECTOR_PSEUDO_CLASS,
13
13
  SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE, SYNTAX_ERR, TYPE_FROM, TYPE_TO, U_FFFD
14
14
  } from './constant.js';
@@ -169,11 +169,12 @@ export const parseSelector = selector => {
169
169
  /**
170
170
  * walk AST
171
171
  * @param {object} ast - AST
172
- * @returns {Array.<object|undefined>} - collection of AST branches
172
+ * @returns {object} - branches and complex
173
173
  */
174
174
  export const walkAST = (ast = {}) => {
175
175
  const branches = new Set();
176
176
  let hasPseudoFunc;
177
+ let hasComplexSelector;
177
178
  const opt = {
178
179
  enter: node => {
179
180
  if (node.type === SELECTOR) {
@@ -204,6 +205,13 @@ export const walkAST = (ast = {}) => {
204
205
  // Selector
205
206
  for (const { children: greatGrandChildren } of grandChildren) {
206
207
  if (branches.has(greatGrandChildren)) {
208
+ for (const greatGrandChild of greatGrandChildren) {
209
+ const { type: greatGrandChildType } = greatGrandChild;
210
+ if (greatGrandChildType === COMBINATOR) {
211
+ hasComplexSelector = true;
212
+ break;
213
+ }
214
+ }
207
215
  branches.delete(greatGrandChildren);
208
216
  }
209
217
  }
@@ -229,7 +237,10 @@ export const walkAST = (ast = {}) => {
229
237
  }
230
238
  });
231
239
  }
232
- return [...branches];
240
+ return {
241
+ branches: [...branches],
242
+ complex: !!hasComplexSelector
243
+ };
233
244
  };
234
245
 
235
246
  /**
@@ -291,12 +302,56 @@ export const parseAstName = selector => {
291
302
  };
292
303
  };
293
304
 
305
+ /* filter selector constants */
306
+ // :first-child, :last-child etc.
307
+ const N_ST = '(?:first|last|only)-(?:child|of-type)';
308
+ const DIGIT = '(?:0|[1-9]\\d*)';
309
+ const ANB = `[+-]?(?:${DIGIT}n?|n)|(?:[+-]?${DIGIT})?n\\s*[+-]\\s*${DIGIT}`;
310
+ // exclude An+B with selector list, e.g. :nth-child(2n+1 of .foo)
311
+ const N_TH = `nth-(?:last-)?(?:child|of-type)\\(\\s*(?:even|odd|${ANB})\\s*\\)`;
312
+ // *, tag
313
+ const TAG_TYPE = '\\*|[A-Za-z][\\w-]*';
314
+ // attr, id, class, pseudo-class
315
+ const SUB_CLASS = '\\[[^\\]]+\\]|[#.:][\\w-]+';
316
+ const LOGICAL_KEY = '(?:is|not|where)';
317
+ const COMBO_A = '\\s?[\\s>~+]\\s?';
318
+ const COMBO_B = '\\s?[~+]\\s?';
319
+ const COMPOUND_A = `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_CLASS})+)`;
320
+ const COMPLEX_A = `${COMPOUND_A}(?:${COMBO_A}${COMPOUND_A})*`;
321
+ const COMPLEX_B = `${COMPOUND_A}(?:${COMBO_B}${COMPOUND_A})*`;
322
+ const NESTED_LOGICAL_A =
323
+ `:${LOGICAL_KEY}\\(\\s*${COMPOUND_A}(?:\\s*,\\s*${COMPOUND_A})*\\s*\\)`;
324
+ const NESTED_LOGICAL_B =
325
+ `:${LOGICAL_KEY}\\(\\s*${COMPLEX_A}(?:\\s*,\\s*${COMPLEX_A})*\\s*\\)`;
326
+ const NESTED_LOGICAL_C =
327
+ `:${LOGICAL_KEY}\\(\\s*${COMPLEX_B}(?:\\s*,\\s*${COMPLEX_B})*\\s*\\)`;
328
+ const COMPOUND_B =
329
+ `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_CLASS}|${NESTED_LOGICAL_A})+)`;
330
+ const COMPOUND_C =
331
+ `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_CLASS}|${NESTED_LOGICAL_B})+)`;
332
+ const COMPOUND_D =
333
+ `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_CLASS}|${NESTED_LOGICAL_C})+)`;
334
+ const COMPLEX_C = `${COMPOUND_C}(?:${COMBO_A}${COMPOUND_C})*`;
335
+ const COMPLEX_D = `${COMPOUND_D}(?:${COMBO_B}${COMPOUND_D})*`;
336
+ const LOGICAL_COMPOUND =
337
+ `${LOGICAL_KEY}\\(\\s*${COMPOUND_B}(?:\\s*,\\s*${COMPOUND_B})*\\s*\\)`;
338
+ const LOGICAL_COMPLEX_A =
339
+ `${LOGICAL_KEY}\\(\\s*${COMPLEX_C}(?:\\s*,\\s*${COMPLEX_C})*\\s*\\)`;
340
+ const LOGICAL_COMPLEX_B =
341
+ `${LOGICAL_KEY}\\(\\s*${COMPLEX_D}(?:\\s*,\\s*${COMPLEX_D})*\\s*\\)`;
342
+ const REG_LOGICAL_KEY = new RegExp(`:${LOGICAL_KEY}\\(`);
343
+ const REG_COMPLEX_A = new RegExp(`:(?!${N_ST}|${N_TH}|${LOGICAL_COMPLEX_A})`);
344
+ const REG_COMPLEX_B = new RegExp(`:(?!${N_ST}|${N_TH}|${LOGICAL_COMPLEX_B})`);
345
+ const REG_COMPOUND = new RegExp(`:(?!${N_ST}|${N_TH}|${LOGICAL_COMPOUND})`);
346
+ const REG_CHILD_INDEXED = new RegExp(`:(?!${N_ST}|${N_TH})`);
347
+
294
348
  /**
295
349
  * filter selector (for nwsapi)
296
350
  * @param {string} selector - selector
351
+ * @param {object} opt - options
297
352
  * @returns {boolean} - result
298
353
  */
299
- export const filterSelector = selector => {
354
+ export const filterSelector = (selector, opt = {}) => {
300
355
  if (!selector || typeof selector !== 'string') {
301
356
  return false;
302
357
  }
@@ -305,31 +360,22 @@ export const filterSelector = selector => {
305
360
  if (/\||::|\[\s*[\w$*=^|~-]+(?:(?:"[\w$*=^|~\s'-]+"|'[\w$*=^|~\s"-]+')?(?:\s+[\w$*=^|~-]+)+|"[^"\]]{1,255}|'[^'\]]{1,255})\s*\]/.test(selector)) {
306
361
  return false;
307
362
  }
308
- // filter pseudo-class selectors
363
+ // filter pseudo-classes other than child-indexed and logical combination
309
364
  if (selector.includes(':')) {
310
- // digit:
311
- // `(?:0|[1-9]\d*)`
312
- // anb:
313
- // `[+-]?(?:${digit}n?|n)|(?:[+-]?${digit})?n\s*[+-]\s*${digit}`
314
- // type: *, tag
315
- // `\*|[A-Za-z][\w-]*`
316
- // subclass: attr, id, class, pseudo-class (nst)
317
- // `\[[^\]]+\]|[#.:][\w-]+`
318
- // compound:
319
- // `(?:${type}|(?:${type})?(?:${subclass})+)`
320
- // nested logical:
321
- // `:(?:is|not|where)\(\s*${compound}(?:\s*,\s*${compound})*\s*\)`
322
- // compoundB:
323
- // `(?:${type}|(?:${type})?(?:${subclass}|${nestedLogical})+)`
324
- // nst:
325
- // `(?:first|last|only)-(?:child|of-type)`
326
- // nth: exclude An+B with selector list, e.g. :nth-child(2n+1 of .foo)
327
- // `nth-(?:last-)?(?:child|of-type)\(\s*(?:even|odd|${anb})\s*\)`
328
- // logical: exclude complex selector, e.g. :is(.foo > .bar)
329
- // `(?:is|not|where)\(\s*${compoundB}(?:\s*,\s*${compoundB})*\s*\)`
330
- // filter pseudos other than child-indexed and logical combination pseudos
331
- // `:(?!${nst}|${nth}|${logical})`
332
- if (/:(?!(?:first|last|only)-(?:child|of-type)|nth-(?:last-)?(?:child|of-type)\(\s*(?:even|odd|[+-]?(?:(?:0|[1-9]\d*)n?|n)|(?:[+-]?(?:0|[1-9]\d*))?n\s*[+-]\s*(?:0|[1-9]\d*))\s*\)|(?:is|not|where)\(\s*(?:\*|[A-Za-z][\w-]*|(?:\*|[A-Za-z][\w-]*)?(?:\[[^\]]+\]|[#.:][\w-]+|:(?:is|not|where)\(\s*(?:\*|[A-Za-z][\w-]*|(?:\*|[A-Za-z][\w-]*)?(?:\[[^\]]+\]|[#.:][\w-]+)+)(?:\s*,\s*(?:\*|[A-Za-z][\w-]*|(?:\*|[A-Za-z][\w-]*)?(?:\[[^\]]+\]|[#.:][\w-]+)+))*\s*\))+)(?:\s*,\s*(?:\*|[A-Za-z][\w-]*|(?:\*|[A-Za-z][\w-]*)?(?:\[[^\]]+\]|[#.:][\w-]+|:(?:is|not|where)\(\s*(?:\*|[A-Za-z][\w-]*|(?:\*|[A-Za-z][\w-]*)?(?:\[[^\]]+\]|[#.:][\w-]+)+)(?:\s*,\s*(?:\*|[A-Za-z][\w-]*|(?:\*|[A-Za-z][\w-]*)?(?:\[[^\]]+\]|[#.:][\w-]+)+))*\s*\))+))*\s*\))/.test(selector)) {
365
+ let reg;
366
+ if (REG_LOGICAL_KEY.test(selector)) {
367
+ const { complex, descendant } = opt;
368
+ if (complex && descendant) {
369
+ reg = REG_COMPLEX_A;
370
+ } else if (complex) {
371
+ reg = REG_COMPLEX_B;
372
+ } else {
373
+ reg = REG_COMPOUND;
374
+ }
375
+ } else {
376
+ reg = REG_CHILD_INDEXED;
377
+ }
378
+ if (reg.test(selector)) {
333
379
  return false;
334
380
  }
335
381
  }
@@ -2,6 +2,7 @@ export class Finder {
2
2
  constructor(window: object);
3
3
  private _onError;
4
4
  private _setup;
5
+ private _setEvent;
5
6
  private _correspond;
6
7
  private _createTreeWalker;
7
8
  private _prepareQuerySelectorWalker;
@@ -1,8 +1,8 @@
1
1
  export function unescapeSelector(selector?: string): string | null;
2
2
  export function preprocess(...args: any[]): string;
3
3
  export function parseSelector(selector: string): object;
4
- export function walkAST(ast?: object): Array<object | undefined>;
4
+ export function walkAST(ast?: object): object;
5
5
  export function sortAST(asts: Array<object>): Array<object>;
6
6
  export function parseAstName(selector: string): object;
7
- export function filterSelector(selector: string): boolean;
7
+ export function filterSelector(selector: string, opt?: object): boolean;
8
8
  export { generate as generateCSS } from "css-tree";