GithubHelp home page GithubHelp logo

rsms / estrella Goto Github PK

View Code? Open in Web Editor NEW
1.1K 8.0 38.0 2.29 MB

Lightweight and versatile build tool based on the esbuild compiler

License: ISC License

JavaScript 35.02% Shell 8.85% TypeScript 56.13%
javascript compiler esbuild typescript

estrella's Introduction

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!

estrella's People

Contributors

atesgoral avatar benmerckx avatar gusso avatar myobie avatar puckey avatar rsms avatar vancura avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

estrella's Issues

How to get build result with write: false?

It's not an issue exactly but a question.

I want to modify esbuild output after the build. I was expecting that I can get the output with onEnd and buildResult. But it contains just errors and warnings (which are both empty arrays in my case because the build was successful).

Thanks for the excellent tool!

Terminating subprocess on Windows when "running" is flaky

"estrella": "^1.4.0",

This config:

estrella.build({
  watch: true,
  run: "node build/index.js" // this process not killing
})

Temporary solution:

let childProcess = null

estrella.build({
  onEnd: () => {
    if (childProcess) { 
      childProcess.kill('SIGINT')
    }

    childProcess = spawn('node', ['build/index.js'], {
      stdio: [process.stdin, process.stdout, process.stderr],
    })
  },
})

Metafile not being written

Hi! I was trying to figure out the bundle size of an app and found that estrella does not output the metafiles produced by esbuild.

The following build.js file is expected to produce a metafile. https://esbuild.github.io/api/#metafile

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

build({
  entry: "src/main.ts",
  outfile: "dist/foo.js",
  metafile: true,
  bundle: true,
})

When using the javascript API for esbuild, it is up to the consumer to parse the result and write the metadata to a file. Currently, estrella simply passes the metafile property to esbuild, but does nothing with it. To keep things simple i propose:

  • for config.outdir : create a metafile in the output directory named ${config.outdir}.meta.json
  • for config.outfile : create a metafile in the output directory named ${config.outfile}.meta.json

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)

Broken sourcemaps

After each build, patchSourceMap modifies the source-maps esbuild emitted, deleting the sourcesContent field and setting sourceRoot to a relative path from the output directory to the working directory. In practice, if the output file is a webap, this breaks source mapping; the relative path doesn't point anywhere sensible when it's treated as a UR. Commenting out the call to patchSourceMap fixes it, as does using sourcemap:"inline" (but the latter drastically increases the size of the output javascript bundle).

ESBuild `define` setting transformed incorrectly

Our ESBuild config for a client bundle contains an option that looks like:

  define: {
    "global": "window"
  }

When using ESBuild directly, this works as expected (the global identifier is replaced with window in some libraries that had weird expectations about their environment). Estrella, however, transforms the settings like this (estrella.js:575):

  for (let k in define) {
    define[k] = json(define[k])
  }

This causes global to be replaced with the string "window" instead of the token window, causing a crash on start.

TypeError: t is not a function on Windows

When we try to run this script:

// We're using require here because this is a JS file that Node loads directly.
/* eslint-disable @typescript-eslint/no-var-requires */
const { build, file, glob } = require('estrella');
const { join, resolve } = require('path');

const makeAllPackagesExternalPlugin = {
	name: 'make-all-packages-external',
	setup(build) {
		const filter = /^[^./]|^\.[^./]|^\.\.[^/]/; // Must not start with "/" or "./" or "../"
		build.onResolve({ filter }, ({ path }) => ({ path, external: true }));
	},
};

const common = {
	// Anything in node_modules should be marked as external.
	plugins: [makeAllPackagesExternalPlugin],

	minify: false,
	bundle: true,
	sourcemap: true,
	platform: 'node',
	target: ['node12'],

	// We are not going to run tslint in dev
	tslint: false,
};

const tasks = glob('./packages/*').map(async (dir) => {
	// Ignore .DS_Store files.
	if (dir.indexOf('/.DS_Store') >= 0) return;

	const { source, main } = JSON.parse(await file.read(join(dir, 'package.json'), 'utf-8'));

	// If source and/or main aren't defined, we don't need to build this package.
	if (source && main) {
		build({
			...common,

			entry: resolve(dir, source),
			outfile: resolve(dir, main),
		});
	}
});

Promise.all(tasks).catch((error) => {
	console.error(error);
	process.exit(1);
});

on Windows, we get:

Unhandled exception: TypeError: t is not a function
    at C:\[project folder]\node_modules\estrella\node_modules\miniglob\src\miniglob.js:59:18
    at Je (C:\[project folder]\node_modules\estrella\node_modules\miniglob\src\miniglob.js:269:23)
    at ln (C:\[project folder]\node_modules\estrella\node_modules\miniglob\src\miniglob.js:24:12)
    at Object.<anonymous> (C:\[project folder]\transpile.js:28:15)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47
error Command failed with exit code 2.

When we look for C:\[project folder]\node_modules\estrella\node_modules\miniglob\src\miniglob.js the file doesn't exist. How can we help debug this?

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!

Warn about naming my own build script `estrella.js`

After about an hour of confusion I realized I should not be calling my build.js as estrella.js, since the package's own estrella.js will mistake it for running itself (

== (DEBUG ? "estrella.g.js" : "estrella.js")
). This results in an unrelated missing <srcfile> argument (see ${prog} -help) error.

I think the README should at least warn about this, if the heuristic can't be changed. I imagine I'm not the only one calling the file estrella.js since there's the convention rollup.config.js and webpack.config.js.

Windows: Watching multiple builds

The docs say multiple builds in one file should be fine (under "Building multiple products at once"). While building works without issues, running --watch on such a build file currently streams a bunch of errors (whereas a single build watches fine):

Wrote dist/server.js (0B, 66.42ms)
Watching files for changes...
Unhandled promise rejection: Error: ENOENT: no such file or directory, open 'dist\.esbuild.ox0ayt1u7797gfpoxjymrn227jid09s.meta.json'
    at Object.openSync (fs.js:462:3)
    at Object.readFileSync (fs.js:364:35)
    at Be ...\node_modules\estrella\<estrella>\util.js:129:16)
    at y ...\node_modules\estrella\<estrella>\estrella.js:754:23)
    at i ...\node_modules\estrella\<estrella>\watch\watch.ts:52:25)
    at Object.nl ...\node_modules\estrella\<estrella>\watch\watch.ts:99:3)
    at rn ...\node_modules\estrella\<estrella>\estrella.js:768:23)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
PS ...> events.js:292    
      throw er; // Unhandled 'error' event
      ^

Error: EPIPE: broken pipe, write
    at Socket._write (internal/net.js:54:25)
    at doWrite (_stream_writable.js:403:12)
    at writeOrBuffer (_stream_writable.js:387:5)
    at Socket.Writable.write (_stream_writable.js:318:11)
    at Object.write ...\node_modules\typescript\lib\tsc.js:3471:36)
    at Object.onWatchStatusChange ...\node_modules\typescript\lib\tsc.js:88582:24)
    at reportWatchDiagnostic ...\node_modules\typescript\lib\tsc.js:89177:22)
    at Object.createWatchProgram ...\node_modules\typescript\lib\tsc.js:88937:9)
    at createWatchOfConfigFile ...\node_modules\typescript\lib\tsc.js:91126:19)
    at executeCommandLineWorker ...\node_modules\typescript\lib\tsc.js:90939:24)
Emitted 'error' event on Socket instance at:
    at errorOrDestroy (internal/streams/destroy.js:108:12)
    at onwriteError (_stream_writable.js:418:5)
    at onwrite (_stream_writable.js:445:5)
    at Socket._write (internal/net.js:58:14)
    at doWrite (_stream_writable.js:403:12)
    [... lines matching original stack trace ...]
    at reportWatchDiagnostic ...\node_modules\typescript\lib\tsc.js:89177:22) {
  errno: 'EPIPE',
  syscall: 'write',
  code: 'EPIPE'
}
events.js:292
      throw er; // Unhandled 'error' event
      ^

Error: EPIPE: broken pipe, write
    at Socket._write (internal/net.js:54:25)
    at doWrite (_stream_writable.js:403:12)
    at writeOrBuffer (_stream_writable.js:387:5)
    at Socket.Writable.write (_stream_writable.js:318:11)
    at Object.write ...\node_modules\typescript\lib\tsc.js:3471:36)
    at Object.onWatchStatusChange ...\node_modules\typescript\lib\tsc.js:88582:24)
    at reportWatchDiagnostic ...\node_modules\typescript\lib\tsc.js:89177:22)
    at Object.createWatchProgram ...\node_modules\typescript\lib\tsc.js:88937:9)
    at createWatchOfConfigFile ...\node_modules\typescript\lib\tsc.js:91126:19)
    at executeCommandLineWorker ...\node_modules\typescript\lib\tsc.js:90939:24)
Emitted 'error' event on Socket instance at:
    at errorOrDestroy (internal/streams/destroy.js:108:12)
    at onwriteError (_stream_writable.js:418:5)
    at onwrite (_stream_writable.js:445:5)
    at Socket._write (internal/net.js:58:14)
    at doWrite (_stream_writable.js:403:12)
    [... lines matching original stack trace ...]
    at reportWatchDiagnostic ...\node_modules\typescript\lib\tsc.js:89177:22) {
  errno: 'EPIPE',
  syscall: 'write',

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.)

How to turn off TypeScript diagnostics?

I have a project where I need to turn off TypeScript diagnostics for one build. I don't seem to be able to do it, no matter what I set for the tslint and/or tsc options. Any hints what I might try?

Spurious rebuilds in `watch` mode due to change in `.git/index.lock`

Some git operations take a lock in the .git directory, by doing operations on .git/index.lock, without changing anything else. In particular, git diff --cached --shortstat does this (and is run automatically by my shell prompt, so quite often). When this happens, Estrella picks up .git/index.lock as a change and triggers a rebuild:

[DEBUG] fsevent (raw) 'moved' '.git/index.lock' {
  path: '[MYPROJECTDIR]/.git/index.lock',
  flags: 71936,
  event: 'moved',
  type: 'file',
  changes: { inode: false, finder: false, access: false, xattrs: false }
}
[DEBUG] fsevent (derived) move .git/index -> .git/index.lock
[DEBUG] fsevent (raw) 'moved' '.git/index' {
  path: '[MYPROJECTDIR]/.git/index',
  flags: 67584,
  event: 'moved',
  type: 'file',
  changes: { inode: false, finder: false, access: false, xattrs: false }
}
[DEBUG] fsevent (raw) 'moved' '.git/index' {
  path: '[MYPROJECTDIR]/.git/index',
  flags: 67584,
  event: 'moved',
  type: 'file',
  changes: { inode: false, finder: false, access: false, xattrs: false }
}
[DEBUG] fsevent (derived) move .git/index -> .git/index
1 file changed: .git/index

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.

watch throws error

As of 1.2.3 -watch throws an error:

Unhandled promise rejection: Error: Cannot find module '/Users/dirk/Downloads/ex1/node_modules/estrella/dist/watch.js'
Require stack:
- /Users/dirk/Downloads/ex1/node_modules/estrella/dist/estrella.js
- /Users/dirk/Downloads/ex1/build.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:893:15)
    at Function.Module._load (internal/modules/cjs/loader.js:743:27)
    at Module.require (internal/modules/cjs/loader.js:965:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at /Users/dirk/Downloads/ex1/node_modules/estrella/<estrella>/aux.ts:23:11
    at en (/Users/dirk/Downloads/ex1/node_modules/estrella/<estrella>/estrella.js:733:11)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

image

To reproduce set up a minimal project like this:

npm init estrella sample
cd sample
npm i
npm start

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

Bumb to latest esbuild version.

Hello,

Esbuild have been updated to a new version, and they have added a nice feature.

esbuild pr

All tests seem to pass with the new esbuild version.
Can I create a PR or maybe you can make an update?

watch: when esbuild fails causes estrella to crash in certain circumstances

Repro:

main.js:

import { nonexisting } from "./lib"
console.log(nonexisting)

lib.js

export const a = 1

Run estrella:

$ estrella -bundle -o=out.js main.js
 > main.js: error: No matching export for import "nonexisting"
    1 │ import { nonexisting } from "./lib"~~~~~~~~~~~

../../dist/estrella.g.js: TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
    at refreshFiles (/Users/rsms/src/src/watch/watch.ts:62:29)
    at Object.watchFiles (/Users/rsms/src/src/watch/watch.ts:106:3)
    at build1 (/Users/rsms/src/src/estrella.js:769:23)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

This happens because esbuild doesn't produce a valid (i.e. what estrella expects) metadata file when the build fails.

When watching multiple TypeScript builds, make it easier to distinguish which one is which

I'm starting to try this project out and really like it so far. When watching multiple TypeScript builds however, it's not clear which typecheck is which:

grafik

I think something as simple as this could work:

————————————————————————————
TS: OK (admin/tsconfig.json)
————————————————————————————
————————————————————————————
TS: OK (tsconfig.json)
————————————————————————————

Build script for reference:

/** Build script to use esbuild without specifying 1000 CLI options */
const { build, cliopts } = require("estrella");
const glob = require("tiny-glob");

const [opts, args] = cliopts.parse(
	["react", "Build React sources"],
	["typescript", "Build typescript soruces"],
);

if (opts.react) {
	(async () => {
		await build({
			entryPoints: ["./admin/src/index"],
			tsconfig: "./admin/tsconfig.json",
			bundle: true,
			minify: true,
			outdir: "admin/build",
			sourcemap: "external",
			logLevel: "info",
			define: {
				"process.env.NODE_ENV": '"production"',
			},
		});
	})().catch(() => process.exit(1));
}

if (opts.typescript) {
	(async () => {
		let entryPoints = await glob("./src/**/*.ts");
		entryPoints = entryPoints.filter((ep) => !ep.endsWith(".d.ts"));
		await build({
			entryPoints,
			outdir: "build",
			bundle: false,
			minify: false,
			sourcemap: "external",
			logLevel: "info",
			platform: "node",
			format: "cjs",
			target: "node10",
		});
	})().catch(() => process.exit(1));
}

Exiting process after building, even with -watch enabled

As the title suggests I am using Estrella directly in the command-line, invoked through a package.json script. When doing so and applying the -watch flag it seems to exit the process directly after, without persisting (which I guess is what I expected). Just curious if -watch is only compatible through invoking Estrella via a build script?

This is with using Node v12.16.3. Cheers!

package.json

  "scripts": {
    "watch": "estrella -watch -minify -bundle ./src/index.js -o ./public/bundle.js"
  }

script invoke

yarn watch

console output

Wrote ./public/bundle.js (1.5kb, 3.89ms)
Watching files for changes...
Done in 0.13s.

Support any version of esbuild

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

estrella/package.json

Lines 44 to 45 in 94b75b5

"dependencies": {
"esbuild": ">=0.6.0 <0.8.0"

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.

node:events and more don't work

 > node_modules/discord.js/src/client/BaseClient.js:3:29: error: Could not resolve "node:events" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
    3 │ const EventEmitter = require('node:events');
      ╵                              ~~~~~~~~~~~~~
      ```

Still maintained?

The latest release was published on May 12, 2021, we have 13 open issues and 2 open PRs.

Feature: npm init

I created a little something to get started with a new estrella project: https://github.com/holtwick/create-estrella

You can now enter:

npm init estrella myproj1

To get started.

If you like the idea, I can also pass the project to you.

Other interesting templates might be:

  • Browser library
  • Node library
  • Simple restarting server, see #7

It might also be nice to have it right inside estrella command line tool itself, like:

estrella -init myproj1

Reduce postinstall noise

Hi, installing our monorepo dependencies this banner gets printed:

> [email protected] postinstall /Users/xyz/projects/abc/node_modules/estrella
> node dist/npm-postinstall.js


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Welcome to fun development with Estrella & esbuild!
Create a build.js file to get started. Here's a simple one:

#!/usr/bin/env node
const { build } = require("estrella")
const pkg = require("./package.json")
build({
  entry: "src/main.ts",
  outfile: pkg.main,
})

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I appreciate the friendly greeting, but it's quite a huge amount of output for one dependency among many. If every depency did this I'd run out of buffer in my terminal quite quickly.

Besides that, if I just installed it I'm already looking at the docs, with proper context. If I or a team member installed it earlier and am (re)installing all dependencies this not likely to be aligned to my current needs.

Can I suggest reducing it to two lines maximum, linking to the documentation instead?

Welcome to fun development with Estrella & esbuild!
Create a build.js file to get started: https://github.com/rsms/estrella#example-use

It bears mentioning: Estrella rocks, thank you! 🙏

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!

How to use node.js --inspect flag

I currently have the following setup:

build.js

const { build, cliopts } = require('estrella');
build({
    entry: 'src/app.ts',
    outfile: 'out/app.js',
    bundle: true,
    platform: 'node',
    watch: true,
    incremental: true,
    run: true
});

I run the app with node --inspect=0.0.0.0:9229 build.js.

When connecting via chrome://inspect I seem to be getting the main estrella process instead of my out/app.js. This is the output:

�[38;5;84mWrote out/app.js (774.8kB, 57.77ms)
estrella.js:6 �[38;5;84mWrote out/app.js (774.8kB, 57.77ms)
estrella.js:6 Watching files for changes...
estrella.js:6 Watching files for changes.

How to inspect the app.js instead?

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).

const testExeExtToo = process.platform.startsWith("win") && !/\.exe$/i.test(executableName)

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):

estrella/src/tslint.js

Lines 89 to 90 in 6d54414

Path.relative(process.cwd(), dirname(tsconfigFile)) + `.`
` Set tslint options.tsc="off" or pass -no-diag on the command line.`

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

estrella doesn't seem to be working within ESM context

As more and more dependencies are updating to ESM, I've figured it's time to bite the bullet and make my project ESM-ready as well.

But adding "type": "module" to my project's package.json leads to these two issues when running node build.js:

  1.   import {build, cliopts} from "estrella";
              ^^^^^
      SyntaxError: Named export 'build' not found. The requested module 'estrella' is a CommonJS module, which may not support all module.exports as named exports.
      CommonJS modules can always be imported via the default export, for example using:
      
      import pkg from 'estrella';
      const {build, cliopts} = pkg;
    

    the proposed workaround works though.

  2. TypeError: Cannot read properties of undefined (reading 'filename')
    this is due to this code:

    export const isCLI = module.id == "." || process.mainModule.filename == __filename

    process.mainModule does not seem to exist in ESM.

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)

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.

Add support tsconfig.json file paths resolving

"bundle" option is false

tsconfig.json:

{
	"compilerOptions": {
		"target": "es5",
		"lib": [
			"dom",
			"dom.iterable",
			"esnext"
		],
		"allowJs": true,
		"skipLibCheck": true,
		"strict": false,
		"forceConsistentCasingInFileNames": true,
		"noEmit": true,
		"esModuleInterop": true,
		"module": "esnext",
		"moduleResolution": "node",
		"resolveJsonModule": true,
		"isolatedModules": true,
		"jsx": "preserve",
		"baseUrl": ".",
		"paths": {
			"@lib": [
				"src/lib"
			],
			"@lib/*": [
				"src/lib/*"
			],

		}
	},
	"include": [
		"next-env.d.ts",
		"**/*.ts",
		"**/*.tsx"
	],
	"exclude": [
		"node_modules",
		"scripts"
	]
}

index.ts:

import {lol} from "@lib/lol";
console.log(lol())

@lib/lol.ts:

export const lol = () => {
	return 'Hello!'
}

output file:

import{lol as l}from"@lib/lol";console.log(l());
//# sourceMappingURL=index.js.map

but must be:

output file:

import{lol as l}from"./lib/lol";console.log(l());
//# sourceMappingURL=index.js.map

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'
  ]

Watch mode unhandled promise rejection (failed to parse JSON)

Running estrella in the watch mode in some cases produces the following error:

Wrote dist/app.js (129.9kB, 72.89ms) Watching files for changes... Unhandled promise rejection: Error: failed to parse dist\.esbuild.7czfvj1r7mzo41vo8xvg1utpi4512zaw2z.meta.json: Unexpected token ',' at Be (d:\temp\estrella-test-1\sample\node_modules\estrella\<estrella>\util.js:133:11) at y (d:\temp\estrella-test-1\sample\node_modules\estrella\<estrella>\estrella.js:754:23) at i (d:\temp\estrella-test-1\sample\node_modules\estrella\<estrella>\watch\watch.ts:52:25) at Object.nl (d:\temp\estrella-test-1\sample\node_modules\estrella\<estrella>\watch\watch.ts:99:3) at rn (d:\temp\estrella-test-1\sample\node_modules\estrella\<estrella>\estrella.js:768:23) at processTicksAndRejections (internal/process/task_queues.js:93:5)

If I run build without the watch mode it completes without errors with a valid JS and CSS files.
I've attached the sample project that reproduces the error.

sample.zip

Environment:
Windows 10
NodeJS - v14.15.0

Child not killed if it's a shell command

When I run build.js -watch with a build script like this:

build({
  outfile: './build/server/js/serverBundle.js',
  run: "node -r source-map-support/register -- ./build/server/js/serverBundle.js --settings settings.json",
  //...
})

this results in a shell process (sh -c node -r ...) which spawns a node process. Killing the watcher with ^C/SIGINT kills the shell process, but leaves the node process still running in the background. Changing this to

  run: ["node", "-r", "source-map-support/register", "--", "./build/server/js/serverBundle.js", "--settings settings.json"],

seems to work around the issue (it spawns node without the shell wrapper).

is it possible to have HMR?

So current by adding platform: "node", run: true we can restart process after build. Is it possible to have HMR here? I know it could be difficult to implement, but just was to ask... Webpack is too heavy today, I'm looking for an alternative to retain class instances and cached refs(states) and meanwhile the changed code updated.

Run without writing the file to disk

I am using estrella to generate scss files in my build process.

As a consequence, the outfile is not needed.

Using esbuild, I can do the following: esbuild --platform=node --target=node14.15.0 index.ts | node.

Would it be possible to use estrella's -run and -watch functions without writing the file to disk?

Can't run scripts with prompts

I have the following build script:

build.mjs

import estrella from 'estrella'

estrella.build({
  entry: 'example/src/cli.ts',
  bundle: true,
  platform: 'node',
  format: 'esm',
  splitting: true,
  outdir: 'lib/example-build',
  watch: true,
  run: 'node lib/example-build/cli'
})

example/src/cli.ts

import inquirer from 'inquirer'

const main = async () => {
  const { message } = await import('./message')
  message()
  inquirer
    .prompt([
      {
        type: 'input',
        name: 'username',
        default: 'World'
      }
    ])
    .then(async answers => {
      console.log(`Hello ${answers.username}!`)
    })
}

main()

example/src/message.ts

export const message = () => console.log('Hey there! What`s your name?')

When i run build.mjs, the build works fine, but the output script doesn't work properly due to it's inquirer prompt, looks like estrella doesn't wait for the prompt to be answered before exiting

Am i missing something that is on the docs, or maybe doing something that i'm not supposed to?

Thanks!

Suggestion - Improve type definitions generation example to support extended tsconfig files.

A lot of monorepo projects will make use of the extends property in their tsconfig files in order to keep things DRY where possible. It took me a few minutes to understand what was wrong with my config.

Quick example:

{
  "extends": "../../tsconfig.settings.json",
  "compilerOptions": {
    "composite": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"]
}

...tsconfig.compilerOptions,

Here are the changes I've had to make to the generateTypeDefs function in the example.

function generateTypeDefs(config, entryfiles, outdir = 'dist') {
  const filenames = Array.from(
    new Set(
      (Array.isArray(entryfiles) ? entryfiles : [entryfiles]).concat(
        config.include || [],
      ),
    ),
  ).filter((v) => v);
  log.info('Generating type declaration files for', filenames.join(', '));
  let extendedCompilerOptions = {};
  if (config.extends) {
    const rawExtendedTsConfig = fs.readFileSync(config.extends);
    const extendedTsConfig = JSON.parse(rawExtendedTsConfig);
    if (extendedTsConfig.compilerOptions) {
      extendedCompilerOptions = extendedTsConfig.compilerOptions;
    }
  }
  const compilerOptions = {
    ...extendedCompilerOptions,
    ...config.compilerOptions,
    moduleResolution: undefined,
    declaration: true,
    outDir: outdir,
  };
  const program = ts.ts.createProgram(filenames, compilerOptions);
  const targetSourceFile = undefined;
  const writeFile = undefined;
  const cancellationToken = undefined;
  const emitOnlyDtsFiles = true;
  program.emit(
    targetSourceFile,
    writeFile,
    cancellationToken,
    emitOnlyDtsFiles,
  );
  log.info('Wrote', glob(`${outdir}/*.d.ts`).join(', '));
}

Hope it helps.

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.

Use incremental build feature of esbuild

Thanks again for this great tool @rsms 🙏

To speed up estrella further when continuously rebuilding a project, it's now possible to use the new incremental build feature.

Note: I've done some quick scan of the code base if this is already the case but couldn't find any indication for this. If you're already using esbuild's incremental build feature, please just close this issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.