Full Code of digital-loukoum/esrun for AI

main 10ad7f7602d3 cached
42 files
41.8 KB
12.1k tokens
33 symbols
1 requests
Download .txt
Repository: digital-loukoum/esrun
Branch: main
Commit: 10ad7f7602d3
Files: 42
Total size: 41.8 KB

Directory structure:
gitextract_3nngibti/

├── .claude/
│   └── settings.json
├── .github/
│   └── workflows/
│       ├── claude.yml
│       └── npm-publish.yaml
├── .gitignore
├── .vscode/
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── scripts/
│   ├── build.js
│   ├── deploy.js
│   └── utilities/
│       └── bumpVersion.js
├── source/
│   ├── bin.ts
│   ├── index.ts
│   ├── plugins/
│   │   └── fileConstants.ts
│   ├── runners/
│   │   ├── Runner.ts
│   │   └── Watcher.ts
│   ├── tools/
│   │   ├── findInputFile.ts
│   │   ├── importRequire.ts
│   │   ├── onExit.ts
│   │   └── resolveDependency.ts
│   └── types/
│       ├── CliOption.ts
│       ├── Options.ts
│       ├── Parameter.ts
│       └── SendCodeMode.ts
├── test/
│   ├── index.ts
│   ├── readline.js
│   ├── readline.ts
│   ├── resolvedAlias/
│   │   └── foo.ts
│   ├── samples/
│   │   ├── Zabu.ts
│   │   ├── backticksInComments.ts
│   │   ├── coco.ts
│   │   ├── dotImport/
│   │   │   ├── dotImport.ts
│   │   │   ├── doubleDotImport/
│   │   │   │   └── index.ts
│   │   │   └── index.ts
│   │   ├── fileConstants.ts
│   │   ├── ipc/
│   │   │   └── child.ts
│   │   └── shebang.ts
│   ├── server/
│   │   ├── index.ts
│   │   └── message.ts
│   └── tsconfig.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .claude/settings.json
================================================
{
  "permissions": {
    "allow": [
      "WebFetch",
      "Bash"
    ],
    "deny": []
  }
}

================================================
FILE: .github/workflows/claude.yml
================================================
name: Claude PR Assistant

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]
  pull_request_review:
    types: [submitted]

jobs:
  claude-code-action:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read
      issues: read
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Run Claude PR Action
        uses: anthropics/claude-code-action@beta
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          timeout_minutes: "60"


================================================
FILE: .github/workflows/npm-publish.yaml
================================================
name: NPM Package

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org/

      # install dependencies
      - uses: actions/cache@v4
        id: pnpm-cache
        with:
          path: ~/.pnpm-store
          key: ${{ runner.os }}-modules-${{ matrix.node-version }}-${{ hashFiles('**/package.json') }}
      - name: Install dependencies
        uses: pnpm/action-setup@v4
        with:
          version: 9
          run_install: true

      # build & test
      - name: Build
        run: npm run build # TODO: Change "build" to "test" when tests are ready
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

  publish:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org/

      # install dependencies
      - uses: actions/cache@v4
        id: pnpm-cache
        with:
          path: ~/.pnpm-store
          key: ${{ runner.os }}-modules-${{ matrix.node-version }}-${{ hashFiles('**/package.json') }}
      - name: Install dependencies
        uses: pnpm/action-setup@v4
        with:
          version: 9
          run_install: true

      # setup git config (for auto-version bumping)
      - name: Setup git config
        run: |
          git config user.name "GitHub actions"
          git config user.email "<>"

      # deploy to npm
      - name: Deploy
        run: npm run deploy
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}


================================================
FILE: .gitignore
================================================
node_modules/
package/

================================================
FILE: .vscode/settings.json
================================================
{
	"typescript.tsdk": "node_modules/typescript/lib"
}

================================================
FILE: CHANGELOG.md
================================================
## 3.2.27
- Move from `@digitak/esrun` to `esrun`.

## 3.2.25
- Fix `url` import that would conflict with user-defined url variables.


## 3.2.24
- Add `esuilbOptions` to the `esrun` function.

## 3.2.23
- Strip shebangs

## 3.2.22
- Don't use crypto anymore (not compatible for old node versions), use timestamp instead

## 3.2.21
- Use crypto.getRandomUUID() and remove deprecated cuid dependency
- Generate source maps for better debugging

## 3.2.20
- Fix temporary file creation for windows and old node versions

## 3.2.19
- Add sudo mode

## 3.2.18
- Update esbuild version from `0.14` to `0.17`

## 3.2.17
- Use latest grubber version that fixes comments with backticks

## 3.2.16
- Fix Windows bug when running esrun from a npm script

## 3.2.15
- Re-add used dependency on cuid

## 3.2.14
- Fix a rare bug where esbuild would need a require function. Use `createRequire()` from node to emulate the require function
- Remove unused dependency on cuid (is a dev dependency only)

## 3.2.13
- Fix a bug on temporary file mode if node_modules folder does not exist

## 3.2.12
- Fix usage with interactive CLI
- On windows, the default mode creates a temporary flie that is then executed

## 3.2.11
- Improve documentation for CLI parameters
- Add an error message when trying to use `--tsconfig` parameter with no value

## 3.2.10
- Fix file watching that would work only once on some OS
- CLI arguments are passed using the '=' instead of ':' (the colon still work for retro compatibility)
- You can now pass custom node's cli options by prefixing your option name with `--node-`. Example: `--node-max-old-space-size=4096`

## 3.2.9
- Fix an error with Windows when passing the code to node. Using stdin now instead of a cli argument. (thanks to **@vendethiel** for the fix)
- Remove error swallowing that could happen when the node process itself crashes
- Add a link to the changelog in the readme

## 3.2.8
- New strategy to detect external dependencies. Now check if paths are inside a parent `node_modules` directory instead of checking if the import start with ".", "/", "~"n "@/" or "$". The previous strategy used to fail for typescript aliases that didn't start with "@/", "~" or "$".

## 3.2.7
- Update EsBuild version to `0.14`

## 3.2.6
- Fix `.mts` and `.cjs` extensions
- Better file watching. Do not use custom plugin anymore but EsBuild's metafile
- Remove unused dependency `anymatch`
- Re-watching updated dependencies is cleaner and does not need a debounce anymore (though it is sill kept as it can be useful in some cases)

## 3.2.5
- Add `--tsconfig:path` cli option

## 3.2.4
- Add support for file constants `__dirname` and `__filename`

## 3.2.3
- Add a message when calling cli with no arguments
- Cli now catches esrun errors and log them instead of throwing

## 3.2.2
- Add `beforeRun` and `afterRun` events

## 3.2.1
- Add `preserveConsole` option to prevent console clear on watch mode
- Add CI/CD with version auto-bumping
- Make CLI options more extensible
- Starting this changelog 🎉


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Digital Loukoums

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# esrun

> **⚠️ DEPRECATION NOTICE**
>
> This package is being deprecated. We recommend using modern alternatives:
>
> - **For new projects:** Use [Bun](https://bun.sh/) instead of Node.js when possible. Bun offers exceptional performance, built-in TypeScript support without any configuration, and can run TypeScript files natively. It's a modern JavaScript runtime that eliminates the need for additional build tools or transpilers.
>
> - **For Node.js backends:** When you specifically need Node.js compatibility, use the official and well-maintained [tsx](https://github.com/esbuild-kit/tsx) package, which provides similar functionality with better maintenance and ecosystem support.

**esrun** is a "work out of the box" library to execute Typescript (as well as modern Javascript with decorators and stuff) without having to use a bundler. This is useful for quick demonstrations or when launching your tests written in Typescript.

This library is a thin wrapper around [esbuild](https://github.com/evanw/esbuild) which compiles Typescript almost instantly.

**esrun** properly handles dependencies, handling importing both Typescript and Javascript libraries, and using either the CJS or the ESM format. Things just work as you would expect.

## Usage

### Global installation

Install the library globally with your favorite package manager:


```shell
npm i -g esrun
```

Then you can execute any Typescript file in the same way Node would execute a Javascript file:

```shell
esrun foo.ts
```

You can pass arguments like any process:

```shell
esrun foo.ts --option=bar --verbose -S
```

All file dependencies will be bundled and executed as well.

External module dependencies won't be bundled, it's up to the `node` engine to resolve dependencies.


### Local installation

Install the library locally with your favorite package manager.

```shell
npm i -D esrun
```

Then you can use it in your `package.json` scripts:

```json
{
   "scripts": {
      "test": "esrun test"
   }
}
```

Running `npm run test` will run the first file that exists in the following list:

- `/test.ts`
- `/test/index.ts`
- `/test/test.ts`
- `/test/main.ts`
- `/test.js`
- `/test/index.js`
- `/test/test.js`
- `/test/main.js`


### CLI parameters syntax

There are two kinds of parameters that you can pass to the esrun cli:

1. **esrun parameters**, that will impact how to execute your Typescript file,
2. and **program parameters**, that will be passed into **process.argv** to the file you want to execute.

**esrun parameters** follow the same syntax convention as node, ie:

- it must start with a double hyphen "--"
- then be followed by the name of the parameter
- then, if a value is necessary, use an equal symbol "=" followed by the value, without space.

Example:

```
esrun --tsconfig=/path/to/tsconfig.json myFileToExecute.ts
```

All parameters that come **after** the file to execute are **program parameters** and will be sent via `process.argv`.

In this example, `"foo"` and `"bar"` will be sent to `myFileToExecute`:

```
esrun myFileToExecute.ts foo bar
```
### Custom tsconfig.json

You can pass a custom path to your `tsconfig.json` file from the CLI:

```
esrun --tsconfig=/custom/path/to/tsconfig.json foo.ts
```

### Top-level await

**Esrun** is compatible with top-level await.

To enable top-level await, you need to set the option `"module": "esnext"` in your tsconfig.json.

### Watch mode

You can also execute **esrun** in watch mode.

In watch mode, your file will automatically be re-executed every time itself or one of its dependencies is updated.

```shell
esrun --watch foo.ts
```

> The `--watch` (or `-w`) option must be placed before the path of the file to execute.
If you place it after the file path, it will be passed as an argument to `foo.ts` instead.

This feature is very useful when you are doing test-driven development. You can just run `esrun --watch test.ts` and enjoy a live output of your changes right into your console.

You may want to watch other files than your code files. For example, if you load data from a configuration file. In this case you can specify a glob (or a list of globs) that have to be watched:

```shell
esrun --watch=src/*.json foo.ts
```

Then any `json` file in the `src/`folder will re-trigger the run.

You can use several globs separated by a comma (but no space):

```shell
esrun --watch=src/*.json,test/*.json foo.ts
```

#### Preventing console clearing

In watch mode, every file change will trigger a console clearing. You can disable this behavior with the `--preserveConsole` option:

```shell
esrun --watch --preserveConsole foo.ts
```

### Inspect mode

You can also execute **esrun** in inspect mode.

When run in inspect mode, your code will be connected to the Webkit DevTools to benefit the power of the browser console instead of the terminal console.

First, run your program in inspect mode:

```shell
esrun --inspect foo.ts
```

Then open `about:inspect` in a **Chrome** / **Brave** / **Edge** browser. You should see your program running in the *Remote targets* section.

Click on `Open dedicated DevTools for Node` and enjoy the browser console for your back-end program.

In case of troubleshooting, read the [node documentation](https://nodejs.org/en/docs/guides/debugging-getting-started/).

> Inspect and watch mode are alas not compatible yet.

### Sudo mode

You can run the script in sudo mode:

```
esrun --sudo myScript.ts
```

### Other node cli options

`esrun` uses esbuild to transform Typescript to Javascript, and then Node to execute it.

You can pass custom options to the node cli by prefixing the option name with "--node", like this:

```
esrun --node-max-old-space-size=4096 foo.ts
esrun --node-no-warnings foo.ts
```

### Importing a CJS module

If you import a CJS module (like the `typescript` library itself), it's likely that you will need to set the [esModuleInterop](https://www.typescriptlang.org/tsconfig#esModuleInterop) flag in your `tsconfig.json` file:

```json
{
	"compilerOptions": {
		"esModuleInterop": true
	}
}
```

This will suppress the import errors from the Typescript compiler and allow you to write `import ts from "typescript"` instead of `import * as ts from "typescript"` - the latest syntax being not standard ESM.


### Using a directory as an entry point
If the given entry point is a directory, the following actions will be executed in order to find the right entry file:

- check if a package.json file exists with a `main` field. The entry file will be the value of the `main` field, relative to the package.json directory.
- check if an `index.ts` file exists in the given directory.
- check if an eponym file exists in the given directory.
- check if an eponym file with the `.ts` extension exists in the given directory.
- check if a `main.ts` file exists in the given directory.
- check if a `index.js` file exists in the given directory.
- check if an eponym file with the `.js` extension exists in the given directory.
- check if a `main.js` file exists in the given directory.

## API

The library exports a single function that you can use to programmatically execute a Typescript file.

``` ts
import esrun from 'esrun'

export async function esrun(filePath: string, options?: Options): Promise<void>

export type Options = {
   // arguments to pass to the script
   args?: string[] = []

   // if true, will reload the script on file changes
   // you can also pass an additional array of globs to watch
   watch?: boolean | string[] = false

   // if true, prevent console clearing on watch mode
   preserveConsole?: boolean

   // if true, turn on inspect mode to use browser's console
   inspect?: boolean = false

   // if false, do not transform __dirname and __filename
   // (the CLI option to disable file constants is --noFileConstants)
   fileConstants?: boolean = true

   // if false, external packages will be bundled
   makeAllPackagesExternal?: boolean = true

   // if false, process.exit() won't be called after execution
   exitAfterExecution?: boolean = true

   // enable use of process.send() from the children
   interProcessCommunication?: boolean = false

   // additional options to pass to node's cli
   nodeOptions?: Record<string, Parameter> = {}

   // indicate the mode to use to run code
   // "cliParameters" means the code is passed to the node program
   // as parameters
   // "temporaryFile" means a temporary file is created inside
   // the node_modules folder, then executed, then deleted
   sendCodeMode?: "cliParameters" | "temporaryFile"

   // executed before the code is executed (after the build)
   beforeRun?: () => unknown

   // executed after the code is executed
   afterRun?: () => unknown
}
```

### Create a new runner to get / transform generated code

To have full control, you can create your own script runner instance:

```ts
import { Runner } from 'esrun'

const runner = new Runner(inputFile: string, options?: Options)

// build the given file and all its dependencies
await runner.build(buildOptions?: BuildOptions)

// you can see what the generated code is
console.log("Generated javascript code:", runner.outputCode)

// you can apply transformations to the code
await runner.transform(code => `console.log('Hello world!');\n` + code)

// then execute the build and return the given status
const status = await runner.execute()
```

### Receive data

You can receive data from a script you executed by turning on the option `interProcessCommunication`.

When the option is on, the script will be able to call [process.send(message: string)](https://nodejs.org/api/process.html#processsendmessage-sendhandle-options-callback).

At the end of the execution, the sent messages will be disponible through `runner.output`.

Let's suppose you have the following file `helloWorld.ts`:

```ts
// helloWorld.ts
process.send('Hello world')
```

Then you can receive data fro this script this way:

```ts
const runner = new Runner('helloWorld.ts', { interProcessCommunication: true })

await runner.execute({ exitAfterExecution: false })

console.log("Received data:", runner.output)
// should log 'Received data: Hello world'
```

## Changelog

The changelog is available [here](https://github.com/digital-loukoum/esrun/blob/main/CHANGELOG.md).


================================================
FILE: package.json
================================================
{
	"name": "esrun",
	"version": "3.2.31",
	"type": "module",
	"description": "Execute directly your Typescript files using Esbuild",
	"main": "./index.js",
	"exports": {
		".": "./index.js",
		"./*": "./*.js"
	},
	"bin": {
		"esrun": "./bin.js"
	},
	"scripts": {
		"lint": "tsc --noEmit",
		"build": "node scripts/build.js",
		"deploy": "node scripts/deploy.js",
		"dev": "./package/bin.js --watch test --watch coco",
		"test": "./package/bin.js test --watch coco",
		"test:tsconfig": "./package/bin.js --tsconfig test/tsconfig.json test --watch coco",
		"test:node-cli": "./package/bin.js --node-max-old-space-size=4096 test --watch coco",
		"test:readline": "./package/bin.js test/readline",
		"test:watch": "./package/bin.js --watch=test/*.json test --watch coco",
		"test:watch:preserve": "./package/bin.js --preserveConsole --watch=test/*.json test --watch coco",
		"test:inspect": "./package/bin.js --inspect test --watch coco",
		"test:watch:inspect": "./package/bin.js --watch --inspect test --watch coco",
		"test:server": "./package/bin.js --watch test/server"
	},
	"engines": {
		"node": ">=14.0"
	},
	"repository": {
		"type": "git",
		"url": "git+https://github.com/digital-loukoum/esrun.git"
	},
	"keywords": [
		"esbuild",
		"run",
		"execute",
		"typescript"
	],
	"author": "Lepzulnag",
	"license": "ISC",
	"bugs": {
		"url": "https://github.com/digital-loukoum/esrun/issues"
	},
	"homepage": "https://github.com/digital-loukoum/esrun#readme",
	"dependencies": {
		"@digitak/grubber": "^3.1.4",
		"chokidar": "^3.5.1",
		"esbuild": "^0.24.2"
	},
	"devDependencies": {
		"@digitak/bunker": "^3.2.1",
		"@types/node": "^14.14.20",
		"cute-print": "^1.0.4",
		"fartest": "^2.1.6",
		"fsevents": "^2.3.2",
		"polka": "^0.5.2",
		"typescript": "^4.7.0-dev.20220421"
	}
}

================================================
FILE: scripts/build.js
================================================
import { execSync } from "child_process"
import { rmSync, chmodSync, copyFileSync } from "fs"

console.log("Cleaning library...")
rmSync("package", { recursive: true, force: true })

console.log("Compiling typescript...")
execSync("tsc", { stdio: "inherit" })

console.log("Copying configuration files...")
copyFileSync("./README.md", "./package/README.md")
copyFileSync("./package.json", "./package/package.json")

console.log("Making binary executable...")
chmodSync("package/bin.js", 0o775)

console.log("✨ Build done\n")


================================================
FILE: scripts/deploy.js
================================================
import { execSync } from "child_process"
import { bumpVersion } from "./utilities/bumpVersion.js"

console.log("Bumping version...")
const version = bumpVersion()

execSync(`git add .`)
execSync(`git commit -m "📌 Version ${version}"`)
execSync(`git push`)

import "./build.js"

console.log(`Starting deploy...`)

try {
	execSync(`npm publish`, { cwd: "./package" })
} catch (error) {
	console.log(`[--- An error occured during deploy ---]`)
	console.log(error, "\n")
	process.exit(1)
}

console.log(`\nDeploy done 🎉\n`)


================================================
FILE: scripts/utilities/bumpVersion.js
================================================
import fs from "fs"

export function bumpVersion() {
	const file = "package.json"
	const packageInfos = JSON.parse(fs.readFileSync(file, "utf8"))
	const version = packageInfos.version.split(".").map(value => +value)
	version[2]++
	packageInfos.version = version.join(".")
	fs.writeFileSync(file, JSON.stringify(packageInfos, null, "\t"))

	const newVersion = version.join(".")

	return newVersion
}


================================================
FILE: source/bin.ts
================================================
#!/usr/bin/env node
import esrun from "./index.js";
import { CliOption } from "./types/CliOption.js";
import { Parameter } from "./types/Parameter.js";

const { argv } = process;
const nodeOptionPrefix = "--node-";

const argumentOptions: Record<string, CliOption> = {
	"--watch": "watch",
	"-w": "watch",
	"--inspect": "inspect",
	"-i": "inspect",
	"--preserveConsole": "preserveConsole",
	"--noFileConstants": "noFileConstants",
	"--tsconfig": "tsconfig",
	"--send-code-mode": "sendCodeMode",
	"--sudo": "sudo",
};

const options: Record<CliOption, boolean | string[] | undefined> & {
	node: Record<string, Parameter>;
} = {
	watch: false,
	inspect: false,
	preserveConsole: false,
	noFileConstants: false,
	sudo: false,
	tsconfig: undefined,
	node: {},
	sendCodeMode: undefined,
};

let argsOffset = 2;
let argument: string;

if (argv.length < argsOffset) {
	console.log("Missing typescript input file");
	process.exit(0);
}

while ((argument = argv[argsOffset]).startsWith("--")) {
	const [command, parameters] = getCommandAndParameters(argument);

	if (command in argumentOptions) {
		options[argumentOptions[command]] = parameters;
		argsOffset++;
	} else if (command.startsWith(nodeOptionPrefix)) {
		options.node[command.slice(nodeOptionPrefix.length)] = parameters;
		argsOffset++;
	} else {
		console.log(`Unknown option ${command}`);
		process.exit(9);
	}
}

if (typeof options.tsconfig === "boolean") {
	console.log(
		"Missing value for the '--tsconfig' parameter. Did you forget to add a \"=\"?",
	);
	console.log(
		"Example of valid syntax: esrun --tsconfig=/path/to/my/tsconfig.json fileToExecute.ts",
	);
	process.exit(9);
}

const tsConfigFile =
	options.tsconfig instanceof Array
		? options.tsconfig.join(",")
		: options.tsconfig;

esrun(argv[argsOffset], {
	args: argv.slice(argsOffset + 1),
	watch: options.watch,
	tsConfigFile,
	inspect: !!options.inspect,
	preserveConsole: !!options.preserveConsole,
	fileConstants: !options.noFileConstants,
	nodeOptions: options.node,
	sendCodeMode: !Array.isArray(options.sendCodeMode)
		? undefined
		: options.sendCodeMode[0] === "cliParameters"
		? "cliParameters"
		: options.sendCodeMode[0] === "temporaryFile"
		? "temporaryFile"
		: undefined,
}).catch((error) => {
	console.error(error);
	process.exit(1);
});

function getCommandAndParameters(argument: string): [string, Parameter] {
	let colonIndex = argument.indexOf(":");
	if (colonIndex === -1) colonIndex = Infinity;
	let equalIndex = argument.indexOf("=");
	if (equalIndex === -1) equalIndex = Infinity;
	const separatorIndex = Math.min(colonIndex, equalIndex);

	if (separatorIndex === Infinity) return [argument, true];
	return [
		argument.slice(0, separatorIndex),
		argument.slice(separatorIndex + 1).split(","),
	];
}


================================================
FILE: source/index.ts
================================================
import Runner from "./runners/Runner.js"
import Watcher from "./runners/Watcher.js"
import { Options } from "./types/Options.js"

export { Runner, Watcher, esrun, Options }

/**
 * Run any .ts or .js file
 */
export default async function esrun(inputFile: string, options?: Options) {
	if (options?.watch && options?.inspect) {
		console.warn(
			`--inspect and --watch options are not compatible together. Disabling watch mode.`
		)
		options.watch = false
	}
	return new (options?.watch ? Watcher : Runner)(inputFile, options).run()
}


================================================
FILE: source/plugins/fileConstants.ts
================================================
import { Plugin, Loader } from "esbuild";
import { posix } from "path";
import process from "process";
import fs from "fs";
import { grub } from "@digitak/grubber";

export type FileConstantsPluginOptions = Record<never, never>;

export const fileConstantsPlugin = (
	options: FileConstantsPluginOptions = {},
): Plugin => ({
	name: "fileConstants",
	setup(build) {
		build.onLoad(
			{ filter: /.\.(c|m)?(js|ts)x?$/, namespace: "file" },
			async (options) => {
				const isWindows = /^win/.test(process.platform);
				const escapeBackslashes = (path: string) =>
					isWindows ? path.replace(/\\/g, "/") : path;

				const filename = escapeBackslashes(options.path);
				const dirname = posix.dirname(options.path);
				const fileContent = fs.readFileSync(options.path, "utf8");

				const contents = grub(fileContent).replace(
					{ from: "__dirname", to: `"${dirname}"` },
					{ from: "__filename", to: `"${filename}"` },
				);

				let loader = posix.extname(options.path).slice(1) as Loader;
				if (["m", "c"].includes(loader[0])) loader = loader.slice(1) as Loader;

				return {
					contents,
					loader,
				};
			},
		);
	},
});


================================================
FILE: source/runners/Runner.ts
================================================
import { BuildOptions, Plugin, context, type BuildContext } from "esbuild";
import type { BuildResult, OutputFile } from "esbuild";
import { ChildProcess, spawn } from "child_process";
import findInputFile from "../tools/findInputFile.js";
import { Options } from "../types/Options.js";
import { fileConstantsPlugin } from "../plugins/fileConstants.js";
import path, { posix } from "path";
import { SendCodeMode } from "../types/SendCodeMode.js";
import { unlinkSync, writeFileSync } from "fs";
import { importRequire } from "../tools/importRequire.js";
import { grub } from "@digitak/grubber";

export type BuildOutput =
	| null
	| (BuildResult & {
			outputFiles: OutputFile[];
	  });

export default class Runner {
	public inputFile: string;
	public outputFile: undefined | string = undefined; // temporary output file
	public output = "";
	public stdout = "";
	public stderr = "";
	public outputCode = "";
	public args: string[] = [];
	public tsConfigFile: string | undefined;
	public preserveConsole: boolean;
	public fileConstants: boolean;
	public beforeRun: Options["beforeRun"];
	public afterRun: Options["afterRun"];
	public nodeOptions: Options["nodeOptions"] = {};
	public sendCodeMode: SendCodeMode;
	public sudo: boolean;
	public esbuildOptions: BuildOptions;

	protected watched: boolean | string[];
	protected inspect: boolean;
	protected interProcessCommunication;
	protected makeAllPackagesExternal;
	protected exitAfterExecution;

	protected buildContext?: BuildContext;
	protected buildOutput?: BuildResult<BuildOptions>;
	protected dependencies: string[] = [];
	protected childProcess?: ChildProcess;

	getDependencies(): readonly string[] {
		return this.dependencies;
	}

	protected retrieveDependencies(): string[] {
		return Object.keys(this.buildOutput?.metafile?.inputs ?? []).map((input) =>
			posix.resolve(input),
		);
	}

	constructor(inputFile: string, options?: Options) {
		this.inputFile = findInputFile(inputFile);

		this.args = options?.args ?? [];
		this.sudo = options?.sudo ?? false;
		this.watched = options?.watch ?? false;
		this.preserveConsole = options?.preserveConsole ?? false;
		this.inspect = options?.inspect ?? false;
		this.fileConstants = options?.fileConstants ?? true;
		this.tsConfigFile = options?.tsConfigFile;
		this.interProcessCommunication =
			options?.interProcessCommunication ?? false;
		this.makeAllPackagesExternal = options?.makeAllPackagesExternal ?? true;
		this.exitAfterExecution = options?.exitAfterExecution ?? true;
		this.beforeRun = options?.beforeRun;
		this.afterRun = options?.afterRun;
		this.nodeOptions = options?.nodeOptions ?? {};
		this.nodeOptions = options?.nodeOptions ?? {};
		this.esbuildOptions = options?.esbuildOptions ?? {};
		this.sendCodeMode =
			options?.sendCodeMode ?? process.platform === "win32"
				? "temporaryFile"
				: "cliParameters";
	}

	async run() {
		try {
			await this.build();
			const status = await this.execute();
			if (this.exitAfterExecution) {
				process.exit(status);
			}
		} catch (error) {
			console.error(error);
			process.exit(1);
		}
	}

	async build(buildOptions: BuildOptions = {}) {
		const plugins: Plugin[] = [];

		if (this.fileConstants) {
			plugins.push(fileConstantsPlugin());
		}
		try {
			this.buildContext = await context({
				entryPoints: [this.inputFile],
				bundle: true,
				platform: "node",
				format: "esm",
				plugins,
				sourcemap: "inline",
				sourceRoot: process.cwd(),
				tsconfig: this.tsConfigFile,
				external: this.makeAllPackagesExternal
					? [
							"./node_modules/*",
							"../node_modules/*",
							"../../node_modules/*",
							"../../../node_modules/*",
							"../../../../node_modules/*",
							"../../../../../node_modules/*",
							"../../../../../../node_modules/*",
							"../../../../../../../node_modules/*",
							"../../../../../../../../node_modules/*",
							"../../../../../../../../../node_modules/*",
							"../../../../../../../../../../node_modules/*",
					  ]
					: [],
				...this.esbuildOptions,
				...buildOptions,
				write: false,
				metafile: true,
			});
			this.buildOutput = await this.buildContext?.rebuild();
			this.outputCode = this.getOutputCode();

			// remove shebangs
			this.outputCode = grub(this.outputCode).replace(
				{ from: /^#!.*$/gm, to: "" },
			)
			
			this.dependencies = this.retrieveDependencies();
		} catch (error) {
			// No need to log the error as it has already been done by esbuild.
			this.buildOutput = undefined;
			this.outputCode = "";
		}
	}

	async transform(transformer: (content: string) => string | Promise<string>) {
		this.outputCode = await transformer(this.outputCode);
	}

	async execute(): Promise<number> {
		this.output = this.stdout = this.stderr = "";
		if (!this.buildOutput) return 1;

		let code = this.outputCode;
		let command = "node";
		let commandArgs: string[] = ["--enable-source-maps"];

		for (const nodeOption in this.nodeOptions) {
			let argument = `--${nodeOption}`;
			const parameters = this.nodeOptions[nodeOption];
			if (Array.isArray(parameters)) {
				argument += `=${parameters.join(",")}`;
			}
			commandArgs.push(argument);
		}

		if (this.inspect) {
			commandArgs.push("--inspect");
			code = `setTimeout(() => console.log("Process timeout"), 3_600_000);\n${code}`;
		}

		const evalArgs: Array<string> = [];

		if (this.sendCodeMode === "temporaryFile") {
			// we create a temporary file that we will execute
			const uniqueId = Date.now();
			this.outputFile = path.join(process.cwd(), `esrun-${uniqueId}.tmp.mjs`);
			code = importRequire(code, this.outputFile);
			code = `process.argv = [process.argv[0], ...process.argv.slice(3)];\n${code}`;
			writeFileSync(this.outputFile, code);
			evalArgs.push(this.outputFile);
		} else {
			code = importRequire(code, posix.resolve("index.js"));
			// we pass the code directly from the command line
			evalArgs.push("--input-type=module", "--eval", code);
		}

		commandArgs.push(...evalArgs, "--", this.inputFile, ...this.args);

		if (this.sudo) {
			commandArgs = [command, ...commandArgs];
			command = "sudo";
		}

		await this.beforeRun?.();

		try {
			this.childProcess = spawn(command, commandArgs, {
				stdio: this.interProcessCommunication
					? ["pipe", "pipe", "pipe", "ipc"]
					: "inherit",
			});

			if (this.interProcessCommunication) {
				this.childProcess?.on("message", (message) => {
					this.output += message.toString();
				});
				this.childProcess?.stdout?.on("data", (data) => {
					this.stdout += data.toString();
				});
				this.childProcess?.stderr?.on("data", (data) => {
					this.stderr += data.toString();
				});
			}

			return new Promise((resolve) => {
				const done = async (code?: number) => {
					await this.afterRun?.();
					if (this.outputFile) {
						unlinkSync(this.outputFile);
					}
					resolve(code ?? 0);
				};

				this.childProcess?.on("close", done);
				this.childProcess?.on("error", async (error) => {
					console.error(error);
					done(1);
				});
			});
		} catch (error) {
			console.error(error);
			return 1;
		}
	}

	getOutputCode() {
		return this.buildOutput?.outputFiles?.[0]?.text || "";
	}
}


================================================
FILE: source/runners/Watcher.ts
================================================
import Runner, { BuildOutput } from "./Runner.js";
import { watch } from "chokidar";
import type { FSWatcher } from "chokidar";
import { posix } from "path";
import { Options } from "../types/Options.js";

function debounce(func: Function, wait: number) {
	let timeout: NodeJS.Timeout | null = null;

	return function (...parameters: unknown[]) {
		if (timeout) clearTimeout(timeout);
		else func(...parameters);
		timeout = setTimeout(() => (timeout = null), wait);
	};
}

export default class Watcher extends Runner {
	protected watcher: FSWatcher | null = null;
	protected watched: string[] = [];

	constructor(input: string, options?: Options) {
		super(input, options);
		this.watched =
			options?.watch instanceof Array
				? options.watch.map((glob) => posix.resolve(glob))
				: [];
	}

	async run() {
		try {
			console.clear();
			await this.build();
			this.execute();
			this.watch();
		} catch (error) {}
	}

	async rerun() {
		if (!this.watcher) throw "Cannot re-run before a first run";

		if (this.childProcess) {
			this.childProcess?.kill("SIGINT");
			this.childProcess = undefined;
		}
		await this.rebuild();

		// we update the list of watched files
		if (this.buildOutput) {
			this.watch();
		}

		await this.execute();
	}

	async rebuild() {
		if (!this.preserveConsole) {
			console.clear();
		}
		this.dependencies.length = 0;

		if (this.buildOutput) {
			try {
				this.buildOutput = await this.buildContext?.rebuild();
				this.outputCode = this.getOutputCode();
				this.dependencies = this.retrieveDependencies();
			} catch (error) {
				this.buildOutput = undefined;
				this.outputCode = "";
			}
		} else {
			await this.build();
		}
	}

	watch() {
		void this.watcher?.close();
		this.watcher = watch([
			...this.dependencies,
			"package.json",
			...this.watched,
		]);
		this.watcher.on("change", debounce(this.rerun.bind(this), 300));
		this.watcher.on("unlink", debounce(this.rerun.bind(this), 300));
	}
}


================================================
FILE: source/tools/findInputFile.ts
================================================
import { existsSync, statSync, readFileSync } from "fs";
import { posix } from "path";

export default function findInputFile(path: string): string {
	if (!existsSync(path)) {
		if (existsSync(`${path}.ts`)) path = `${path}.ts`;
		else if (existsSync(`${path}.js`)) path = `${path}.js`;
		else throw `Path '${path}' does not exist`;
	}

	const stat = statSync(path);
	if (stat.isFile()) return path;
	else if (stat.isDirectory()) {
		// first we check if there is a package.json file with a `main` key
		const packageFile = posix.resolve(path, "package.json");
		if (existsSync(packageFile) && statSync(packageFile).isFile()) {
			const { main } = JSON.parse(readFileSync(packageFile, "utf8"));
			if (main) return findInputFile(posix.resolve(path, main));
		}

		// otherwise we look for a default entry point
		const name = posix.basename(path);
		for (const subpath of [
			posix.resolve(path, "index.ts"),
			posix.resolve(path, name),
			posix.resolve(path, `${name}.ts`),
			posix.resolve(path, "main.ts"),
			posix.resolve(path, "index.js"),
			posix.resolve(path, `${name}.js`),
			posix.resolve(path, "main.js"),
		])
			if (existsSync(subpath) && statSync(subpath).isFile()) return subpath;

		throw `Could not resolve an entry point in folder '${path}`;
	} else throw `Path '${path}' should be a file or a directory`;
}


================================================
FILE: source/tools/importRequire.ts
================================================
export function importRequire(code: string, location: string) {
	// return `import { createRequire } from "module";\nconst require = createRequire("${location}");\n` + code
	return `
		import __esrun_url from 'url';\n
		import { createRequire as __esrun_createRequire } from "module";\n
		const __esrun_fileUrl = __esrun_url.pathToFileURL("${location}");\n
		const require = __esrun_createRequire(__esrun_fileUrl);\n${code}
	`
}


================================================
FILE: source/tools/onExit.ts
================================================
export default function onExit(cleanUp: Function) {
	// const events = [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`]
	for (const event of [
		`exit`,
		`SIGINT`,
		`SIGUSR1`,
		`SIGUSR2`,
		`uncaughtException`,
		`SIGTERM`,
	]) {
		process.on(event, () => cleanUp(event))
	}
}


================================================
FILE: source/tools/resolveDependency.ts
================================================
import { createRequire } from "module"

const require = createRequire(process.cwd())

export default (dependency: string) =>
	require.resolve(dependency, { paths: [process.cwd()] })


================================================
FILE: source/types/CliOption.ts
================================================
export type CliOption =
	| "watch"
	| "inspect"
	| "preserveConsole"
	| "noFileConstants"
	| "tsconfig"
	| "sendCodeMode"
	| "sudo";


================================================
FILE: source/types/Options.ts
================================================
import { BuildOptions } from "esbuild";
import { Parameter } from "./Parameter.js";
import { SendCodeMode } from "./SendCodeMode.js";

export type Options = {
	args?: string[];
	watch?: boolean | string[];
	preserveConsole?: boolean;
	inspect?: boolean;
	interProcessCommunication?: boolean;
	makeAllPackagesExternal?: boolean;
	exitAfterExecution?: boolean;
	fileConstants?: boolean;
	tsConfigFile?: string;
	sendCodeMode?: SendCodeMode;
	sudo?: boolean;
	beforeRun?: () => unknown;
	afterRun?: () => unknown;
	nodeOptions?: Record<string, Parameter>;
	esbuildOptions?: BuildOptions;
};


================================================
FILE: source/types/Parameter.ts
================================================
export type Parameter = string[] | true


================================================
FILE: source/types/SendCodeMode.ts
================================================
export type SendCodeMode = "cliParameters" | "temporaryFile"

================================================
FILE: test/index.ts
================================================
import Zabu from "~/samples/Zabu";
import coco from "~/samples/coco";
import ts from "typescript";
import fsevents from "fsevents";
import { bunker } from "@digitak/bunker";
import { bunkerFile } from "@digitak/bunker/io";
import start from "fartest";
import print from "cute-print";
import Runner from "../source/runners/Runner";
import dotImport from "./samples/dotImport/dotImport";
import doubleDotImport from "./samples/dotImport/doubleDotImport";
import { foo } from "alias/foo";

start(async ({ stage, same, test }) => {
	stage("CLI arguments received");
	same(process.argv[3], "coco");

	stage("CLI arguments received [--watch is passed as argument]");
	same(process.argv[2], "--watch");

	stage("Import typescript library");
	same(ts.SyntaxKind.EndOfFileToken, 1);

	stage("Import fsevents");
	same(!!fsevents.watch, true);

	stage("Import CJS library");
	same(typeof print, "function");

	stage("Import ESM library");
	same(bunker(3), Uint8Array.of(5, 3));

	stage("Import specific file in library");
	same(typeof bunkerFile, "function");

	stage("Import custom typescript file");
	same(new Zabu().yell(), "ZABU");

	stage("Import another custom typescript file");
	same(coco, 11);

	stage("import '.' syntax");
	same(dotImport, 12);

	stage("import '..' syntax");
	same(doubleDotImport, 12);

	stage("Backticks in comments");
	{
		const runner = new Runner("test/samples/backticksInComments");
		await runner.build();
	}

	stage("File constants");
	{
		const runner = new Runner("test/samples/fileConstants");
		await runner.build();
		test(
			runner.outputCode.includes(
				`["/Users/Lepzulnag/Code/esrun/test/samples", "/Users/Lepzulnag/Code/esrun/test/samples/fileConstants.ts", "__dirname", "__filename"]`,
			),
			"__filename and __dirname are transformed",
		);
	}

	stage("Temporary file mode");
	{
		const runner = new Runner("test/samples/fileConstants", {
			sendCodeMode: "temporaryFile",
		});
		await runner.build();
		test(
			runner.outputCode.includes(
				`["/Users/Lepzulnag/Code/esrun/test/samples", "/Users/Lepzulnag/Code/esrun/test/samples/fileConstants.ts", "__dirname", "__filename"]`,
			),
			"__filename and __dirname are transformed in temporary file mode",
		);
	}

	stage("Use transformer");
	{
		const runner = new Runner("test/samples/coco");
		await runner.build();
		await runner.transform((content) => content.replace("11", "12"));
		test(runner.outputCode.includes("12"), "Should export 12");
	}

	stage("Inter process communication");
	{
		const runner = new Runner("test/samples/ipc/child", {
			interProcessCommunication: true,
		});
		await runner.build();
		await runner.execute();
		same(runner.output, "Hello", "process.send");
	}

	stage("Build events");
	{
		const messages = <string[]>[];
		const runner = new Runner("test/samples/coco", {
			beforeRun: () => messages.push("beforeRun"),
			afterRun: () => messages.push("afterRun"),
		});
		await runner.build();
		await runner.execute();
		same(messages, ["beforeRun", "afterRun"]);
	}

	stage("alias");
	{
		same(foo, "foo", "resolved alias not treated as a dependency");
	}
});


================================================
FILE: test/readline.js
================================================
// test/readline.ts
import readline from "readline"
async function input(msg) {
	return new Promise(resolve => {
		const rl = readline.createInterface({
			input: process.stdin,
			output: process.stdout,
		})
		rl.question(msg, answer => {
			rl.close()
			resolve(answer)
		})
	})
}
try {
	console.log(await input("test"))
} catch (error) {
	console.log("error", error)
}


================================================
FILE: test/readline.ts
================================================

// When using readline or any other command line input library (I tested prompt-sync, cli-select, inquirer, prompts and readline) the program instantly exits without any errors

// here's some code that results in the issue

// main.ts
import readline from 'readline'

async function input(msg: string): Promise<string> {
    return new Promise((resolve) => {
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        })
        rl.question(msg, (answer) => {
            rl.close()
            resolve(answer)
        } )
    } )
}

try {
	console.log(await input("test"))
} catch (error) {
	console.log("error", error)
}

// input("test").then(() => {
// 	console.log("This line will never be printed")
// })



================================================
FILE: test/resolvedAlias/foo.ts
================================================
export const foo = "foo"

================================================
FILE: test/samples/Zabu.ts
================================================
export default class Zabu {
	yell() {
		return "ZABU"
	}
}


================================================
FILE: test/samples/backticksInComments.ts
================================================
// `/a`
// `/b`


================================================
FILE: test/samples/coco.ts
================================================
export default 11


================================================
FILE: test/samples/dotImport/dotImport.ts
================================================
import { randomExportedValue } from "."

export default randomExportedValue


================================================
FILE: test/samples/dotImport/doubleDotImport/index.ts
================================================
import { randomExportedValue } from ".."

export default randomExportedValue


================================================
FILE: test/samples/dotImport/index.ts
================================================
export const randomExportedValue = 12


================================================
FILE: test/samples/fileConstants.ts
================================================
export default [__dirname, __filename, "__dirname", "__filename"]


================================================
FILE: test/samples/ipc/child.ts
================================================
process.send("Hello")
process.stdout.write("world")
process.disconnect()


================================================
FILE: test/samples/shebang.ts
================================================
#! hello i'm a shebang
export const shebang = "Shebangs are stripped"


================================================
FILE: test/server/index.ts
================================================
import polka from "polka"
import { message } from "./message"

const port = 3000

polka()
	.get("/", (_, response) => response.end(message))
	.listen(port, (error: Error) => {
		if (error) throw error
		console.log(`> Running on localhost:${port}`)
	})


================================================
FILE: test/server/message.ts
================================================
export const message = "Hellow from Polka server :)"


================================================
FILE: test/tsconfig.json
================================================
{
	"compilerOptions": {
		"esModuleInterop": true,
		"module": "esnext",
		"moduleResolution": "Node",
		"paths": {
			"~/*": [
				"./*"
			],
			"alias/*": [
				"./resolvedAlias/*"
			],
		}
	},
}

================================================
FILE: tsconfig.json
================================================
{
	"compilerOptions": {
		"moduleResolution": "NodeNext",
		"skipLibCheck": true,
		"target": "es2020",
		"lib": [
			"es2020",
			"dom"
		],
		"module": "esnext",
		"strict": true,
		"declaration": true,
		"esModuleInterop": true,
		"outDir": "package",
	},
	"include": [
		"source"
	],
	"exclude": [
		"test"
	]
}
Download .txt
gitextract_3nngibti/

├── .claude/
│   └── settings.json
├── .github/
│   └── workflows/
│       ├── claude.yml
│       └── npm-publish.yaml
├── .gitignore
├── .vscode/
│   └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── scripts/
│   ├── build.js
│   ├── deploy.js
│   └── utilities/
│       └── bumpVersion.js
├── source/
│   ├── bin.ts
│   ├── index.ts
│   ├── plugins/
│   │   └── fileConstants.ts
│   ├── runners/
│   │   ├── Runner.ts
│   │   └── Watcher.ts
│   ├── tools/
│   │   ├── findInputFile.ts
│   │   ├── importRequire.ts
│   │   ├── onExit.ts
│   │   └── resolveDependency.ts
│   └── types/
│       ├── CliOption.ts
│       ├── Options.ts
│       ├── Parameter.ts
│       └── SendCodeMode.ts
├── test/
│   ├── index.ts
│   ├── readline.js
│   ├── readline.ts
│   ├── resolvedAlias/
│   │   └── foo.ts
│   ├── samples/
│   │   ├── Zabu.ts
│   │   ├── backticksInComments.ts
│   │   ├── coco.ts
│   │   ├── dotImport/
│   │   │   ├── dotImport.ts
│   │   │   ├── doubleDotImport/
│   │   │   │   └── index.ts
│   │   │   └── index.ts
│   │   ├── fileConstants.ts
│   │   ├── ipc/
│   │   │   └── child.ts
│   │   └── shebang.ts
│   ├── server/
│   │   ├── index.ts
│   │   └── message.ts
│   └── tsconfig.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (33 symbols across 16 files)

FILE: scripts/utilities/bumpVersion.js
  function bumpVersion (line 3) | function bumpVersion() {

FILE: source/bin.ts
  function getCommandAndParameters (line 92) | function getCommandAndParameters(argument: string): [string, Parameter] {

FILE: source/index.ts
  function esrun (line 10) | async function esrun(inputFile: string, options?: Options) {

FILE: source/plugins/fileConstants.ts
  type FileConstantsPluginOptions (line 7) | type FileConstantsPluginOptions = Record<never, never>;
  method setup (line 13) | setup(build) {

FILE: source/runners/Runner.ts
  type BuildOutput (line 13) | type BuildOutput =
  class Runner (line 19) | class Runner {
    method getDependencies (line 48) | getDependencies(): readonly string[] {
    method retrieveDependencies (line 52) | protected retrieveDependencies(): string[] {
    method constructor (line 58) | constructor(inputFile: string, options?: Options) {
    method run (line 83) | async run() {
    method build (line 96) | async build(buildOptions: BuildOptions = {}) {
    method transform (line 148) | async transform(transformer: (content: string) => string | Promise<str...
    method execute (line 152) | async execute(): Promise<number> {
    method getOutputCode (line 239) | getOutputCode() {

FILE: source/runners/Watcher.ts
  function debounce (line 7) | function debounce(func: Function, wait: number) {
  class Watcher (line 17) | class Watcher extends Runner {
    method constructor (line 21) | constructor(input: string, options?: Options) {
    method run (line 29) | async run() {
    method rerun (line 38) | async rerun() {
    method rebuild (line 55) | async rebuild() {
    method watch (line 75) | watch() {

FILE: source/tools/findInputFile.ts
  function findInputFile (line 4) | function findInputFile(path: string): string {

FILE: source/tools/importRequire.ts
  function importRequire (line 1) | function importRequire(code: string, location: string) {

FILE: source/tools/onExit.ts
  function onExit (line 1) | function onExit(cleanUp: Function) {

FILE: source/types/CliOption.ts
  type CliOption (line 1) | type CliOption =

FILE: source/types/Options.ts
  type Options (line 5) | type Options = {

FILE: source/types/Parameter.ts
  type Parameter (line 1) | type Parameter = string[] | true

FILE: source/types/SendCodeMode.ts
  type SendCodeMode (line 1) | type SendCodeMode = "cliParameters" | "temporaryFile"

FILE: test/readline.js
  function input (line 3) | async function input(msg) {

FILE: test/readline.ts
  function input (line 9) | async function input(msg: string): Promise<string> {

FILE: test/samples/Zabu.ts
  class Zabu (line 1) | class Zabu {
    method yell (line 2) | yell() {
Condensed preview — 42 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (49K chars).
[
  {
    "path": ".claude/settings.json",
    "chars": 94,
    "preview": "{\n  \"permissions\": {\n    \"allow\": [\n      \"WebFetch\",\n      \"Bash\"\n    ],\n    \"deny\": []\n  }\n}"
  },
  {
    "path": ".github/workflows/claude.yml",
    "chars": 1088,
    "preview": "name: Claude PR Assistant\n\non:\n  issue_comment:\n    types: [created]\n  pull_request_review_comment:\n    types: [created]"
  },
  {
    "path": ".github/workflows/npm-publish.yaml",
    "chars": 1832,
    "preview": "name: NPM Package\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  build:\n    if: github."
  },
  {
    "path": ".gitignore",
    "chars": 22,
    "preview": "node_modules/\npackage/"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 53,
    "preview": "{\n\t\"typescript.tsdk\": \"node_modules/typescript/lib\"\n}"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3024,
    "preview": "## 3.2.27\n- Move from `@digitak/esrun` to `esrun`.\n\n## 3.2.25\n- Fix `url` import that would conflict with user-defined u"
  },
  {
    "path": "LICENSE",
    "chars": 1073,
    "preview": "MIT License\n\nCopyright (c) 2021 Digital Loukoums\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "README.md",
    "chars": 10267,
    "preview": "# esrun\n\n> **⚠️ DEPRECATION NOTICE**\n>\n> This package is being deprecated. We recommend using modern alternatives:\n>\n> -"
  },
  {
    "path": "package.json",
    "chars": 1781,
    "preview": "{\n\t\"name\": \"esrun\",\n\t\"version\": \"3.2.31\",\n\t\"type\": \"module\",\n\t\"description\": \"Execute directly your Typescript files usi"
  },
  {
    "path": "scripts/build.js",
    "chars": 525,
    "preview": "import { execSync } from \"child_process\"\nimport { rmSync, chmodSync, copyFileSync } from \"fs\"\n\nconsole.log(\"Cleaning lib"
  },
  {
    "path": "scripts/deploy.js",
    "chars": 520,
    "preview": "import { execSync } from \"child_process\"\nimport { bumpVersion } from \"./utilities/bumpVersion.js\"\n\nconsole.log(\"Bumping "
  },
  {
    "path": "scripts/utilities/bumpVersion.js",
    "chars": 399,
    "preview": "import fs from \"fs\"\n\nexport function bumpVersion() {\n\tconst file = \"package.json\"\n\tconst packageInfos = JSON.parse(fs.re"
  },
  {
    "path": "source/bin.ts",
    "chars": 2752,
    "preview": "#!/usr/bin/env node\nimport esrun from \"./index.js\";\nimport { CliOption } from \"./types/CliOption.js\";\nimport { Parameter"
  },
  {
    "path": "source/index.ts",
    "chars": 537,
    "preview": "import Runner from \"./runners/Runner.js\"\nimport Watcher from \"./runners/Watcher.js\"\nimport { Options } from \"./types/Opt"
  },
  {
    "path": "source/plugins/fileConstants.ts",
    "chars": 1145,
    "preview": "import { Plugin, Loader } from \"esbuild\";\nimport { posix } from \"path\";\nimport process from \"process\";\nimport fs from \"f"
  },
  {
    "path": "source/runners/Runner.ts",
    "chars": 7131,
    "preview": "import { BuildOptions, Plugin, context, type BuildContext } from \"esbuild\";\nimport type { BuildResult, OutputFile } from"
  },
  {
    "path": "source/runners/Watcher.ts",
    "chars": 1950,
    "preview": "import Runner, { BuildOutput } from \"./Runner.js\";\nimport { watch } from \"chokidar\";\nimport type { FSWatcher } from \"cho"
  },
  {
    "path": "source/tools/findInputFile.ts",
    "chars": 1331,
    "preview": "import { existsSync, statSync, readFileSync } from \"fs\";\nimport { posix } from \"path\";\n\nexport default function findInpu"
  },
  {
    "path": "source/tools/importRequire.ts",
    "chars": 429,
    "preview": "export function importRequire(code: string, location: string) {\n\t// return `import { createRequire } from \"module\";\\ncon"
  },
  {
    "path": "source/tools/onExit.ts",
    "chars": 304,
    "preview": "export default function onExit(cleanUp: Function) {\n\t// const events = [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaugh"
  },
  {
    "path": "source/tools/resolveDependency.ts",
    "chars": 182,
    "preview": "import { createRequire } from \"module\"\n\nconst require = createRequire(process.cwd())\n\nexport default (dependency: string"
  },
  {
    "path": "source/types/CliOption.ts",
    "chars": 133,
    "preview": "export type CliOption =\n\t| \"watch\"\n\t| \"inspect\"\n\t| \"preserveConsole\"\n\t| \"noFileConstants\"\n\t| \"tsconfig\"\n\t| \"sendCodeMode"
  },
  {
    "path": "source/types/Options.ts",
    "chars": 588,
    "preview": "import { BuildOptions } from \"esbuild\";\nimport { Parameter } from \"./Parameter.js\";\nimport { SendCodeMode } from \"./Send"
  },
  {
    "path": "source/types/Parameter.ts",
    "chars": 40,
    "preview": "export type Parameter = string[] | true\n"
  },
  {
    "path": "source/types/SendCodeMode.ts",
    "chars": 60,
    "preview": "export type SendCodeMode = \"cliParameters\" | \"temporaryFile\""
  },
  {
    "path": "test/index.ts",
    "chars": 3091,
    "preview": "import Zabu from \"~/samples/Zabu\";\nimport coco from \"~/samples/coco\";\nimport ts from \"typescript\";\nimport fsevents from "
  },
  {
    "path": "test/readline.js",
    "chars": 374,
    "preview": "// test/readline.ts\nimport readline from \"readline\"\nasync function input(msg) {\n\treturn new Promise(resolve => {\n\t\tconst"
  },
  {
    "path": "test/readline.ts",
    "chars": 779,
    "preview": "\n// When using readline or any other command line input library (I tested prompt-sync, cli-select, inquirer, prompts and"
  },
  {
    "path": "test/resolvedAlias/foo.ts",
    "chars": 24,
    "preview": "export const foo = \"foo\""
  },
  {
    "path": "test/samples/Zabu.ts",
    "chars": 59,
    "preview": "export default class Zabu {\n\tyell() {\n\t\treturn \"ZABU\"\n\t}\n}\n"
  },
  {
    "path": "test/samples/backticksInComments.ts",
    "chars": 16,
    "preview": "// `/a`\n// `/b`\n"
  },
  {
    "path": "test/samples/coco.ts",
    "chars": 18,
    "preview": "export default 11\n"
  },
  {
    "path": "test/samples/dotImport/dotImport.ts",
    "chars": 76,
    "preview": "import { randomExportedValue } from \".\"\n\nexport default randomExportedValue\n"
  },
  {
    "path": "test/samples/dotImport/doubleDotImport/index.ts",
    "chars": 77,
    "preview": "import { randomExportedValue } from \"..\"\n\nexport default randomExportedValue\n"
  },
  {
    "path": "test/samples/dotImport/index.ts",
    "chars": 38,
    "preview": "export const randomExportedValue = 12\n"
  },
  {
    "path": "test/samples/fileConstants.ts",
    "chars": 66,
    "preview": "export default [__dirname, __filename, \"__dirname\", \"__filename\"]\n"
  },
  {
    "path": "test/samples/ipc/child.ts",
    "chars": 73,
    "preview": "process.send(\"Hello\")\nprocess.stdout.write(\"world\")\nprocess.disconnect()\n"
  },
  {
    "path": "test/samples/shebang.ts",
    "chars": 70,
    "preview": "#! hello i'm a shebang\nexport const shebang = \"Shebangs are stripped\"\n"
  },
  {
    "path": "test/server/index.ts",
    "chars": 253,
    "preview": "import polka from \"polka\"\nimport { message } from \"./message\"\n\nconst port = 3000\n\npolka()\n\t.get(\"/\", (_, response) => re"
  },
  {
    "path": "test/server/message.ts",
    "chars": 53,
    "preview": "export const message = \"Hellow from Polka server :)\"\n"
  },
  {
    "path": "test/tsconfig.json",
    "chars": 199,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"module\": \"esnext\",\n\t\t\"moduleResolution\": \"Node\",\n\t\t\"paths\": {\n\t\t\t\""
  },
  {
    "path": "tsconfig.json",
    "chars": 315,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t\"moduleResolution\": \"NodeNext\",\n\t\t\"skipLibCheck\": true,\n\t\t\"target\": \"es2020\",\n\t\t\"lib\": [\n\t\t\t\"e"
  }
]

About this extraction

This page contains the full source code of the digital-loukoum/esrun GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 42 files (41.8 KB), approximately 12.1k tokens, and a symbol index with 33 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!