Skip to content

kekyo/screw-up

Repository files navigation

screw-up

Simply package metadata inserter for NPM.

screw-up

Project Status: Active – The project has reached a stable, usable state and is being actively developed. License: MIT npm version


(For Japanese language/ζ—₯本θͺžγ―こけら)

What is this?

Looking for a simple solution to apply versions to TypeScript projects and NPM packages? screw-up could be the tool you need.

It is a Vite plugin that automatically inserts banner comments containing package metadata (name, version, description, author, license, etc.) into bundled files, and a CLI tool that applies them to NPM packages.

The Vite plugin automatically reads metadata from package.json:

{
  "name": "my-awesome-library",
  "version": "2.1.0",
  "description": "An awesome TypeScript library",
  "author": "Jane Developer <jane@example.com>",
  "license": "Apache-2.0",
  "repository": {
    "url": "https://github.com/user/my-awesome-library"
  }
}

To insert banner header each bundled source files (dist/index.js and etc.):

/*!
 * name: my-awesome-library
 * version: 2.1.0
 * description: An awesome TypeScript library
 * author: Jane Developer <jane@example.com>
 * license: Apache-2.0
 * repository.url: https://github.com/user/my-awesome-library
 * git.commit.hash: c94eaf71dcc6522aae593c7daf85bb745112caf0
 */
// Your bundled code here...

You may have noticed the line git.commit.hash:. That's right, if your project is managed by Git (it is, right?), you can also insert commit IDs, branch information, and tag information. Most importantly, if a version is applied to a Git tag, you can automatically reflect that version tag in the version field of package.json. In other words, you can manage version numbers using only Git tags!

This calculates the version number by measuring the commit height to the current HEAD based on the last applied version tag.

git-versioning

Instead of using npm pack, you can use the CLI tool screw-up to generate packages, which will automatically apply the collected metadata to the NPM package's package.json:

# Generate a package using `screw-up` command
$ screw-up pack
my-awesome-library-2.1.0.tgz

Key Features

  • Automatic metadata extraction: Reads metadata from package.json automatically.
  • Workspace support: Works with monorepos and automatically inherits metadata from parent packages.
  • Flexible output: Specify exactly which metadata to include and in what order.
  • TypeScript metadata generation: Can automatically generates TypeScript files with metadata constants for use in your source code.
  • Declaration import normalization: Rewrites emitted declaration imports for NodeNext-compatible package consumers and adjusts .d.ts.map mappings.
  • Git metadata extraction: Automatically extracts Git commit hash, tags, branches, and version information from local Git repository.
  • Supported pack/publish CLI interface: When publishing using this feature, the package is generated after applying the above processing to package.json.

Installation

Install as a devDependencies since screw-up does not require any runtime code.

npm install -D screw-up

Or, make it available as a global command:

npm install -g screw-up

Usage

The configuration method is described below. If you want to quickly learn about recommended configurations and operation methods, refer to the "Recommended configuration" section.

Setup the Vite plugin

Add the plugin to your vite.config.ts:

import { defineConfig } from 'vite';
import screwUp from 'screw-up';

export default defineConfig({
  plugins: [
    screwUp(), // Uses default output keys
  ],
  // ...
});

Custom Output Keys

You can specify which metadata fields to include and in what order:

import { defineConfig } from 'vite';
import screwUp from 'screw-up';

export default defineConfig({
  plugins: [
    screwUp({
      outputKeys: ['name', 'version', 'license'], // Only include these fields
    }),
  ],
  // ...
});

This will generate a banner with only the specified fields:

/*!
 * name: my-awesome-library
 * version: 2.1.0
 * license: Apache-2.0
 */

When no outputKeys are specified, the plugin uses these metadata keys with exact sequence: name, version, description, author, license, repository.url and git.commit.hash.

Working with Nested Objects

The plugin automatically flattens nested objects using dot notation. For example package.json declarations:

{
  "name": "my-package",
  "author": {
    // Nested metadata
    "name": "Jane Developer",
    "email": "jane@example.com"
  },
  "repository": {
    // Nested metadata
    "type": "git",
    "url": "https://github.com/user/my-package"
  }
}

You can reference nested fields in your outputKeys:

screwUp({
  outputKeys: ['name', 'author.name', 'author.email', 'repository.url'],
});

Results in:

/*!
 * name: my-package
 * author.name: Jane Developer
 * author.email: jane@example.com
 * repository.url: https://github.com/user/my-package
 */

TypeScript Metadata Generation

The plugin can generate TypeScript files containing metadata constants that you can import and use in your source code:

import { defineConfig } from 'vite';
import screwUp from 'screw-up';

export default defineConfig({
  plugins: [
    screwUp({
      // Enable metadata file generation
      outputMetadataFile: true,
      // Custom path (optional)
      outputMetadataFilePath: 'src/generated/packageMetadata.ts',
      // Keys to include (optional)
      outputMetadataKeys: [
        'name',
        'version',
        'description',
        'author',
        'license',
      ],
    }),
  ],
  // ...
});

This generates src/generated/packageMetadata.ts with TypeScript constants:

// This file is auto-generated by screw-up plugin
// Do not edit manually

export const name = 'my-awesome-library';
export const version = '2.1.0';
export const description = 'An awesome TypeScript library';
export const author = 'Jane Developer <jane@example.com>';
export const license = 'Apache-2.0';

You can then import and use these constants in your source code:

import { name, version } from './generated/packageMetadata.js';

// Output: my-awesome-library v2.1.0
console.log(`${name} v${version}`);

export function getLibraryInfo() {
  return { name, version };
}

Key Sanitization

Keys with special characters are automatically sanitized to valid TypeScript identifiers:

  • repository.url β†’ repository_url
  • custom-key β†’ custom_key
  • 123invalid β†’ _123invalid

Git Metadata

The plugin automatically extracts Git metadata from your local Git repository and makes it available as metadata keys.

Available Git Metadata

  • git.version: Automatically calculated version based on Git tags and commit depth (see below)
  • git.commit.hash: Full commit hash of the current commit
  • git.commit.short: Short commit hash (first 7 characters)
  • git.commit.date: Commit date in ISO format
  • git.commit.message: Commit message
  • git.tags: Array of all tags pointing to the current commit
  • git.branches: Array of branches containing the current commit

By default, the version value in package.json is automatically overridden by the value of git.version. Therefore, you can apply versions based on Git tags even when generating NPM packages (using the CLI).

Version calculation

The Git version calculation follows below algorithm:

  1. Tagged commits: Uses the tag version directly (e.g., v1.2.3 --> 1.2.3)
  2. Untagged commits: Detects depth from the furthest ancestor tag and increments the last version component
  3. Modified working directory: When uncommitted changes exist, increments the version by one
  4. No tags found: Defaults to 0.0.1 and increments for each commit

Example with Git metadata:

screwUp({
  outputKeys: [
    'name',
    'version',
    'git.version',
    'git.commit.hash',
    'git.commit.short',
  ],
});

Results in:

/*!
 * name: my-awesome-library
 * version: 2.1.0
 * git.version: 2.1.0
 * git.commit.hash: c94eaf71dcc6522aae593c7daf85bb745112caf0
 * git.commit.short: c94eaf7
 */

Build date insertion

buildDate is metadata indicating the build time, inserted in ISO format with the time zone. It is output when specified in outputKeys / outputMetadataKeys or when {buildDate} is specified in the CLI's format command.

Default import fixups for CJS/ESM

Default imports can behave inconsistently across ESM/CJS boundaries. Screw-up rewrites default import statements into a helper call and passes whether the dependency is ESM.

For example below code:

// Importing CJS packages that do not expose ESM definitions by default
import dayjs from 'dayjs';

This is converted as follows due to a screw-up:

// Convert the default import of a CJS package to a safe format
import __screwUpDefaultImportModule0 from 'dayjs';
const dayjs = __resolveDefaultExport(__screwUpDefaultImportModule0, false);

Behavior depends on the output format and dependency kind:

Output Dependency Generated code fragment Runtime behavior
ESM CJS import default + __resolveDefaultExport(default, false) fallback to module/default
ESM ESM import * as ns + __resolveDefaultExport(ns, true) requires default, throws if missing
CJS CJS require() + __resolveDefaultExport(default, false) fallback to module/default
CJS ESM require() + __resolveDefaultExport(ns, true) fallback to module/default

This keeps ESM default-missing errors visible in ESM output while allowing CJS output to recover from common interop mismatches. The dependency kind follows Node-style resolution (exports with import/node/default, or main + type). Only project source files are transformed (not node_modules), and type-only imports are ignored.

If you need to disable this behavior, apply option with fixDefaultImport: false.

Declaration import fixups for published types

Many projects prefer extensionless relative imports in source code and rely on bundlers to resolve them. However, published declaration files are consumed directly by TypeScript, and moduleResolution: "node16" / "nodenext" expect the specifier to match the final JavaScript module name.

When enabled, screw-up rewrites emitted relative imports in .d.ts, .d.mts, and .d.cts files to the correct runtime suffix:

Declaration output Rewritten suffix
.d.ts .js
.d.mts .mjs
.d.cts .cjs

For example, an emitted declaration like:

export { screwUp } from './vite-plugin';
export type { ScrewUpOptions } from './types';

is normalized to:

export { screwUp } from './vite-plugin.js';
export type { ScrewUpOptions } from './types.js';

Directory-style imports are also resolved against the actual emitted declaration files, so ./nested can become ./nested/index.js when appropriate. Bare specifiers such as package names are left unchanged, and related .d.ts.map files are adjusted to keep generated-column mappings aligned after the rewrite.

If you need to disable this behavior, apply option with fixDeclarationImportExtensions: false.


Advanced Usage

Monorepo Support

The plugin automatically detects workspace configurations and inherits metadata from parent packages:

my-monorepo/
β”œβ”€β”€ package.json          # Root package with shared metadata
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ ui/
β”‚   β”‚   └── package.json  # Child package
β”‚   └── core/
β”‚       └── package.json  # Child package

Child packages automatically inherit metadata from the root package, with the ability to override specific fields:

// Root package.json
{
  "name": "my-monorepo",
  "version": "1.0.0",
  "author": "Company Team",
  "license": "MIT"
}

// packages/ui/package.json
{
  "name": "@my-monorepo/ui",
  "description": "UI components library"
}

When building the UI package, the banner will include:

/*!
 * name: @my-monorepo/ui
 * version: 1.0.0
 * description: UI components library
 * author: Company Team
 * license: MIT
 */

Supported Workspace Types

The plugin automatically detects and supports:

  • npm/yarn workspaces: Detected via workspaces field in package.json
  • pnpm workspaces: Detected via pnpm-workspace.yaml file
  • Lerna: Detected via lerna.json file

CLI Usage

The screw-up package includes a command-line interface for packaging and publishing your projects.

Examples

# Generate TypeScript metadata file
screw-up metadata

# Dump how `package.json` is resolved (JSON)
screw-up dump

# Format a template with package metadata placeholders
screw-up format -i ./template.txt ./output.txt

# Pack with custom README and limited inheritance
screw-up pack --readme ./docs/DIST_README.md --inheritable-fields "version,license"

# Pack with custom peerDependencies prefix
screw-up pack --peer-deps-prefix "~"

# Pack without peerDependencies replacement
screw-up pack --no-replace-peer-deps

# Build and publish with dry run
screw-up publish --dry-run

# Publish to beta channel
screw-up publish --tag beta

# Publish scoped package as public
screw-up publish --access public

# Pack to custom directory then publish
screw-up pack --pack-destination ./release
screw-up publish ./release/my-package-1.0.0.tgz

For help with any command:

screw-up --help
screw-up metadata --help
screw-up dump --help
screw-up format --help
screw-up pack --help
screw-up publish --help

MEMO: While it can also be used in projects unrelated to NPM projects, such as C,C++ or other projects, in that case, you might also be interested in screw-up-native sibling project, which is a ported native code implementation.

Metadata Command

Generate TypeScript metadata file (same output as plugin outputMetadataFile: true):

# Generate metadata file for current directory
screw-up metadata

# Custom output path
screw-up metadata --output-metadata-file-path ./src/generated/meta.ts

# Limit keys
screw-up metadata --output-metadata-keys "name,version"

The metadata command:

  • Generates a TypeScript metadata file with exported constants
  • Creates a .gitignore entry next to the metadata file if missing
  • If your project using screw-up is a Vite plugin, you can avoid bootstrap issues for self-hosting (where packageMetadata.ts doesn't exist during Vite plugin initialization to generate packageMetadata.ts) by running it as an NPM package.json script entry.

Options

  • --output-metadata-file-path <path>: Output path for metadata file (default: src/generated/packageMetadata.ts)
  • --output-metadata-keys <list>: Comma-separated list of metadata keys to include (default: name,version,description,author,license,repository.url,git.commit.hash)
  • --no-wds: Disable working directory status check for version increment
  • --no-git-version-override: Do not override version from Git (use package.json version)

Dump Command

Dump computed package.json as JSON:

# Dump current directory package.json
screw-up dump

# Dump specific directory package.json
screw-up dump ./my-project

# Dump with custom inheritable fields
screw-up dump --inheritable-fields "author,license"

As shown in the following output example, Git commit information is also added:

{
  "git": {
    "tags": [],
    "branches": ["develop"],
    "version": "1.13.2",
    "commit": {
      "hash": "49a4245d6c5ce6604167005f5234c1c4a38a852b",
      "shortHash": "49a4245",
      "date": "2025-12-05T11:50:38+09:00Z",
      "message": "feat: Added force dump mode."
    }
  },
  "version": "1.13.2",
  "name": "screw-up",
  "description": "Simply package metadata inserter on Vite plugin"

  // ...
}

The dump command:

  • Shows the final computed package.json after all processing (workspace inheritance, Git metadata, etc.)
  • Useful for debugging and understanding how your package metadata will be resolved
  • Outputs clean JSON that can be piped to other tools (ex: jq)
  • -f/--force lets you dump even without package.json; only Git (or default) metadata is included

Options

  • --inheritable-fields <list>: Comma-separated list of fields to inherit from parent (default: version,description,author,license,repository,keywords,homepage,bugs,readme,files)
  • --no-wds: Disable working directory status check for version increment
  • --no-merge-files: Do not merge files from parent package.json
  • -f, --force: Allow dumping without package.json (Git/default metadata only)

Generic usage

With -f, you can use screw-up outside NPM projects and still leverage Git metadata for versioning. For example, combine with jq to generate a C header that embeds version and commit IDs:

# Generate version.h
screw-up dump -f | jq -r '
  "#pragma once\n" +
  "#define APP_VERSION \"" + (.version // "0.0.1") + "\"\n" +
  "#define APP_COMMIT \"" + (.git.commit.shortHash // "unknown") + "\"\n"
' > version.h

Format Command

Replace placeholders in text with computed package metadata.

This command is similar to the dump command, but instead of outputting JSON, it can be used to format text within screw-up. Therefore, it might be easier to handle than processing screw-up output with jq:

# Format stdin template and print to stdout
screw-up format

# Format a file and write the result to another file
screw-up format -i ./template.txt ./output.txt

# Use custom brackets instead of {...}
screw-up format -i ./template.txt -b "#{,}#"

Placeholders use {field} by default. Dot notation lets you reach nested values such as {repository.url} or {git.commit.hash}.

Input comes from stdin unless -i/--input is provided, and output always goes to stdout; if you pass an output path, the formatted text is also written there.

Options

  • -i, --input <path>: Template file to format (defaults to stdin)
  • -b, --bracket <open,close>: Change placeholder brackets (default {,})
  • --inheritable-fields <list>: Comma-separated list of fields to inherit from parent (default: version,description,author,license,repository,keywords,homepage,bugs,readme,files)
  • --no-wds: Disable working directory status check for version increment
  • --no-git-version-override: Do not override version from Git (use package.json version)
  • -f, --force: Allow formatting without package.json (Git/default metadata only)

Example

For example, prepare an input text file like the following (version.h.in):

#pragma once
#define APP_VERSION "{version}"
#define APP_COMMIT "{git.commit.shortHash}"

By entering the following into screw-up, you can generate C language header files:

screw-up format -i ./version.h.in ./version.h

version.h:

#pragma once
#define APP_VERSION "1.13.2"
#define APP_COMMIT "49a4245"

Pack Command

Create a tar archive of your project:

# Pack current directory
screw-up pack

# Pack specific directory
screw-up pack ./my-project

# Pack to specific output directory
screw-up pack --pack-destination ./dist

The pack command:

  • Automatically reads package.json for metadata
  • Supports workspace inheritance (inherits metadata from parent packages)
  • Creates a compressed .tgz archive with format: {name}-{version}.tgz

The pack command uses npm pack by default to generate a temporary package file. When --use-pnpm is specified, it uses pnpm pack instead and preserves pnpm workspace protocol resolution in the packed manifest. It then performs operations such as replacing package.json metadata and modifying README on the package file. File selection follows the selected package manager's pack behavior, except for the workspace files merge described below.

However, to successfully pack the files, you must define the version key. screw-up itself can automatically determine the version to specify in the version key and reflect that value in the final NPM package file. When the version key does not exist, an error will occur during the first npm pack execution. To avoid this, the "Recommended configuration" section example specifies a DUMMY version key.

Options

  • --pack-destination <path>: Specify output directory for the archive
  • --readme <path>: Replace README.md with specified file
  • --use-pnpm: Use pnpm pack and preserve workspace protocol resolution in the generated tarball
  • --inheritable-fields <list>: Comma-separated list of fields to inherit from parent (default: version,description,author,license,repository,keywords,homepage,bugs,readme,files)
  • --no-wds: Disable working directory status check for version increment
  • --no-replace-peer-deps: Disable replacing "*" in peerDependencies with actual versions
  • --no-merge-files: Do not merge files from parent package.json
  • --peer-deps-prefix <prefix>: Version prefix for replaced peerDependencies (default: "^")

Merging "files" entires

The files section in package.json merges file entries from the parent and child projects according to the β€œmerge process”. This feature allows you to list common distribution files in the parent package.json's files section, while only appending differences to the child package.json's files section.

When files is inherited, screw-up pack includes files matching the files in the workspace root package.json in the package. However, if a child project's package.json specifies a file in the same location, that specification takes precedence.

  • The negation patterns in the workspace root's .npmignore and the child project's package.json files do not apply to these merged files.
  • The screw-up dump command displays the combined result of the files patterns, so it may differ from the actual pack result.

Publish Command

Publish your project to registry server:

# Publish current directory (creates archive and publishes)
screw-up publish

# Publish specific directory
screw-up publish ./my-project

# Publish existing tarball
screw-up publish package.tgz

# Publish with npm options (all npm publish options are supported)
screw-up publish --dry-run --tag beta --access public

The publish command:

  • Supports all npm publish options transparently. This command creates an archive and then executes the actual publishing by calling npm publish.
  • When --use-pnpm is specified, archive generation uses pnpm pack, while registry publication still uses the npm-compatible publish flow.
  • Can publish from directory (automatically creates archive) or existing tarball
  • Handles workspace packages with proper metadata inheritance
  • Uses the same packaging logic as the pack command

Options

  • --use-pnpm: Use pnpm pack before publish when creating a tarball from a directory
  • --inheritable-fields <list>: Comma-separated list of fields to inherit from parent (default: version,description,author,license,repository,keywords,homepage,bugs,readme,files)
  • --no-wds: Disable working directory status check for version increment
  • --no-replace-peer-deps: Disable replacing "*" in peerDependencies with actual versions
  • --no-merge-files: Do not merge files from parent package.json
  • --peer-deps-prefix <prefix>: Version prefix for replaced peerDependencies (default: "^")
  • All npm publish options are supported (e.g., --dry-run, --tag, --access, --registry)

README replacement feature

The pack and publish command supports README replacement using multiple methods:

Via CLI option

# Replace README.md with custom file
screw-up pack --readme ./docs/README_package.md

Via package.json field

{
  "name": "my-package",
  "readme": "docs/PACKAGE_README.md"
}

When both are specified, the --readme CLI option takes priority over the package.json field.

PeerDependencies replacement feature

In workspace environments, it's common to reference sibling packages using "*" in peerDependencies to avoid version constraints during development. When packaging, screw-up automatically replaces these wildcards with actual version numbers:

{
  "name": "@workspace/cli",
  "peerDependencies": {
    "@workspace/core": "*"
  }
}

After packaging, the "*" is replaced with the actual version:

{
  "name": "@workspace/cli",
  "peerDependencies": {
    "@workspace/core": "^2.1.0"
  }
}

Controlling the feature

# Default behavior (uses "^" prefix)
screw-up pack

# Disable the feature entirely
screw-up pack --no-replace-peer-deps

# Use different version prefix
screw-up pack --peer-deps-prefix "~"
screw-up pack --peer-deps-prefix ">="

# Use exact version (no prefix)
screw-up pack --peer-deps-prefix ""

This feature:

  • Only works in workspace environments (requires workspace root with workspaces field or pnpm-workspace.yaml)
  • Only replaces "*" values that match workspace sibling package names
  • Leaves non-workspace dependencies unchanged
  • Is enabled by default for pack and publish commands

pnpm workspace protocol

When your monorepo uses pnpm workspaces, you can reference sibling packages using workspace:*, workspace:^, or workspace:~ in dependencies, peerDependencies, and optionalDependencies.

Run pnpm install first, then use screw-up pack --use-pnpm or screw-up publish --use-pnpm. screw-up uses pnpm pack as the pack backend and keeps the packed manifest's dependency sections, so the generated tarball contains npm-compatible package names and version ranges instead of workspace: references.

Workspace Field Inheritance

Control which metadata fields are inherited from parent packages in monorepos:

# Inherit only specific fields
screw-up pack --inheritable-fields "version,author,license"

# Disable inheritance completely
screw-up pack --inheritable-fields ""

# Use custom fields for publishing
screw-up publish --inheritable-fields "version,description,keywords"

Default inheritable fields: version, description, author, license, repository, keywords, homepage, bugs, readme, files


Recommended configuration

screw-up allows you to keep your development lifecycle simple. Below are typical configurations for single projects and monorepos using workspaces.

Single project configuration

For single projects, follow these recommendations for optimal screw-up usage:

my-project/
β”œβ”€β”€ package.json                   # No version field
β”œβ”€β”€ README.md                      # Development README (show in github/gitlab)
β”œβ”€β”€ README_pack.md                 # Distribution README (optional)
β”œβ”€β”€ vite.config.ts                 # screw-up plugin configuration
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts
β”‚   └── generated/
β”‚       β”œβ”€β”€ packageMetadata.ts     # Auto-generated by `outputMetadataFile`
β”‚       └── .gitignore             # Auto-generated to ignore `packageMetadata.ts`
└── dist/                          # Build output with metadata banners

Package.json structure

{
  "name": "my-awesome-library",
  "description": "An awesome TypeScript library for developers",
  "author": "Jane Developer <jane@example.com>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/user/my-awesome-library"
  },
  "keywords": ["typescript", "library", "awesome"],
  "homepage": "https://github.com/user/my-awesome-library#readme",
  "bugs": {
    "url": "https://github.com/user/my-awesome-library/issues"
  },
  "readme": "README_pack.md",
  "files": ["dist/**/*"],
  "scripts": {
    "build": "vite build",
    "test": "npm run build && vitest run",
    "pack": "npm run build && screw-up pack --pack-destination artifacts/"
  }
}

Key Points:

  • DUMMY version: Version specification (0.0.1 in the above example) is NOT used, and version management is performed through Git tags by screw-up
  • Include metadata fields: name, description, author, license, etc.
  • Specify files: Control which files are included in the package
  • Optional readme field: Point to a distribution-specific README file. The README file specified here does not need to be included in files.
  • Add pack to scripts to enable packaging with screw-up

Vite configuration

import { defineConfig } from 'vite';
import screwUp from 'screw-up';

export default defineConfig({
  plugins: [
    screwUp({
      // (Generate `packageMetadata.ts` when you need it)
      outputMetadataFile: true,
    }),
  ],
  // ...
});

Setting outputMetadataFile to true will generate packageMetadata.ts. By default, packageMetadata.ts contains Git commit IDs, so it is updated on every commit. screw-up now creates a .gitignore next to the metadata file (if missing) so the file stays out of version control, but keep the ignore entry if you customize the directory.

Development setup

# Install as dev dependency
npm install --save-dev screw-up

Workspace configuration (Monorepo)

For monorepo setups, organize shared and project-specific metadata:

my-monorepo/
β”œβ”€β”€ package.json          # Root metadata (no version)
β”œβ”€β”€ README.md             # Development README (show in GitHub/GitLab)
β”œβ”€β”€ README_shared.md      # Shared README
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ package.json      # Project-specific metadata (no version)
β”‚   β”œβ”€β”€ vite.config.ts
β”‚   └── src/
β”œβ”€β”€ ui/
β”‚   β”œβ”€β”€ package.json      # References core with "*" (no version)
β”‚   └── src/
└── cli/
    β”œβ”€β”€ package.json      # References core with "*" (no version)
    └── src/

Root package.json

{
  "name": "my-monorepo",
  "description": "Monorepo containing multiple packages",
  "author": "Development Team <team@company.com>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/company/my-monorepo"
  },
  "homepage": "https://github.com/company/my-monorepo#readme",
  "bugs": {
    "url": "https://github.com/company/my-monorepo/issues"
  },
  "readme": "README_shared.md",
  "workspaces": ["core", "ui", "cli"],
  "private": true,
  "scripts": {
    "build": "npm run build --workspaces",
    "test": "npm run test --workspaces",
    "pack": "npm run pack --workspaces"
  }
}

Marked as private to ignore packing. Since there is no packing, the version key is also unnecessary.

Sub-project package.json

{
  "name": "@company/ui-components",
  "version": "0.0.1",
  "description": "Reusable UI components library",
  "keywords": ["ui", "components", "react"],
  "peerDependencies": {
    "@company/core": "*",
    "react": "^18.0.0"
  },
  "files": ["dist/**/*"],
  "scripts": {
    "build": "vite build",
    "test": "npm run build && vitest run",
    "pack": "npm run build && screw-up pack --pack-destination artifacts/"
  }
}

Key Points:

  • Root package: Define shared metadata (author, license, repository, etc.)
  • Sub-projects: Override with project-specific values (name, description, keywords)
  • Sibling references: Use "*" in peerDependencies for workspace siblings when you need to refer on peer
  • DUMMY version: Version specification (0.0.1 in the above example) is NOT used, and version management is performed through Git tags by screw-up
  • Shared README: Can be defined at root level and inherited by sub-projects

Vite configuration

Same as single project configuration.

Development environment setup

Install screw-up in each sub project.

CLI usage examples

# Pack individual sub-project
screw-up pack packages/ui-components

# Pack with custom inheritance
screw-up pack packages/cli --inheritable-fields "author,license,repository"

# Pack without peerDependencies replacement
screw-up pack packages/plugin --no-replace-peer-deps

# Publish with custom prefix
screw-up publish packages/core --peer-deps-prefix "~"

Note

This project was developed as a successor to RelaxVersioner. While RelaxVersioner was designed for the .NET platform and added NPM support options, it did not integrate well with Git tags. Therefore, this project was designed with Vite plugin usage in mind, focusing on the most optimal workflow and specifications.

The algorithm for calculating version numbers from Git tags is identical to RelaxVersioner. This means that if you are maintaining server code with ASP.NET Core, you can treat both the .NET and NPM project version as completely unified.

See also the sibling project screw-up-native. It should be portable and easy to use, especially for projects that don't need NPM for versioning.

Discussions and Pull Requests

For discussions, please refer to the GitHub Discussions page. We have currently stopped issue-based discussions.

Pull requests are welcome! Please submit them as diffs against the develop branch and squashed changes before send.

License

Under MIT

About

Simply Git version/tags inserter for NPM 🎁

Topics

Resources

License

Stars

Watchers

Forks

Contributors