Metadata-Version: 2.1
Name: wexample-filestate-javascript
Version: 6.6.4
Summary: Extends wexample-filestate with JavaScript targets: Biome formatting via Docker and npm package-lock.json management.
Author-Email: weeger <contact@wexample.com>
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Requires-Dist: attrs>=23.1.0
Requires-Dist: cattrs>=23.1.0
Requires-Dist: wexample-filestate>=17.2.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Description-Content-Type: text/markdown

# filestate_javascript

Version: 6.6.4

`wexample-filestate-javascript` extends [wexample-filestate](https://pypi.org/project/wexample-filestate/) with two JavaScript-specific targets: it formats and lints `.js`, `.ts`, and related files through [Biome](https://biomejs.dev/) running in a managed Docker container, and it regenerates `package-lock.json` whenever the file is absent or older than `package.json`. It is for Python developers who describe JavaScript project structure with the filestate declarative model and want Biome formatting and npm lockfile freshness enforced automatically during the rectify cycle.

## Table of Contents

- [Installation](#installation)
- [Quickstart](#quickstart)
- [Tests](#tests)
- [Architecture](#architecture)
- [Integration in the Suite](#integration-in-the-suite)
- [Dependencies](#dependencies)
- [Versioning & Compatibility Policy](#versioning--compatibility-policy)
- [License](#license)
- [About us](#about-us)
- [Known Limitations & Roadmap](#known-limitations--roadmap)
- [Status & Compatibility](#status--compatibility)
- [Useful Links](#useful-links)
- [Migration Notes](#migration-notes)

## Installation

```bash
pip install wexample-filestate-javascript
```

Requires Python >=3.10.

## Quickstart

Install with pip:

```bash
pip install wexample-filestate-javascript
```

The package extends `wexample-filestate` through `JavascriptOptionsProvider`. Pass it when constructing your filestate tree so the framework discovers the `javascript` option key.

```python
from wexample_filestate_javascript.options_provider.javascript_options_provider import JavascriptOptionsProvider
```

Declare the `javascript` key on any target in your filestate config. It accepts a dict of sub-options or a plain list of sub-option names.

**Biome formatting** — set `biome: True` on a file or directory target:

```python
{
    "path": "/path/to/project",
    "javascript": {"biome": True},
}
```

On the rectify cycle the package builds and starts a Docker container from src/wexample_filestate_javascript/resources/docker/Dockerfile.javascript-option (`node:20-alpine` with `@biomejs/biome` installed globally). It then calls `biome format --write` against the target's `.js`, `.ts`, `.jsx`, and `.tsx` files in chunks of 30, using the Biome configuration baked into src/wexample_filestate_javascript/resources/docker/biome.json (2-space indent, line width 100, single quotes, semicolons always, trailing commas ES5, linter recommended rules plus `useConst: error`). Generated files — those ending in `.gen.ts`, `.generated.ts`, named `next-env.d.ts`, `vite-env.d.ts`, or located under `/__generated__/` — are skipped silently.

**npm lockfile freshness** — set `npm_package_lock: True` on a directory target that contains `package.json`:

```python
{
    "path": "/path/to/js-project",
    "javascript": {"npm_package_lock": True},
}
```

The operation runs only when `package-lock.json` is absent or its modification time is older than `package.json`. When it fires it deletes the stale lockfile and runs `npm install --package-lock-only --ignore-scripts --prefer-online` directly on the host in the target directory, writing a fresh `package-lock.json`.

Both options can be combined in a single declaration:

```python
{
    "path": "/path/to/js-project",
    "javascript": {"biome": True, "npm_package_lock": True},
}
```

## Tests

This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.

### Installation

First, install the required testing dependencies:
```bash
.venv/bin/python -m pip install pytest pytest-cov
```

### Basic Usage

Run all tests with coverage:
```bash
.venv/bin/python -m pytest --cov --cov-report=html
```

### Common Commands
```bash
# Run tests with coverage for a specific module
.venv/bin/python -m pytest --cov=your_module

# Show which lines are not covered
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing

# Generate an HTML coverage report
.venv/bin/python -m pytest --cov=your_module --cov-report=html

# Combine terminal and HTML reports
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html

# Run specific test file with coverage
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
```

### Viewing HTML Reports

After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.

### Coverage Threshold

To enforce a minimum coverage percentage:
```bash
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
```

This will cause the test suite to fail if coverage drops below 80%.

## Architecture

The package extends `wexample-filestate` with two JavaScript-specific behaviours: formatting and linting `.js`/`.ts` files through [Biome](https://biomejs.dev/) running in a managed Docker container, and regenerating `package-lock.json` whenever it is absent or older than `package.json`. Everything in the package lives under `src/wexample_filestate_javascript/`.

### Parts

**Entry point — options provider**

src/wexample_filestate_javascript/options_provider/javascript_options_provider.py is the package's registration point. `JavascriptOptionsProvider` extends `AbstractOptionsProvider`, advertises a single top-level option (`JavascriptOption`), and forwards the Docker image name from `AbstractJavascriptFileContentOption.DOCKER_IMAGE_NAME`.

**File type**

src/wexample_filestate_javascript/file/javascript_file.py defines `JavascriptFile`, which extends `ItemTargetFile` and sets `EXTENSION_ENV = "js"`. It owns the extension assertion; nothing else does.

**Top-level option**

src/wexample_filestate_javascript/option/javascript_option.py defines `JavascriptOption`, a `@base_class`-decorated `AbstractNestedConfigOption`. Its `get_allowed_options` returns `[BiomeOption, NpmPackageLockOption]`, making it a pure dispatcher: it accepts the raw value (list or dict), normalises a plain list to a dict, then delegates `create_required_operation` to `_create_child_required_operation`.

**Typed config value and config-option name**

src/wexample_filestate_javascript/config_value/javascript_config_value.py provides `JavascriptConfigValue`, a `ConfigValue` subclass with a single typed field `biome: bool | None`. Its `to_option_raw_value` converts that field to `{"biome": self.biome}`, bridging the typed API to the string-keyed option dispatch.

src/wexample_filestate_javascript/config_option/biome_config_option.py provides the canonical name string `"biome"` used as that key.

**File extension constants**

src/wexample_filestate_javascript/const/javascript_file.py collects `JAVASCRIPT_FILE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]` and `JAVASCRIPT_FILE_EXTENSION = ".js"` in one place.

**Docker base for file-content options**

src/wexample_filestate_javascript/option/javascript/abstract_javascript_file_content_option.py defines `AbstractJavascriptFileContentOption`, which extends both `WithBatchDockerOptionMixin` and `AbstractFileContentOption`. It sets `DOCKER_IMAGE_NAME = "javascript-option"` and returns the path to src/wexample_filestate_javascript/resources/docker/Dockerfile.javascript-option via `_get_dockerfile_path`. Concrete options inherit this class to run inside that container.

**Biome option**

src/wexample_filestate_javascript/option/javascript/biome_option.py defines `BiomeOption`. `_is_excluded_from_batch` silently skips generated files (`.gen.ts`, `.generated.ts`, `next-env.d.ts`, paths under `/__generated__/`, etc.) to avoid non-idempotent output on large codegen trees. `_run_batch_on_paths` sends paths to the container in chunks of 30 (`_BATCH_CHUNK_SIZE = 30`) to avoid OOM kills, calling `biome format --write --config-path=/tmp/biome.json` per chunk. `_apply_content_change` reads the result from the batch cache built by that run.

**Npm package-lock option**

src/wexample_filestate_javascript/option/javascript/npm_package_lock_option.py defines `NpmPackageLockOption`. `create_required_operation` returns `None` immediately if the option value is false/none, if `package.json` is absent, or if `package-lock.json` already exists and its mtime is at least as recent as `package.json`. Otherwise it returns a `NpmPackageLockOperation`.

**Npm package-lock operation**

src/wexample_filestate_javascript/operation/npm_package_lock_operation.py defines `NpmPackageLockOperation`. `apply_operation` deletes any stale lockfile then runs `npm install --package-lock-only --ignore-scripts --prefer-online` in the package directory on the host (no Docker). `undo` is a no-op because lockfile creation is not reversible automatically.

**Docker resources**

src/wexample_filestate_javascript/resources/docker/Dockerfile.javascript-option builds the `javascript-option` image: `node:20-alpine` with `@biomejs/biome` installed globally, and src/wexample_filestate_javascript/resources/docker/biome.json copied to `/tmp/biome.json`. The container runs as UID/GID 1000 and stays alive with `tail -f /dev/null`.

src/wexample_filestate_javascript/resources/docker/biome.json configures Biome: formatter enabled with 2-space indent and line width 100, single quotes, semicolons always, trailing commas ES5; linter enabled with the recommended rule set plus `useConst: error` and `noUnusedVariables: warn`.

### Call path: Biome formatting

1. A filestate config declares `javascript: {biome: true}` for a target.
2. `JavascriptOptionsProvider.get_options` surfaces `JavascriptOption`.
3. `JavascriptOption.set_value` normalises the raw value; `get_allowed_options` routes it to `BiomeOption`.
4. On the rectify cycle, `BiomeOption.create_required_operation` (via `AbstractFileContentOption`) collects all matching target paths, filters out generated files through `_is_excluded_from_batch`, and builds a batch operation.
5. The batch operation calls `_run_batch_on_paths`, which calls `_ensure_docker_container` (building the `javascript-option` image from `Dockerfile.javascript-option` if it does not exist) and then runs `biome format --write` inside the container for each chunk of up to 30 paths.
6. `_apply_content_change` reads back the formatted content from the in-memory cache populated by step 5.

### Call path: npm package-lock

1. A filestate config declares `npm_package_lock: true` for a directory target.
2. `JavascriptOption.get_allowed_options` routes it to `NpmPackageLockOption`.
3. `NpmPackageLockOption.create_required_operation` checks: is the option enabled? Does `package.json` exist? Is `package-lock.json` absent or older than `package.json`? If all conditions pass it returns a `NpmPackageLockOperation`.
4. `NpmPackageLockOperation.apply_operation` deletes any stale lockfile and runs `npm install --package-lock-only --ignore-scripts --prefer-online` in the target directory directly on the host.

## Integration in the Suite

This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.

### Related Packages

The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.

Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.

## Dependencies

- attrs: >=23.1.0
- cattrs: >=23.1.0
- wexample-filestate: >=17.2.0

## Versioning & Compatibility Policy

Wexample packages follow **Semantic Versioning** (SemVer):

- **MAJOR**: Breaking changes
- **MINOR**: New features, backward compatible
- **PATCH**: Bug fixes, backward compatible

We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

Free to use in both personal and commercial projects.

## About us

[Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.

This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.

Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.

## Known Limitations & Roadmap

Current limitations and planned features are tracked in the GitHub issues.

See the [project roadmap](https://github.com/wexample/python-filestate_javascript/issues) for upcoming features and improvements.

## Status & Compatibility

**Maturity**: Production-ready

**Python Support**: >=3.10

**OS Support**: Linux, macOS, Windows

**Status**: Actively maintained

## Useful Links

- **Homepage**: https://github.com/wexample/python-filestate-javascript
- **Documentation**: [docs.wexample.com](https://docs.wexample.com)
- **Issue Tracker**: https://github.com/wexample/python-filestate-javascript/issues
- **Discussions**: https://github.com/wexample/python-filestate-javascript/discussions
- **PyPI**: [pypi.org/project/wexample-filestate-javascript](https://pypi.org/project/wexample-filestate-javascript/)

## Migration Notes

When upgrading between major versions, refer to the migration guides in the documentation.

Breaking changes are clearly documented with upgrade paths and examples.
