Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add python getDef helper #98

Merged
merged 1 commit into from
Nov 24, 2023
Merged
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
28 changes: 28 additions & 0 deletions lib/__tests__/python.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as helper from "../index";

describe("python", () => {
const { python } = helper;
it("getDef", () => {
const code = `
a = 1

def b():
a = 2

def c():
a = 1
`;
const match = python.getDef(code, "b");
if (match) {
const { def, function_indentation, function_body, function_parameters } =
match;
expect(def).toEqual(`

def b():
a = 2`);
expect(function_indentation).toEqual(0);
expect(function_body).toEqual(" a = 2");
expect(function_parameters).toEqual("");
}
});
});
133 changes: 81 additions & 52 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { strip } from './strip.js';
import { strip } from "./strip.js";

/**
* Removes every HTML-comment from the string that is provided
Expand Down Expand Up @@ -26,15 +26,15 @@ export function removeCssComments(str: string): string {
* @returns {String}
*/

export function removeJSComments(codeStr: string): string {
export function removeJSComments(codeStr: string): string {
// TODO: publish type declarations and re-enable eslint
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
return strip(codeStr);
} catch (err) {
return codeStr;
}
};
}

/**
* Removes every white-space from the string that is provided
Expand All @@ -43,7 +43,7 @@ export function removeJSComments(codeStr: string): string {
*/

export function removeWhiteSpace(str: string): string {
return str.replace(/\s/g, '');
return str.replace(/\s/g, "");
}

/**
Expand Down Expand Up @@ -83,9 +83,9 @@ interface ExtendedStyleDeclaration extends CSSStyleDeclaration {
const getIsDeclaredAfter = (styleRule: CSSStyleRule) => (selector: string) => {
const cssStyleRules = Array.from(
styleRule.parentStyleSheet?.cssRules || []
)?.filter(ele => ele.type === CSSRule.STYLE_RULE) as CSSStyleRule[];
)?.filter((ele) => ele.type === CSSRule.STYLE_RULE) as CSSStyleRule[];
const previousStyleRule = cssStyleRules.find(
ele => ele?.selectorText === selector
(ele) => ele?.selectorText === selector
);
if (!previousStyleRule) return false;
const currPosition = Array.from(
Expand All @@ -97,7 +97,32 @@ const getIsDeclaredAfter = (styleRule: CSSStyleRule) => (selector: string) => {
return currPosition > prevPosition;
};

export module python {
export function getDef(code: string, functionName: string) {
const regex = new RegExp(
`\\n(?<function_indentation>\\s*?)def\\s+${functionName}\\s*\\((?<function_parameters>[^\\)]*)\\)\\s*:\\n(?<function_body>.*?)(?=\\n\\k<function_indentation>[\\w#])`,
"s"
);

const matchedCode = regex.exec(code);
if (matchedCode) {
const { function_parameters, function_body, function_indentation } =
matchedCode.groups;

const functionIndentationSansNewLine = function_indentation.replace(
/\n+/,
""
);
return {
def: matchedCode[0],
function_parameters,
function_body,
function_indentation: functionIndentationSansNewLine.length,
};
}
return null;
}
}

export class CSSHelp {
doc: Document;
Expand All @@ -107,23 +132,23 @@ export class CSSHelp {
private _getStyleRules() {
const styleSheet = this.getStyleSheet();
return this.styleSheetToCssRulesArray(styleSheet).filter(
ele => ele.type === CSSRule.STYLE_RULE
(ele) => ele.type === CSSRule.STYLE_RULE
) as CSSStyleRule[];
}

getStyleDeclarations(selector: string): CSSStyleDeclaration[] {
return this._getStyleRules()
?.filter(ele => ele?.selectorText === selector)
.map(x => x.style);
?.filter((ele) => ele?.selectorText === selector)
.map((x) => x.style);
}
getStyle(selector: string): ExtendedStyleDeclaration | null {
const style = this._getStyleRules().find(
ele => ele?.selectorText === selector
(ele) => ele?.selectorText === selector
)?.style as ExtendedStyleDeclaration | undefined;
if (!style) return null;
style.getPropVal = (prop: string, strip = false) => {
return strip
? style.getPropertyValue(prop).replace(/\s+/g, '')
? style.getPropertyValue(prop).replace(/\s+/g, "")
: style.getPropertyValue(prop);
};
return style;
Expand All @@ -132,7 +157,7 @@ export class CSSHelp {
getStyleAny(selectors: string[]): ExtendedStyleDeclaration | null {
for (const selector of selectors) {
const style = this.getStyle(selector);

if (style) {
return style;
}
Expand All @@ -142,13 +167,13 @@ export class CSSHelp {
}
getStyleRule(selector: string): ExtendedStyleRule | null {
const styleRule = this._getStyleRules()?.find(
ele => ele?.selectorText === selector
(ele) => ele?.selectorText === selector
);
if (styleRule) {
return {
...styleRule,
isDeclaredAfter: (selector: string) =>
getIsDeclaredAfter(styleRule)(selector)
getIsDeclaredAfter(styleRule)(selector),
};
} else {
return null;
Expand All @@ -158,26 +183,26 @@ export class CSSHelp {
const styleSheet = this.getStyleSheet();
const cssRules = this.styleSheetToCssRulesArray(styleSheet);
switch (element) {
case 'media':
return cssRules.filter(ele => ele.type === CSSRule.MEDIA_RULE);
case 'fontface':
return cssRules.filter(ele => ele.type === CSSRule.FONT_FACE_RULE);
case 'import':
return cssRules.filter(ele => ele.type === CSSRule.IMPORT_RULE);
case 'keyframes':
return cssRules.filter(ele => ele.type === CSSRule.KEYFRAMES_RULE);
case "media":
return cssRules.filter((ele) => ele.type === CSSRule.MEDIA_RULE);
case "fontface":
return cssRules.filter((ele) => ele.type === CSSRule.FONT_FACE_RULE);
case "import":
return cssRules.filter((ele) => ele.type === CSSRule.IMPORT_RULE);
case "keyframes":
return cssRules.filter((ele) => ele.type === CSSRule.KEYFRAMES_RULE);
default:
return cssRules;
}
}
isPropertyUsed(property: string): boolean {
return this._getStyleRules().some(ele =>
return this._getStyleRules().some((ele) =>
ele.style?.getPropertyValue(property)
);
}
getRuleListsWithinMedia(mediaText: string): CSSStyleRule[] {
const medias = this.getCSSRules('media') as CSSMediaRule[];
const cond = medias?.find(x => x?.media?.mediaText === mediaText);
const medias = this.getCSSRules("media") as CSSMediaRule[];
const cond = medias?.find((x) => x?.media?.mediaText === mediaText);
const cssRules = cond?.cssRules;
return Array.from(cssRules || []) as CSSStyleRule[];
}
Expand All @@ -189,12 +214,12 @@ export class CSSHelp {

// When using the styles.css tab, we add a 'fcc-injected-styles' class so we can target that. This allows users to add external scripts without them interfering
const stylesDotCss: HTMLStyleElement | null = this.doc?.querySelector(
'style.fcc-injected-styles'
"style.fcc-injected-styles"
);

// For steps that use <style> tags, where they don't add the above class - most* browser extensions inject styles with class/media attributes, so it filters those
const styleTag: HTMLStyleElement | null = this.doc?.querySelector(
'style:not([class]):not([media])'
"style:not([class]):not([media])"
);

if (link?.sheet?.cssRules?.length) {
Expand All @@ -208,41 +233,45 @@ export class CSSHelp {
}
}
styleSheetToCssRulesArray(
styleSheet: ReturnType<CSSHelp['getStyleSheet']>
styleSheet: ReturnType<CSSHelp["getStyleSheet"]>
): CSSRule[] {
return Array.from(styleSheet?.cssRules || []);
}
// takes a CSS selector, returns all equivelant selectors from the current document
// or an empty array if there are no matches
selectorsFromSelector(selector: string): string[] {
const elements = this.doc.querySelectorAll(selector);
const allSelectors = Array.from(elements).map((element: Element) => {
let directPath = [];
let indirectPath = [];
let allPaths = [];

while (element.parentNode) {
let tag = element.tagName.toLowerCase();
let siblings = Array.from(element.parentNode.children);

if (siblings.filter(e => e.tagName === element.tagName).length > 1) {
let allSiblings = Array.from(element.parentNode.childNodes);
let index = allSiblings.indexOf(element);
tag += `:nth-child(${index + 1})`;
}
const allSelectors = Array.from(elements)
.map((element: Element) => {
let directPath = [];
let indirectPath = [];
let allPaths = [];

directPath.unshift(tag);
indirectPath.unshift(tag);
allPaths.push([directPath.join(' > '), indirectPath.join(' ')]);

// traverse up the DOM tree
element = element.parentNode as Element;
}
while (element.parentNode) {
let tag = element.tagName.toLowerCase();
let siblings = Array.from(element.parentNode.children);

if (
siblings.filter((e) => e.tagName === element.tagName).length > 1
) {
let allSiblings = Array.from(element.parentNode.childNodes);
let index = allSiblings.indexOf(element);
tag += `:nth-child(${index + 1})`;
}

return allPaths.flat();
}).flat();
directPath.unshift(tag);
indirectPath.unshift(tag);
allPaths.push([directPath.join(" > "), indirectPath.join(" ")]);

// traverse up the DOM tree
element = element.parentNode as Element;
}

return allPaths.flat();
})
.flat();

// remove duplicates
return [...new Set(allSelectors)];
}
}
}
Loading