Build scripts and CLI for bundling libraries with esbuild (standalone packages or monorepos).
Requirements: Node.js 16+
npm install --save-dev @omi-io/pkg-scripts- Programmatic API:
runBuild()runClean()runAlias()runSyncFiles()loadPackageConfig()getEntriesFromPackageExports()getBuildOutputGlobsFromConfig()(for custom tooling; Nx plugin uses this internally)syncAliasDirGitignore()(optional helper used byrunAlias()for alias folders)
- CLI:
omi-io-pkg buildomi-io-pkg cleanomi-io-pkg aliasomi-io-pkg sync-filesomi-io-pkg-release-with-config <cmd> [...args](injects release types intonx.jsontemporarily and restores the file after command run)
Sub-entries (all targets besides the package root) are built like this:
- Read
package.json→exports. Whenexportsis a plain object, walk its keys in declaration order. Skip keys in the ignore set: always"."and"./package.json", plus any listed inignoreExportsin the config file (if present). Map each remaining key to a folder undersourceDirby stripping a leading./(for example"./math"→math,"./nested/deep"→nested/deep). - Merge
entriesfrompkg-scripts.config.jsonwhen that key is an array: append names that are not already in the list from step 1 (export-derived names stay first, inexportskey order).
If exports is missing or is a single string (shorthand for the main entry only), step 1 yields an empty list; use entries in the config file to declare sub-entries explicitly.
Place it in the package root next to package.json. You can rely on exports alone and skip this file when you do not need extra options.
Example:
{
"ignoreExports": ["check"],
"ignoreFilesEntries": ["internal"],
"entries": ["internal"],
"sourceDir": "src",
"sourceIndex": "index.ts",
"outDir": "dist",
"formats": ["cjs", "esm"]
}entries:string[](optional)- Sub-entry folders relative to
sourceDir. - For each entry, scripts expect
<sourceDir>/<entry>/<sourceIndex>. - When present, these names are merged with entries inferred from
package.json→exports(export order first, then any new names fromentries, without duplicates). Non-array values are ignored.
- Sub-entry folders relative to
ignoreExports:string[](optional)- Extra
exportskeys to skip when building the list (same strings as inpackage.json, or a short name without./, e.g."check"is treated like"./check"). - Default ignores always apply:
"."and"./package.json".
- Extra
ignoreFilesEntries:string[](optional)- Entries to force-exclude from
runSyncFiles()output (package.json->files). - Accepts both short names (
"check") and export-style keys ("./check"). - Useful when an entry should still be built/aliased but must not be published as
<entry>/package.json.
- Entries to force-exclude from
sourceDir:string(default:"src")- Source root for entry points.
sourceIndex:string(default:"index.ts")- Entry filename used for root and sub-entries.
outDir:string(default:"dist")- Build output base directory.
formats:("cjs" | "esm" | "iife")[](default:["cjs", "esm"])- esbuild output formats.
loadPackageConfig({ cwd, configFile }) reads package.json and, if it exists, pkg-scripts.config.json (or the file name you pass as configFile). getEntriesFromPackageExports(packageJson, ignoreExportKeys?) applies the same export-key rules as the loader (defaults for ignored keys match the loader when you omit the second argument).
This package also ships reusable Nx release helpers for conventional commits:
@omi-io/pkg-scripts/release-conventional-commits- exports
COMMIT_TYPESandTYPE_SUBJECTS
- exports
@omi-io/pkg-scripts/changelog-type-first-renderer- changelog renderer that keeps "type section + scope-first line item" style
@omi-io/pkg-scripts/changelog-scope-filtered-renderer- same type-first renderer, but project changelog entries are filtered by package
scopeCommitName - useful in monorepos to avoid mixing commits from other package scopes (
root, other libs, release scope, etc.)
- same type-first renderer, but project changelog entries are filtered by package
- CLI
omi-io-pkg-release-with-config- injects
release.conventionalCommits.typesinto workspacenx.jsonfor one command execution and restores the original file
- injects
Example in CI:
omi-io-pkg-release-with-config yarn nx release version 1.2.3 --projects=my-lib
omi-io-pkg-release-with-config yarn nx release changelog 1.2.3 --projects=my-libExample renderer in nx.json:
{
"release": {
"changelog": {
"projectChangelogs": {
"renderer": "@omi-io/pkg-scripts/changelog-scope-filtered-renderer"
}
}
}
}Example for @acme/colors:
- Add dependency (same as above, or declare in
package.json):
{
"devDependencies": {
"@omi-io/pkg-scripts": "^2.1.0"
}
}- Add scripts:
{
"scripts": {
"build": "omi-io-pkg clean && omi-io-pkg build && tsc && omi-io-pkg alias && omi-io-pkg sync-files"
}
}- List sub-entries under
package.json→exports(recommended), and addpkg-scripts.config.jsononly when you needignoreExports, extraentries, or other options.
Nx can skip your build script when the output is served from the cache. By default, inferred outputs often cover only dist/, not the top-level alias folders that omi-io-pkg alias creates from exports. After a cache hit, those folders may be missing until the next full run.
Optional fix (one-time per workspace): register the Nx plugin from this package so every library that uses omi-io-pkg in scripts gets correct build outputs (merged with any nx.targets.build.outputs you already set in that package.json).
In the repo root nx.json:
{
"plugins": [
{
"plugin": "@omi-io/pkg-scripts/nx",
"options": {}
}
]
}Requirements: nx and @nx/devkit compatible with your workspace (v19+). They are optional peerDependencies of @omi-io/pkg-scripts; install them in the workspace root as usual for Nx.
The plugin matches **/package.json outside node_modules, keeps packages whose any script mentions omi-io-pkg and that define a build script, then sets:
targets.build.outputsfromdist(oroutDirinpkg-scripts.config.json) plus one{projectRoot}/<entry>/**per merged entry (same rules asexports+ configentries)targets.build.inputswith conservative defaults (default,^production, and explicit package-local source/config globs) to reduce stale cache hits in workspaces with customizednamedInputs
runAlias()creates<package>/<entry>/package.jsonaliases for subpath imports (one folder per merged entry name).runSyncFiles()rewritespackage.json->filesto["dist", "<entry>/package.json", ...]using the merged entry list (fromexports+ optional configentries), then removes any entries listed inignoreFilesEntries.- Git: those stub directories are build outputs.
runAlias()/omi-io-pkg aliaswrites<package>/<entry>/.gitignorefor every generated alias folder, with the fixed content*(newline-terminated). This keeps generated files in alias directories out ofgit statuswithout requiring package-root ignore rules. - CLI commands resolve config from the current working directory.
- Ensure tests pass:
npm test - Inspect the tarball (no
test/or stray files):npm run pack:dry-run - Dry-run publish:
npm publish --dry-run - Bump version:
npm version patch(orminor/major), then push tags if you use Git tags - Log in to the npm scope:
npm login(account must have publish rights for@omi-io) - Publish:
npm publish
prepublishOnly runs tests automatically on npm publish.