Skip to content
This repository has been archived by the owner on Sep 4, 2023. It is now read-only.

Commit

Permalink
feat: add BoxContext with flags + eval callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
astahmer committed Mar 2, 2023
1 parent 77bec81 commit 10ffcb0
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 177 deletions.
5 changes: 5 additions & 0 deletions .changeset/twenty-insects-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@box-extractor/core": patch
---

feat: add BoxContext with flags + eval callbacks
17 changes: 9 additions & 8 deletions packages/box-extractor/src/extractor/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createLogger } from "@box-extractor/logger";
import { evaluate } from "ts-evaluator";
import type { Expression } from "ts-morph";
import { ts } from "ts-morph";
import { ts, Node } from "ts-morph";
import type { BoxContext } from "./types";

const TsEvalError = Symbol("EvalError");
const logger = createLogger("box-ex:extractor:evaluator");
Expand All @@ -15,17 +16,16 @@ const cacheMap = new WeakMap<Expression, unknown>();
* Evaluates with strict policies restrictions
* @see https://github.com/wessberg/ts-evaluator#setting-up-policies
*/
export const evaluateNode = (node: Expression) => {
// console.trace();
// return;
const compilerNode = node.compilerNode;
export const evaluateNode = (node: Expression, stack: Node[], ctx: BoxContext) => {
if (ctx.flags?.skipEvaluate) return;
if (ctx.canEval && !ctx.canEval?.(node, stack)) return;

if (cacheMap.has(node)) {
return cacheMap.get(node);
}

const result = evaluate({
node: compilerNode as any,
node: node.compilerNode as any,
// TODO only with a flag
// typeChecker: node.getProject().getTypeChecker().compilerObject as any,
typescript: ts as any,
Expand All @@ -41,6 +41,7 @@ export const evaluateNode = (node: Expression) => {
environment: {
preset: envPreset.startsWith("__REPLACE_ME_") ? "NODE" : (envPreset as any),
},
...ctx.getEvaluateOptions?.(node, stack),
});

logger({ compilerNodeKind: node.getKindName() });
Expand Down Expand Up @@ -75,8 +76,8 @@ export const evaluateNode = (node: Expression) => {
return expr;
};

export const safeEvaluateNode = <T>(node: Expression) => {
const result = evaluateNode(node);
export const safeEvaluateNode = <T>(node: Expression, stack: Node[], ctx: BoxContext) => {
const result = evaluateNode(node, stack, ctx);
if (result === TsEvalError) return;

return result as T;
Expand Down
9 changes: 5 additions & 4 deletions packages/box-extractor/src/extractor/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ const getComponentName = (node: JsxOpeningElement | JsxSelfClosingElement) => {
return tagNameNode.getText();
};

export const extract = ({ ast, components, functions, extractMap = new Map() }: ExtractOptions) => {
export const extract = ({ ast, extractMap = new Map(), ...ctx }: ExtractOptions) => {
// contains all the extracted nodes from this ast parsing
// whereas `extractMap` is the global map that could be populated by this function in multiple `extract` calls
const localExtraction = new Map() as ExtractResultByName;
const queryComponentMap = new Map() as QueryComponentMap;

const visitedComponentFromSpreadList = new WeakSet<Node>();
const { components, functions } = ctx;

ast.forEachDescendant((node, traversal) => {
// quick win
Expand Down Expand Up @@ -92,7 +93,7 @@ export const extract = ({ ast, components, functions, extractMap = new Map() }:

const matchProp = ({ propName, propNode }: MatchPropArgs) =>
components.matchProp({ tagNode: componentNode, tagName: componentName, propName, propNode });
const spreadNode = extractJsxSpreadAttributeValues(node, matchProp as any);
const spreadNode = extractJsxSpreadAttributeValues(node, ctx, matchProp as any);
const parentRef = queryComponentMap.get(componentNode)!;

// increment count since there might be conditional
Expand Down Expand Up @@ -193,7 +194,7 @@ export const extract = ({ ast, components, functions, extractMap = new Map() }:
}
// console.log({ componentName, propName });

const maybeBox = extractJsxAttribute(node);
const maybeBox = extractJsxAttribute(node, ctx);
if (!maybeBox) return;

logger({ propName, maybeBox });
Expand Down Expand Up @@ -250,7 +251,7 @@ export const extract = ({ ast, components, functions, extractMap = new Map() }:
const localList = localFnMap.queryList;
// console.log(componentName, componentMap);

const nodeList = extractCallExpressionArguments(node, matchProp, functions.matchArg).value.map(
const nodeList = extractCallExpressionArguments(node, ctx, matchProp, functions.matchArg).value.map(
(boxNode) => {
if (boxNode.isObject() || boxNode.isMap()) {
const map = castObjectLikeAsMapValue(boxNode, node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { maybeBoxNode } from "./maybeBoxNode";

import { maybeObjectLikeBox } from "./maybeObjectLikeBox";
import { box } from "./type-factory";
import type { MatchFnArgs, MatchFnArguments, MatchFnPropArgs } from "./types";
import type { BoxContext, MatchFnArgs, MatchFnArguments, MatchFnPropArgs } from "./types";
import { unwrapExpression } from "./utils";

const logger = createLogger("box-ex:extractor:call-expr");

export const extractCallExpressionArguments = (
node: CallExpression,
ctx: BoxContext,
matchProp: (prop: MatchFnPropArgs) => boolean = () => true,
matchArg: (prop: MatchFnArgs & MatchFnArguments) => boolean = () => true
) => {
Expand All @@ -27,13 +28,13 @@ export const extractCallExpressionArguments = (

const stack = [node, argNode] as Node[];

const maybeValue = maybeBoxNode(argNode, stack);
const maybeValue = maybeBoxNode(argNode, stack, ctx);
logger({ extractCallExpression: true, maybeValue });
if (maybeValue) {
return maybeValue;
}

const maybeObject = maybeObjectLikeBox(argNode, stack, matchProp);
const maybeObject = maybeObjectLikeBox(argNode, stack, ctx, matchProp);
logger({ maybeObject });
if (maybeObject) return maybeObject;

Expand Down
7 changes: 4 additions & 3 deletions packages/box-extractor/src/extractor/extractJsxAttribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { Node } from "ts-morph";
import { maybeBoxNode } from "./maybeBoxNode";
import { maybeObjectLikeBox } from "./maybeObjectLikeBox";
import { box } from "./type-factory";
import type { BoxContext } from "./types";
import { unwrapExpression } from "./utils";

const logger = createLogger("box-ex:extractor:jsx-attr");

export const extractJsxAttribute = (jsxAttribute: JsxAttribute) => {
export const extractJsxAttribute = (jsxAttribute: JsxAttribute, ctx: BoxContext) => {
// <ColorBox color="red.200" backgroundColor="blackAlpha.100" />
// ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// identifier = `color` (and then backgroundColor)
Expand All @@ -33,14 +34,14 @@ export const extractJsxAttribute = (jsxAttribute: JsxAttribute) => {
if (!expression) return;

stack.push(expression);
const maybeValue = maybeBoxNode(expression, stack);
const maybeValue = maybeBoxNode(expression, stack, ctx);
logger({ extractJsx: true, maybeValue });
// !maybeValue && console.log("maybeBoxNode empty", expression.getKindName(), expression.getText());
if (maybeValue) {
return maybeValue;
}

const maybeObject = maybeObjectLikeBox(expression, stack);
const maybeObject = maybeObjectLikeBox(expression, stack, ctx);
logger({ maybeObject });
// console.log("expr", expression.getKindName(), expression.getText());
if (maybeObject) return maybeObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ import type { JsxSpreadAttribute, Node } from "ts-morph";
import { maybeBoxNode } from "./maybeBoxNode";
import { maybeObjectLikeBox } from "./maybeObjectLikeBox";
import { box } from "./type-factory";
import type { MatchFnPropArgs, MatchPropArgs } from "./types";
import type { BoxContext, MatchFnPropArgs, MatchPropArgs } from "./types";
import { unwrapExpression } from "./utils";

const logger = createLogger("box-ex:extractor:jsx-spread");

export const extractJsxSpreadAttributeValues = (
spreadAttribute: JsxSpreadAttribute,
ctx: BoxContext,
matchProp: (prop: MatchFnPropArgs | MatchPropArgs) => boolean
) => {
const node = unwrapExpression(spreadAttribute.getExpression());
logger.scoped("extractJsxSpreadAttributeValues", { node: node.getKindName() });

const stack = [] as Node[];
const maybeValue = maybeBoxNode(node, stack);
const maybeValue = maybeBoxNode(node, stack, ctx);
if (maybeValue) {
if (maybeValue.isMap() || maybeValue.isObject() || maybeValue.isUnresolvable() || maybeValue.isConditional()) {
return maybeValue;
Expand All @@ -28,7 +29,7 @@ export const extractJsxSpreadAttributeValues = (
}
}

const maybeEntries = maybeObjectLikeBox(node, stack, matchProp);
const maybeEntries = maybeObjectLikeBox(node, stack, ctx, matchProp);
logger({ maybeEntries });
if (maybeEntries) return maybeEntries;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createLogger } from "@box-extractor/logger";
import { Identifier, Node } from "ts-morph";
// eslint-disable-next-line import/no-cycle
import { getExportedVarDeclarationWithName, getModuleSpecifierSourceFile } from "./maybeBoxNode";
import type { BoxContext } from "./types";

const logger = createLogger("box-extractor:extractor:findIdentifierValueDeclaration");

Expand All @@ -18,7 +19,7 @@ export function isScope(node: Node): boolean {
);
}

export function getDeclarationFor(node: Identifier, stack: Node[] = []) {
export function getDeclarationFor(node: Identifier, stack: Node[], ctx: BoxContext) {
const parent = node.getParent();
if (!parent) {
return;
Expand All @@ -38,12 +39,14 @@ export function getDeclarationFor(node: Identifier, stack: Node[] = []) {
declarationStack.push(parent);
declaration = parent;
} else if (Node.isImportSpecifier(parent) && parent.getNameNode() == node) {
if (ctx.flags?.skipTraverseFiles) return;

const sourceFile = getModuleSpecifierSourceFile(parent.getImportDeclaration());
logger.scoped("getDeclarationFor", { isImportDeclaration: true, sourceFile: Boolean(sourceFile) });

if (sourceFile) {
const exportStack = [parent, sourceFile] as Node[];
const maybeVar = getExportedVarDeclarationWithName(node.getText(), sourceFile, exportStack);
const maybeVar = getExportedVarDeclarationWithName(node.getText(), sourceFile, exportStack, ctx);

logger.scoped("getDeclarationFor", {
from: sourceFile.getFilePath(),
Expand Down Expand Up @@ -86,7 +89,8 @@ const getInnermostScope = (from: Node) => {

export function findIdentifierValueDeclaration(
identifier: Identifier,
stack: Node[] = [],
stack: Node[],
ctx: BoxContext,
visitedsWithStack: WeakMap<Node, Node[]> = new Map()
): ReturnType<typeof getDeclarationFor> | undefined {
let scope = identifier as Node | undefined;
Expand Down Expand Up @@ -119,7 +123,7 @@ export function findIdentifierValueDeclaration(

if (Node.isIdentifier(node) && node.getText() == refName) {
const declarationStack = [node] as Node[];
const maybeDeclaration = getDeclarationFor(node, declarationStack);
const maybeDeclaration = getDeclarationFor(node, declarationStack, ctx);
if (maybeDeclaration) {
if (Node.isParameterDeclaration(maybeDeclaration)) {
const initializer = maybeDeclaration.getInitializer();
Expand Down
Loading

0 comments on commit 10ffcb0

Please sign in to comment.