You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 2 of the modernization plan: src/jquery.autocomplete.js (one big ES5
UMD file) replaced by ~8 TypeScript modules under src/, compiled to a UMD
bundle, an ESM bundle, a minified UMD bundle, and tsc-emitted .d.ts files
in dist/. All 31 specs pass unmodified — that is the behavior contract.
What changed
- src/ now contains: index.ts (ESM entry), umd-body.ts (UMD entry),
jquery-plugin.ts (installAutocomplete), Autocomplete.ts (the class),
defaults.ts, format.ts, utils.ts, jquery-ref.ts (live $ binding),
types.ts. Old src/jquery.autocomplete.js removed.
- typings/jquery.autocomplete.d.ts (hand-maintained, drifted) removed;
types are generated from source by tsc into dist/.
- scripts/build.mjs rewritten: uses esbuild for both bundles, wraps the
IIFE output in a hand-written UMD shim that matches the pre-2.0 detection
format byte-for-byte. terser dropped (esbuild handles minify).
- tsconfig.json added: target ES2020, strict, emit declarations only.
- package.json: version 2.0.0, jquery peer >=3.0, exports map with
types/import/require conditions, new typecheck script.
- .github/workflows/ci.yml: typecheck step added between format:check
and test.
- vitest.config.js: pool switched to forks + isolate:false. The threads
pool starved the worker handshake once src moved to TS (transform
overhead pushed startup past the 60s timeout). isolate:false keeps the
shared-module-state model the original Jasmine runner used.
- test/setup.js: calls installAutocomplete(jQuery) directly instead of
loading the dist UMD wrapper. Same end-state, simpler chain.
- dist/license.txt removed; the canonical license.txt lives at repo root
and the build no longer mirrors it into dist (avoids drift).
BREAKING CHANGE: drops IE-era browsers (source targets ES2020) and drops
jquery <3.0 support (peer bumped from >=1.9 to >=3.0). Public surface
unchanged — every option, callback, method, CSS class, and suggestion
shape behaves identically. The 31-spec test suite is the enforcement.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+48-21Lines changed: 48 additions & 21 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,48 +4,75 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
5
5
## Project shape
6
6
7
-
This is a single-file jQuery plugin (Ajax Autocomplete). The entire implementation lives in `src/jquery.autocomplete.js` (~1k lines). Everything else — `dist/`, `typings/`, `spec/`, `scripts/`, `index.htm` — is produced from, tests, types, or demos that one source file. Edits almost always belong in `src/jquery.autocomplete.js`; never edit `dist/*` directly (the build overwrites it).
7
+
TypeScript source under `src/` (~700 lines split into ~8 modules) compiles to a UMD bundle + an ESM bundle + a minified UMD + bundled `.d.ts` types in `dist/`. Edits belong in `src/*.ts`; never touch `dist/*` (the build overwrites it). The plugin is jQuery-only — `jQuery >=3.0` is a peer dependency.
|`src/umd-body.ts`| UMD entry point. References `$` as a free variable that the hand-written UMD wrapper (in `scripts/build.mjs`) provides as a factory parameter. |
|`src/jquery-ref.ts`|`export let $: JQueryStatic` set at install time via `setJQuery`. Live ES-module binding — every importer sees the value once `installAutocomplete` has run. |
21
+
|`src/types.ts`| Public types (`AutocompleteOptions`, `Suggestion`, callback signatures). |
8
22
9
23
## Commands
10
24
11
25
-`npm test` — Vitest run (headless, jsdom). Single-shot, exits nonzero on failure.
12
-
-`npm run test:watch` — Vitest watch mode for local TDD.
13
-
-`npm run lint` — ESLint (flat config in `eslint.config.mjs`) over `src/`, `test/`, and `scripts/build.mjs`.
14
-
-`npm run format` — Prettier rewrite of `src/`, `test/`, and `scripts/build.mjs` (100-col, 4-space, ES5 trailing commas — config in `package.json`). Demo files under `scripts/` (`countries.js`, `demo.js`) are intentionally excluded.
15
-
-`npm run format:check` — Prettier check-only, same scope. Used by CI; fails if anything would be rewritten.
16
-
-`npm run build` — runs `scripts/build.mjs` (Node ESM, no bundler): copies `src/jquery.autocomplete.js` to `dist/jquery.autocomplete.js` while substituting the `%version%` placeholder with `package.json``version`, minifies to `dist/jquery.autocomplete.min.js` via terser with a fresh banner, and syncs `devbridge-autocomplete.jquery.json` version. Run this before release/commit when source changes.
26
+
-`npm run test:watch` — Vitest watch mode.
27
+
-`npm run lint` — ESLint over `test/` and `scripts/build.mjs`. **TS source is not linted by ESLint** — `tsc --noEmit` covers it via the `typecheck` script.
28
+
-`npm run typecheck` — `tsc --noEmit`. Strict mode; runs on `src/`.
29
+
-`npm run format` — Prettier rewrite of `src/`, `test/`, and `scripts/build.mjs` (100-col, 4-space, ES5 trailing commas). Demo files under `scripts/` (`countries.js`, `demo.js`) are intentionally excluded.
30
+
-`npm run format:check` — Prettier check-only, same scope. CI gate.
31
+
-`npm run build` — runs `scripts/build.mjs` (Node ESM): esbuild emits `dist/jquery.autocomplete.esm.js` (ESM) and `dist/jquery.autocomplete.js` / `.min.js` (UMD, hand-wrapped); `tsc --declaration` emits the `.d.ts` files; the version field in `devbridge-autocomplete.jquery.json` is synced from `package.json`.
17
32
18
33
## CI
19
34
20
-
`.github/workflows/ci.yml` runs on every push to `master` and every pull request: `npm ci`, then `lint`, `format:check`, `test`, `build` — in that order, all required. Node 20 LTS, Ubuntu, single job. The `engines.node` field in `package.json` mirrors the runner version.
35
+
`.github/workflows/ci.yml` runs on every push to `master` and every pull request: `npm ci`, then `lint`, `format:check`, `typecheck`, `test`, `build` — in that order, all required. Node 20 LTS, Ubuntu, single job. The `engines.node` field in `package.json` mirrors the runner version.
21
36
22
37
## Tests
23
38
24
-
Vitest + jsdom, headless. Specs live in `test/autocomplete.test.js`. `test/setup.js` attaches a single jQuery instance to the jsdom `window` and `globalThis`, registers `jquery-mockjax` against it, silences mockjax's per-request console logging, then loads `src/jquery.autocomplete.js`. The plugin's UMD wrapper picks up `globalThis.jQuery` and registers `$.Autocomplete` plus `$.fn.autocomplete`/`$.fn.devbridgeAutocomplete` against that same instance.
39
+
Vitest + jsdom, headless. Specs live in `test/autocomplete.test.js`. `test/setup.js` attaches a single jQuery instance to `globalThis` / the jsdom `window`, registers `jquery-mockjax`, silences mockjax's per-request console logging, then calls `installAutocomplete(jQuery)` directly (bypassing the UMD wrapper). All test code shares one jQuery instance, one DOM, one set of plugin registrations.
25
40
26
-
The Vitest pool is pinned to `threads` in `vitest.config.js` — the default `forks` pool times out the worker handshake on Windows when the setup file does heavy synchronous work (jQuery + mockjax + UMD plugin load). Don't switch pools without re-verifying.
41
+
`vitest.config.js` pins `pool: "forks"` with `isolate: false`. **Don't change either.**`threads` pool starved the worker handshake once we moved to TS source (esbuild transform overhead pushed startup past the 60s timeout). `isolate: false` keeps every spec in one process — same shared-module-state model the original Jasmine runner used, so describe blocks that mutate global jQuery state stay consistent.
27
42
28
-
To run a single test, use `npx vitest run -t "test name substring"` or temporarily change `describe`/`it` to `describe.only`/`it.only`.
43
+
To run a single test: `npx vitest run -t "test name substring"` or temporarily `describe.only` / `it.only`.
29
44
30
-
The demo page `index.htm` is the manual test surface (Ajax lookup, local lookup with grouping, custom container, dynamic width). It loads jQuery + mockjax from CDN; just open in a browser.
45
+
The demo page `index.htm` is the manual test surface (Ajax lookup, local lookup with grouping, custom container, dynamic width). It loads jQuery + mockjax from CDN; open in a browser.
46
+
47
+
## Build internals
48
+
49
+
`scripts/build.mjs` does three things in order:
50
+
51
+
1.**ESM bundle** (`dist/jquery.autocomplete.esm.js`) — esbuild bundles `src/index.ts` with `external: ['jquery']`. Consumers `import 'devbridge-autocomplete'` and the plugin self-registers.
52
+
2.**UMD bundles** (`dist/jquery.autocomplete.js` and `.min.js`) — esbuild bundles `src/umd-body.ts` as IIFE (no `external`); the result is wrapped by a hand-written UMD detection shim (AMD / CommonJS / browser-global), with `$` flowing in as the factory parameter. The shim format intentionally matches the JS source that shipped before 2.0.0 so consumers don't see a contract change.
53
+
3.**Types** (`dist/*.d.ts`) — `tsc --declaration --emitDeclarationOnly`. One `.d.ts` per source file; `package.json``types` points at `dist/index.d.ts`.
54
+
55
+
The minified UMD is ~13 KB; the unminified is ~26 KB.
31
56
32
57
## Release/version flow
33
58
34
59
1. Bump `version` in `package.json`.
35
-
2.`npm run build` — this propagates the new version into `dist/jquery.autocomplete.js`(via `%version%` placeholder) and `devbridge-autocomplete.jquery.json`. The placeholder only exists in `src/`; do not hand-edit version strings in `dist/`.
60
+
2.`npm run build` — propagates the new version into the banner of each `dist/` JS file (via the build script) and syncs `devbridge-autocomplete.jquery.json`.
36
61
37
-
## Architecture notes that aren't obvious from a single file
62
+
## Architecture notes that aren't obvious from a glance
38
63
39
-
-**UMD wrapper** at the top of `src/jquery.autocomplete.js` registers under AMD, CommonJS, or browser global — keep all three branches working when touching the wrapper.
40
-
-**Dual plugin name**: registers `$.fn.devbridgeAutocomplete` unconditionally, and aliases `$.fn.autocomplete` only if it is not already taken (jQuery UI defines one). Tests and the README rely on this fallback behavior — don't remove the guard.
41
-
-**Defaults live on `Autocomplete.defaults`** and are merged per-instance via `$.extend(true, {}, defaults, options)`. New options must be added to the defaults object so deep-merge picks them up, and mirrored in `typings/jquery.autocomplete.d.ts` and the option tables in `readme.md`.
42
-
-**Response normalization**: server responses pass through `transformResult` (default JSON.parse for `dataType: 'text'`). Local `lookup` may be an array or a `function(query, done)` callback; both paths converge on the same `{ value, data }` suggestion shape used everywhere downstream (`formatResult`, `onSelect`, grouping).
43
-
-**Caching + bad-query guard**: `cachedResponse` keys by query string, and `preventBadQueries` records prefixes that returned no results so future queries with the same prefix short-circuit. `clearCache` / `clear` reset these — when adding new request paths, decide whether they should populate or honor these caches.
64
+
-**Dual plugin name**: `$.fn.devbridgeAutocomplete` is always registered. `$.fn.autocomplete` is only aliased to it if not already taken (jQuery UI defines its own). Tests and the README rely on this fallback — don't remove the guard.
65
+
-**Live `$` binding**: `src/jquery-ref.ts` exports a `let $` that `installAutocomplete` mutates at install time via `setJQuery`. Every other module (Autocomplete, format, etc.) imports `{ $ }` and sees the live value. This avoids passing `$` through every constructor / function signature.
66
+
-**Defaults**: `Autocomplete.defaults` (in `src/defaults.ts`) is merged per-instance via `$.extend(true, {}, defaults, options)`. New options must be added there AND in the `AutocompleteOptions` interface in `src/types.ts`; the option tables in `readme.md` also need updating.
67
+
-**Defaults uses `() => {}` not `$.noop`** as the no-op callback. That avoids load-time `$` access (the file is imported before `installAutocomplete` runs). Specs don't assert on identity, only behavior.
68
+
-**Response normalization**: server responses pass through `transformResult` (default JSON.parse for `dataType: 'text'`). Local `lookup` may be an array or a `function(query, done)` callback; both paths converge on the same `{ value, data }` suggestion shape used everywhere downstream.
69
+
-**Caching + bad-query guard**: `cachedResponse` keys by query string; `preventBadQueries` records prefixes that returned no results so future queries with the same prefix short-circuit. `clearCache` / `clear` reset these — when adding new request paths, decide whether they should populate or honor these caches.
44
70
45
71
## Conventions
46
72
47
-
- Source style is the existing one: ES5 inside the IIFE (no `let`/`const`/arrow funcs in `src/jquery.autocomplete.js`), `"use strict"`, `var that = this` pattern. ESLint config targets `ecmaVersion: 2022` but the file is intentionally written to run in old browsers — match the existing style rather than modernizing.
48
-
- Prettier owns formatting; run `npm run format` before committing source changes.
73
+
- TypeScript strict mode is on. Don't introduce `any` in public types. Internal `as unknown as X` casts are OK at jQuery boundaries where typings are imprecise.
74
+
- The IE-only `document.selection` branch in `isCursorAtEnd` is kept for behavioral parity with the pre-2.0 source. Modern browsers fall through it.
75
+
- Prettier owns formatting. Run `npm run format` before committing source changes.
49
76
50
77
## Commit messages
51
78
@@ -56,4 +83,4 @@ Use [Conventional Commits](https://www.conventionalcommits.org/): `<type>(<optio
56
83
- Breaking changes: append `!` after the type/scope (`feat!: ...`) and/or add a `BREAKING CHANGE:` footer.
57
84
- Scope, when useful, names the area touched: `build`, `test`, `deps`, `autocomplete`.
58
85
59
-
Examples: `test: port specs to vitest + jsdom`, `build: replace grunt with node script`, `chore(deps): bump prettier to 3.6.2`, `feat!: drop ie11 support`.
86
+
Examples: `test: port specs to vitest + jsdom`, `build: replace grunt with node script`, `chore(deps): bump prettier to 3.6.2`, `refactor!: rewrite source in typescript`.
0 commit comments