Skip to content

Dependency upgrade and V1 dead-code cleanup#660

Open
wikirby wants to merge 25 commits into
masterfrom
user/wikirby/dep-cleanup
Open

Dependency upgrade and V1 dead-code cleanup#660
wikirby wants to merge 25 commits into
masterfrom
user/wikirby/dep-cleanup

Conversation

@wikirby
Copy link
Copy Markdown
Contributor

@wikirby wikirby commented May 22, 2026

Summary

  • Removes V1-era source files (Mithril sidebar wiring, page-nav tooltip system, video extractor pipeline, polyfills, etc.) and their npm packages
  • Upgrades the build toolchain: gulp 3 → 5, TypeScript 3.5 → 5.7, pdfjs-dist 1.7 → 5.7, jwt-decode 2 → 4, plus minor majors across the gulp ecosystem
  • Pins a set of transitive dev-deps via npm overrides
  • Bumps @types/chrome 0.0.257 → 0.1.42 (now possible with TS 5+) and renames the chrome event-info types accordingly
  • Vendors the section-picker PNGs into src/images/ (previously copied from onenotepicker/target/images/, which is now removed)
  • One source-file case fix: dataBoundary.tsDataBoundary.ts to match the internal repo's import
  • Round 2 (below): drops browserify / gulp-typescript / gulp-cssnano / onenoteapi / tslint, replaces with esbuild / direct tsc / gulp-postcss + cssnano 8 / local types shim / ESLint flat config; bumps JS target to ES2022 with a first modernization sweep

Round 2: Build infra modernization

  • Replace browserify with esbuild for bundling. Each entry point becomes an esbuild.build({ format: "iife", ... }) call; the LogManager UMD standalone wrap is handled by esbuild's globalName option. Bundle sizes net -71KB (-7.2%) from better tree-shaking. Drops the unused crypto-browserify / elliptic polyfill chain that browserify auto-pulled.
  • Drop gulp-typescript (6+ years stale; no v6 stable, latest is 6.0.0-alpha.1 from Nov 2019). Compile step becomes a child_process.spawn of local typescript/bin/tsc -p tsconfig.json. Output byte-identical to before.
  • Replace gulp-cssnano with gulp-postcss + cssnano 8 + postcss 8 (the modern decomposition recommended by cssnano's own README since 2018). Build-time only; renderer.css minifies to the same ~17% reduction.
  • Drop onenoteapi runtime dep. V3 calls the OneNote API directly via fetch already; the only consumed surface was the RequestErrorType enum + a few type names. Replaced with a 27-line local src/scripts/oneNoteApi.ts shim imported explicitly by the 17 consumers (also drops the unused IOneNoteApi interface from responsePackage.ts and the gulpfile concat that injected oneNoteApi.min.js into the SW bundle).
  • Migrate tslintESLint 10 + typescript-eslint 8 (flat config in eslint.config.mjs). tslint went end-of-life in 2019; the 23 rules from src/tslint.json are translated to ESLint equivalents. Code touch-ups: varlet across 18 files (262 occurrences in the three namespace files alone), four surgical // eslint-disable-next-line no-console at legitimate call sites + one file-level disable on webConsole.ts (its entire purpose is console wrapping), indent + trailing-space eslint --fix. The gulp tslint task is replaced by a lint task (spawn-based eslint) and kept as an alias so external callers / pipeline refs keep working.
  • Fix prod build under gulp 5: mergeSettings passes allowEmpty: true to gulp.src. Gulp 5 errors hard on a singular glob that doesn't resolve; gulp 4 silently emitted no files for missing overrides. The override files (production.json / dogfood.json) live in WebClipper_Internal, not the public repo, so they were tripping --production builds since the gulp 5 upgrade.

Round 3: ES2022 + first modernization sweep

  • Bump JS target ES2017 → ES2022 across tsconfig (target + lib), gulpfile (esbuild target), eslint config (ecmaVersion), and both manifests (minimum_chrome_version: "94"). Chromium 94 (Sep 2021) is the floor; covers private class fields, top-level await, Object.hasOwn, .at() on arrays/strings, and logical assignment operators. MV3's hard minimum is Chromium 88 — real-world auto-updated installs are far ahead.
  • Enable typed linting (parserOptions.project pointing at tsconfig.json) and the @typescript-eslint/prefer-optional-chain rule. First sweep covers 14 source files: x && x.y (and x && x.y ? x.y : z ternary forms) → x?.y / x?.y ?? z. Mostly eslint --fix; ~25 ternary cases the rule won't autofix were done by hand. All semantically equivalent for our LHS shapes (objects/functions, never 0/false/"" sensitive).
  • One Object.prototype.hasOwnProperty.call(o, k)Object.hasOwn(o, k) (new in ES2022).

Followup typed-linting rules (prefer-nullish-coalescing, prefer-includes, prefer-string-starts-ends-with, no-floating-promises) and structural cleanup of the larger V3 worker/renderer files are deferred to a focused successor PR — scope discipline for review.

Dropped from devDependencies: browserify, gulp-typescript, gulp-cssnano, gulp-plumber, gulp-tslint, tslint, vinyl-source-stream, merge-stream, onenoteapi.
Added: esbuild, eslint, typescript-eslint, cssnano, gulp-postcss, postcss.

Renamed: src/scripts/logging/logManager.d.tslogManagerGlobals.d.ts. The legacy gulp.src(\"**/*.+(ts|tsx)\") glob happened to include .d.ts files (suffix is .ts), masking the fact that tsc's default project discovery treats a co-named .d.ts as the auto-generated declaration for its .ts sibling and drops it from inputs. Renaming makes the ambient LogManager global visible again.

Notable behavioral changes (none expected at runtime)

  • pdfjs-dist v5 is ESM-only, so a small src/pdfLoader.mjs shim now imports pdf.mjs and exposes it on window.pdfjsLib. renderer.ts waits on a pdfjs-ready event before calling getDocument. PDF mode end-to-end smoke test is the main regression risk.
  • gulp 5's streamx engine doesn't compose merge-stream pipelines well; the gulpfile now uses Promise.all + a small streamToPromise helper for parallel work and gulp.series for sequencing (replaces run-sequence).
  • jwt-decode v4 changed from default export to named export — the one call site in cachedHttp.ts was updated.
  • ESLint no-console is now actually enforced. The four pre-existing intentional call sites (offscreen warn paths, public-stub logManager, webConsole wrapper) have surgical disables.

Test plan

  • Sign in (MSA + OrgId)
  • Full Page clip
  • Article mode clip (incl. highlighter + font toggles)
  • Region mode clip
  • Bookmark mode clip
  • PDF mode — https:// PDF and file:/// PDF
  • Sign out + sign back in
  • Verify Aria telemetry still flushing (Network tab for browser.events.data.microsoft.com)
  • npm run build clean
  • npm run build:prod clean (with WebClipper_Internal sibling clone)
  • CI build green

wikirby and others added 25 commits May 12, 2026 12:25
Required by upcoming dep upgrades: gulp 5 (>=10), TypeScript 5
(>=12.20), ESLint 9 (>=18.18), pdfjs-dist 5 (>=20.16). Node 10 was
EOL April 2025; aligning with Office monorepo standard and matching
local dev (Node 24 LTS).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five string constants / enum entries are no longer referenced anywhere
in the codebase after the V2 migration:

  - Constants.CommunicationChannels.injectedAndUi (INJECTED_AND_UI)
    -- V1 channel between injected sidebar and clipper UI
  - Constants.FunctionKeys.escHandler (ESC_HANDLER)
    -- V1 sidebar Escape-key passthrough
  - Constants.FunctionKeys.setInjectOptions (SET_INJECT_OPTIONS)
    -- V1 inject-options message routing
  - Constants.FunctionKeys.toggleClipper (TOGGLE_CLIPPER)
    -- V1 toggle-from-page-nav message
  - Log.Failure.Label.IFrameMessageHandlerHasNoOtherWindow
    -- V1 IFrameMessageHandler failure scenario (handler removed)

All five verified orphaned via grep -- zero references outside the
declaration files. Failure.Label is used by string name only
(Log.Failure.Label[label] reverse lookup), so removing one entry
shifts subsequent numeric values but doesn't affect wire format or
any code path.

Narrow first-pass cleanup; the broader Communicator-stack removal
(frontEndGlobals, remoteStorage, parent OAuth path,
extensionWorkerBase V1 paths, the remaining extensionAndUi /
createHiddenIFrame constants) lands in a subsequent commit once
the two module-level callers of Clipper.logger are migrated off
the static slot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two module-level callers of `Clipper.logger` are migrated to take
the logger as an explicit parameter, eliminating the static-slot
dependency entirely:

  - Log.ErrorUtils.sendFailureLogRequest(logger, data) -- was reading
    Clipper.logger internally; now takes logger from caller. Updated
    sites: extensionBase (GetChangeLog + UnhandledExceptionThrown),
    webExtensionWorker (UnclippablePage).
  - Log.ErrorUtils.handleCommunicatorError(logger, channel, e, ...) --
    same change, passes logger to sendFailureLogRequest internally.
    Updated sites: extensionWorkerBase (extensionAndUi +
    injectedAndExtension Communicator error handlers).
  - UserDataBoundaryHelper(logger) -- takes logger in constructor;
    AuthenticationHelper passes its own this.logger when instantiating.

ExtensionBase constructor reorders so `setUnhandledExceptionLogging()`
runs after `this.logger` is created. The onerror handler closure
captured `this`, but errors fired during the brief construction
window between the two calls would have hit an undefined logger.
Now the handler is registered only once a valid logger exists.

The `Clipper.logger = this.logger` assignment added in #657 and its
explanatory comment are removed -- no live code reads `Clipper.logger`
anymore. The static slot itself remains in frontEndGlobals.ts for
deletion in the next commit; this one is scoped to the caller-side
migration.

unsupportedBrowserInject.ts is deleted -- pure V1 inject pattern
(IE<10 unsupported-browser fallback), zero external references in
src/, gulpfile, or HTML. The lone sendFailureLogRequest call site
inside it goes with the file rather than being maintained.

Pre-existing type issue fixed as a drive-by: webExtensionWorker.ts
line 155 was passing `chrome.runtime.lastError.message` (typed as
`string | undefined`) into a field requiring `string`. Added `|| ""`
fallback. Surface only fires on UnclippablePage logging path.

Verified clean: npm run build:prod completes with zero TS errors and
tslint passes. Bundle scan confirms zero `Clipper.logger` references
remain and failure-logging code paths are preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All three files were V1 leftovers held alive only by mutual imports
after the previous commit migrated the last live callers off the
Clipper.logger static slot:

  - frontEndGlobals.ts: V1 Mithril-era Clipper class with static slots
    for injectCommunicator / extensionCommunicator / logger /
    sessionId / storage. The setters/getters were called from
    V1 clipper.tsx during sidebar bootstrap; the file stopped being
    referenced once V2's renderer-window architecture took over and
    clipper.tsx was deleted. The only remaining readers (Clipper.logger
    in errorUtils and userDataBoundaryHelper) were migrated to take
    the logger as an explicit parameter in the previous commit.
  - storage/remoteStorage.ts: V1 storage wrapper that round-tripped
    chrome.storage reads/writes through the extensionCommunicator.
    V2 reads chrome.storage directly via the offscreen document and
    has no use for the IPC indirection.
  - storage/storageAsync.ts: interface only implemented by
    RemoteStorage and only consumed by Clipper.storage in
    frontEndGlobals.

Verified clean: zero remaining references to any of the three across
src/, gulpfile.js, and HTML. Build passes (npm run build:prod) with
no TS or tslint errors.

The remaining V1 Communicator stack -- the Communicator class itself,
the log decorators, messageHandler, parent OAuth path, and the V1
constants used in extensionWorkerBase -- lands in a subsequent commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The parent launchPopupAndWaitForClose method used window.addEventListener("message", ...)
to monitor an OAuth popup opened with window.open(). That entire path is
unreachable in the MV3 service worker (no window global) and was MSRC-flagged
during the V2 audit. Its only caller was a catch fallback in the MV3
subclass that would have thrown ReferenceError immediately if executed.

- Drop launchPopupAndWaitForClose from ExtensionWorkerBase.
- Replace the unreachable fallback in launchWebExtensionPopupAndWaitForClose
  with log + reject so a windows.create() failure surfaces cleanly.
- Delete browserUtils.ts (openPopupWindow + IE-detection helpers are all
  unreferenced after the parent method goes).

The live OAuth path (renderer "signIn" → doSignInAction →
launchWebExtensionPopupAndWaitForClose with browser.windows.create() +
webRequest.onCompleted) is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
V3 worker flow uses port-based messaging (signIn, save, capture progress)
plus direct chrome.runtime.onMessage listeners for content-capture and
region-overlay payloads. None of it touches the V1 Communicator class —
its registered functions (signInUser, signOutUser, getStorageValue,
takeTabScreenshot, telemetry, etc.) had no senders left after the
Mithril sidebar / content-script flow died.

This commit removes the wiring without touching the Communicator class
itself, which WebClipper_Internal/src/scripts/logging/logManager.ts still
references via copyInternal. Those four files stay as orphan code until
the internal repo can be updated in parallel.

Changes:
- ExtensionWorkerBase: drop the 5 Communicator fields (uiCommunicator,
  pageNavUi, debugLoggingInject, inject, pageNavInject), the two
  message-handler-thunk ctor params, and every initialize/teardown
  method (~250 LOC). closeAllFramesAndInvokeClipper and invokeClipper
  lose their dead pageNav/UI callRemoteFunction calls. invokeWhatsNew/
  invokeTooltip lose registerLocalizedStringsForPageNav (V3 reads
  locStrings from localStorage). setOnUnloading/onUnloading field
  removed — destruction already goes through removeWorker on tab close.
- WebExtensionWorker: drop messageHandlerThunk + noOpTrackerInvoked +
  setUpNoOpTrackers call.
- logManager.ts / logManager.d.ts: drop the now-unused uiCommunicator
  parameter from createExtLogger. (Internal logManager unchanged for now.)
- errorUtils.ts: drop handleCommunicatorError, setNoOpTrackerRequestTimeout,
  sendNoOpTrackerRequest, NoOpLogEventData — all only called from the
  removed wiring.
- log.ts: drop NoOp re-export.
- Delete injectOptions.ts (no callers), webExtensionMessageHandler.ts
  (the WebExtensionBackgroundMessageHandler was instantiated only by the
  thunk we just removed), submodules/noop.ts (label enum was only read
  by the removed setUpNoOpTrackers).

Net: -470 / +21. Telemetry path unchanged: AriaLoggerDecorator in
WebExtensionWorker's logger chain is untouched; BrowserLanguage,
FlightInfo, sign-in/out, save flows still log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Pdf/Product/Recipe/Video/WhatsNew tooltip system fired in V1 via
addPageNavListener (webNavigation.onCompleted) → shouldShowTooltip →
worker.invokeTooltip → executeScript({ files: [pageNavInjectUrl] }).
In V3 the pageNavInject bundle isn't built (no such source) so the
inject failed silently while the listener still fired on every nav.
shouldShowWhatsNewTooltip was already hardcoded false.

Files deleted:
- src/scripts/extensions/tooltipHelper.ts (storage-backed tooltip
  cadence calculations, 102 LOC)
- src/scripts/clipperUI/tooltipType.ts, tooltipProps.ts
- src/scripts/versioning/version.ts, changeLog.ts, changelogHelper.ts
  (Version class + changelog fetch — only consumed by What's New
  tooltip flow)

extensionBase.ts: drop tooltip field, constructor wiring,
listenForOpportunityToShowPageNavTooltip, showTooltip,
shouldShowTooltip, shouldShowVideoTooltip, showWhatsNewTooltip,
shouldShowWhatsNewTooltip, getLastSeenVersion, getNewUpdates,
updateLastSeenVersionInStorageToCurrent, shouldCheckForMajorUpdates,
and the addPageNavListener / checkIfTabIsOnWhitelistedUrl /
checkIfTabMatchesATooltipType / checkIfTabIsAVideoDomain abstracts.

extensionWorkerBase.ts: drop invokeWhatsNewTooltip, invokeTooltip,
and their two *BrowserSpecific abstracts.

webExtensionWorker.ts: drop invokePageNavBrowserSpecific,
invokeWhatsNewTooltipBrowserSpecific, invokeTooltipBrowserSpecific.

webExtension.ts: drop addPageNavListener (the
webNavigation.onCompleted listener), checkIfTabIsOnWhitelistedUrl,
checkIfTabMatchesATooltipType, checkIfTabIsAVideoDomain. Imports of
TooltipType, VideoUtils, ChangeLog, Version, ResponsePackage,
Constants, Localization-via-Log, ClientType-via-Log all reduced.

urlUtils.ts: drop checkIfUrlMatchesAContentType (only called by the
removed checkIfTabMatchesATooltipType).

invokeSource.ts: drop the five tooltip-source enum values.
constants.ts: drop maximumNumberOfTimesToShowTooltips,
timeBetweenSameTooltip, timeBetweenDifferentTooltips,
timeBetweenTooltips, changelogUrl, QueryParams.changelogLocale.
clipperStorageKeys.ts: drop lastSeenVersion, lastSeenTooltipTimeBase,
lastClippedTooltipTimeBase, numTimesTooltipHasBeenSeenBase.

Net: -359 / +2. webNavigation.onCompleted listener no longer fires on
every page nav. Telemetry impact: TooltipImpression, WhatsNewImpression,
InvokeTooltip, InvokeWhatsNew events no longer fire (they couldn't
deliver a usable signal in V3 anyway — the tooltip never rendered).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The V3 worker overrides closeAllFramesAndInvokeClipper to open the
renderer window directly, bypassing the parent class's invokeClipper
chain entirely. After Tier 2 removed the Communicator wiring that
exposed these methods, every remaining piece of the V1 invoke flow
became unreachable. The InjectUrls config that pointed at the legacy
inject bundles (chromeInject.js, chromePageNavInject.js,
chromeDebugLoggingInject.js — none of which the build even produces)
fell along with it.

ExtensionWorkerBase (-80 LOC):
- Drop clipperFunnelAlreadyLogged, keepAlive, setKeepAlive
  (V3 keepalive lives in the renderer's 25s port ping; SW-side
  setInterval was V1-only)
- Drop invokeClipper, parent closeAllFramesAndInvokeClipper body
  (now declared abstract — only WebExtensionWorker.override is alive)
- Drop abstract invokeClipperBrowserSpecific,
  invokeDebugLoggingBrowserSpecific,
  isAllowedFileSchemeAccessBrowserSpecific,
  takeTabScreenshot, takeFullPageScreenshot
- Drop invokeDebugLoggingIfEnabled, default cancelFullPageScreenshot

WebExtensionWorker (-114 LOC):
- Drop invokeClipperBrowserSpecific (executeScript with
  chromeInject.js that doesn't exist)
- Drop invokeDebugLoggingBrowserSpecific
  (chromeDebugLoggingInject.js doesn't exist)
- Drop isAllowedFileSchemeAccessBrowserSpecific (file-scheme alert
  path was only triggered by the dead invokeClipper)
- Drop takeTabScreenshot / takeFullPageScreenshot (V3 renderer
  captures via captureVisibleTab in its own loop)
- Drop cancelFullPageScreenshot override
  (activeRendererCleanup is still invoked from tabs.onUpdated
  nav-away listener — that call site is kept)
- Drop injectUrls field + ctor param
- Drop invokeDebugLoggingIfEnabled() call from ctor
- Drop InjectHelper + InjectUrls imports

WebExtension (-9 LOC):
- Drop injectUrls field + ctor param
- Drop new WebExtensionWorker(injectUrls, ...) arg

chromeExtension.ts / edgeExtension.ts (-12 LOC):
- new WebExtension(ClientType) — drop the InjectUrls literal that
  pointed at the three nonexistent inject bundles

Deleted:
- src/scripts/extensions/webExtensionBase/injectUrls.ts
  (interface for the three dead URL fields)
- src/scripts/extensions/injectHelper.ts (alertUserOfUnclippablePage
  was a no-op stub; isKnownUninjectablePage was buggy and unused —
  iterates over fn.length instead of regexes.length)

Net: -208 / +7. No telemetry change; the InvokeClipper event is
still logged by WebExtensionWorker.closeAllFramesAndInvokeClipper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trim constants and log enum values whose only callers were removed by
Tier 1/2/3a/3b. Verified zero remaining references via grep. Audit
each candidate: separate truly-dead (V3 concept absent) from
parity-gap candidates (V3 has feature but doesn't yet log).

Truly-dead constants removed in constants.ts:
- CommunicationChannels module entirely (5 V1 communicator channels)
- SmartValueKeys module entirely (5 V1 SmartValue broadcast keys)
- FunctionKeys reduced to just `telemetry` (the only key still
  referenced — by the kept-orphan communicatorLoggerPure.ts).
  Removes 31 entries.
- Settings.noOpTrackerTimeoutDuration
- QueryParams.{category, channel, clientType, clipperVersion,
  correlationId, event, eventName, failureId, failureInfo,
  failureType, inlineInstall, label, noOpType, stackTrace,
  timeoutInMs, url, wdFromClipper}. Live ones kept: authType,
  clipperId, domain, error, errorDescription, userSessionId.
- LogCategories module (oneNoteClipperUsage was V1 misc-log endpoint)

Truly-dead Event.Label entries (12):
- Tooltip system: TooltipImpression, WhatsNewImpression,
  InvokeTooltip, InvokeWhatsNew, ClosePageNavTooltip
- V1 augmentation API: AugmentationApiCall, ClipAugmentationOptions
- V1 device-ID telemetry: DeviceIdMap
- V1 region compression: CompressRegionSelection (V3 captures JPEG
  directly, no separate compress step)
- V1 DOM video extractor: AddEmbeddedVideo (V3 oEmbed is a different
  operation via oembedExtractor.ts hitting oembed.json endpoints —
  V1's videoExtractor pipeline in domParsers/ is fully dead)
- V1 communicator: ClearNoOpTracker
- V1 Edge debug button: DebugFeedback

Truly-dead Failure.Label entries (4):
- GetChangeLog (V3 has no What's New tooltip)
- UnclippablePage (V3 disables the action button on chrome://*/edge://*
  tabs rather than firing telemetry — user can't even click)
- UnsupportedBrowser (V3 is MV3-only)
- OrphanedWebClippersDueToExtensionRefresh (V3 worker has no orphan
  tracking — renderer windows close on extension reload)

Truly-dead PropertyName.Custom entries (18):
- Tooltip: TooltipType, LastSeenTooltipTime, NumTimesTooltipHasBeenSeen,
  PageNavTooltipType, FeatureEnabled
- Augmentation: AugmentationModel
- Device-ID: DeviceIdInStorage, DeviceIdInCookie
- Region compression: InitialDataUrlLength, FinalDataUrlLength
- Ratings prompt (V3 has zero ratings prompt logic): RatingsInfo,
  ShouldShowRatingsPrompt
- Communicator/NoOp: Channel, TimeToClearNoOpTracker
- PDF redundant: NumPages (V3 uses PdfFileTotalPageCount/
  PdfFileSelectedPageCount), AverageProcessingDurationPerPage
- DOM speculative: MaxDepth
- Batch request speculative: IsRetryable

Parity-gap labels KEPT (V3 has the feature, telemetry could be
added in a follow-up parity PR — comments mark them):
- Event.Label: FullPageScreenshotCall, GetCleanDom
- PropertyName.Custom: IsSerif, FontSize, ContainsAtLeastOneHighlight,
  DomSizeInBytes, BytesTrimmed, FullPageScreenshotContentFound

Telemetry labels are sent as strings (not numeric enum values) so
removing entries doesn't affect Aria pipeline schema — past events in
the data warehouse retain their original label names.

Net: -119 / +15.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweeps unused npm packages, V1 source files, and the dead-code surface
they pulled in.

Packages removed from devDependencies (28):
- V1 UI: mithril, @types/mithril, popper.js, @types/popper.js,
  velocity-animate, @types/velocity-animate, rangy, @types/rangy
- V1 URL/lib: urijs, @types/urijs, onenotepicker, sanitize-html,
  @types/sanitize-html
- V1 polyfills: es5-shim, json3, es6-promise, @types/es6-promise
- V1 testing (zero tests exist): qunitjs, sinon, sinon-qunit,
  node-qunit-phantomjs, @types/qunit, @types/sinon
- V1 build infra: gulp-msx (Mithril JSX), manifoldjs (Edge Classic
  packager), forever, node-static, ssl-root-cas, gulp-shell, gulp-open
- V1 unused @types: jquery, safari-extension, filesystem, har-format,
  lodash
- V1 utility: lodash (stringUtils.parsePageRange rewritten with
  Array#filter dedupe + native sort; was using _.range/_.last/
  _.sortBy/_.sortedUniq)

Plus pack-edge npm script (manifoldjs-based Edge Classic packager).

Source files deleted:
- src/scripts/polyfills.ts (IE9 endsWith/Object.assign/Promise/rAF
  polyfills all no-op in MV3 Chrome/Edge)
- src/scripts/domParsers/ (8 files: domUtils, videoUtils,
  videoExtractor + factory + Vimeo/YouTube/KhanAcademy variants -
  all V1, no live caller after earlier cleanup tiers verified the
  V3 oEmbed flow uses oembed.json endpoints not DOM scanning)
- src/unsupportedBrowser.html (V1 IE9 fallback page)
- lib/sanitize-html.js (V1 vendored library, never loaded - V3
  oembedExtractor.sanitizeProviderHtml is hand-rolled with DOMParser)
- lib/tests/{bind_polyfill,jquery-2.2.0.min}.js (V1 test scaffold)

Source edits:
- extensionWorkerBase.ts: drop Polyfills import + Polyfills.init() call
- stringUtils.ts: drop lodash, rewrite parsePageRange dedupe/sort with
  native JS

tsconfig.json - set explicit target/lib for ES2017:
- Trim types[] from 11 entries down to 2 (chrome, pdf)
- ADD target: "es2017" + lib: ["es2017", "dom"] - required after
  dropping @types/es6-promise. Without it, TypeScript's default target
  (ES3) loses the Promise constructor declaration that es6-promise
  was providing, which caused a silent failure: the public-repo's
  stub createExtLogger would compile and bundle correctly, but the
  WebClipper_Internal logManager.ts (copied in via copyInternal) would
  fail TypeScript compilation, leaving build/scripts/logging/
  logManager_internal.js missing. The bundleLogManager task then fell
  through to the stub variant, and target/chrome/logManager.js shipped
  with a no-op StubSessionLogger as createExtLogger - silently
  disabling all Aria telemetry without breaking the build. Verified
  the fix by grep'ing target/chrome/logManager.js for the Aria SDK
  symbol microsoft.applications.telemetry.LogManager (8 hits restored).

Gulpfile edits:
- Drop dead require()s: forever, gulp-msx (msx), gulp-shell (shell),
  gulp-open (open), node-qunit-phantomjs (qunit)
- Drop mithrilify task definition + its slot in the compile sequence
- Drop exportPickerFiles function (V3 renderer has its own
  section-picker DOM/CSS, doesn't use the onenotepicker UI assets)
- Drop V1 library files from exportCommonLibFiles: json3, es5-shim,
  mithril, rangy, urijs, velocity, onenotepicker, sanitize-html. Keep
  pdf.combined.js (live - loaded by renderer.html).
- Drop unsupportedBrowser.html from exportCommonSrcFiles + watch list
- Drop PATHS.LIBROOT, PATHS.TARGET.TESTS (unreferenced after the above)

Note: 4 orphan files (communicator.ts, messageHandler.ts,
communicatorLoggerDecorator.ts, communicatorLoggerPure.ts) intentionally
preserved per Tier 2 - WebClipper_Internal logManager.ts still imports
them via the relative ../communicator/communicator path. Their
transitive dep onenoteapi (kept for the triple-slash type reference
in webExtensionWorker.ts) also stays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
renderer.ts:588-672 + renderer.less:827-851 still load these PNGs by
the relative paths images/{arrow_down,arrow_right,notebook,section,
section_group}.png. The previous commit dropped onenotepicker (no
live code consumer for the package itself) along with the gulpfile
exportPickerFiles task that used to copy them from
node_modules/onenotepicker/target/images/* to target/. That left
the renderer firing 4× ERR_FILE_NOT_FOUND on every open.

Copying the 5 PNGs from onenotepicker@1.0.9 directly into src/images/
fixes the regression — the existing exportCommonSrcFiles task already
ships src/images/**/* to target/{chrome,edge}/images/. Now the package
itself can stay gone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps almost all surviving packages and rewrites the gulpfile for gulp
4/5's API. Vulnerabilities: 178 (start of dep-cleanup) → 89 (after dead
package sweep) → 52 (after upgrades). The remaining 52 cluster around
tslint 3, gulp-typescript 3, browserify 17 transitive deps, and
pdfjs-dist 1.7 — those need their own targeted upgrades (Tier 3, code-
impacting).

Package version bumps in package.json:
- gulp 3.9.1 → 5.0.1
- browserify 14.1.0 → 17.0.1
- del 2.2.2 → 8.0.1
- file-exists 2.0.0 → 5.0.1 (then dropped entirely — replaced with
  fs.statSync wrapper)
- globby 6.1.0 → 16.2.0
- gulp-less 3.3.0 → 5.0.0
- gulp-merge-json 0.6.0 → 2.2.1
- gulp-rename 1.2.2 → 2.1.0
- gulp-zip 4.0.0 → 6.1.0
- gulp-cssnano 2.1.2 → 2.1.3
- gulp-plumber 1.1.0 → 1.2.1
- merge-stream 1.0.1 → 2.0.0
- vinyl-source-stream 1.1.0 → 2.0.0
- yargs 6.6.0 → 18.0.0
- jwt-decode 2.2.0 → 4.0.0

Additional drops:
- run-sequence (no longer needed — gulp.series replaces it)
- file-exists (replaced with one-line fs.statSync wrapper)
- gulp-rtlcss (no consumer after V1 cleanup — renderer.less handles
  RTL inline via [dir="rtl"] selectors instead of gulp-rtlcss generating
  a separate file)
- gulp-shell, gulp-open (already-unused requires)

Held at current versions (Tier 3, would need code changes elsewhere):
- @types/chrome 0.0.257 — 0.1.42 requires TypeScript 5.3+; we're on 3.5
- typescript 3.5.3, tslint 3.15.1, gulp-typescript 3.1.4, pdfjs-dist
  1.7.290, onenoteapi 1.1.0 — each needs its own focused upgrade PR

Gulpfile migration (gulp 3 → gulp 5):
- Drop run-sequence import + 14 runSequence(...) calls → gulp.series
- Convert task signatures from gulp.task("name", [deps], fn) →
  gulp.task("name", gulp.series("dep", fn))
- Convert gulp.watch(glob, ["taskName"]) → gulp.watch(glob,
  gulp.series("taskName"))
- copyInternal: return Promise.resolve() on no-op path (gulp 4+ requires
  every task to signal completion)
- Replace merge-stream-based composition with Promise.all+streamToPromise
  wrapper — gulp 5's streamx engine throws "Writable stream closed
  prematurely" when merge-stream composes multiple gulp.dest writers
- Split exportChrome/exportEdge into per-step tasks (JS/CSS/SrcFiles/
  LibFiles/PackageFiles) wired through gulp.series, removing the
  nested-merge pattern that fails under streamx
- buildOnly: gulp.series.apply(gulp, tasks)(done) replaces
  runSequence.apply
- Drop clipper.less + clipper.css compile paths (clipper.less hasn't
  existed since V1 cleanup; gulp 5 fails on missing-singular-glob
  without allowEmpty)
- Drop sectionPicker.css copying (live picker uses inline DOM/CSS)
- Use { encoding: false } on gulp.src for binary file copies (images,
  icons, edge assets, zip source) — gulp 5 mangles bytes without it
- yargs 18 needs require("yargs/yargs")(process.argv.slice(2)).argv
- del v8 is ESM-style; use require("del").deleteAsync
- gulp-zip v6 default export changed; use require("gulp-zip").default
- gulp-merge-json v2 takes options object: mergeJSON({fileName: "..."})

Source change:
- src/scripts/http/cachedHttp.ts: jwt-decode v4 changed default-export
  to named export. `import * as jwt_decode` + `jwt_decode(token)` →
  `import { jwtDecode } from "jwt-decode"` + `jwtDecode(token)`.

Verified:
- target/chrome/logManager.js: 8× microsoft.applications.telemetry.LogManager
  (Aria intact)
- target/chrome/images/{notebook,section,section_group,arrow_down,
  arrow_right}.png all present
- target/chrome/chromeExtension.js: jwt_decode_1.jwtDecode(token) call
  in the auth path

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Forces a set of deeply-nested transitive deps onto patched versions
without bumping their parent packages. Pure dependency-hygiene cleanup
- routine npm overrides, no API surface change.

Overrides added:
- braces ^3.0.3 (gulp-typescript transitive)
- color-string ^1.5.5 (cssnano chain)
- diff ^5.0.0 (tslint transitive)
- elliptic ^6.6.1 (browserify-sign chain)
- fast-xml-parser ^5.7.0 (is-svg chain)
- is-svg ^4.3.0 (cssnano chain)
- js-yaml ^4.0.0 (cssnano chain)
- lodash.template ^4.5.5 (gulp-tslint transitive)
- minimist ^1.2.6 (tslint optimist chain)

Verified: build succeeds, target/chrome/logManager.js still bundles
the Aria SDK (8x microsoft.applications.telemetry.LogManager hits).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pdfjs-dist 1.7.290 -> 5.7.284:
- v5 is ESM-only; the old pdf.combined.js UMD bundle is gone.
- Added src/pdfLoader.mjs as a tiny shim that imports pdf.mjs, sets
  GlobalWorkerOptions.workerSrc, and exposes the module on
  window.pdfjsLib for the CJS renderer bundle. Extension CSP
  "script-src 'self'" forbids inline modules, so the shim is its own file.
- renderer.html loads pdfLoader.mjs as <script type="module"> (deferred);
  renderer.ts waits on a "pdfjs-ready" event before calling getDocument.
- API call-site updates: getDocument(source).then -> .promise.then
  + .catch (the old (ok, err) two-arg pattern was hiding errors from
  the success handler), page.getViewport(2) -> getViewport({scale:2}),
  page.render(...).then -> page.render(...).promise.then.
- gulpfile copies pdf.mjs + pdf.worker.mjs from pdfjs-dist/legacy/build/
  (legacy build retains broader runtime compat).

TypeScript 3.5.3 -> 5.7.2 + @types/chrome 0.0.257 -> 0.1.42:
- gulp-typescript 3.1.4 still works with TS 5 at our (non-strict)
  tsconfig setting; no compiler swap needed.
- 4 source edits to clear case-sensitivity and rename errors:
  1. Rename src/scripts/extensions/dataBoundary.ts -> DataBoundary.ts
     and update the two import sites (authenticationHelper.ts,
     userDataBoundaryHelper.ts). The internal repo's
     ariaLoggerDecorator.ts has imported from "../extensions/DataBoundary"
     all along - TS 5 catches the case mismatch that TS 3.5 silently
     accepted on Windows.
  2. promiseUtils.ts: explicit Promise<void> on the wait() helper -
     TS 5 requires an argument to resolve() when the Promise generic
     can't be inferred from setTimeout's callback.
  3. webExtensionWorker.ts: chrome types renamed in @types/chrome 0.1.x -
     TabRemoveInfo -> OnRemovedInfo, TabChangeInfo -> OnUpdatedInfo,
     WebResponseCacheDetails -> OnCompletedDetails.
  4. webExtensionWorker.ts:1065: chrome.webRequest header values are
     now string | undefined in @types/chrome 0.1.x - coerce with || ""
     when assigning to a non-nullable correlationId local.

Verified: build clean, Aria still bundled (8x
microsoft.applications.telemetry.LogManager hits in
target/chrome/logManager.js), pdf.mjs/pdf.worker.mjs/pdfLoader.mjs
all ship to target/chrome/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The onenoteapi package was bundled into chromeExtension.js via gulpfile
concat and surfaced types globally through an ambient namespace
declaration referenced from webExtensionWorker.ts. V3 never instantiates
OneNoteApi.OneNoteApi at runtime — it posts to the OneNote REST API
directly via fetch. The only runtime surface still consumed was the
RequestErrorType enum, plus a handful of type names in the logging /
error pipelines.

Replace with src/scripts/oneNoteApi.ts (RequestErrorType, ContentType,
GenericError, RequestError) imported explicitly by the 17 consumers.
Delete the unused IOneNoteApi interface from responsePackage.ts, the
triple-slash reference, and the gulpfile concat sites for both Chrome
and Edge targets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gulp-cssnano is unmaintained and pins ancient versions of cssnano /
postcss / svgo across ~30 transitive packages. The canonical modern
setup is the three-package decomposition recommended by cssnano's own
README since 2018: gulp-postcss as the gulp adapter, postcss as the
engine, cssnano as the minify plugin.

The minifyCss task body is now a single postcss([cssnano()]) pipe
producing equivalent output (31918 -> 26461 bytes on renderer.css, ~17%
reduction with the default safe preset).

All three packages MIT-licensed, matching the prior dep and the rest of
devDependencies. Build-time only; no runtime payload change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gulp-typescript 3.1.4 is six years stale (latest stable 5.0.1 from Mar
2019, abandoned 6.0.0-alpha.1 from Nov 2019). Its transitive deps
(vinyl-fs 3.x -> glob-stream 6.x -> micromatch 3.x) are similarly old
and no longer aligned with the modern TypeScript release cadence.

Replace the gulp plugin with a direct child_process spawn of the local
typescript/bin/tsc against tsconfig.json. The tsconfig gains the
rootDir, noEmitOnError, and include fields that were previously
supplied inline by ts.createProject. Output is byte-identical:
chromeExtension.js / renderer.js / logManager.js all match the prior
build.

logManager.d.ts is renamed to logManagerGlobals.d.ts so it no longer
shadows logManager.ts when tsc enumerates inputs. The old gulp.src
pattern (**/*.+(ts|tsx)) happened to match .d.ts files too, masking the
collision; tsc's default project discovery treats co-named .d.ts as
auto-generated and drops it.

Aria still bundled (8x microsoft.applications.telemetry.LogManager in
target/chrome/logManager.js).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
browserify auto-polyfills Node's crypto module pulling in
crypto-browserify -> browserify-sign / create-ecdh -> elliptic. Our
code never touches crypto, so those polyfills are dead weight in the
extension build.

Swap to esbuild — single-binary bundler with zero runtime deps and
better tree-shaking. Each entry point becomes a Promise-returning
esbuild.build({ format: "iife", ... }) call instead of a
browserify(...).bundle().pipe(source).pipe(gulp.dest) stream. The
LogManager UMD standalone wrapping is handled with esbuild's
globalName option, which produces `var LogManager = (() => {...})()`
— a top-level var that becomes self.LogManager in the SW context,
the same effective shape as browserify's UMD output.

Dropped: browserify, vinyl-source-stream (only used to convert
browserify's stream output to vinyl files), and merge-stream (only
used to merge browserify task streams).

Build still completes in ~8s end-to-end. Bundle sizes net -71KB
(-7.2%) thanks to better tree-shaking. Aria intact (8x telemetry
LogManager hits in target/chrome/logManager.js).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gulp 5's gulp.src now errors on a singular glob that doesn't resolve to
a file; gulp 4 silently emitted no files. The mergeSettings task pushes
src/settings/production.json into the mergeOrder array unconditionally
when --production is set, but that file lives in WebClipper_Internal,
not the public repo. Same for dogfood.json on --dogfood.

The regression landed in 4277d46 (gulp 4 -> 5 upgrade) and surfaces only
on prod / dogfood builds; the default build path was unaffected because
the override files are only added to mergeOrder under those flags.

Pass allowEmpty: true to gulp.src to restore the silent-skip behavior.
Verified: build:prod with WebClipper_Internal sibling clone completes
clean (10s), Aria still bundled (logManager.js -> microsoft.applications.telemetry namespace preserved).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tslint went end-of-life in 2019. Migrating to the modern ESLint stack
drops the EOL footprint and lets style rules actually be enforced during
builds going forward (the old gulp-tslint task ran with emitError: false,
so violations have accumulated for years).

Config:
- eslint.config.mjs (flat config) translates the 23 rules from
  src/tslint.json into the closest ESLint equivalents. A handful
  (jsdoc-format, one-line, variable-name, whitespace, typedef-whitespace)
  don't have clean ports; typedef-whitespace was removed from
  @typescript-eslint in 8.x. None of those were materially enforced
  under tslint.
- The legacy gulp.src **/*.+(ts|tsx) glob matched .d.ts files because
  ".d.ts" ends in ".ts"; the new config excludes .d.ts explicitly. It
  also excludes *_internal.ts copies sourced from WebClipper_Internal
  via copyInternal (lint those in their own repo, not in our build).
- no-unused-expressions allows ternary and short-circuit forms to match
  the codebase's existing one-line conditional control flow.

Code touch-ups (the parts that didn't translate cleanly):
- `var` -> `let` across 18 files (262 occurrences in the three
  namespace files alone — Constants, ClipperStorageKeys,
  OffscreenMessageTypes — plus assorted others picked up by --fix).
  `declare var browser` -> `declare let browser`.
- 4 surgical `// eslint-disable-next-line no-console` comments at the
  legitimate console call sites (offscreenCommunicator.ts,
  offscreen.ts, logManager.ts public stub) plus a file-level disable
  on webConsole.ts whose entire purpose is console wrapping.
- Indentation and trailing-space fixes from `eslint --fix`.

Gulpfile:
- The `tslint` task is replaced by a `lint` task that spawns
  node_modules/eslint/bin/eslint.js via child_process (same pattern
  the tsc task uses). require.resolve("eslint/package.json") + path
  walking gets us the bin without tripping eslint's restrictive
  "exports" field.
- `tslint` is kept as an alias of `lint` so any external callers /
  pipeline references keep working.
- --no-error-on-unmatched-pattern keeps the .tsx glob future-proof
  (the V3 codebase has no .tsx files at the moment).

Dropped: tslint (3.15.1), gulp-tslint (6.1.2), gulp-plumber (1.2.1 —
only used by the old tslint stream). Deleted: src/tslint.json.
Added: eslint (10.4.0), typescript-eslint (8.59.4).

Verified: build clean (8.85s end-to-end), lint reports 0 errors, Aria
still bundled (8x microsoft.applications.telemetry.LogManager hits in
target/chrome/logManager.js).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five places all moved together so the tooling agrees:
- tsconfig.json: target + lib es2017 -> es2022
- gulpfile.js: esbuild bundleEntry target es2017 -> es2022
- eslint.config.mjs: ecmaVersion 2017 -> 2022
- chrome + edge manifest.json: minimum_chrome_version "94"

Chromium 94 (Sep 2021) is the floor; covers private class fields (#x),
top-level await, Object.hasOwn, .at() on arrays/strings, and logical
assignment (??=, ||=, &&=). MV3's hard minimum is Chromium 88, but
real-world auto-updated installs are far ahead of that.

No source code changes — existing source predates the syntax and emits
roughly identical output (renderer.js 267812 -> 267856, +44B drift).
The bump is opt-in for new code; a follow-up commit applies a first
modernization sweep using ESLint's prefer-optional-chain rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
With the target now at ES2022, enable @typescript-eslint/prefer-optional-chain
(requires typed linting, so also wire up parserOptions.project pointing
at tsconfig.json — small lint-time cost, unlocks several typed rules
that would be useful in followups).

Applied across 14 source files: convert `x && x.y` (and various
ternary forms `x && x.y ? x.y : z`) to `x?.y` / `x?.y ?? z`. Most went
through `eslint --fix`; ~25 ternary cases the rule won't autofix
(potential return-type changes that aren't safe in general) were done
by hand. All semantically equivalent in our codebase since the LHS in
each spot is either an object/function (not 0/false/"" sensitive) or
the same value is used to gate access.

Also bumps the single `Object.prototype.hasOwnProperty.call(o, k)` site
to `Object.hasOwn(o, k)` (new in ES2022).

No bundle-shape change beyond what tsc/esbuild naturally emit for the
new operators. Aria intact, lint reports 0 errors, build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round 2 dropped the dependencies (browserify / tslint / gulp-cssnano /
gulp-typescript / onenoteapi) that were pulling the deep transitives
these overrides were neutralizing. With those branches of the dep tree
gone, the overrides have no targets to bind to and serve only as
documentation noise.

Kept: graceful-fs (still applies via gulp-less -> less) and braces
(still applies via globby -> fast-glob -> micromatch).
Dropped: color-string, diff, elliptic, fast-xml-parser, is-svg,
js-yaml, lodash.template, minimist — all confirmed orphan via
`npm ls <name>` returning empty.

Build clean, no audit regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two small fixes from the code review:

1. printGlobResults gulpfile helper called globby.sync(...), but globby
   dropped the .sync property in 14.x (we ship 16.2). The function had
   no callers — debug-only and silently broken. Deleted. With it gone,
   the only direct use of globby is removed, so the direct dep is
   pruned too (del still pulls its own nested globby@14, untouched).

2. The lint task previously called done() unconditionally, swallowing
   eslint's non-zero exit code "to match legacy gulp-tslint behavior."
   That was a sensible default while the rule set was inherited and
   undisciplined; now that the rule set is curated in this PR and the
   codebase is lint-clean, propagate the exit code so a future lint
   regression actually fails the build (and CI). Same pattern the
   tsc task uses.

Verified: npm run build still clean (0 lint errors, 8.5s end-to-end),
npm audit still 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant