Files
scriptc/docs/content/docs/dependencies.mdx
T
2026-10-20 10:11:51 -05:00

95 lines
6.2 KiB
Plaintext

---
title: "npm Dependencies"
---
Programs can import npm packages the same way they do under Node.js. scriptc compiles each package your program uses to native code along with your own code. The executable doesn't read `node_modules` at runtime.
Package support is partial. Packages that scriptc can't compile are reported at build time with a diagnostic, so check new dependencies with [`scriptc coverage`](/docs/coverage) before relying on them.
## Use a package
Create `tool.ts`:
```ts:tool.ts
import { Command } from "commander";
const program = new Command();
program
.name("greet")
.argument("<name>", "who to greet")
.option("-u, --upper", "use uppercase")
.action((name: string, opts: { upper?: boolean }) => {
const text = `hello, ${name}`;
console.log(opts.upper ? text.toUpperCase() : text);
});
program.parse();
```
Install the dependency, then build and run the executable:
```console
$ npm install commander
$ scriptc build tool.ts -o greet >/dev/null
$ ./greet ada --upper
HELLO, ADA
```
Your callback and the commander implementation both compile to native code. No extra flags are needed.
<span id="package-resolution-and-embedding" />
<span id="runtime-behavior" />
<span id="engine-and-builtin-modules" />
## Package resolution
scriptc finds packages in `node_modules` with Node.js's resolution rules, including the export conditions in `package.json`, and compiles their dependencies too. TypeScript workspace packages compile as part of your program, including `.js` import specifiers that resolve to `.ts` sources.
Type-only imports follow Node.js's type stripping. `import type` and `export type … from` statements are erased, so a package or module named only by them supplies types and isn't compiled or loaded; it can even be a package with no JavaScript, such as `type-fest`. A clause whose specifiers are all marked `type`, such as `import { type Options } from "./config.ts"`, still loads its module, as it does under Node.js.
## Which packages compile
scriptc compiles a package automatically when it:
- ships its own type declarations, or is typed by its DefinitelyTyped `@types/` package, or is a dependency of another package that has no declarations at all, and
- ships readable JavaScript: unminified, or minified by a bundler whose helpers keep their names.
The declarations check your program; the package's JavaScript compiles with the types scriptc infers from it. A program that doesn't typecheck against the inferred types, for example because an `@types/` package describes another release, fails the build at that use.
A package that doesn't meet these requirements, or that uses something scriptc can't compile, gets a package-specific diagnostic that explains why. The usual fixes are to switch to a version or alternative package that compiles, or to replace the call with code you write yourself.
`graceful-fs` is the exception: it compiles as `node:fs` instead of from its JavaScript, with one [documented difference](/docs/limitations#graceful-fs).
<span id="value-conversion" />
<span id="static-package-compilation-experimental" />
<span id="type-declarations-and-boundary-restrictions" />
## Explicit package selection
Some packages your program imports directly ship no declarations, so their types can only be inferred from their JavaScript. To try compiling one of them, name it with `--npm-static`:
```console
$ scriptc build app.ts --npm-static=three -o app >/dev/null
```
Naming a package skips the declaration requirement above, but the package still has to compile. Add a trailing `...` to select a package together with every package it depends on, for example `--npm-static=fs-extra...`. `--npm-static=auto` uses the default automatic selection. The option accepts a comma-separated list and can be repeated.
## CommonJS packages
CommonJS packages compile with Node.js's module semantics. Each `require()` evaluates its module at the call, once, and later calls return the cached exports. A package can assign `module.exports` and run code before it requires its dependencies. If that code reads a `const` or `let` require binding before its `require` runs, the read throws the same `ReferenceError` as Node.js. A `var` binding that earlier code can reach is reported at build time. A `require()` of a JSON file, such as a package's own `package.json`, returns one object parsed at build time, shared by every module that requires it. A require cycle compiles when each of its modules assigns its exports before requiring anything, as semver's classes and esbuild's per-file output do, and uses the other modules only inside functions that its top-level code can't call. Bundles built by esbuild compile with their bundled CommonJS modules and lazily initialized ES modules, each evaluated once, at its first use, as under Node.js. Other require cycles are reported at build time. The [limitations](/docs/limitations) page lists the supported `require` forms.
## Unsupported code inside a package
A JavaScript package can contain operations scriptc can't compile, often in functions your program never calls. These don't fail the build: each one compiles to code that throws a runtime error if it ever runs. Coverage reports list them as deferred sites, so review the list and test the parts of the package your program uses.
## Provenance sources (experimental)
`--provenance-sources` compiles a package from the source at the commit its npm provenance attestation names, instead of from its published JavaScript. This lets packages written in TypeScript compile from their original TypeScript. Packages without a usable attestation compile from their installed files as usual.
## Upgrading from scriptc 0.2
scriptc 0.2 had a `--dynamic` mode that embedded a JavaScript engine in the executable to run code that didn't compile, including packages that failed native compilation. scriptc 0.3 removed `--dynamic` and the embedded engine, so all application and package code now compiles to native code.
If you built with `--dynamic`, remove the flag and run `scriptc coverage` on your entry file. The report lists the operations and packages that need a change; rewrite those sites or replace the package. If you link [native program objects](/docs/native-objects) into your own builds, use the runtime pack that matches the 0.3 compiler.