Skip to content
Draft
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
21 changes: 21 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: [
'**/__tests__/**/*.ts',
'**/?(*.)+(spec|test).ts'
],
transform: {
'^.+\\.ts$': 'ts-jest'
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/index.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
moduleFileExtensions: ['ts', 'js', 'json'],
verbose: true
};
155 changes: 155 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"build:esm": "tsc --module esnext --outDir dist",
"prepublishOnly": "npm run build",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src --ext .ts,.js,.cjs"
},
"keywords": [
Expand Down Expand Up @@ -54,6 +56,7 @@
"@typescript-eslint/parser": "^6.13.1",
"eslint": "^8.54.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"typescript": "^5.3.2"
},
"repository": {
Expand Down
22 changes: 18 additions & 4 deletions src/matchers/context/Stylesheet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import css, { CssRuleAST, CssStylesheetAST } from '@adobe/css-tools';

export type StylesheetRule = {
selectors: string;
/**
* The full selector string (e.g. ".class1, .class2").
*/
selector: string;

/**
* The individual selector parts (e.g. [".class1", ".class2"]).
*/
selectorParts: string[];

/**
* The declarations within the rule (e.g. { color: "red", fontSize: "12px" }).
*/
declarations: Record<string, string>;
}

Expand All @@ -27,7 +39,8 @@ export class Stylesheet {
this.rules = this.ast!.stylesheet.rules.reduce<Record<string, StylesheetRule>>((acc, rule) => {
if (rule.type === 'rule') {
const cssRule = rule as CssRuleAST;
const selectors = cssRule.selectors.join(', ');
const { selectors: selectorParts } = cssRule;
const selector = selectorParts.join(', ');

const declarations: Record<string, string> = rule.declarations.reduce((declAcc, decl) => {
if (decl.type === 'declaration') {
Expand All @@ -38,8 +51,9 @@ export class Stylesheet {

return {
...acc,
[selectors]: {
selectors,
[selector]: {
selector,
selectorParts: cssRule.selectors,
declarations,
}
};
Expand Down
14 changes: 14 additions & 0 deletions src/matchers/css/ParsingError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ParsingErrorPosition } from "./types";

/**
* Represents a parsing error with a specific message.
*/
export class ParsingError extends Error {
public location: ParsingErrorPosition;

constructor(message: string, location: ParsingErrorPosition) {
super(`Parsing Error [${location.line}:${location.column}]: ${message}`);

this.location = location;
}
}
48 changes: 48 additions & 0 deletions src/matchers/css/SpecificityCalculator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
type Specificity = {
inline: number;
idSelectors: number;
classSelectors: number;
typeSelectors: number;
}

const isTypeSelector = (part: string): boolean => {
const pseudoElement = /(::[a-zA-Z]+[a-zA-Z0-9-]*)*/;
const elementSelector = /^[a-zA-Z]+[a-zA-Z0-9-]*$/;
return elementSelector.test(part) || pseudoElement.test(part);
}

const isClassSelector = (part: string): boolean => {
const classSelector = /^\.[a-zA-Z]+[a-zA-Z0-9-]*$/;
const attributeSelector = /^\[.+\]$/;
const pseudoClassSelector = /^:[^:].*$/;
return false;
}

export class SpecificityCalculator {
constructor(public selector: string) {}

calculate() {
console.log(this.selector);

const parts = this.selector.split(/\s+/);
console.log(parts);

const specificity = parts.reduce<Specificity>((specificity, part) => {
if (part.startsWith('.')) {
specificity.classSelectors += 1;
}

if (part.startsWith('#')) {
specificity.idSelectors += 1;
}

if (isTypeSelector(part)) {
specificity.typeSelectors += 1;
}

return specificity;
}, { inline: 0, idSelectors: 0, classSelectors: 0, typeSelectors: 0 });

console.log(specificity);
}
}
Loading