diff --git a/package.json b/package.json index 8ca2574..d140b82 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,13 @@ "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", - "fictoan-react": "^0.33.10", + "ace-builds": "^1.4.13", + "acorn": "^8.6.0", + "deep-diff": "^1.0.2", + "fictoan-react": "^0.35.3", "polished": "^4.0.4", "react": "^16.13.1", + "react-ace": "^9.5.0", "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", @@ -34,5 +38,13 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/acorn": "^4.0.6", + "@types/node": "^16.11.11", + "@types/react": "^17.0.37", + "@types/react-dom": "^17.0.11", + "@types/styled-components": "^5.1.16", + "typescript": "^4.5.2" } } diff --git a/src/App.jsx b/src/App.jsx index 1f8ef5e..9dc0ed7 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -32,6 +32,7 @@ import { TableDocs } from "./pages/Components/Table/Table"; // Local assets import { Sidebar } from "./components/Sidebar/Sidebar"; import { ThemeDocs } from "./pages/03Theme/Theme"; +import { ThemeEditorDocs } from "./pages/03ThemeBuilder/ThemeEditor"; import { CodeBlockDocs } from "./pages/Components/CodeBlock/CodeBlock"; import { BaseElementDocs } from "./pages/BaseElement/BaseElement"; import { InfoPanelDocs } from "./pages/Components/InfoPanel/InfoPanel"; @@ -91,6 +92,12 @@ export const App = () => { component={ ThemeDocs } /> + + { {/* COMPONENTS ============================================= */} - + <> @@ -286,41 +284,32 @@ export const Sidebar = ({toggleTheme}) => { {/* SIDEBAR ================================================ */} - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + {/* TABS =================================================== */} @@ -339,7 +328,7 @@ export const Sidebar = ({toggleTheme}) => { - + {/* FOOTER ================================================= */} diff --git a/src/pages/03ThemeBuilder/CodeManipulation.ts b/src/pages/03ThemeBuilder/CodeManipulation.ts new file mode 100644 index 0000000..1d5e69b --- /dev/null +++ b/src/pages/03ThemeBuilder/CodeManipulation.ts @@ -0,0 +1,73 @@ +import { DeepDiff } from "deep-diff"; +import dep2 from "polished/lib/color/lighten"; +import dep3 from "polished/lib/color/darken"; +import { defaultColours as dep1 } from "fictoan-react"; +import { ThemeVisitor, JSVisitor } from "./Visitor"; + +interface DiffType { + kind: "E" | "N" | "D" | "A"; + path: string[]; + lhs: any; + rhs: any; + index?: number; + item?: any; +} + +function templatize(val: string): string { + return `{{${val}}}`; +} + +function unTemplatize(val: string): string { + return val.replaceAll(/\'\{\{([^\{\}]+)\}\}\'/g, "$1"); +} + +function setDeepValue(obj: any, path: string[], value: string) { + const key = path.shift(); + if (!key) { + return value; + } + obj[key] = setDeepValue(obj[key] ? obj[key] : {}, path, value); + return obj; +} + +export function evaluateJS(javascriptCode: string) { + const defaultColours = dep1; + const lighten = dep2; + const darken = dep3; + return eval(`( + function () { + ${javascriptCode} + return Theme; + } + )();`); +} + +export function performDiff(newJavascriptCode: string, oldJavascriptCode: string) { + const newCode = evaluateJS(newJavascriptCode); + const newCodeVisitor = new ThemeVisitor(newJavascriptCode); + const oldCode = evaluateJS(oldJavascriptCode); + const oldCodeVisitor = new ThemeVisitor(oldJavascriptCode); + const deepDiffs = DeepDiff(oldCode, newCode); + + const diffs = deepDiffs + ? DeepDiff(oldCode, newCode) + .filter((diff: DiffType) => diff.path[0] != "customColours") + .map((diff: DiffType) => { + const _newVal = newCodeVisitor.getValueAtPath([ "Theme", ...diff.path ]); + const _oldVal = oldCodeVisitor.getValueAtPath([ "Theme", ...diff.path ]); + if (_newVal.trim() !== _oldVal.trim()) + return {path : diff.path, val : _newVal} + return undefined; + }).filter(Boolean) + : []; + + const minifiedTheme = diffs.reduce((acc: object, curr: { path: string[], val: string }) => { + return setDeepValue(acc, curr.path.map(templatize), templatize(curr.val)) + }, {[templatize("customColours")] : templatize("customColours")}); + + const v = new JSVisitor(newJavascriptCode); + return ( + `${v.getAllVariableDeclarations()} + \nconst Theme = ${unTemplatize(JSON.stringify(minifiedTheme, null, "\t"))}` + ) +} diff --git a/src/pages/03ThemeBuilder/Editor.tsx b/src/pages/03ThemeBuilder/Editor.tsx new file mode 100644 index 0000000..48e4439 --- /dev/null +++ b/src/pages/03ThemeBuilder/Editor.tsx @@ -0,0 +1,52 @@ +import React, { FunctionComponent, useRef } from "react"; +import AceEditor from "react-ace"; +import "ace-builds/src-noconflict/mode-javascript"; +import "ace-builds/src-noconflict/theme-tomorrow"; +import { Button } from "fictoan-react"; +import { STRINGIFIED_FICTOAN_THEME } from "./Theme"; +import { evaluateJS, performDiff } from "./CodeManipulation"; + +interface EditorProps { + setTheme: Function; + setMinified: Function; +} + +export const Editor: FunctionComponent = ({setTheme, setMinified}) => { + + const editorRef = useRef(); + + function handleClick() { + if (editorRef.current) { + const code = editorRef.current.editor.getValue(); + const theme = evaluateJS(code); + setTheme(theme); + } + } + + function handleMinify() { + if (editorRef.current) { + const codeString = editorRef.current.editor.getValue(); + const minified = performDiff(codeString, STRINGIFIED_FICTOAN_THEME); + setMinified(minified); + } + } + + return ( + <> + + + + + ) +} + diff --git a/src/pages/03ThemeBuilder/Result.tsx b/src/pages/03ThemeBuilder/Result.tsx new file mode 100644 index 0000000..67fbe2e --- /dev/null +++ b/src/pages/03ThemeBuilder/Result.tsx @@ -0,0 +1,59 @@ +import React, { ReactElement, useState } from "react"; +import { + Element, + Button, + Card, + FormWrapper, + Heading, + InputField, + ProgressBar, + Text, + ThemeProvider, +} from "fictoan-react"; +import { evaluateJS } from "./CodeManipulation"; +import { STRINGIFIED_FICTOAN_THEME } from "./Theme"; + +export const FictoanThemeOutputSample = () => { + const [json, setJson] = useState(() => evaluateJS(STRINGIFIED_FICTOAN_THEME)); + + return ( + + + ACME CORP + + + + + This is a heading + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. + + + + + + + + + + + + + + ) +} diff --git a/src/pages/03ThemeBuilder/Theme.ts b/src/pages/03ThemeBuilder/Theme.ts new file mode 100644 index 0000000..54071d8 --- /dev/null +++ b/src/pages/03ThemeBuilder/Theme.ts @@ -0,0 +1,561 @@ +export const STRINGIFIED_FICTOAN_THEME= ` +const customColours = { + hue: defaultColours.blue90, + tint: defaultColours.amber, + shade: defaultColours.grey, + analogue: defaultColours.indigo50, + accent: defaultColours.green80 +}; + +const Theme= { + customColours: customColours, + // GLOBALS ////////////////////////////////////////////////////////////// + globals : { + borderWidth : "1px" + }, + + // BASICS /////////////////////////////////////////////////////////////// + body : { + bg : String(defaultColours.white) + }, + + + // BREADCRUMBS /////////////////////////////////////////////////////////// + breadcrumbs : { + wrapper : { + bg : String(defaultColours.white) + }, + item : { + text : String(customColours.shade), + active : String(customColours.shade), + inactive : String(customColours.shade) + }, + separator : { + text : String(defaultColours.slate80), + content : "\"/\"" + } + }, + + + // BUTTON /////////////////////////////////////////////////////////////// + button : { + font : "sans-serif", + isLoading : { + spinnerBorder : String(customColours.hue) + }, + primary : { + default : { + bg : String(customColours.hue), + border : String(customColours.hue), + text : String(defaultColours.white), + borderRadius : "4px" + }, + onHover : { + bg : String(customColours.hue), + border : String(customColours.hue), + text : String(defaultColours.white) + }, + isActive : { + bg : String(customColours.hue), + border : String(customColours.hue), + text : String(defaultColours.white) + }, + isLoading : { + spinnerBorder : String(defaultColours.white) + } + }, + secondary : { + default : { + bg : lighten(0.4, String(customColours.hue)), + border : String(customColours.hue), + text : String(customColours.hue), + borderRadius : "4px" + }, + onHover : { + bg : lighten(0.4, String(customColours.hue)), + border : String(customColours.hue), + text : String(customColours.hue) + }, + isActive : { + bg : lighten(0.2, String(customColours.hue)), + border : String(customColours.hue), + text : String(customColours.hue) + }, + isLoading : { + spinnerBorder : String(customColours.hue) + } + }, + tertiary : { + default : { + bg : String(defaultColours.transparent), + border : String(customColours.hue), + text : String(customColours.hue), + borderRadius : "4px" + }, + onHover : { + bg : lighten(0.40, String(customColours.hue)), + border : String(defaultColours.transparent), + text : String(customColours.hue) + }, + isActive : { + bg : lighten(0.32, String(customColours.hue)), + border : String(defaultColours.transparent), + text : String(customColours.hue) + }, + isLoading : { + spinnerBorder : String(customColours.hue) + } + } + }, + + + // CARD ///////////////////////////////////////////////////////////////// + card : { + bg : String(defaultColours.white), + border : lighten(0.96, String(defaultColours.black)), + borderRadius : "4px" + }, + + + // RULE ///////////////////////////////////////////////////////////////// + hr : { + default : { + bg : String(defaultColours.blue80), + height : "1px" + }, + primary : { + bg : String(defaultColours.blue80), + height : "1px" + }, + secondary : { + bg : String(defaultColours.slate40), + height : "1px" + }, + tertiary : { + bg : String(defaultColours.slate20), + height : "1px" + } + }, + + + // INPUT //////////////////////////////////////////////////////////////// + inputField : { + default : { + bg : String(defaultColours.white), + border : String(defaultColours.slate40), + label : String(customColours.shade), + text : String(customColours.shade), + borderRadius : "4px", + helpText : String(defaultColours.slate60) + }, + onFocus : { + bg : String(defaultColours.white), + border : String(customColours.hue), + text : String(customColours.shade), + }, + isValid : { + bg : String(defaultColours.white), + border : String(defaultColours.green80), + label : String(customColours.shade) + }, + isInvalid : { + bg : String(defaultColours.red10), + border : String(defaultColours.red80), + label : String(defaultColours.red), + errorText : String(defaultColours.red) + }, + isReadOnly : { + bg : String(defaultColours.slate10), + border : String(defaultColours.slate20), + text : String(defaultColours.slate60), + label : String(customColours.shade) + }, + required : { + text : String(defaultColours.red) + }, + icons : { + default : { + fill : String(defaultColours.slate30) + }, + onFocus : { + fill : String(customColours.hue) + }, + isValid : { + bg : String(defaultColours.grey50), + border : String(defaultColours.red30) + } + } + }, + + select : { + chevron : String(customColours.hue) + }, + + radioButton : { + inset : { + default : { + bg : String(defaultColours.slate20) + }, + onHover : { + bg : String(defaultColours.slate40) + }, + isSelected : { + bg : String(customColours.hue) + }, + isDisabled : { + bg : String(defaultColours.slate10) + } + }, + circle : { + default : { + bg : String(defaultColours.white) + } + } + }, + + checkBox : { + square : { + default : { + bg : String(defaultColours.slate20) + }, + onHover : { + bg : String(defaultColours.slate40) + }, + isChecked : { + bg : String(customColours.hue) + }, + isDisabled : { + bg : String(defaultColours.slate10) + } + }, + check : { + default : { + border : String(defaultColours.white) + } + } + }, + + toggleSwitch : { + switch : { + default : { + bg : String(defaultColours.white) + }, + isChecked : { + bg : String(defaultColours.white) + } + } + }, + + + // INFO PANEL /////////////////////////////////////////////////////////// + infoPanel : { + bg : String(defaultColours.white), + border : String(defaultColours.slate20), + dismissButton : { + color : String(defaultColours.slate90) + } + }, + + + // NOTIFICATION ///////////////////////////////////////////////////////// + notification : { + default : { + bg : String(defaultColours.white), + text : String(customColours.shade) + }, + kinds : { + info : { + border : String(defaultColours.blue60) + }, + warning : { + border : String(defaultColours.amber) + }, + error : { + border : String(defaultColours.red90) + }, + success : { + border : String(defaultColours.green90) + } + } + }, + + + // PROGRESS BAR ///////////////////////////////////////////////////////// + progressBar : { + bg : String(defaultColours.slate20), + fill : String(customColours.hue), + label : String(customColours.shade), + value : String(customColours.shade), + borderRadius : "4px" + }, + + + // SIDEBAR ////////////////////////////////////////////////////////////// + sidebar : { + width : "240px", + bg : String(defaultColours.white), + + isCollapsed : { + width : "48px", + label : { + text : String(defaultColours.white), + bg : String(customColours.hue) + } + }, + + header : { + bg : String(defaultColours.white), + borderBottom : String(defaultColours.slate10), + logoWidth : "50%" + }, + + linksWrapper : { + icons : { + size : "24px", + stroked : { + thickness : 2, + default : { + line : String(defaultColours.slate40) + }, + onHover : { + line : String(defaultColours.slate80) + }, + isActive : { + line : String(defaultColours.slate) + } + }, + filled : { + default : { + bg : String(defaultColours.slate40) + }, + onHover : { + bg : String(defaultColours.slate80) + }, + isActive : { + bg : String(defaultColours.slate) + } + } + }, + + links : { + default : { + bg : String(defaultColours.white), + text : String(customColours.shade), + scale : 100, + weight : 600 + }, + onHover : { + bg : String(defaultColours.slate10), + text : String(customColours.hue) + }, + isSelected : { + bg : String(defaultColours.white), + border : String(customColours.hue), + text : String(customColours.hue) + }, + hasAlert : { + bg : String(defaultColours.red70) + } + }, + + subLinks : { + header : { + weight : 600 + }, + default : { + bg : String(defaultColours.white), + text : lighten(0.24, String(customColours.shade)), + weight : 400, + scale : 92 + }, + onHover : { + bg : String(defaultColours.slate10), + text : String(customColours.hue) + }, + chevron : { + border : String(defaultColours.slate40) + } + }, + }, + + footer : { + height : "32px", + bg : String(defaultColours.white), + borderTop : String(defaultColours.slate10) + } + }, + + + // TABLE //////////////////////////////////////////////////////////////// + table : { + bg : String(defaultColours.white), + text : String(customColours.shade), + border : String(defaultColours.slate40), + striped : { + header : { + bg : String(defaultColours.blue40) + }, + cell : { + bg : String(defaultColours.slate20) + } + }, + onHover : { + bg : String(defaultColours.amber20), + text : String(customColours.shade) + } + }, + + tablePagination : { + bg : String(defaultColours.white), + text : String(defaultColours.grey), + svg : { + onHover : { + stroke : String(defaultColours.slate60) + } + } + }, + + + // TABS //////////////////////////////////////////////////////////////// + tabs : { + label : { + default : { + text : lighten(0.16, String(defaultColours.grey)) + }, + onHover : { + text : lighten(0.16, String(customColours.hue)) + }, + isActive : { + border : String(customColours.hue), + text : String(customColours.hue) + }, + isDisabled : { + text : darken(0.24, String(defaultColours.slate)) + }, + hasAlert : { + circle : { + bg : String(defaultColours.red90), + border : String(defaultColours.slate10) + } + } + } + }, + + + // TEXT ///////////////////////////////////////////////////////////////// + text : { + font : { + sans : "sans-serif", + serif : "serif", + mono : "monospace" + }, + + paras : { + font : "sans-serif", + size : 1, + color : String(defaultColours.grey), + weight : 400, + lineHeight : 1.64 + }, + + headings : { + font : "sans-serif", + color : String(customColours.shade), + weight : 600, + multiplier : 1.24, + lineHeight : 1.24 + }, + + links : { + font : "sans-serif", + default : { + color : String(defaultColours.blue90) + }, + onHover : { + color : String(defaultColours.blue60) + } + }, + + selection : { + bg : String(customColours.hue), + text : String(defaultColours.white) + }, + + code : { + inline : { + bg : String(defaultColours.blue10), + text : String(defaultColours.blue90), + scale : 80 + }, + block : { + bg : lighten(0.02, String(defaultColours.slate10)), + text : String(defaultColours.blue70), + scale : 80, + lineHeight : 1.8 + }, + prism : { + tokens : { + tag : String(defaultColours.violet), + atrule : String(defaultColours.teal90), + attrName : String(defaultColours.orange), + attrValue : String(defaultColours.green80), + boolean : String(defaultColours.green80), + cdata : String(defaultColours.grey70), + className : String(defaultColours.red), + comment : String(defaultColours.grey70), + constant : String(defaultColours.green80), + deleted : String(defaultColours.slate80), + delimiter : String(defaultColours.grey90), + doctype : String(defaultColours.grey90), + entity : String(defaultColours.green80), + function : String(defaultColours.orange), + hexcode : String(defaultColours.green), + inserted : String(defaultColours.green80), + italic : String(defaultColours.green80), + keyword : String(defaultColours.orange90), + namespace : String(defaultColours.green80), + number : String(defaultColours.green80), + operator : String(defaultColours.pistachio), + plain : String(defaultColours.grey), + prolog : String(defaultColours.grey90), + property : String(defaultColours.red90), + punctuation : String(defaultColours.grey60), + regex : String(defaultColours.green80), + selector : String(defaultColours.violet), + string : String(defaultColours.crimson60), + symbol : String(defaultColours.green80), + url : String(defaultColours.green80), + variable : String(defaultColours.orange80) + }, + + languages : { + css : { + fallback : String(defaultColours.orange90) + }, + html : { + fallback : String(defaultColours.grey) + }, + js : { + fallback : String(defaultColours.violet90) + }, + json : { + fallback : String(defaultColours.teal), + tokens : { + string : String(defaultColours.teal) + } + } + } + } + }, + + kbd : { + text : String(defaultColours.grey), + bg : String(defaultColours.grey10) + } + }, + + spinner: { + color: String(defaultColours.teal), + } +} +`; diff --git a/src/pages/03ThemeBuilder/ThemeEditor.styled.tsx b/src/pages/03ThemeBuilder/ThemeEditor.styled.tsx new file mode 100644 index 0000000..7c40140 --- /dev/null +++ b/src/pages/03ThemeBuilder/ThemeEditor.styled.tsx @@ -0,0 +1,5 @@ +import styled from "styled-components"; + + +export const ThemeEditorStyled = styled.article` +` diff --git a/src/pages/03ThemeBuilder/ThemeEditor.tsx b/src/pages/03ThemeBuilder/ThemeEditor.tsx new file mode 100644 index 0000000..3c7e2e4 --- /dev/null +++ b/src/pages/03ThemeBuilder/ThemeEditor.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useRef, useState } from "react"; +import { + Element, + Portion, + Row, + CodeBlock, + Heading, + Text, Card +} from "fictoan-react"; + +import { ThemeEditorStyled } from "./ThemeEditor.styled"; +import { FictoanThemeOutputSample } from "./Result"; +import { Editor } from "./Editor"; +import {STRINGIFIED_FICTOAN_THEME} from "./Theme"; +import { evaluateJS, performDiff } from "./CodeManipulation"; + + +export const ThemeEditorDocs = () => { + useEffect(() => { + document.title = "Theme editor — Fictoan"; + + try { + window.scroll({ + top : 0, + left : 0 + }); + } catch (error) { + window.scrollTo(0, 0); + } + }, []); + + const [json, setJson] = useState(() => evaluateJS(STRINGIFIED_FICTOAN_THEME)); + const [min, setMin] = useState(""); + + return ( + + + + Theme editor + + Use this page to edit the default theme that comes with Fictoan to suit your needs. + + + + + + + + + + {/**/} + + + + + + + + ) +} diff --git a/src/pages/03ThemeBuilder/Visitor.ts b/src/pages/03ThemeBuilder/Visitor.ts new file mode 100644 index 0000000..8bcb18f --- /dev/null +++ b/src/pages/03ThemeBuilder/Visitor.ts @@ -0,0 +1,179 @@ +/** + * Acorn AST node visitor + */ + +import acorn, { Parser, defaultOptions } from "acorn"; + +interface ProgramNode extends acorn.Node { + body: acorn.Node[] +} +interface VariableDeclarationNode extends acorn.Node { + declarations: VariableDeclaratorNode[] +} +interface VariableDeclaratorNode extends acorn.Node { + id: IdentifierNode, + init: any +} +interface IdentifierNode extends acorn.Node { + name: string; +} +interface LiteralNode extends acorn.Node { + value: String; +} +interface ObjectExpressionNode extends acorn.Node { + properties: ObjectPropertyNode[] +} +interface ObjectPropertyNode extends acorn.Node { + key: IdentifierNode; + value: ObjectExpressionNode | IdentifierNode; +} +interface MemberExpressionNode extends acorn.Node { + object: MemberExpressionNode | IdentifierNode; + property: IdentifierNode; +} +interface CallExpressionNode extends acorn.Node { + arguments: acorn.Node[]; + callee: IdentifierNode; +} + + + +export class Visitor { + constructor() { + this.visitNode = this.visitNode.bind(this) + } + visitVariableDeclaration(node: VariableDeclarationNode) { + return this.visitNodes(node.declarations); + } + visitVariableDeclarator(node: VariableDeclaratorNode) { + this.visitNode(node.id); + this.visitNode(node.init); + return node.init; + } + visitIdentifier(node: IdentifierNode) { + return node.name + } + visitLiteral(node: LiteralNode) { + return node.value; + } + visitMemberExpression(node: MemberExpressionNode): any { + return [this.visitNode(node.object), this.visitNode(node.property)]; + } + visitObjectExpression(node: ObjectExpressionNode): any { + return node.properties.map(this.visitNode); + } + visitObjectProperty(node: ObjectPropertyNode) { + this.visitNode(node.key); + this.visitNode(node.value); + } + visitCallExpression(node: CallExpressionNode) { + this.visitNode(node.callee); + this.visitNodes(node.arguments); + } + visitProgram(node: ProgramNode): any { + return node.body.map(this.visitNode) + } + visitNodes(nodes: acorn.Node[]): any { + return nodes.map(this.visitNode); + } + visitNode(node: acorn.Node) { + if (node.type == 'Program') + return this.visitProgram(node as ProgramNode); + if (node.type == 'VariableDeclaration') + return this.visitVariableDeclaration(node as VariableDeclarationNode); + if (node.type == 'VariableDeclarator') + return this.visitVariableDeclarator(node as VariableDeclaratorNode); + if (node.type == 'MemberExpression') + return this.visitMemberExpression(node as MemberExpressionNode) + if (node.type == 'ObjectExpression') + return this.visitObjectExpression(node as ObjectExpressionNode); + if (node.type == 'Property') + return this.visitObjectProperty(node as ObjectPropertyNode); + if (node.type == 'Identifier') + return this.visitIdentifier(node as IdentifierNode); + if (node.type == 'Literal') + return this.visitLiteral(node as LiteralNode); + } +} + + + +export class ThemeVisitor extends Visitor { + key: string = ''; + sourceCodeString: string = ''; + astRoot: acorn.Node; + constructor(sourceCodeString: string) { + super(); + this.sourceCodeString = sourceCodeString; + this.astRoot = Parser.parse(sourceCodeString, defaultOptions); + } + __toString(node: acorn.Node) { + return this.sourceCodeString.substring(node.start, node.end); + } + visitProgram(node: ProgramNode) { + return super.visitProgram(node).filter(filterOut)[0] + } + visitObjectExpression(node: ObjectExpressionNode): any { + return super.visitObjectExpression(node).filter(filterOut)[0] + } + visitNodes(nodes: acorn.Node[]) { + return super.visitNodes(nodes).filter(filterOut)[0] + } + visitVariableDeclarator(node: VariableDeclaratorNode) { + if (node.id.name == this.key) + return node.init; + } + visitObjectProperty(node: ObjectPropertyNode) { + if (node.key.name == this.key) + return node.value + } + + visitCallExpression(node: CallExpressionNode) { + return this.__toString(node); + } + visitMemberExpression(node: MemberExpressionNode) { + return this.__toString(node); + } + + visitPath(node: acorn.Node, path: string[]): any { + if (path.length == 0) + return node; + this.key = path.shift()!; + const nextNode = this.visitNode(node); + return this.visitPath(nextNode, path); + } + getValueAtPath(path: string[]): string { + const node = this.visitPath(this.astRoot, path); + return this.__toString(node); + } +} + +export class JSVisitor extends Visitor { + sourceCodeString: string = ''; + astRoot: acorn.Node; + constructor(sourceCodeString: string) { + super(); + this.sourceCodeString = sourceCodeString; + this.astRoot = Parser.parse(sourceCodeString, defaultOptions); + } + __toString(node: acorn.Node) { + return this.sourceCodeString.substring(node.start, node.end); + } + visitVariableDeclaration(node: VariableDeclarationNode) { + return this.__toString(node); + } + getAllVariableDeclarations(){ + return this.visitNode(this.astRoot).filter((declaration:string) => !declaration.startsWith("const Theme")) + } +} + +function filterOut(val: Array | any) { + if (val == undefined) + return false; + if (val == []) + return false; + if (val.map) { + return val.filter(filterOut).length == 0 ? false : true + } + return true; +} \ No newline at end of file diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts new file mode 100644 index 0000000..6431bc5 --- /dev/null +++ b/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a273b0c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +}