diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..73760265 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,42 @@ +# Set default behavior to automatically normalize line endings to LF +* text=auto eol=lf + +# Explicitly declare text files you want to always be normalized and converted to LF on checkout +*.xml text eol=lf +*.mediawiki text eol=lf +*.tsv text eol=lf +*.md text eol=lf +*.rst text eol=lf +*.txt text eol=lf +*.py text eol=lf +*.sh text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.json text eol=lf +*.toml text eol=lf +*.svg text eol=lf +*.css text eol=lf +*.js text eol=lf +*.html text eol=lf + +# Denote all files that are truly binary and should not be modified +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.zip binary +*.gz binary +*.tar binary +*.mp3 binary +*.mp4 binary +*.mov binary +*.avi binary +*.exe binary +*.dll binary +*.so binary +*.dylib binary +*.class binary +*.jar binary +*.war binary \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..cb41a2cf --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,150 @@ +# hed-javascript Copilot instructions + +> **Local environment:** If `.status/local-environment.md` exists in this repository, read it first. +> It contains machine-specific overrides (OS, shell, paths) that take precedence over this file. + +This is a Node.js/JavaScript project managed with `npm`. Node.js >= 22.0.0 is required. +Always run `npm install` before building or testing. In CI contexts use `npm ci` instead. + +When you create summaries of what you did — always put them in a `.status/` directory at the root of the repository. + +Markdown headers in this repository use sentence case: capitalize only the first word (and proper nouns). + +## Branch and PR policy + +All changes must be submitted as pull requests targeting the `main` branch. Direct pushes to `main` are not permitted. + +## Project overview + +**hed-validator** (`hed-javascript`) is a JavaScript library for validating HED (Hierarchical Event Descriptor) strings, tags, and BIDS-compatible event files. It is published as the `hed-validator` npm package and supports both Node.js (CommonJS and ESM) and browser environments. + +## Architecture overview + +Source code is plain JavaScript (`.js`) in `src/`. TypeScript type declarations live in `types/index.d.ts`. + +**Entry Point:** + +- `index.js` (root): Re-exports the public API + +**Source Modules** (`src/`): + +- `bids/` — BIDS dataset and TSV/sidecar parsing and validation + - `datasetParser.js`, `tsvParser.js`, `schema.js`, `index.js` + - `types/` — BIDS-specific type helpers + - `validator/` — BIDS validation logic + +- `parser/` — HED string parsing pipeline + - `tokenizer.js`, `splitter.js`, `parser.js`, `parseUtils.js` + - `parsedHedString.js`, `parsedHedTag.js`, `parsedHedGroup.js`, `parsedHedSubstring.js`, `parsedHedColumnSplice.js` + - `definitionManager.js`, `definitionChecker.js`, `reservedChecker.js` + - `tagConverter.js`, `eventManager.js` + +- `schema/` — HED schema loading, parsing, and merging + - `loader.js`, `parser.js`, `schemaMerger.js` + - `config.js`, `containers.js`, `entries.js`, `specs.js`, `init.js` + +- `issues/` — Issue codes and formatting + - `issues.js`, `data.js` + +- `data/` — Bundled JSON configs and XML schema files + +- `utils/` — General utilities + - `array.js`, `string.js`, `files.js`, `paths.js`, `hedStrings.js`, `memoizer.js`, `xml.js` + +**Browser App** (`browser/`): + +- A separate Vite/React app for browser-based HED validation. Has its own `package.json` and `vite.config.js`. + +## Common commands + +```powershell +npm install # Install dependencies (use npm ci in CI) +npm run build # Build dist/ with esbuild (CommonJS + ESM) +npm test # Run Jest tests in tests/ +npm run testSpecs # Run spec tests in spec_tests/ +npm run coverage # Run tests with coverage report +npm run lint # ESLint on src/ +npm run build ; npm run test-types-local-windows # Type-check types/ (build required first) +npm run docs # Generate TypeDoc documentation +npm run clear_jest_cache # Clear Jest cache +``` + +**Running individual test files:** + +```powershell +npx jest tests/otherTests/hed.spec.js +``` + +**Browser app (separate package):** + +```powershell +cd browser ; npm install ; npm test # Run browser (Vitest) tests +cd browser ; npm run build # Build browser app +cd browser ; npm run preview # Serve browser app locally +``` + +## Testing structure + +``` +tests/ + jsonTests/ # JSON-driven validation tests (spec files) + jsonTestData/ # Data files for JSON tests (.data.js) + otherTests/ # Additional spec files (bids, schema, issues, etc.) + otherTestData/ # Data files for other tests + testHelpers/ # Shared test utilities + bidsDemoData/ # Sample BIDS dataset used in integration tests +spec_tests/ + jsonTests.spec.js # Runs the HED spec test suite + javascriptTests.json +browser/ + vitest.config.js # Separate Vitest suite for browser components +``` + +Jest is configured via `jest.config.js` and `babel.config.js`. Note: `jest.config.js` has a +`transformIgnorePatterns` entry that allows `unicode-name` and `semver` to be transformed — +do not remove it or those imports will fail in tests. + +## Key dependencies + +- **fast-xml-parser** — XML schema parsing +- **lodash**, **semver**, **pluralize**, **unicode-name** — Utility support +- **esbuild** — Build tool (CommonJS + ESM output) +- **jest** / **babel-jest** — Testing +- **eslint** / **prettier** — Linting and formatting +- **typescript** / **typedoc** — Type declarations and docs generation +- **husky** — Git hooks (runs prettier on commit) + +## File organization + +- `src/` — JavaScript source code +- `tests/` — Jest test suite +- `spec_tests/` — HED spec-level tests +- `types/` — TypeScript type declarations (`index.d.ts`, `test.ts`) +- `browser/` — Vite/React browser demo app +- `dist/` — Compiled output, generated by `npm run build` (not committed) +- `scripts/` — Helper scripts for type testing +- `esbuild.mjs` — Build configuration +- `.status/` — Local summaries and environment notes (not published) + +## CI checks (all must pass on PRs) + +CI runs on every push/PR to `main`. Replicate these locally before opening a PR: + +| Workflow | Command | Notes | +| --------------------- | --------------------------------------------------- | -------------------------------------------------- | +| `tests.yml` | `npm ci ; npm test` | Runs `tests/` suite on Node 22, lts/\*, latest | +| `tests.yml` | `npm ci ; npm run testSpecs` | Runs `spec_tests/` suite | +| `tests.yml` (browser) | `cd browser ; npm install ; npm test` | Separate Vitest browser suite | +| `test-types.yml` | `npm ci ; npm run build ; npm pack` then type-check | **Build required before type check** | +| `codeql.yml` | automatic | CodeQL security scan (JavaScript) | +| `codespell.yaml` | automatic | Spell check source files | +| `links.yml` | automatic | Lychee link checker (configured via `lychee.toml`) | + +**Type-check locally (without packing):** + +```powershell +npm run build ; npm run test-types-local-windows # Windows +npm run build ; npm run test-types-local-unix # Linux/macOS +``` + +Publishing to npm happens automatically via `publish.yml` when a GitHub Release is created. diff --git a/.github/workflows/claude.yaml b/.github/workflows/claude.yaml new file mode 100644 index 00000000..f9be06ae --- /dev/null +++ b/.github/workflows/claude.yaml @@ -0,0 +1,58 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + ( + github.event_name == 'issue_comment' && + contains(github.event.comment.body, '@claude') && + (github.event.comment.author_association == 'OWNER' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'COLLABORATOR') + ) || + ( + github.event_name == 'pull_request_review_comment' && + contains(github.event.comment.body, '@claude') && + (github.event.comment.author_association == 'OWNER' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'COLLABORATOR') + ) || + ( + github.event_name == 'pull_request_review' && + contains(github.event.review.body, '@claude') && + (github.event.review.author_association == 'OWNER' || + github.event.review.author_association == 'MEMBER' || + github.event.review.author_association == 'COLLABORATOR') + ) || + ( + github.event_name == 'issues' && + (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) && + (github.event.issue.author_association == 'OWNER' || + github.event.issue.author_association == 'MEMBER' || + github.event.issue.author_association == 'COLLABORATOR') + ) + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: write + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_args: '--allowedTools "Bash(gh issue comment:*),Bash(gh issue view:*),Bash(gh pr comment:*),Bash(gh pr view:*),Read,Glob,Grep"' diff --git a/.github/workflows/codespell.yaml b/.github/workflows/codespell.yaml deleted file mode 100644 index a37d0d32..00000000 --- a/.github/workflows/codespell.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Codespell - -on: - push: - branches: '*' - pull_request: - branches: '*' - -permissions: - contents: read - -jobs: - codespell: - name: Check for spelling errors - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Codespell - uses: codespell-project/actions-codespell@v2 diff --git a/.github/workflows/typos.yaml b/.github/workflows/typos.yaml new file mode 100644 index 00000000..a9a3c92e --- /dev/null +++ b/.github/workflows/typos.yaml @@ -0,0 +1,29 @@ +--- +name: Typos + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + typos: + name: Check for spelling errors + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + cache-dependency-glob: '.typos.toml' + + - name: Run typos + run: uvx 'typos>=1.29.0' diff --git a/.gitignore b/.gitignore index 64122eb5..780507bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,15 @@ coverage node_modules .directory -.github/copilot-instructions.md .idea dist/ +.status/ /venv/ spec_tests/*.txt spec_tests/temp*.json spec_tests/temp.spec.js tests/temp.spec.js +.vscode/ # Ignore local build output /build/ diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000..f4a88857 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,32 @@ +[files] +# Match the previous codespell skip behavior. +extend-exclude = [ + "datasets", + ".git", + "deprecated", + "*.pdf", + "*.svg", + "*.xml", + "*.mediawiki", + "*.omn", + ".venv", + "docs/_build", + "docs/html", + "docs/source/_static", + "dist", + "build", + "node_modules", + "**/*.min.js", + "**/*.min.css", + "**/package-lock.json", + "**/*.lock", +] + +[default.extend-words] +# Match previous codespell ignore-words-list behavior. +covert = "covert" +hed = "hed" +recuse = "recuse" +hertzs = "hertzs" +rouge = "rouge" +isnt = "isnt"