Lightweight and versatile build tool based on the esbuild compiler

Overview

Estrella is a lightweight and versatile build tool based on the fantastic esbuild TypeScript and JavaScript compiler.

  • Rebuild automatically when source files change
  • Build multiple projects at once, in parallel
  • TypeScript diagnostics run in parallel
  • TypeScript diagnostics are optional even for TypeScript projects
  • Ability to remap TSXXXX TypeScript diagnostic severity levels, for example to treat some issues as warnings instead of errors
  • Scriptable — run any JavaScript you want as part of your build process
  • Can run your program automatically when it's rebuilt
  • Well-tested code
  • Fast!

See estrella.d.ts for API documentation.

Estrella tries hard to retain the blazing startup speed of esbuild. Being just a single file, with only a dependency on esbuild, estrella starts very quickly. time estrella -help completes in about 50ms with NodeJS 12. Building a simple example with time examples/minimal/build.js completes in about 65ms.

Unlike some other "builders" Estrella does not use a config file or require you to make changes to your package.json file. Instead, Estrella recognizes & embraces the fact that most projects have unique build requirements. You run Estrella from a script (instead of Estrella running a script or config file.) Essentially you create a "build.js", or lolcat.js—name it whatever you want—script in which you invoke estrella. This turns your script into a fully-featured CLI program with access to a rich API exposed by the estrella module.

Example use

  1. Add Estrella to your project: npm install -D estrella
  2. Create a build.js file in your project directory:
#!/usr/bin/env node
const { build } = require("estrella")
build({
  entry: "src/main.ts",
  outfile: "out/foo.js",
  bundle: true,
  // pass any options to esbuild here...
})

Invoke your build script: (assumes chmod +x build.js has been set)

$ ./build.js -watch
Wrote out/foo.js (419 bytes, 6.84ms)
Watching files for changes...

And that's it for basic use.

See the examples directory for more examples which you can use as templates for new projects.

See evanw/esbuild/lib/shared/types.ts for documentation of esbuild options.

TypeScript diagnostics

If there is a tsconfig.json file in the project directory, or if build({tsc:true}) is set, TypeScript's tsc is run in parallel to esbuild, checking types and providing diagnostics:

$ ./build.js
Wrote out/foo.js (419 bytes, 7.11ms)
src/main.ts:2:7 - warning TS6133: 'count' is declared but its value is never read.

2   let count = 4
        ~~~~~

TS: OK   1 warning

What tsc program is run? estrella will ask nodejs to find typescript in node_modules. If found, tsc from that package is used. Otherwise, tsc from the environment PATH is used. This way there is no hard dependency on typescript for estrella.

estrella allows remapping the severity of TypeScript diagnostics and even filtering things out completely. This is particularly useful when you are converting a codebase to a stricter set of rules but don't want your build to fail. For example, let's say that you enable noImplicitAny in your tsconfig.json file. If you just run tsc on your project, it will fail with errors. Using estrella these errors can be treated as warnings instead, still making a loud appearance on screen but not causing an error exit code.

$ ./build.js
Wrote out/foo.js (14.6kb, 11.07ms)
src/main.ts:212:24 - error TS7006: Parameter 'ev' implicitly has an 'any' type.

212   window.onmousemove = ev => { grid.origin = [ev.clientX, ev.clientY] }
                           ~~
TS: 1 error
$ echo $?
1

To make this a warning, add a rule to build() in your build script:

#!/usr/bin/env node
const { build } = require("estrella")
build({
  entry: "src/main.ts",
  outfile: "out/foo.js",
  bundle: true,
  tsrules: {
    7006: "WARNING",
  }
})

Now if we try to build again:

$ ./build.js
Wrote out/foo.js (14.6kb, 10.42ms)
src/main.ts:212:24 - warning TS7006: Parameter 'ev' implicitly has an 'any' type.

212   window.onmousemove = ev => { grid.origin = [ev.clientX, ev.clientY] }
                           ~~
TS: OK   1 warning
$ echo $?
0

The program exits successfully while still logging the issue.

tsrules is an object of type { [tscode:number] : "IGNORE"|"INFO"|"WARN"|"ERROR" } which maps TS diagnostic codes to:

  • "IGNORE" completely ignore and don't even log it.
  • "INFO" log it as information, unless the -quiet flag is set.
  • "WARN" log it as a warning
  • "ERROR" log it as an error, causing the program's exit code to be !0.

"ERROR" is the default for most issues. Too list predefined tsrules, run: node -p 'require("estrella").defaultTSRules'. Rules which you provide take precedence, so if there are any predefined rules you'd like to change, simply set those in your tsrules object.

In case you need to disable TypeScript diagnostics for a project with a tsconfig.json file, you can set tslint:false in your build config:

build({
  // your regular options ...
  tslint: false,
})

Examples and feature documentation

Your build script becomes a CLI program

When using estrella as a library from a build script, your build script becomes a program with command-line options:

$ ./build.js -help
usage: ./build.js [options]
options:
  -w, -watch         Watch source files for changes and rebuild.
  -g, -debug         Do not optimize and define DEBUG=true.
  -r, -run           Run the output file after a successful build.
  -sourcemap         Generate sourcemap.
  -inline-sourcemap  Generate inline sourcemap.
  -no-color          Disable use of colors.
  -no-clear          Disable clearing of the screen, regardless of TTY status.
  -no-diag           Disable TypeScript diagnostics.
  -color             Color terminal output, regardless of TTY status.
  -diag              Only run TypeScript diagnostics (no esbuild.)
  -quiet             Only log warnings and errors but nothing else.
  -silent            Don't log anything, not even errors.
  -estrella-version  Print version of estrella and exit 0.
  -estrella-debug    Enable debug logging of estrella itself.

You can define your own custom CLI options and parse arbitrary arguments using the cliopts object exported by the estrella module:

#!/usr/bin/env node
const { build, cliopts, file } = require("estrella")
const [ opts, args ] = cliopts.parse(
  ["c, cat" , "Prints a nice cat."],
  ["file"   , "Show contents of <file>.", "<file>"],
)
opts.cat && console.log(stdoutStyle.pink(ASCII_cat))
if (opts.file) {
  console.log(`contents of file ${opts.file}:`)
  console.log(await file.read(opts.file, "utf8"))
}
build({ ... })

Ask for help to see your options documented:

./build.js -h
usage: ./build.js [options]
options:
  [...common estrella options here...]
  -c, -cat           Prints a nice cat.
  -file=<file>       Show contents of <file>.

For a full example, see examples/custom-cli-options

Watching source files for changes

One of the key features of Estrella is its ability to watch source files for changes and rebuild only the products needed. It does this in cooperation with esbuild which provides "perfect" information about the source file graph for a given build product (via esbuild.metafile). Estrella then uses this information to watch the relevant source files for changes and trigger a rebuild. Either set watch in your config or pass -watch on the command line:

$ ./build.js -watch
Wrote out/main.js (341B, 8.03ms)
Watching files for changes...

# [make an edit to a source file]
1 file changed: foo.ts
Wrote out/main.js (341B, 10.18ms)
...

Running your program

Estrella can run and manage sub processes, making it easy to run and restart your program upon a successful build. Simply set run in your config or pass -run on the command line:

$ ./build.js -run
Hello world

Combining -run with -watch makes for a powerful "exploratory programming" setup where changes to your source files are compiled and the results of running the program shown.

$ ./build.js -watch -run
Wrote out/main.js (341B, 8.21ms)
Running out/main.js [98609]
Hello world
out/main.js exited (0)

# [make an edit to a source file]
1 file changed: message.ts
Wrote out/main.js (341B, 8.21ms)
Running out/main.js [98609]
Hello future

Estrella is good at handling processes and can make a few valuable guarantees:

  • A running process is always terminated before Estrella terminates. The only exception to this is if the estrella process is killed with an uncapturable signal like SIGKILL.
  • A running process that is restarted is always terminates before a new instance is launched. This is important if your program relies on exclusive access to resources like TCP ports or UNIX sockets.
  • Secondary subprocesses spawned by a running process are always terminated when the process Estrella controls is terminated. This guarantee only applies to OSes that support signaling process groups (most POSIX OSes like Linux, macOS, BSD, etc.)

"run" can be configured to run arbitrary commands by specifying run in your config.

Examples: (effective process invocation in comment)

// build config               // effective program invocation
run: true                     // [node, outfile] (same as `-run` on the command line)
run: ["deno", "file name.js"] // ["deno", "file name.js"]
run: "./prettier foo.js"      // runs script "./prettier foo.js" in a shell

When run is set in your config, the product will be run no matter how you invoke your build script. If you want to execute a more complex command that just node outfile while still only running it when -run is passed on the command line, conditionally enable run in your build script like this:

#!/usr/bin/env node
const { build, cliopts } = require("estrella")
const p = build({
  entry: "main.ts",
  outfile: "out/main.js",
  run: cliopts.run && ["/bin/zsh", "-e", "-c", "echo **/*.ts"],
})

./build.js -run will run your command as specified while simply ./build.js won't cause the program to run.

Building multiple products at once

estrella can build multiple variants of your program at once. Example build.js script:

#!/usr/bin/env node
const { build } = require("estrella")
const common = {
  entry: "src/main.ts",
  bundle: true,
}
build({
  ...common,
  outfile: "out/foo.min.js",
})
build({
  ...common,
  outfile: "out/foo.debug.js",
  sourcemap: true,
  debug: true,
})

Then run the script to build both an optimized product and a debug product:

$ ./build.js
Wrote out/foo.min.js (445 bytes, 6.67ms)
Wrote out/foo.debug.{js,js.map} (2.4kb, 10.59ms)
TS: OK

TypeScript diagnostics are run for all unique tsconfig.json files (or project directory in the absence of a tsconfig.json file), so in this case we get just one report, as is expected.

In fact, since estrella is just a simple library, you can really do whatever you want in your build script.

Pre-processing and post-processing

Setting onStart and/or onEnd in a build config allows you to hook into the esbuild cycle. onStart(config, changedFiles) is called when a build starts and onEnd(config, result) when a build finishes. This works in both -watch mode and regular "one shot" mode.

These callbacks can optionally be async (i.e. return a Promise) which estrella will await. This gives you the ability to perform detailed pre-processing and post-processing, like requesting some stuff from the internet.

Example build script using onEnd to show desktop notifications with node-notifier in watch mode:

#!/usr/bin/env node
const { build } = require("estrella")
const notifier = require("node-notifier")
build({
  entry: "src/main.ts",
  outfile: "out/foo.js",
  onEnd(config, result) {
    config.watch && notifier.notify({
      title: config.title,
      message: result.errors.length > 0 ?
        `Build failed with ${result.errors.length} errors` :
        `Build succeeded`
    })
  },
})

Watching arbitrary files for changes

estrella comes with functions for scanning and watching any files or directories for changes, making it easy to work with other source files not handled by esbuild. Like for example CSS or code generation.

Example build script:

#!/usr/bin/env node
const { build, scandir, watch, cliopts } = require("estrella")

build({
  entry: "src/main.ts",
  outfile: "out/foo.js",
})

function generateCode(file) {
  console.log(`generate ${file} -> ${file}.js`)
  // replace with actual logic
}

// generate all files initially
const dir = "src", filter = /\..*$/i
scandir(dir, filter, {recursive:true}).then(files => {
  files.map(generateCode)
  // in watch mode, generate files as they change
  cliopts.watch && watch(dir, {filter, recursive:true}, changes => {
    changes.map(c => generateCode(c.name))
  })
})

Running a livereload web server

Say you are building a website. You may want to run a HTTP server while in watch mode which automatically reloads your website as you develop it. Simply run a web server like serve-http in your build script:

#!/usr/bin/env node
const { build, cliopts } = require("estrella")
build({
  entry: "src/main.ts",
  outfile: "docs/app.js",
})
// Run a local web server with livereload when -watch is set
cliopts.watch && require("serve-http").createServer({
  port: 8181,
  pubdir: require("path").join(__dirname, "docs"),
})

Now when you run your build script in watch mode a web server is run as well:

$ ./build.js -w
serving ./ at http://localhost:8181/
Wrote docs/app.js (914 bytes, 12.44ms)
TS: OK

estrella as a program

estrella can also be used directly as a program:

$ estrella src/foo.ts -o foo.js
Wrote foo.js (222 bytes, 7.91ms)
TS: OK
$ estrella -h
usage: estrella [options] <srcfile> ...
options:
  -w, -watch           Watch source files for changes and rebuild.
  -g, -debug           Do not optimize and define DEBUG=true.
  -r, -run             Run the output file after a successful build.
  -sourcemap           Generate sourcemap.
  -inline-sourcemap    Generate inline sourcemap.
  -no-color            Disable use of colors.
  -no-clear            Disable clearing of the screen, regardless of TTY status.
  -no-diag             Disable TypeScript diagnostics.
  -color               Color terminal output, regardless of TTY status.
  -diag                Only run TypeScript diagnostics (no esbuild.)
  -quiet               Only log warnings and errors but nothing else.
  -silent              Don't log anything, not even errors.
  -estrella-version    Print version of estrella and exit 0.
  -estrella-debug      Enable debug logging of estrella itself.
  -o=,-outfile=<file>  Write output to <file> instead of stdout.
  -bundle              Include all dependencies.
  -minify              Simplify and compress generated code.
  -outdir=<dir>        Write output to <dir> instead of stdout.
  -esbuild=<json>      Pass arbitrary JSON to esbuild's build function.

<srcfile> is a filename, or "-" for stdin.

See estrella -h for more details.

Developing for Estrella

Like any respectable compiler, Estrella of course builds itself.

Setup instructions:

git clone https://github.com/rsms/estrella.git
cd estrella
npm install

Build instructions:

  • Build debug products: ./build.js -g (add -w for incremental compilation)
  • Build release products: ./build.js (add -w for incremental compilation)
  • Build release products and run all tests: ./test/test.sh (or npm test)
  • Build debug products and run all tests: ./test/test.sh -debug

Contributing to Estrella

Contributions are very welcome! When contributing, please follow these guidelines:

  • Use welcoming and inclusive language
  • Be respectful of differing viewpoints and experiences
  • Gracefully accept constructive criticism
  • Focus on what is best for the community
  • Show empathy towards other community members

Types of contributions and how best to make them:

  • Proposal for a new feature: Open an issue and start a conversion. Please do not create a PR until we've had time to discuss how to best approach the change.

  • Fix for a bug: Please open a PR with your fix and if you can, include a test that fails without your change but passes with it.

  • Minor changes like spelling mistakes: Open an issue and point out the concern. Please do not create a PR for small things like spelling mistakes.

Thank you for being a great person & contributor!

Comments
  • nodemon like option

    nodemon like option

    I love estrella, hope you will continue to work on it.

    Some users probably have a scenario where they would like to start a the resulting code using node any time the source changes. Usually this is done using nodemon. I wrote a little workaround, not being sure if this is a smart way to do it:

    #!/usr/bin/env node
    
    const pkg = require('./package.json')
    const common = require('./build.config.js')
    const { spawn } = require('child_process')
    const { build } = require('estrella')
    
    let node
    
    build({
      ...common,
      entry: 'src/index.js',
      outfile: pkg.main,
      onEnd(config, result) {
        if (config.watch) {
          if (node) {
            node.kill('SIGHUP')
            node = null
          }
          if (result.errors.length > 0) {
            console.error(`Build failed with ${result.errors.length} errors`)
          } else {
            console.info(`Build succeeded. Launching 'node ${pkg.main}'`)
            node = spawn('node', [pkg.main], {
              shell: true,
              stdio: 'inherit',
            })
          }
        }
      },
    })
    

    In case it is, it would be great to get an estrella option to say, start the outFile with node, like:

    #!/usr/bin/env node
    
    const pkg = require('./package.json')
    const common = require('./build.config.js')
    const { buildAndRun } = require('estrella')
    
    buildAndRun({
      ...common,
      entry: 'src/index.js',
      outfile: pkg.main
    })
    

    Thanks!

    opened by holtwick 10
  • Watching subdirectories unavailable on Linux

    Watching subdirectories unavailable on Linux

    Hey @rsms I wasn't immediately sure if this was something on estrella's end or a limitation of esbuild, so excuse my assumptions if they're off!

    When I have an entry point file which is importing a module from a sub-directory, estrella only seems to execute a rebuild when something changes in the entry-point file. However if I move my files from the sub-directory to the same directory as the entry-point file, things will rebuild when any file is changed.

    I guess my expectation would be to have files, which are referenced by the entry-point file but are not in the base-level directory, trigger a rebuild when they change.

    An example set up running on Node 12.16.3.

    ./src/index.js

    import MyModule from "./modules/my-module";
    
    (function () {
      
       MyModule.foo();
       // returns string bar
    
    })();
    

    ./src/modules/my-module.js

    const MyModule = (function () {
    
       return {
           foo: function () {
               return "bar";
           }
       };
    
    })();
    
    export default MyModule;
    

    ./build.js

    #!/usr/bin/env node
    
    const path = require("path");
    const { build, cliopts } = require("estrella");
    const { createServer } = require("serve-http");
    
    const options = {
      entry: "./src/index.js",
      outfile: "./public/bundle.js",
      bundle: true,
      minify: true
    };
    
    build(options);
    
    cliopts.watch && createServer({
      port: 3000,
      pubdir: path.join(__dirname, "./public"),
    });
    

    Thus far whenever I change anything within my-module.js a rebuild won't trigger. Only when the change is within index.js.

    opened by jonathontoon 9
  • Watch mode breaks for esbuild v0.9.0

    Watch mode breaks for esbuild v0.9.0

    After upgrading to esbuild 0.9.0 I'm receiving this error message

    error: "metafile" must be a boolean

    Looking at the release notes it seems that the metafile attribute now only needs to be true rather than pointing to a file.

    If I put {metafile: true} into my estrella build config I'm receiving this though:

    The "path" argument must be of type string or an instance of Buffer or URL. Received type boolean (true)

    opened by danielberndt 8
  • Watch mode doesn't handle filesystem changes

    Watch mode doesn't handle filesystem changes

    Steps to reproduce

    1. Configure a build with a list of entries (e.g. by glob)
    2. Run the build with -w/--watch
    3. Rename one of the entries

    Expected

    Well, it's not clear what's expected because the API doesn't support glob entries or a way to provide them dynamically. But ideally there would be a way to react to filesystem changes, somehow.

    Observed

    error: Could not resolve [... previous path to the file ...]

    Mitigation

    For anyone looking to work around this limitation, I've used a flag called once (so watch mode isn't automatically enabled), run an initial build, then call the watch function to run subsequent builds, which retrieves a new list of entries on each change. (This is probably not the most efficient, as the watch function provides information about what changed, but I've honestly lost too much time today to worry about that right now.)

    bug enhancement 
    opened by eyelidlessness 8
  • Windows clone issues with src/aux.ts

    Windows clone issues with src/aux.ts

    Hi Rasmus,

    It seems it's currently impossible to clone on Windows. I tried SSH and HTTPS, no luck.

    D:\Repos\Libraries>git clone [email protected]:rsms/estrella.git
    Cloning into 'estrella'...
    Enter passphrase for key '/c/Users/vacla/.ssh/id_rsa':
    remote: Enumerating objects: 280, done.
    remote: Counting objects: 100% (280/280), done.
    remote: Compressing objects: 100% (176/176), done.
    remote: Total 1003 (delta 158), reused 199 (delta 97), pack-reused 723
    Receiving objects: 100% (1003/1003), 1.43 MiB | 793.00 KiB/s, done.
    Resolving deltas: 100% (579/579), done.
    error: invalid path 'src/aux.ts'
    fatal: unable to checkout working tree
    warning: Clone succeeded, but checkout failed.
    You can inspect what was checked out with 'git status'
    and retry with 'git restore --source=HEAD :/'
    

    Is there anything specific about this file that stops the repo from cloning on Windows?

    Enjoy the break! :)

    Vaclav

    opened by vancura 6
  • Support any version of esbuild

    Support any version of esbuild

    I would like to use Estrella, but I am concerned that it doesn’t support the latest esbuild:

    https://github.com/rsms/estrella/blob/94b75b5989f136648c8552956274f680aa3e8bb9/package.json#L44-L45

    Would you consider moving the esbuild dependency into peerDependencies? Ideally as "esbuild": "*" so that users are free to install any version of esbuild, and in the README you can note which versions you’ve tested Estrella against.

    enhancement 
    opened by GeoffreyBooth 5
  • Windows support (i.e. directly via cmd.exe or powershell, not WSL)

    Windows support (i.e. directly via cmd.exe or powershell, not WSL)

    Having a tsconfig.json results in:

    ...\build.js: ReferenceError: prog is not defined
        at ...\node_modules\estrella\dist\estrella.js:2:12202
        at new Promise (<anonymous>)
        at V (...\node_modules\estrella\dist\estrella.js:2:11825)
        at n (...\node_modules\estrella\dist\estrella.js:2:6134)
        at Aa (...\node_modules\estrella\dist\estrella.js:25:4614)
        at ...\node_modules\estrella\dist\estrella.js:25:1271
        at new Promise (<anonymous>)
        at da (...\node_modules\estrella\dist\estrella.js:25:1228)
        at build (...\node_modules\estrella\dist\estrella.js:26:801)
        at Object.<anonymous> (...\build.js:3:1)
    

    ~~Which I assume might actually be a bug in esbuilds minification. The distributed estrella.js does not define prog (I'm thinking the name was minified but this reference was not).~~ Edit: it is actually never defined in the source

    The actual error it was trying to print refers to "tsc" not being found in path. I actually see a windows check here but it's not used further down in the function. As a note, I installed typescript through yarn locally which actually creates a ./node_modules/.bin/tsc.cmd file (not exe).

    https://github.com/rsms/estrella/blob/6d54414c235a22b1b5a710b73cc63aca07d8708c/src/util.js#L18

    Messing around to see the message also led me to find this (a + was forgotten here since we get a runtime TypeError: "." is not a function):

    https://github.com/rsms/estrella/blob/6d54414c235a22b1b5a710b73cc63aca07d8708c/src/tslint.js#L89-L90

    opened by benmerckx 5
  • Only execute

    Only execute "run" command after onEnd is done

    Currently, estrella runs the "run" command before "onEnd" is complete. Sometimes, onEnd can perform actions like moving directories which is useful to execute before the run command is executed. Perhaps this behavior could be configurable.

    opened by Utsav2 4
  • DevTools fail to show source files for SourceMap

    DevTools fail to show source files for SourceMap

    Trying out the web-livereload example on windows and getting following error while trying to browse source code from DevTools

    Could not load content for http://localhost:8181/src/app.ts (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)

    Do you know what changes are required to config or serve-http to enable linking source files

    opened by TipTopSailor 4
  • Allow stdin in place of entry

    Allow stdin in place of entry

    Currently, one cannot use stdin because of this estrella error which requires at least one entry point. The stdin contents will be added as an entryPoint by esbuild further down the line.

    opened by myobie 4
  • Watch error: the filename, directory name, or volume label syntax incorrect

    Watch error: the filename, directory name, or volume label syntax incorrect

    Getting the following error for watch mode:

    PS C:\projects\xxx> estrella -w index.js -o dist.js     
    error: Failed to write to output file: open C:\projects\xxx\.esbuild.?.meta.json: The filename, directory name, or volume label syntax is incorrect.
    1 error
    Watching files for changes...
    estrella.js: TypeError: Cannot convert undefined or null to object
        at Function.keys (<anonymous>)
        at i (C:\Users\dfcre\AppData\Roaming\nvm\v14.3.0\node_modules\estrella\<estrella>\watch\watch.ts:55:29)
        at Object.nl (C:\Users\dfcre\AppData\Roaming\nvm\v14.3.0\node_modules\estrella\<estrella>\watch\watch.ts:99:3)
        at rn (C:\Users\dfcre\AppData\Roaming\nvm\v14.3.0\node_modules\estrella\<estrella>\estrella.js:768:23)
        at processTicksAndRejections (internal/process/task_queues.js:97:5)
    
    bug help wanted Windows 
    opened by dy 4
  • Register hook for node -r and mocha -r

    Register hook for node -r and mocha -r

    See https://github.com/rsms/estrella/issues/59 for more info.

    Example usage:

    • node -r estrella/register program.ts
    • mocha -r estrella/register 'src/**/*.test.ts'

    This is much faster than using ts-node/register :smile:

    opened by ccorcos 5
  • Mocha / Node.js inline require option?

    Mocha / Node.js inline require option?

    Admittedly, I'm not an expert on how this is supposed to work, but I know that you can run Typescript via node -r 'ts-node/register' as well as run tests via mocha -r 'ts-node/register'. But this is much slower than esbuild/estrella...

    Any suggestions on how to do this with Estrella? It would be a nice feature if its simple enough to build.

    enhancement good first issue 
    opened by ccorcos 5
  • upgrade to esbuild 0.15.x.

    upgrade to esbuild 0.15.x.

    This PR upgrades esbuild to ^0.15.x. One of the main changes here is that esbuild now respects alwaysStrict in the tsconfig.json, so I've updated several tests to include "use strict"; in the built files.

    opened by lorenries 0
  • Relative imports aren't rewritten relative to outfile

    Relative imports aren't rewritten relative to outfile

    I'd expect the imports are rewritten or copied in no-bundle mode otherwise the result can't be executed.

    // index.js
    const { build, cliopts } = require("estrella")
    const p = build({
      	entry: "foo/src/entry.ts",
      	outfile: "foo/dist/server.js",
    	watch: true,
    	cwd: ".",
    	incremental: true,
    	clear: true,
    	logLevel: "debug",
    	target: "node16",
    	format: "cjs",
    	tsc: false,
      	run: ["node", "foo/dist/server.js"],
    })
    
    // entry.ts
    import "./config";
    console.log("hello")
    

    Directory structure

    .
    ├── foo/
    │   ├── dist/
    │   │   └── server.js
    │   └── src/
    │       ├── config.ts
    │       └── entry.ts
    └── index.js
    
    

    Error:

    Error: Cannot find module './config.js'
    ...
     code: 'MODULE_NOT_FOUND',
      requireStack: [
        '/home/starptech/repositories/foo/dist/server.js'
      ]
    
    
    opened by StarpTech 0
  • Doesn't find TSC in pnpm monorepo

    Doesn't find TSC in pnpm monorepo

    Unhandled exception: Error: spawn /home/starptech/repositories/foo/node_modules/.bin/tsc ENOENT
        at Process.ChildProcess._handle.onexit (node:internal/child_process:283:19)
        at onErrorNT (node:internal/child_process:478:16)
        at processTicksAndRejections (node:internal/process/task_queues:83:21)
    
    
    .
    └── foo/
        ├── node_modules
        ├── packages/
        │   └── foo/
        │       ├── node_modules/
        │       │   └── .bin/
        │       │       └── tsc
        │       └── index.js
        └── pnpm-workspace.yaml
    
    opened by StarpTech 0
  • Unhandled promise rejection: Error: start() called while command is running

    Unhandled promise rejection: Error: start() called while command is running

    Unhandled promise rejection: Error: start() called while command is running

    ..REDACTED../node_modules/estrella/dist/estrella.js:8 +u),new Error(command exited with status ${l}${u})}let c=i.buffer();return t?c.toString(t):c})}wait(t,r){return t===void 0||t<=0?this.promise:this._waitTimeout(t,(n,o,i)=>(a.debug(()=>${this} wait timeout reached; killing process),n.message="Cmd.wait timeout",this.kill(r).then(()=>i(n))))}signal(t,r){let n=this._checkproc();if(r=="group")try{return process.kill(-n.pid,t),!0}catch(o){}return n.kill(t)}async kill(t="SIGTERM",r=500,n){let o=this._checkproc();return this.signal(t,n||"group")?r<=0?this.promise:this._waitTimeout(r,(i,s)=>(a.debug(()=>${this} kill timeout reached; sending SIGKILL),o.kill("SIGKILL"),this.promise.then(s))):o.exitCode||0}toString(){return this.process?Cmd[${this.pid}]:"Cmd"}_checkproc(){if(!this.process)throw new Error(Cr);return this.process}_rejectAndKill(t){this._reject(t)}_waitTimeout(t,r){return new Promise((n,o)=>{let i=!1;return this.promise.then(s=>{i||n(s)}),xr(this.promise,t,s=>{i=!0,r(s,n,o)})})}};Ue.prototype.start=function(){let t=this;if(t.running)throw new Error("start() called while command is running");t.exitCode=-1,t.promise=new Promise((l,c)=>{t._resolve=l,t._reject=c});let r=null,n=null;t.stdin instanceof Buffer?r="pipe":gr(t.stdin)?typeof t.stdin.stream.fd=="string"?r=t.stdin.stream:(r="pipe",n=t.stdin.stream):r=t.stdin;let o={stdio:[r||"ignore",t.stdout===process.stdout?1:t.stdout||"ignore",t.stderr===process.stderr?2:t.stderr?t.stderr:"ignore",...t.extraFiles],cwd:t.dir?Yt(t.dir):void 0,env:t.env,shell:t.shell,windowsHide:t.windowsHide,detached:!ye},i=Sr.spawn(t.command,t.args,o);if(i.pid===void 0){t.process=null,t.pid=0;let l=Mn(t);throw t._reject(l),l}if(t.running=!0,t.process=i,t.pid=i.pid,i.on("exit",t._onexit),i.on("error",t._reject),a.debug(()=>${t} started (${L(t.command)})),i.stdin)if(t.stdin instanceof Buffer){let l=new Pr.PassThrough;l.end(t.stdin),l.pipe(i.stdin),i.stdin=null}else n&&(n.pipe(i.stdin),i.stdin=null);return!i.stdin&&!i.stdout&&!i.stderr&&i.stdio.length<4?null:{stdin:i.stdin?mt(i.stdin):null,stdout:i.stdout?De(i.stdout):null,stderr:i.stderr?De(i.stderr):null,extraFiles:i.stdio.slice(3).map(l=>pr(l)?De(l):mr(l)?mt(l):null)}};function Mn(e){let t="",r="unspecified error";if(e.shell==!1){try{re.accessSync(e.dir,re.constants.R_OK|re.constants.X_OK),(re.statSync(e.command).mode&re.constants.S_IFREG)==0?t="EACCES":t="EIO"}catch(o){t=o.code||"ENOENT"}r=ht(t)||r}if(!t){try{re.accessSync(e.dir,re.constants.R_OK|re.constants.X_OK),t="EIO"}catch(o){t=o.code||"ENOENT"}r=ht(t)||r,t&&(r=r+"; cmd.dir="+L(e.dir))}t||(t="UNKNOWN");let n=new Error(failed to spawn process ${L(e.command)} (${t} ${r}));return n.code=t,n}var yt=S(require("fs")),_r=S(require("os")),Tr=new Map;function Oe(e,t){let r=o=>yt.writeSync(process.stderr.fd,o+ ^ at Ue.start (..REDACTED../node_modules/estrella/dist/estrella.js:8:1016) at Fr.onEndBuild (..REDACTED../node_modules/estrella/dist/estrella.js:11:1492) at runMicrotasks () at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Or.e.onEnd (..REDACTED../node_modules/estrella/dist/estrella.js:11:371) at async Kn.P (..REDACTED../node_modules/estrella/dist/estrella.js:38:220) Looks like you found a bug in Estrella!

    opened by prafed 0
Releases(v1.4.1)
  • v1.4.1(May 12, 2021)

    Improvements:

    • adds support for source on stdin (both in script mode and CLI mode; see test/stdin) #26
    • improves watch incremental build performance (by utilizing esbuild's incremental option)
    • upgrades esbuild dependency to 0.11.x which brings many improvements and expands BuildConfig.entry and .entryPoints to accept a record mapping output files to input files. #36
    • removes the noisy postinstall message #34

    Changes in behavior:

    • when running a program after building it, any user-provided onEnd callback is now invoked & awaited before starting the sub process. In Estrella <=1.4.0 onEnd was called concurrently with starting the subprocess. #38

    Bug fixes:

    • config.entry would in some cases not be set in the config object passed to onStart and onEnd callbacks.

    Install this version:

    npm i -D [email protected]
    

    Compare v1.4.0–1.4.1...

    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(Mar 12, 2021)

    • Improved file rename and move tracking; Estrella now tracks and handles rename of entryPoint files during -watch mode (only verified on macOS). #29
    • Breaking API change to the watch function (see details below)
    • Makes detection of CLI mode more robust. #31
    • Fixes a bug specific to Microsoft Windows where in some cases the default "projectID" would interfere with file system rules. #15
    • An internal change to how define entries in BuildConfig are handled might cause some build scripts to behave differently. See below for details. #23
    • Improved source maps. You can now embed the source code into the source map by setting sourcesContent:true in your build config. #28
    • Fixes a minor bug where a BuildProcess promise would resolve to undefined, when in watch mode and the user calls cancel() on the promise. This would effectively look like the build failed while in practice cancelling the build should not signal failure.
    • Fixes an issue where custom watchers (from the watch function) would react to self-originating file modifications.

    Breaking API change to WatchCallback

    WatchCallback used with the watch function now receives a list of objects describing file changes in more detail. In Estrella <=1.3x this function received a list of strings.

    If you use watch here's an example of how to quickly update your code to work with Estrella >=1.4:

    Before: (for Estrella <=1.3x)

    watch(".", watchOptions, filenames => {
      doSomethingWithFilenames(filenames)
    })
    

    After: (for Estrella >=1.4.0)

    watch(".", watchOptions, changes => {
      doSomethingWithFilenames(changes.map(c => c.name))
    })
    

    Note: This does not affect the onStart callback which continues to receive a plain list of filenames.

    Change in behavior of BuildConfig.define

    An internal change to how define entries in BuildConfig are handled might cause some build scripts to behave differently. Prior to Estrella 1.4.0, values assigned to define would be implicitly JSON encoded. For example, {define:{foo:"window"}} would be equivalent to const foo = "window" in the compiled output. With Estrella 1.4.0 the same define config is equivalent to const foo = window (notice that window is a name here, not a string.)

    Here's how you can fix up a build script making use of define:

    Before: (for Estrella <=1.3x)

    const version = "1.2.3"
    build({
      define: { VERSION: version }
    })
    

    After: (for Estrella >=1.4.0)

    const version = "1.2.3"
    build({
      define: { VERSION: JSON.stringify(version) }
    })
    

    The rationale here is that sometimes you want to define a name with a variable value from the environment. For example, you could do this now to load a JSON file at runtime: define:{ somedata: "require('somedata.json')" } (which in prior versions of Estrella would end up just naming the string "require('somedata.json')".)

    Source code(tar.gz)
    Source code(zip)
  • v1.3.3(Mar 10, 2021)

  • v1.3.1(Mar 10, 2021)

  • v1.3.0(Dec 19, 2020)

    npm install -D [email protected]

    • Fixes a bug where Estrella would crash after esbuild failed to build, while in watch mode. #22
    • Adds the ability to output to stdout when using the API: build({ outfile:"-" }) #17
    • Adds the ability to output straight to the API's onEnd function instead of writing to file. Simply omit outfile and outdir from your call to build({ ... }); results are provided via the onEnd's second argument (results). #17 #18
    • Adds a -silent option which completely silences output logging
    • Relaxes esbuild compatibility — you can now specialize the exact esbuild version you want by adding an entry to your own project's "dependencies" (or similar) entry of package.json. #19
    • Improvements to watching files and running output on Windows #12
    Source code(tar.gz)
    Source code(zip)
Owner
Rasmus
Personal Software, languages, compilers, Humans & Computers, and other fun things. Past professional life at Figma, Facebook, Spotify, Dropbox, etc.
Rasmus
JIT Compiler is a open source online code compiler. You can run more than 40+ most popular programming languages in your browser just-in-time using jitcompiler.

JIT Compiler is a open source online code compiler. You can run more than 40+ most popular programming languages in your browser just-in-time using jitcompiler.

Rajkumar Dusad 36 Jan 5, 2023
⚡️ Fast, lightweight and powerful development server for esbuild ⚡️

esbuild-server ⚡️ Fast, lightweight and powerful development server for esbuild ⚡️ Zero dependencies besides esbuild API proxy support Live reload SPA

Joel Arvidsson 22 Sep 14, 2022
A Versatile, Extensible Dapp Boilerplate built with Rainbowkit, Next.js, and Chakra-ui.

rainplate • A Versatile, Extensible Dapp Boilerplate built with Rainbowkit, Next.js, and Chakra-ui. Getting Started Click use this template to create

White Noise 12 Nov 22, 2022
A dockerfile to build ARM cross-compiler for Tauri (React Typescript)

tauri-arm A dockerfile to build a ARM cross-compiler for Tauri(React Typescript Template). MacOS Windows 10 Linux Setup $ yarn Installation Please bu

Shih-Cheng Huang 10 Sep 6, 2022
🚀 Using top-level await in AWS Lambda with TypeScript, esbuild and Serverless Framework

?? Top-level await in AWS Lamba with TypeScript Articles https://dev.to/oieduardorabelo/top-level-await-in-aws-lamba-with-typescript-1bf0 https://medi

Eduardo Rabelo 17 Nov 23, 2022
esbuild plugin to generate mix-manifest.json file compatible with Laravel Mix.

esbuild-mix-manifest-plugin An esbuild plugin to generate a mix-manifest.json compatible with Laravel Mix. Installation You can install the plugin via

Stefan Zweifel 6 Dec 25, 2022
An esbuild plugin to inject your application's version number or today's date into your files

esbuild-plugin-version-injector An esbuild plugin to inject your application's version number or today's date into your files This plugin was inspired

Favware 6 Dec 6, 2022
An esbuild plugin for simplifying global API calls.

esbuild-plugin-global-api This plugin is still experimental, not recommended for production. It may break your code in some cases. An esbuild plugin f

null 4 Nov 15, 2022
基于React开发的新一代web调试工具,支持React组件调试,类似于Chrome Devtools。A Lightweight, Easy To Extend Web Debugging Tool Build With React

English | 简体中文 基于React开发的移动web调试工具 更新日志 简单易用 功能全面 易扩展 高性能 使用cdn方式,一键接入 类Chrome devtools, 内嵌React开发者工具,支持日志,网络,元素,代理,存储,性能等, 具有更好的网络捕获能力和丰富的日志展现形式 暴露内部

腾讯TNTWeb前端团队 236 Dec 25, 2022
A markdown parser and compiler. Built for speed.

Marked ⚡ built for speed ⬇️ low-level compiler for parsing markdown without caching or blocking for long periods of time ⚖️ light-weight while impleme

Marked 28.9k Jan 7, 2023
A "Basic-to-Lisp" compiler. But Basic is not real Basic, and Lisp is not real Lisp.

Basic2Lisp A "Basic-to-Lisp" compiler. But Basic is not real Basic, and Lisp is not real Lisp. Syntax Print-Sth Put some-value to standard output. PRI

Hana Yabuki 5 Jul 10, 2022
A Laravel Blade parser, compiler, and static analyzer written in TypeScript.

Blade Parser This library provides a Laravel Blade parser written in TypeScript. In addition to being able to parse Blade template files, this library

Stillat 7 Jan 4, 2023
An oversimplification of the TypeScript Compiler API for defining and generating source files.

Tanu ?? A simplified abstraction of the TypeScript Compiler API for defining and generating source files. Tanu ?? Why? What does Tanu mean? ?? How do

Aries 124 Oct 29, 2022
A Compiler npm Package.

vcompiler ?? Version 1.x is live ! ?? Introducation It is the npm package for the compilation of the code. Currently it supports the following program

Ankit Choudhary अंकित चौधरी 2 May 30, 2022
The full power of the Go Compiler directly in your browser, including a virtual file system implementation. Deployable as a static website.

Static Go Playground Features Full Go Compiler running on the browser. Supports using custom build tags. Incremental builds (build cache). Supports mu

null 25 Jun 16, 2022
Patches the AssemblyScript compiler to utilize WASI imports instead of Web APIs.

WASI shim for AssemblyScript Patches the AssemblyScript compiler to utilize WASI imports instead of Web APIs. Note that this shim also serves a higher

The AssemblyScript Project 37 Dec 23, 2022
The repository shows the compiler (simulator) of the Little Man Computer, which also contains some programs in the LMC programming language for implementing different functions.

Little Man Computer The repository shows the compiler (simulator) of the Little Man Computer, which also contains some programs in the LMC programming

Cow Cheng 2 Nov 17, 2022
Web-based tool to build gradient data for retro platforms using a visual editor

Gradient Blaster https://gradient-blaster.grahambates.com Gradient Blaster is a web-based tool to build gradient data for retro platforms using a visu

Graham Bates 5 Dec 13, 2022