diff --git a/index.d.ts b/index.d.ts index 28573cf..5eec1d9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3342,20 +3342,42 @@ export type AtRules = | "@supports" | "@viewport"; -export type AdvancedPseudos = ":-moz-any" | ":-webkit-any" | "::cue" | ":dir" | ":lang" | ":not" | ":nth-child" | ":nth-last-child" | ":nth-last-of-type" | ":nth-of-type"; +export type AdvancedPseudos = + | ":-moz-any" + | ":-moz-dir" + | ":-webkit-any" + | "::cue" + | ":dir" + | ":lang" + | ":not" + | ":nth-child" + | ":nth-last-child" + | ":nth-last-of-type" + | ":nth-of-type"; export type SimplePseudos = + | "-moz-full-screen" + | "-webkit-full-screen" + | ":-moz-any-link" + | ":-moz-placeholder" + | ":-moz-read-only" + | ":-moz-read-write" + | ":-ms-fullscreen" + | ":-webkit-any-link" + | "::-moz-placeholder" | "::-moz-progress-bar" | "::-moz-range-progress" | "::-moz-range-thumb" | "::-moz-range-track" + | "::-moz-selection" + | "::-ms-backdrop" | "::-ms-browse" | "::-ms-check" | "::-ms-clear" - | "::-ms-clear" | "::-ms-fill" | "::-ms-fill-lower" | "::-ms-fill-upper" + | "::-ms-placeholder" | "::-ms-reveal" | "::-ms-thumb" | "::-ms-ticks-after" @@ -3363,6 +3385,8 @@ export type SimplePseudos = | "::-ms-tooltip" | "::-ms-track" | "::-ms-value" + | "::-webkit-backdrop" + | "::-webkit-placeholder" | "::-webkit-progress-bar" | "::-webkit-progress-inner-value" | "::-webkit-progress-value" diff --git a/index.js.flow b/index.js.flow index d23788e..165becb 100644 --- a/index.js.flow +++ b/index.js.flow @@ -3106,20 +3106,42 @@ export type AtRules = | "@supports" | "@viewport"; -export type AdvancedPseudos = ":-moz-any" | ":-webkit-any" | "::cue" | ":dir" | ":lang" | ":not" | ":nth-child" | ":nth-last-child" | ":nth-last-of-type" | ":nth-of-type"; +export type AdvancedPseudos = + | ":-moz-any" + | ":-moz-dir" + | ":-webkit-any" + | "::cue" + | ":dir" + | ":lang" + | ":not" + | ":nth-child" + | ":nth-last-child" + | ":nth-last-of-type" + | ":nth-of-type"; export type SimplePseudos = + | "-moz-full-screen" + | "-webkit-full-screen" + | ":-moz-any-link" + | ":-moz-placeholder" + | ":-moz-read-only" + | ":-moz-read-write" + | ":-ms-fullscreen" + | ":-webkit-any-link" + | "::-moz-placeholder" | "::-moz-progress-bar" | "::-moz-range-progress" | "::-moz-range-thumb" | "::-moz-range-track" + | "::-moz-selection" + | "::-ms-backdrop" | "::-ms-browse" | "::-ms-check" | "::-ms-clear" - | "::-ms-clear" | "::-ms-fill" | "::-ms-fill-lower" | "::-ms-fill-upper" + | "::-ms-placeholder" | "::-ms-reveal" | "::-ms-thumb" | "::-ms-ticks-after" @@ -3127,6 +3149,8 @@ export type SimplePseudos = | "::-ms-tooltip" | "::-ms-track" | "::-ms-value" + | "::-webkit-backdrop" + | "::-webkit-placeholder" | "::-webkit-progress-bar" | "::-webkit-progress-inner-value" | "::-webkit-progress-value" diff --git a/src/compat.ts b/src/compat.ts index 6df0a77..d6a8eab 100644 --- a/src/compat.ts +++ b/src/compat.ts @@ -6,10 +6,20 @@ export function getCompat(data: { __compat: MDN.Compat }): MDN.Compat { return data.__compat; } -function getSupport(support: MDN.Support | MDN.Support[]): MDN.Support[] { +export function getSupport(support: MDN.Support | MDN.Support[]): MDN.Support[] { return Array.isArray(support) ? support : [support]; } +export function getAtRuleData(name: string): MDN.CompatData | null { + const data = getData('at-rules/' + name); + + if (data) { + return data.css['at-rules'][name]; + } + + return null; +} + export function getPropertyData(name: string): MDN.CompatData | null { const data = getData('properties/' + name); @@ -20,21 +30,21 @@ export function getPropertyData(name: string): MDN.CompatData | null { return null; } -export function getTypesData(name: string): MDN.CompatData | null { - const data = getData('types/' + name); +export function getSelectorsData(name: string): MDN.CompatData | null { + const data = getData('selectors/' + name); if (data) { - return data.css.types[name]; + return data.css.selectors[name]; } return null; } -export function getAtRuleData(name: string): MDN.CompatData | null { - const data = getData('at-rules/' + name); +export function getTypesData(name: string): MDN.CompatData | null { + const data = getData('types/' + name); if (data) { - return data.css['at-rules'][name]; + return data.css.types[name]; } return null; diff --git a/src/declarator.ts b/src/declarator.ts index 326bc7e..34f13ce 100644 --- a/src/declarator.ts +++ b/src/declarator.ts @@ -11,7 +11,7 @@ import { vendorPrefixedLonghandProperties, vendorPrefixedShorthandProperties, } from './properties'; -import { advancedPseudos, simplePseudos } from './pseudos'; +import { advancedPseudos, simplePseudos } from './selectors'; import { IDataType, Type, TypeType } from './typer'; export interface IAlias { diff --git a/src/pseudos.ts b/src/pseudos.ts deleted file mode 100644 index 9654a0f..0000000 --- a/src/pseudos.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as selectors from 'mdn-data/css/selectors.json'; -import { IStringLiteral, Type } from './typer'; - -const REGEX_SIMPLE_PSEUDO_SELECTOR = /(?!:?:[\w-]+\()(:?:[\w-]+)/g; -const REGEX_ADVANCED_PSEUDO_SELECTOR = /(:?:[\w-]+)\(/g; - -export const simplePseudos: IStringLiteral[] = []; -export const advancedPseudos: IStringLiteral[] = []; - -for (const selector in selectors) { - let match: RegExpMatchArray | null = null; - while ((match = REGEX_SIMPLE_PSEUDO_SELECTOR.exec(selectors[selector].syntax))) { - simplePseudos.push({ type: Type.StringLiteral, literal: match[1] }); - } - while ((match = REGEX_ADVANCED_PSEUDO_SELECTOR.exec(selectors[selector].syntax))) { - advancedPseudos.push({ type: Type.StringLiteral, literal: match[1] }); - } -} diff --git a/src/selectors.ts b/src/selectors.ts new file mode 100644 index 0000000..121d4e2 --- /dev/null +++ b/src/selectors.ts @@ -0,0 +1,63 @@ +import * as selectors from 'mdn-data/css/selectors.json'; +import { compatNames, getCompat, getSelectorsData, getSupport } from './compat'; +import { addType, ResolvedType, Type } from './typer'; + +const REGEX_SIMPLE_PSEUDO_SELECTOR = /(?!:?:[\w-]+\()(:?:[\w-]+)/g; +const REGEX_ADVANCED_PSEUDO_SELECTOR = /(:?:[\w-]+)\(/g; + +export let simplePseudos: ResolvedType[] = []; +export let advancedPseudos: ResolvedType[] = []; + +for (const selector in selectors) { + let match: RegExpMatchArray | null = null; + while ((match = REGEX_SIMPLE_PSEUDO_SELECTOR.exec(selectors[selector].syntax))) { + simplePseudos = addType(simplePseudos, { type: Type.StringLiteral, literal: match[1] }); + + for (const alternative of alternativeSelectors(match[1])) { + simplePseudos = addType(simplePseudos, { type: Type.StringLiteral, literal: alternative }); + } + } + while ((match = REGEX_ADVANCED_PSEUDO_SELECTOR.exec(selectors[selector].syntax))) { + advancedPseudos = addType(advancedPseudos, { type: Type.StringLiteral, literal: match[1] }); + + for (const alternative of alternativeSelectors(match[1])) { + advancedPseudos = addType(advancedPseudos, { type: Type.StringLiteral, literal: alternative }); + } + } +} + +function alternativeSelectors(selector: string): string[] { + const alternatives: string[] = []; + + // Pseudo without ':' + const colons = ':'.repeat(selector.lastIndexOf(':') + 1); + const name = selector.slice(colons.length); + const compatibilityData = getSelectorsData(name); + + if (compatibilityData) { + const compat = getCompat(compatibilityData); + + let browser: MDN.Browsers; + for (browser in compat.support) { + const support = compat.support[browser]; + + for (const version of getSupport(support)) { + // Assume that the version has the property implemented if `null` + const isAdded = !!version.version_added || version.version_added === null; + + if (isAdded) { + if (version.prefix) { + alternatives.push(colons + version.prefix + name); + } + if (version.alternative_name) { + alternatives.push(version.alternative_name); + } + } + } + } + + return alternatives; + } + + return alternatives; +}