From cc4a0dc27d8f9ebe4765c59e5256fbaba784dc95 Mon Sep 17 00:00:00 2001 From: Shaun Hamilton Date: Thu, 14 Sep 2023 10:05:51 +0000 Subject: [PATCH] use bun to build --- .gitpod.yml | 2 +- lib/babeliser/index.ts | 184 +++++++++++++++++++++++++++++++++++++++++ lib/index.ts | 2 +- package-lock.json | 128 ++++++++++++++++++++++++---- package.json | 16 ++-- tsconfig.json | 3 +- 6 files changed, 306 insertions(+), 29 deletions(-) create mode 100644 lib/babeliser/index.ts diff --git a/.gitpod.yml b/.gitpod.yml index 30c7b74..2d04a51 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,2 +1,2 @@ tasks: - - init: npm install && npm run build + - init: curl -fsSL https://bun.sh/install | bash && npm install diff --git a/lib/babeliser/index.ts b/lib/babeliser/index.ts new file mode 100644 index 0000000..d17b8fd --- /dev/null +++ b/lib/babeliser/index.ts @@ -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; +type ScopedStatement = Statement & { scope: Scope }; + +export class Babeliser { + public parsedCode: ReturnType; + private maxScopeDepth = 4; + public codeString: string; + constructor( + codeString: string, + options?: Partial + ) { + this.parsedCode = parse(codeString, { + sourceType: "module", + ...options, + }); + if (options?.maxScopeDepth) { + this.maxScopeDepth = options.maxScopeDepth; + } + this.codeString = codeString; + } + public getArrowFunctionExpressions() { + const arrowFunctionDeclarations = + this._recurseBodiesForType( + "ArrowFunctionExpression" + ); + return arrowFunctionDeclarations; + } + public getExpressionStatements() { + const expressionStatements = + this._recurseBodiesForType("ExpressionStatement"); + return expressionStatements; + } + public getFunctionDeclarations() { + const functionDeclarations = + this._recurseBodiesForType("FunctionDeclaration"); + return functionDeclarations; + } + public getImportDeclarations() { + const expressionStatements = + this._recurseBodiesForType("ImportDeclaration"); + return expressionStatements; + } + public getType(type: string) { + return this._recurseBodiesForType(type); + } + public getVariableDeclarations() { + const variableDeclarations = + this._recurseBodiesForType("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(type: string): Array { + 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 + ): 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; + } +} diff --git a/lib/index.ts b/lib/index.ts index 5ead34d..423d9ee 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -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 }; diff --git a/package-lock.json b/package-lock.json index 353949e..ac7bc24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,25 @@ { "name": "@freecodecamp/curriculum-helpers", - "version": "1.0.5", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@freecodecamp/curriculum-helpers", - "version": "1.0.5", + "version": "1.1.0", "license": "BSD-3-Clause", "dependencies": { - "babeliser": "github:ShaunSHamilton/babeliser#feat_bun", + "@babel/generator": "7.22.15", + "@babel/parser": "7.22.16", "browserify": "^17.0.0" }, "devDependencies": { "@babel/core": "7.22.17", "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", + "@babel/types": "7.22.17", "@types/jest": "27.5.2", + "bun": "1.0.1", "chai": "4.3.6", "eslint": "6.8.0", "eslint-config-prettier": "6.15.0", @@ -31,11 +34,6 @@ }, "engines": { "npm": ">= 4.0.0" - }, - "peerDependencies": { - "@babel/generator": "7.X", - "@babel/parser": "7.X", - "@babel/types": "7.X" } }, "node_modules/@ampproject/remapping": { @@ -2538,6 +2536,84 @@ "node": ">= 8" } }, + "node_modules/@oven/bun-darwin-aarch64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.0.1.tgz", + "integrity": "sha512-73YfDA7+ZRQc/U3d0bjeJVsvSg4UVOhOadlRsZtRN8OYew1fshx91DZnfNBGjLMQP0hG8bk5PxGWbKmqp7MsJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.0.1.tgz", + "integrity": "sha512-hg638foFQdmo0+Jri6LAQhhpcbaE3Fb2youY1m/iFhVhH6Xuzt8izAgq30imT9qfd+FlcY8bRnjJN8uSGrvQrw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64-baseline": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.0.1.tgz", + "integrity": "sha512-p28kziO67RMjhCiWbao9iCFUJaxmbhfPzy4nNEve7nGXJVB4wMq9Hr+DNp1VYbEyt1auXowF2+u4v3zDlM/ILw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-linux-aarch64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.0.1.tgz", + "integrity": "sha512-XvjyARKiIWZ5RAHT3oZtjbZZGf/bJFCDAAJxM4mxMjZKqmulWb+erHyylcXDb0tmnJ2uGWDHio4lnN5PoDs7hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.0.1.tgz", + "integrity": "sha512-ZVg0wd8px/j0fB61xGDR9vqmpjlWKglbjM7YZbp/OwDXRrEzLwZ+COMzauVCWojRiyWx9VQja1cjpfWBlKfF2g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-baseline": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.0.1.tgz", + "integrity": "sha512-mwzDqoZnqwJXNsTN4q9FHnaPaTwFwRSCvxzkn8PqV2mKkOF4H+KUR/VZuZ18lYlixD7hCj1plbpx/B59pp293g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@samverschueren/stream-to-observable": { "version": "0.3.1", "dev": true, @@ -3315,15 +3391,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/babeliser": { - "version": "0.7.0", - "resolved": "git+ssh://git@github.com/ShaunSHamilton/babeliser.git#af10ea9625064e56f206154d7b950ada3cce0917", - "license": "MIT", - "peerDependencies": { - "@babel/generator": "7.x", - "@babel/parser": "7.x" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" @@ -3610,6 +3677,33 @@ "version": "3.0.0", "license": "MIT" }, + "node_modules/bun": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bun/-/bun-1.0.1.tgz", + "integrity": "sha512-rdrGbCuDKrwidZ+I05k127JyvKiLIsIov1/Qcf1u1s0zv+e1csjA/7cSOQ1AjmdZ1sqm0VFx/N1rccZ91SQylw==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "hasInstallScript": true, + "os": [ + "darwin", + "linux" + ], + "bin": { + "bun": "bin/bun", + "bunx": "bin/bun" + }, + "optionalDependencies": { + "@oven/bun-darwin-aarch64": "1.0.1", + "@oven/bun-darwin-x64": "1.0.1", + "@oven/bun-darwin-x64-baseline": "1.0.1", + "@oven/bun-linux-aarch64": "1.0.1", + "@oven/bun-linux-x64": "1.0.1", + "@oven/bun-linux-x64-baseline": "1.0.1" + } + }, "node_modules/cached-path-relative": { "version": "1.1.0", "license": "MIT" diff --git a/package.json b/package.json index b2be268..50b8092 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@freecodecamp/curriculum-helpers", - "version": "1.0.5", + "version": "1.1.0", "description": "Helper functions to test challenges in freeCodecamp's curriculum", "homepage": "https://freecodecamp.org", "author": { @@ -19,7 +19,9 @@ "@babel/core": "7.22.17", "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", + "@babel/types": "7.22.17", "@types/jest": "27.5.2", + "bun": "1.0.1", "chai": "4.3.6", "eslint": "6.8.0", "eslint-config-prettier": "6.15.0", @@ -76,20 +78,16 @@ ] }, "scripts": { - "build": "tsc", + "build": "bun build lib/index.ts --outdir dist --minify --sourcemap=external && tsc", "test": "jest", - "prepublishOnly": "tsc" + "prepublishOnly": "npm run build" }, "type": "module", "repository": "git@github.com:freeCodeCamp/curriculum-helpers.git", "license": "BSD-3-Clause", "dependencies": { - "babeliser": "github:ShaunSHamilton/babeliser#feat_bun", + "@babel/generator": "7.22.15", + "@babel/parser": "7.22.16", "browserify": "^17.0.0" - }, - "peerDependencies": { - "@babel/generator": "7.X", - "@babel/parser": "7.X", - "@babel/types": "7.X" } } diff --git a/tsconfig.json b/tsconfig.json index 2793e3a..de0f9ce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "declaration": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "moduleResolution": "NodeNext" + "moduleResolution": "NodeNext", + "emitDeclarationOnly": true } }