Skip to content

Task/prod 952 implement typescript in codebase#237

Open
Zahrahasannhs wants to merge 2 commits intomainfrom
task/PROD-952_implement_typescript_in_codebase
Open

Task/prod 952 implement typescript in codebase#237
Zahrahasannhs wants to merge 2 commits intomainfrom
task/PROD-952_implement_typescript_in_codebase

Conversation

@Zahrahasannhs
Copy link
Copy Markdown
Contributor

@Zahrahasannhs Zahrahasannhs commented Apr 9, 2026

Developer checklist for this pull request

  • [ x] Documentation has been updated (README's, CHANGELOG etc.)
  • [x ] Software builds without errors or warnings
  • [x ] Any debugger/console.log() statements and commented out code have been removed
  • [ x] Unit test suites have been run, all tests are passing and absent of console warnings/errors
  • [x ] Make sure that new code is unit tested and linted.

Description

Implemented typescript in code base with null safety and build config as well as resolved sonarQube warnings. Also restructured the project to create better hierarchy and align with modern TypeScript practices.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the cookie-consent codebase to TypeScript, updates build/test tooling to handle .ts sources, and tightens null-safety across DOM interactions.

Changes:

  • Switch webpack entrypoint and Babel pipeline to compile TypeScript sources.
  • Add TypeScript configuration and ambient type declarations for non-TS imports/globals.
  • Update source files and unit tests to TypeScript with stricter typing/null checks.

Reviewed changes

Copilot reviewed 19 out of 24 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
webpack.config.js Build entry switched to main.ts; Babel configured to transpile TS.
tsconfig.json Introduces strict TypeScript compiler settings and includes/excludes.
src/types.d.ts Adds ambient typings for html/scss imports and global test/runtime vars.
src/settings.ts Adds TS type annotations (incl. nullable link handling).
src/settings.test.ts Updates tests for TS module/test patterns.
src/main.ts Adds TS types and changes global API attachment/event binding approach.
src/main.test.ts Updates tests to align with globalThis API exposure.
src/enable.ts Adds TS types and refactors DOM enablement logic.
src/enable.test.ts Updates tests for TS and null-safety adjustments.
src/cookies.ts Adds TS types and refactors some string/domain handling.
src/cookies.test.ts Updates tests for new TS function signatures.
src/cookieconsent.ts Adds TS types and null-safety around cookie parsing/event handling.
src/cookieconsent.test.ts Updates tests for TS typing and session cookie behavior changes.
src/banner.ts Adds TS types and null-safe DOM element access.
src/banner.test.ts Updates tests for TS typing and safer DOM access.
src/banner.html Updates template requires to reference settings.ts.
src/analyticsCookieMatcher.ts Adds TS types and refactors pattern escaping implementation.
src/analyticsCookieMatcher.test.ts Fixes test import path for colocated TS source.
src/snapshots/banner.test.ts.snap Adds Jest snapshot output for banner test.
README.md Documents TypeScript usage and adds typecheck command.
package.json Updates entrypoint, adds typecheck script and TS-related dev deps.
package-lock.json Locks new TS/Jest type dependencies and transitive updates.
jest.config.js Updates Jest config for TS tests and Babel transform.
babel.config.js Updates Babel config to include TypeScript preset.
Comments suppressed due to low confidence (7)

babel.config.js:6

  • babel.config.js now uses export default, but the repository is CommonJS (package.json has no "type": "module") and Babel loads babel.config.js via require. This will cause a syntax error when running webpack/jest. Switch back to module.exports = babel (or rename to babel.config.mjs and migrate the rest of the build tooling to ESM consistently).
const babel = (api) => {
  const isTest = api.env('test');

  let targets = {};
  let plugins = [];

src/analyticsCookieMatcher.ts:27

  • String.prototype.replaceAll is used here at runtime. This library already ships @babel/polyfill (core-js@2) which does not polyfill replaceAll, so older browsers will throw at runtime. Prefer replace with a global regex (as before) or ensure the runtime polyfill strategy is updated to include replaceAll.
    src/enable.ts:15
  • enableScript now relies on Element.before() and Element.remove(), which are not supported in some older browsers and are not polyfilled by @babel/polyfill (core-js@2). To avoid runtime failures, use the previous DOM-parent approach (parentNode.insertBefore / removeChild) or add an explicit polyfill requirement.
    src/main.ts:60
  • globalThis is used for the public browser API attachment and event listener registration. If this script is expected to run in older browsers, globalThis is not available by default and is not provided by @babel/polyfill (core-js@2), leading to a runtime ReferenceError. Prefer window for browser-only globals or update/polyfill globalThis explicitly.
    src/cookies.ts:126
  • globalThis.location is used here, but globalThis is not available in some older browsers and isn’t polyfilled by @babel/polyfill (core-js@2). If legacy browser support is required, use window.location (or a small helper that falls back to window when globalThis is missing).
    src/cookieconsent.ts:259
  • handleSharedConsentLinkClick casts event.target to HTMLElement and calls .closest('a'). In real click events, event.target can be a Text node or another non-Element, which would make closest undefined and throw. Guard with if (!(event.target instanceof Element)) return; before calling closest.
    src/enable.test.ts:19
  • This test now wraps all assertions in if (script) { ... }. If the DOM query unexpectedly returns null, the test will pass without asserting anything (false positive). Prefer asserting script is not null (or using a non-null assertion) so the test fails when the expected script isn’t present.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from 2e5f2fb to 2490d3d Compare April 9, 2026 13:57
@Zahrahasannhs Zahrahasannhs requested a review from Copilot April 9, 2026 13:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 30 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (8)

babel.config.js:26

  • babel.config.js now uses export default (ESM) but the file extension is .js and package.json does not declare "type": "module". Babel will typically load babel.config.js as CommonJS, so this will throw a syntax error when the build/tests try to read the config. Use module.exports = ... or convert the project/config file to ESM explicitly (e.g., .mjs + ESM package configuration).
const babel = (api) => {
  const isTest = api.env('test');

  let targets = {};
  let plugins = [];

  if (isTest) {
    // Add node to babel targets when running tests
    targets = {
      ...targets,
      node: 'current',
    };

    // Add rewire for jest unit tests
    plugins = [...plugins, 'babel-plugin-rewire'];
  }

  const config = {
    plugins,
    presets: [['@babel/preset-env', { targets }], '@babel/preset-typescript'],
  };

  return config;
};
export default babel;

src/utils/enable.ts:14

  • Switching from parent.insertBefore/removeChild to script.before() and script.remove() can break compatibility with older browsers (notably IE11) that don't implement these DOM APIs. If IE support is still required (there are other IE-related comments in the codebase), consider reverting to the previous DOM operations or adding an explicit polyfill requirement.
    src/utils/enable.ts:41
  • Replacing indexOf(...) === -1 with allowedCategories.includes(...) changes the runtime requirements (Array.prototype.includes is not supported in IE11 without a polyfill). If legacy browser support is expected, keep indexOf or ensure the runtime polyfill set covers includes.
    src/utils/enable.test.ts:12
  • document.querySelector('script') can return null, but enableScript expects an HTMLScriptElement. With strict TypeScript checking, this should be a type error (and is also a potential runtime error if the DOM setup changes). Add a non-null assertion or an explicit null check before calling enableScript.
    src/utils/enable.test.ts:19
  • This test now conditionally asserts only if script is truthy. If enableScript fails to insert a new <script>, the test will silently pass with zero assertions. Prefer asserting that script is not null (or using a non-null assertion) so the test fails when the DOM isn’t in the expected state.
    src/services/cookieconsent.ts:263
  • event.target is typed as EventTarget | null and is not guaranteed to be an Element. Casting to HTMLElement and calling closest() can throw at runtime if the click target is not an element (e.g., text node). Guard with event.target instanceof Element (or similar) before calling closest.
    src/utils/analyticsCookieMatcher.ts:28
  • Using String.prototype.replaceAll here increases runtime requirements. The project still depends on @babel/polyfill (core-js@2), which does not polyfill replaceAll, so this can throw in older browsers. Consider using replace(/.../g, ...) as before or updating the polyfill strategy (e.g., core-js@3) if modern-only browser support is intended.
    package.json:56
  • jest is pinned to ^29.7.0 but @types/jest is ^30.0.0. This mismatch can cause type incompatibilities in test code (especially with strict TS settings). Align the @types/jest major version with the installed Jest major, or upgrade Jest to match the types.
    "@eslint/js": "^9.39.4",
    "@lhci/cli": "^0.15.1",
    "@types/jest": "^30.0.0",
    "@types/node": "^25.5.2",
    "babel-core": "^7.0.0-bridge.0",
    "babel-jest": "^29.7.0",
    "babel-loader": "^9.1.3",
    "babel-plugin-rewire": "^1.2.0",
    "css-loader": "^7.0.0",
    "eslint": "^9.0.0",
    "eslint-config-nhsuk": "^1.0.0",
    "html-loader": "^5.1.0",
    "http-server": "^14.1.1",
    "husky": "^9.0.11",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from 2490d3d to f323f3d Compare April 10, 2026 16:21
"watch": "webpack --watch",
"prepare": "husky"
"prepare": "husky",
"typecheck": "tsc --noEmit"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm run typecheck fails for some files. Also you can include tsc during build and pre commit check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed typecheck errors. Added tsc during build and pre-commit checks

README.md Outdated
The release also must contain changes to the package version number to match the new tag.

If the release contains a change that will require the banner to be redisplayed to users, then the `COOKIE_VERSION` variable in cookieconsent.js must be increased by 1.
If the release contains a change that will require the banner to be redisplayed to users, then the `COOKIE_VERSION` variable in cookieconsent.ts must be increased by 1.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no cookieconsent.ts file in this change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readme updated; you're right file name was updated

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from f323f3d to 796ecfb Compare April 14, 2026 13:28
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from 796ecfb to 4f981e7 Compare April 14, 2026 13:29
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 34 out of 45 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

babel.config.js:26

  • babel.config.js is now written as an ES module (export default), but the project is not configured for ESM and Babel will treat babel.config.js as CommonJS by default. This will cause Babel/Jest/webpack to fail to load the config. Use module.exports = (api) => ({ ... }) or rename to babel.config.mjs and update module type accordingly.
const babel = (api) => {
  const isTest = api.env('test');

  let targets = {};
  let plugins = [];

  if (isTest) {
    // Add node to babel targets when running tests
    targets = {
      ...targets,
      node: 'current',
    };

    // Add rewire for jest unit tests
    plugins = [...plugins, 'babel-plugin-rewire'];
  }

  const config = {
    plugins,
    presets: [['@babel/preset-env', { targets }], '@babel/preset-typescript'],
  };

  return config;
};
export default babel;


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +130 to +139
/**
* Consumes the `nhsa.sc` shared consent query parameter from the URL.
*
* - If the parameter is absent, nothing is done.
* - If the parameter is invalid (not 1 or 0), it is removed without modifying cookies.
* - If valid:
* - If the user has not previously consented, a new session cookie is created.
* - If the user has previously consented, but the value has changed, their
* `statistics` consent is updated and written as a session cookie.
*
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring for consumeSharedConsentQuery says that if the user has previously consented and the nhsa.sc value has changed, their statistics consent will be updated. The current implementation only calls setConsent when !isCookieConsentGiven(), so previously-consented users will never be updated. Please either implement the documented behavior or amend the comment so it matches what the code actually does.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +106
export function registerSharedConsentLinkHandler(
getConsentSetting: GetConsentSettingFn,
): void {
const handler = createSharedConsentLinkClickHandler(getConsentSetting);
currentHandler = handler;
document.addEventListener('click', handler);
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registerSharedConsentLinkHandler overwrites currentHandler but never removes an already-registered listener before adding a new one. If this is called multiple times (e.g. once on load and again after accepting consent), you'll end up with duplicate click handlers and repeated query-param mutation. Consider calling unregisterSharedConsentLinkHandler() at the start of this function (or no-op if currentHandler is already set).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is is important, can you make sure we dispose the handler as that would cause issues if we dont

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from 4f981e7 to db4188a Compare April 14, 2026 13:42
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from db4188a to a7abcae Compare April 14, 2026 13:54
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from a7abcae to 785ea79 Compare April 14, 2026 14:45
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

@Zahrahasannhs Zahrahasannhs force-pushed the task/PROD-952_implement_typescript_in_codebase branch from 785ea79 to e9f00ec Compare April 14, 2026 14:54
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants