Skip to content

Latest commit

 

History

History
253 lines (206 loc) · 8.97 KB

File metadata and controls

253 lines (206 loc) · 8.97 KB

Project Audit

emulsify-audit scans an Emulsify project for migration and configuration problems. Its default report is intended for people. The --json report is a versioned interface for CI systems and downstream tools.

Run the audit from an installed project:

npx --no-install emulsify-audit

From an Emulsify Core checkout, select another project root explicitly:

node scripts/audit.js --root /path/to/project

Findings do not change the default exit status. Use a failure threshold when the audit should enforce policy in CI.

JSON Report Contract

Use --json to write exactly one JSON document to stdout:

npx --no-install emulsify-audit --json > audit-report.json

A schema-version 1 report has this shape:

{
  "schemaVersion": 1,
  "tool": {
    "name": "@emulsify/core",
    "version": "4.4.0"
  },
  "root": ".",
  "summary": {
    "error": 0,
    "warn": 1,
    "info": 0
  },
  "files": {
    "stories": 1,
    "twig": 1,
    "code": 1,
    "styles": 0
  },
  "findings": [
    {
      "id": "legacy-twig-story",
      "severity": "warn",
      "path": "src/components/card/card.stories.js",
      "line": 5,
      "message": "Twig story appears to return an HTML string directly. This remains compatible, but renderTwig() is preferred for active migrations.",
      "details": [
        "imports Twig templates without renderTwig()",
        "appears to return Twig HTML strings directly"
      ],
      "docs": "https://github.com/emulsify-ds/emulsify-core/blob/4.x/docs/storybook.md#legacy-twig-story-compatibility"
    }
  ]
}

The top-level fields are:

  • schemaVersion: Integer version of the JSON contract. This is independent of the package version and changes only for incompatible contract changes.
  • tool: Package identity. tool.version is the installed Emulsify Core version and changes independently of schemaVersion.
  • root: Always ".", representing the selected scan root.
  • summary: Finding counts. The error, warn, and info keys are always present, including when their value is zero.
  • files: Counts for the normalized story, Twig, code, and style scan sets. These categories can overlap; for example, a JavaScript story is counted in both stories and code. Do not add them together as a unique-file total.
  • findings: Findings in deterministic scan order.

Each finding contains:

  • id: Stable identifier intended for filters and automation.
  • severity: One of error, warn, or info.
  • message: Human-readable explanation.
  • path: Optional project-relative path using /, with no leading ./.
  • line: Optional positive, one-based line number.
  • details: Optional array of human-readable detail strings.
  • docs: Optional documentation URL.

Optional finding fields are omitted when unavailable. They are not emitted as null or as internal undefined values. The formatter removes the selected project root from finding messages and details. Other configured values in that human-readable text are not structured paths and should not be parsed as such.

Compatibility Expectations

The following are part of the machine-readable contract:

  • required field names and value types;
  • severity names and meanings;
  • finding object field names and value types;
  • existing finding IDs.

Renaming or removing those fields or IDs, or changing their meaning incompatibly, requires a schemaVersion increase. Adding a new finding ID or a new optional field is compatible and does not require a schema-version change. Consumers should tolerate both.

Finding messages, details, documentation URLs, counts, and ordering can change as checks improve or project contents change. Output ordering is deterministic for reproducible reports, but consumers should select JSON fields by name rather than relying on object-key order.

The focused emulsify-audit-twig-stories --json command uses the same envelope and normalized finding shape. Its files counts describe only that focused story scan, and its existing --fail-on-found option remains the way to make migration candidates fail the command.

Failure Thresholds

For the combined emulsify-audit command, --fail-on controls whether a completed scan exits with status 1:

Option Exit 1 when the completed scan contains
No threshold Never because of findings
--fail-on error At least one error
--fail-on warn At least one error or warning
--fail-on info At least one error, warning, or info finding
--fail-on any Any finding
--fail-on-found Compatibility alias for --fail-on any

The process exit codes are:

  • 0: The scan completed and did not meet its failure threshold.
  • 1: The scan completed and met its failure threshold.
  • 2: Arguments were invalid, or audit setup/execution failed.

Fixing CSS Asset URLs

With asset rebasing enabled (the default), /assets/... and @assets/... are equivalent first-class Sass/CSS asset aliases. The audit validates both against the same project asset roots and leaves both unchanged without a repair finding. If assets.rebase is disabled, the audit warns that Core will not resolve @assets/....

--fix rewrites legacy CSS and Sass asset URLs to /assets/..., the stable canonical autofix output, when exactly one file under one asset root answers to them. That covers the two non-portable shapes: the bare url('assets/...') form, and a relative URL whose depth suits the emitted CSS rather than the stylesheet. See Asset References for why those break.

npx emulsify-audit --fix --dry-run   # report the rewrites, touch nothing
npx emulsify-audit --fix             # apply them

--dry-run requires --fix; on its own it is an argument error. Quote style and any ?query or #hash suffix are preserved. A URL whose Sass interpolation has not been expanded is never rewritten — the edit belongs on the variable declaration, and same-file variable scanning cannot see what else depends on it. An ambiguous URL is reported with its candidates and left alone.

--fail-on is evaluated against the findings that remain after fixing, so a real --fix run can turn a failing audit green. That is safe because the fix is idempotent: re-running the audit on the rewritten source reports nothing. A --dry-run subtracts nothing. If one source file cannot be read, validated, or replaced, its fixes are listed as skipped with the reason, its findings remain in the report, and other files are still attempted.

Real fixes use an exclusive same-directory temporary file and atomic rename. The replacement preserves the target's mode and ownership, cleans active temp files on normal errors, process exit, SIGINT, and SIGTERM, and sweeps temp orphans belonging to dead processes at the start of the next real --fix run. A dry run never performs that cleanup.

Atomic rename replaces the inode at the target path. Consequently, hardlinks to the old inode keep the old bytes while the rewritten path becomes a new single-link inode. On POSIX systems, a read-only file can still be replaced when its parent directory permits replacement; the new inode retains the read-only mode. File mode alone is therefore not a way to protect a source from --fix—make the parent directory non-writable or use --dry-run instead.

In JSON mode, --fix adds an optional top-level fixes block:

{
  "fixes": {
    "dryRun": false,
    "applied": [
      {
        "path": "src/components/card/card.scss",
        "line": 4,
        "from": "../../assets/images/hero.jpg",
        "to": "/assets/images/hero.jpg"
      }
    ],
    "skipped": []
  }
}

An exit status of 1 still produces a normal report. In JSON mode, an exit status of 2 produces a distinct error document instead of a findings report:

{
  "schemaVersion": 1,
  "tool": {
    "name": "@emulsify/core",
    "version": "4.4.0"
  },
  "error": {
    "code": "invalid-arguments",
    "message": "--fail-on must be one of: error, warn, info, any."
  }
}

The error code is invalid-arguments for command-line errors and audit-failed for setup or execution failures. JSON errors are written as one document to stdout without usage text. Human-readable errors retain concise messages and usage guidance on stderr. --help is human-readable and should not be combined with --json.

CI Examples

Preserve the audit status while inspecting its report:

status=0
npx --no-install emulsify-audit --json --fail-on warn > audit-report.json || status=$?
jq '{summary, files}' audit-report.json
exit "$status"

Use jq to own a custom findings policy while leaving the audit itself non-failing:

npx --no-install emulsify-audit --json |
  jq -e '.summary.error == 0 and .summary.warn == 0'

Filter by stable finding ID:

jq '.findings[] | select(.id == "legacy-twig-story")' audit-report.json