Skip to content

Commit

Permalink
use bun to build
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaunSHamilton committed Sep 14, 2023
1 parent 1166f27 commit cc4a0dc
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
tasks:
- init: npm install && npm run build
- init: curl -fsSL https://bun.sh/install | bash && npm install
184 changes: 184 additions & 0 deletions lib/babeliser/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { parse, ParserOptions } from "@babel/parser";
import generate, { GeneratorOptions } from "@babel/generator";
import {
ArrowFunctionExpression,
ExpressionStatement,
FunctionDeclaration,
Identifier,
ImportDeclaration,
is,
Node,
VariableDeclaration,
Statement,
} from "@babel/types";

type BabeliserOptions = { maxScopeDepth: number };
type Scope = Array<string>;
type ScopedStatement = Statement & { scope: Scope };

export class Babeliser {
public parsedCode: ReturnType<typeof parse>;
private maxScopeDepth = 4;
public codeString: string;
constructor(
codeString: string,
options?: Partial<ParserOptions & BabeliserOptions>
) {
this.parsedCode = parse(codeString, {
sourceType: "module",
...options,
});
if (options?.maxScopeDepth) {
this.maxScopeDepth = options.maxScopeDepth;
}
this.codeString = codeString;
}
public getArrowFunctionExpressions() {
const arrowFunctionDeclarations =
this._recurseBodiesForType<ArrowFunctionExpression>(
"ArrowFunctionExpression"
);
return arrowFunctionDeclarations;
}
public getExpressionStatements() {
const expressionStatements =
this._recurseBodiesForType<ExpressionStatement>("ExpressionStatement");
return expressionStatements;
}
public getFunctionDeclarations() {
const functionDeclarations =
this._recurseBodiesForType<FunctionDeclaration>("FunctionDeclaration");
return functionDeclarations;
}
public getImportDeclarations() {
const expressionStatements =
this._recurseBodiesForType<ImportDeclaration>("ImportDeclaration");
return expressionStatements;
}
public getType<T>(type: string) {
return this._recurseBodiesForType<T>(type);
}
public getVariableDeclarations() {
const variableDeclarations =
this._recurseBodiesForType<VariableDeclaration>("VariableDeclaration");
return variableDeclarations;
}

public getExpressionStatement(
name: string,
scope: Scope = ["global"]
): (ExpressionStatement & { scope: Scope }) | undefined {
const expressionStatements = this.getExpressionStatements().filter((a) =>
this._isInScope(a.scope, scope)
);
const expressionStatement = expressionStatements.find((e) => {
const expression = e.expression;
if (is("CallExpression", expression)) {
if (name.includes(".")) {
const [objectName, methodName] = name.split(".");
const memberExpression = expression.callee;
if (is("MemberExpression", memberExpression)) {
const object = memberExpression.object;
const property = memberExpression.property;
if (is("Identifier", object) && is("Identifier", property)) {
return object.name === objectName && property.name === methodName;
}
}
}
const identifier = expression.callee;
if (is("Identifier", identifier) && identifier.name === name) {
return true;
}
}
if (is("AwaitExpression", expression)) {
const callExpression = expression.argument;
if (is("CallExpression", callExpression)) {
const identifier = callExpression.callee;
if (is("Identifier", identifier)) {
return identifier.name === name;
}
}
}
return false;
});
return expressionStatement;
}

public generateCode(ast: Node, options?: GeneratorOptions) {
// @ts-ignore TypeScript is confused
return generate(ast, options).code;
}

public getLineAndColumnFromIndex(index: number) {
const linesBeforeIndex = this.codeString.slice(0, index).split("\n");
const line = linesBeforeIndex.length;
const column = linesBeforeIndex.pop()?.length;
return { line, column };
}

private _isInScope(scope: Scope, targetScope: Scope = ["global"]): boolean {
if (targetScope.length === 1 && targetScope[0] === "global") {
return true;
}
if (scope.length < targetScope.length) {
return false;
}
const scopeString = scope.join(".");
const targetScopeString = targetScope.join(".");
return scopeString.includes(targetScopeString);
}

private _recurseBodiesForType<T>(type: string): Array<T & { scope: Scope }> {
const body = this.parsedCode.program.body;
const types = [];
for (const statement of body) {
const a = this._recurse(statement, (a) => a?.type === type, ["global"]);
if (a?.length) {
types.push(...a);
}
}
// @ts-ignore There is no easy way to type this without writing out constraining to the 40+ types
return types;
}

private _recurse(
// this is kind of a hack, since we're mutating val. It needs to be able to
// have a scope parameter, though it's never passed in with one.
val: Statement & { scope?: Scope },
isTargetType: (...args: any) => boolean,
scope: Array<string>
): ScopedStatement[] {
const matches: ScopedStatement[] = [];
if (scope.length >= this.maxScopeDepth) {
return matches;
}
if (val && typeof val === "object") {
if (!Array.isArray(val)) {
val.scope = scope;
}
if (isTargetType(val)) {
// @ts-ignore See `val` parameter
matches.push(val);
}

let currentScope = [...scope];
const nearestIdentifier: undefined | Identifier = Object.values(val).find(
(v) => v?.type === "Identifier"
);
if (nearestIdentifier) {
currentScope.push(nearestIdentifier.name);
}

for (const v of Object.values(val)) {
const mat = this._recurse(v, isTargetType, currentScope);
const toPush = mat?.filter(Boolean).flat();
if (toPush?.length) {
matches.push(...toPush.flat());
}
}
}

// @ts-ignore See `val` parameter
return matches;
}
}
2 changes: 1 addition & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { strip } from "./strip.js";
import { Babeliser } from "babeliser";
import { Babeliser } from "./babeliser/index.js";
import { CSSHelp } from "./css-help/index.js";

export { Babeliser, CSSHelp };
Expand Down
128 changes: 111 additions & 17 deletions package-lock.json

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

Loading

0 comments on commit cc4a0dc

Please sign in to comment.