Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`.gitattributes`). The default remains `{ kind: "jsdoc" }`, preserving the
existing JSDoc/C-style output byte-for-byte.
- `CommentSyntax` is re-exported from the package root.
- New `verify-codelock` CLI (`npx -p @elg/tscodegen verify-codelock`). Accepts
eslint- / prettier-style glob patterns, auto-detects the comment syntax of
each matched file, and exits non-zero if any tscodegen-generated file has
been modified outside its manual sections. Files that are not tscodegen
output are silently skipped, so the CLI can be pointed at a broad glob like
`**/*` without an allowlist. Intended for use in CI and pre-commit hooks,
and a replacement for the previously planned ESLint rule (which could not
cover non-`.ts` generated files).
- `detectCommentSyntax` helper and the `codelock` module are re-exported from
the package root so consumers can build their own verification tooling on
top of them.

### Changed

Expand Down
79 changes: 76 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ code, with optional manually editable sections and tamper detection.

- Codelock tamper detection. A hash is added to the docblock of every
generated file to make it easy to detect if a file has been changed outside
the generated manual sections. In the future, this can be [enforced with an
ESLint rule](https://github.com/taneliang/tscodegen/issues/1) (PRs
welcome!).
the generated manual sections. The [bundled `verify-codelock`
CLI](#verifying-codelocks-in-ci-and-pre-commit-hooks) enforces this for you
across TypeScript _and_ non-TypeScript generated files.

- No template files. tscodegen uses [template
literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)
Expand Down Expand Up @@ -164,3 +164,76 @@ path/to/generated.ts linguist-generated=true
Everything else works the same: `verify()` detects tampering outside the
manual sections, `lock()` adds the hash, and `saveToFile()` writes the
result. Manual-section keys still must be non-empty and whitespace-free.

## Verifying codelocks in CI and pre-commit hooks

tscodegen ships with a `verify-codelock` CLI that walks a set of files and
confirms that every tscodegen-generated file still has a valid codelock hash.
It accepts eslint- / prettier-style glob patterns and automatically detects
the comment syntax of each file (JSDoc for `.ts`, line comments for
`.gitattributes`, etc.), so a single invocation covers both TypeScript and
non-TypeScript generated output.

Files that are _not_ tscodegen-generated (i.e. do not contain an
`@generated Codelock<<...>>` or `@generated-editable Codelock<<...>>` marker)
are silently skipped, so it's safe to point `verify-codelock` at a broad
glob like `**/*` without having to maintain a separate allowlist.

### Usage

Run it through `npx` without installing anything:

```sh
npx -p @elg/tscodegen verify-codelock 'src/**' 'packages/**/*.gen.ts'
```

Or install tscodegen as a dev dependency and call it from a script:

```json
{
"scripts": {
"verify:codegen": "verify-codelock 'src/**' '.gitattributes'"
}
}
```

### CLI reference

```
verify-codelock [options] <pattern> [<pattern>...]

Options:
--ignore <pattern> Exclude files matching <pattern>. May be repeated.
--quiet, -q Suppress per-file output and the summary line.
--verbose Log the status of every file, including skipped ones.
--no-color Disable colored output.
--help, -h Print help and exit.
--version, -v Print the installed tscodegen version and exit.

Exit codes:
0 All matched tscodegen-generated files have valid codelocks.
1 One or more files were tampered with, matched no files, or could not be
read.
2 Invalid invocation (e.g. missing patterns, unknown option).
```

### Example: CI check

```yaml
- run: npx -p @elg/tscodegen verify-codelock '**/*' --ignore '**/node_modules/**' --ignore '**/dist/**'
```

### Example: pre-commit hook

With [Husky](https://typicode.github.io/husky/) + [lint-staged](https://github.com/okonet/lint-staged):

```json
{
"lint-staged": {
"*": "verify-codelock"
}
}
```

`lint-staged` passes the staged file paths as positional arguments, and
`verify-codelock` will skip any non-generated files automatically.
6 changes: 6 additions & 0 deletions bin/verify-codelock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env node
"use strict";

const { main } = require("../dist/cli/verifyCodelock");

process.exitCode = main(process.argv.slice(2));
8 changes: 7 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import globals from "globals";

export default [
{
ignores: ["dist/**", "coverage/**", "test-reports/**", "node_modules/**"],
ignores: [
"bin/**",
"dist/**",
"coverage/**",
"test-reports/**",
"node_modules/**",
],
},
js.configs.recommended,
...tseslint.configs.recommended,
Expand Down
14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
"description": "TypeScript string-based code generation tool, with hashed files and manually editable sections",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"verify-codelock": "bin/verify-codelock.js"
},
"files": [
"dist",
"bin",
"CHANGELOG.md",
"LICENSE",
"README.md"
],
"repository": "git@github.com:taneliang/tscodegen.git",
"author": "E-Liang Tan <eliang@eliangtan.com>",
"license": "MIT",
Expand All @@ -25,7 +35,9 @@
"prepare": "yarn clean && yarn build"
},
"dependencies": {
"@prettier/sync": "^0.6.1"
"@prettier/sync": "^0.6.1",
"picocolors": "^1.1.1",
"tinyglobby": "^0.2.16"
},
"peerDependencies": {
"prettier": "3.x"
Expand Down
Loading