From 722d5ac2a7391e4f6333c182bfabf82188620325 Mon Sep 17 00:00:00 2001 From: Jason Buckner Date: Wed, 27 May 2026 13:00:55 -0700 Subject: [PATCH] WEBDEV-8512: Enforce import type for type-only imports Add @typescript-eslint/consistent-type-imports with the same configuration offshoot adopted in WEBDEV-8471. Rule options: - prefer: 'type-imports' - fixStyle: 'separate-type-imports' - disallowTypeAnnotations: true Plus a one-time autofix + prettier sweep across src/ and demo/. Co-Authored-By: Claude Opus 4.7 --- demo/app-root.ts | 81 +++++++++++-------- demo/story-components/story-prop-settings.ts | 10 +-- demo/story-template.test.ts | 13 +-- eslint.config.mjs | 8 ++ .../ia-combo-box/ia-combo-box-story.ts | 4 +- src/elements/ia-combo-box/ia-combo-box.ts | 11 +-- src/elements/ia-combo-box/models.ts | 2 +- .../ia-dropdown-search-bar-story.ts | 4 +- .../ia-dropdown-search-bar.test.ts | 2 +- .../ia-dropdown-search-bar.ts | 12 +-- src/elements/ia-dropdown-search-bar/models.ts | 2 +- src/elements/ia-otp-form/ia-otp-form.ts | 11 +-- .../ia-otp-input/ia-otp-input.test.ts | 2 +- src/elements/ia-otp-input/ia-otp-input.ts | 10 +-- .../ia-status-indicator.ts | 3 +- src/util/lazy-load-template.ts | 5 +- 16 files changed, 87 insertions(+), 93 deletions(-) diff --git a/demo/app-root.ts b/demo/app-root.ts index 89f9ab1..a01fd43 100644 --- a/demo/app-root.ts +++ b/demo/app-root.ts @@ -6,11 +6,11 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js'; const storyModules = import.meta.glob( ['../src/elements/**/*-story.ts', '../src/labs/**/*-story.ts'], - { eager: true } + { eager: true }, ); const storyEntries = Object.keys(storyModules) - .map(path => { + .map((path) => { const labs = path.includes('/src/labs/'); const parts = path.split('/'); const filename = parts[parts.length - 1]; // e.g. "ia-button-story.ts" @@ -19,13 +19,15 @@ const storyEntries = Object.keys(storyModules) }) .sort((a, b) => a.tag.localeCompare(b.tag)); -const productionEntries = storyEntries.filter(e => !e.labs); -const labsEntries = storyEntries.filter(e => e.labs); +const productionEntries = storyEntries.filter((e) => !e.labs); +const labsEntries = storyEntries.filter((e) => e.labs); const ALL_ENTRIES = [...productionEntries, ...labsEntries]; @customElement('app-root') export class AppRoot extends LitElement { - createRenderRoot() { return this; } + createRenderRoot() { + return this; + } private _observer?: IntersectionObserver; private _abortController = new AbortController(); @@ -34,33 +36,42 @@ export class AppRoot extends LitElement { return html`

Internet Archive Elements

Production-Ready Elements

- ${productionEntries.map(e => html` -
- ${unsafeHTML(`<${e.storyTag}>`)} -
- `)} + ${productionEntries.map( + (e) => html` +
+ ${unsafeHTML(`<${e.storyTag}>`)} +
+ `, + )}

Labs Elements

- ${labsEntries.map(e => html` -
- ${unsafeHTML(`<${e.storyTag}>`)} -
- `)} + ${labsEntries.map( + (e) => html` +
+ ${unsafeHTML(`<${e.storyTag}>`)} +
+ `, + )}
`; } firstUpdated() { - const allIds = ALL_ENTRIES.map(e => e.id); + const allIds = ALL_ENTRIES.map((e) => e.id); const links = Object.fromEntries( - allIds.map(id => [id, this.querySelector(`#ia-sidebar a[href="#${id}"]`)]) + allIds.map((id) => [ + id, + this.querySelector(`#ia-sidebar a[href="#${id}"]`), + ]), ); const visible = new Set(); @@ -68,31 +79,37 @@ export class AppRoot extends LitElement { // Only anchors in the top 30% of the viewport count as "active". // The first (topmost) visible anchor wins. this._observer = new IntersectionObserver( - entries => { + (entries) => { for (const entry of entries) { if (entry.isIntersecting) visible.add(entry.target.id); else visible.delete(entry.target.id); } - const activeId = allIds.find(id => visible.has(id)) ?? allIds[0]; - allIds.forEach(id => links[id]?.classList.toggle('active', id === activeId)); + const activeId = allIds.find((id) => visible.has(id)) ?? allIds[0]; + allIds.forEach((id) => + links[id]?.classList.toggle('active', id === activeId), + ); }, { rootMargin: '0px 0px -70% 0px' }, ); - allIds.forEach(id => { + allIds.forEach((id) => { const el = document.getElementById(id); if (el) this._observer!.observe(el); }); - allIds.forEach(id => { - links[id]?.addEventListener('click', (e: Event) => { - e.preventDefault(); - const el = document.getElementById(id); - if (el) { - const top = el.getBoundingClientRect().top + window.scrollY; - window.scrollTo({ top: Math.max(0, top - 16), behavior: 'smooth' }); - } - }, { signal: this._abortController.signal }); + allIds.forEach((id) => { + links[id]?.addEventListener( + 'click', + (e: Event) => { + e.preventDefault(); + const el = document.getElementById(id); + if (el) { + const top = el.getBoundingClientRect().top + window.scrollY; + window.scrollTo({ top: Math.max(0, top - 16), behavior: 'smooth' }); + } + }, + { signal: this._abortController.signal }, + ); }); } diff --git a/demo/story-components/story-prop-settings.ts b/demo/story-components/story-prop-settings.ts index 5b20bbd..1954d81 100644 --- a/demo/story-components/story-prop-settings.ts +++ b/demo/story-components/story-prop-settings.ts @@ -1,11 +1,5 @@ -import { - css, - html, - LitElement, - nothing, - TemplateResult, - type CSSResultGroup, -} from 'lit'; +import type { TemplateResult } from 'lit'; +import { css, html, LitElement, nothing, type CSSResultGroup } from 'lit'; import { property, queryAll } from 'lit/decorators.js'; import { customElement } from 'lit/decorators/custom-element.js'; import { choose } from 'lit/directives/choose.js'; diff --git a/demo/story-template.test.ts b/demo/story-template.test.ts index e5087d5..f5c9db1 100644 --- a/demo/story-template.test.ts +++ b/demo/story-template.test.ts @@ -68,7 +68,8 @@ describe('StoryTemplate', () => { `); // Only import + usage highlighters; styling section is absent when cssCode is empty - const highlighters = el.shadowRoot?.querySelectorAll('syntax-highlighter'); + const highlighters = + el.shadowRoot?.querySelectorAll('syntax-highlighter'); expect(highlighters?.length).to.equal(2); }); @@ -80,13 +81,12 @@ describe('StoryTemplate', () => { (el as any).stringifiedStyles = 'color: red;'; await el.updateComplete; - const highlighters = el.shadowRoot?.querySelectorAll('syntax-highlighter'); + const highlighters = + el.shadowRoot?.querySelectorAll('syntax-highlighter'); expect(highlighters?.length).to.equal(3); const stylingHighlighter = highlighters?.[2] as any; - expect(stylingHighlighter.code).to.equal( - 'ia-button {\n color: red;\n}', - ); + expect(stylingHighlighter.code).to.equal('ia-button {\n color: red;\n}'); }); test('has no trailing whitespace on any line', async () => { @@ -97,7 +97,8 @@ describe('StoryTemplate', () => { (el as any).stringifiedStyles = '--my-var: blue;'; await el.updateComplete; - const highlighters = el.shadowRoot?.querySelectorAll('syntax-highlighter'); + const highlighters = + el.shadowRoot?.querySelectorAll('syntax-highlighter'); const code: string = (highlighters?.[2] as any).code; for (const line of code.split('\n')) { expect(line).to.equal(line.trimEnd()); diff --git a/eslint.config.mjs b/eslint.config.mjs index 97a59e9..d4782d3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -39,6 +39,14 @@ export default [ '@typescript-eslint/no-unsafe-function-type': 'warn', '@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + fixStyle: 'separate-type-imports', + disallowTypeAnnotations: true, + }, + ], }, }, { diff --git a/src/elements/ia-combo-box/ia-combo-box-story.ts b/src/elements/ia-combo-box/ia-combo-box-story.ts index 5dd283c..fc40f07 100644 --- a/src/elements/ia-combo-box/ia-combo-box-story.ts +++ b/src/elements/ia-combo-box/ia-combo-box-story.ts @@ -1,7 +1,7 @@ import { css, html, LitElement, type CSSResultGroup } from 'lit'; import { customElement, state, query } from 'lit/decorators.js'; -import { StyleInputSettings } from '@demo/story-components/story-styles-settings'; -import { +import type { StyleInputSettings } from '@demo/story-components/story-styles-settings'; +import type { IAComboBoxBehavior, IAComboBoxFilterOption, IAComboBoxFilterPreset, diff --git a/src/elements/ia-combo-box/ia-combo-box.ts b/src/elements/ia-combo-box/ia-combo-box.ts index 6e925c7..3dd146b 100644 --- a/src/elements/ia-combo-box/ia-combo-box.ts +++ b/src/elements/ia-combo-box/ia-combo-box.ts @@ -1,12 +1,5 @@ -import { - html, - LitElement, - nothing, - TemplateResult, - CSSResultGroup, - css, - PropertyValues, -} from 'lit'; +import type { TemplateResult, CSSResultGroup, PropertyValues } from 'lit'; +import { html, LitElement, nothing, css } from 'lit'; import { customElement, property, state, query } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; diff --git a/src/elements/ia-combo-box/models.ts b/src/elements/ia-combo-box/models.ts index ee57c27..53af8ff 100644 --- a/src/elements/ia-combo-box/models.ts +++ b/src/elements/ia-combo-box/models.ts @@ -1,4 +1,4 @@ -import { TemplateResult } from 'lit'; +import type { TemplateResult } from 'lit'; /** * Represents a single predefined option in a combo box. diff --git a/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar-story.ts b/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar-story.ts index db1618c..4580fbf 100644 --- a/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar-story.ts +++ b/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar-story.ts @@ -1,8 +1,8 @@ import { css, html, LitElement, type CSSResultGroup } from 'lit'; import { customElement, state, query } from 'lit/decorators.js'; import { map } from 'lit/directives/map.js'; -import { StyleInputSettings } from '@demo/story-components/story-styles-settings'; -import { SearchRequestedDetail } from './models'; +import type { StyleInputSettings } from '@demo/story-components/story-styles-settings'; +import type { SearchRequestedDetail } from './models'; import '@demo/story-template'; import './ia-dropdown-search-bar'; diff --git a/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.test.ts b/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.test.ts index 24415cd..cacbe7e 100644 --- a/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.test.ts +++ b/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test, beforeEach, afterEach, vi } from 'vitest'; import { fixture } from '@open-wc/testing-helpers'; import { html } from 'lit'; import type { IaClearableTextInput } from '@internetarchive/ia-clearable-text-input'; -import { IADropdownSearchBar } from './ia-dropdown-search-bar'; +import type { IADropdownSearchBar } from './ia-dropdown-search-bar'; import type { SearchRequestedDetail } from './models'; import './ia-dropdown-search-bar'; diff --git a/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.ts b/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.ts index 0ee51c3..b2335c4 100644 --- a/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.ts +++ b/src/elements/ia-dropdown-search-bar/ia-dropdown-search-bar.ts @@ -1,12 +1,6 @@ import { msg } from '@lit/localize'; -import { - css, - html, - LitElement, - nothing, - PropertyValues, - TemplateResult, -} from 'lit'; +import type { PropertyValues, TemplateResult } from 'lit'; +import { css, html, LitElement, nothing } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; import type { IaClearableTextInput } from '@internetarchive/ia-clearable-text-input'; import type { IaDropdown, optionInterface } from '@internetarchive/ia-dropdown'; @@ -75,7 +69,7 @@ export class IADropdownSearchBar extends LitElement { `; } - + willUpdate(changed: PropertyValues) { // Push new categories down to the inner dropdown immediately, since ia-dropdown // mutates its own selected option on interaction which can cause Lit's diff --git a/src/elements/ia-dropdown-search-bar/models.ts b/src/elements/ia-dropdown-search-bar/models.ts index 172a459..cefc909 100644 --- a/src/elements/ia-dropdown-search-bar/models.ts +++ b/src/elements/ia-dropdown-search-bar/models.ts @@ -1,4 +1,4 @@ -import { TemplateResult } from 'lit'; +import type { TemplateResult } from 'lit'; /** * A category to include in the dropdown. Consists of an internal `id` string that is diff --git a/src/elements/ia-otp-form/ia-otp-form.ts b/src/elements/ia-otp-form/ia-otp-form.ts index 8b7db7b..cb57d1c 100644 --- a/src/elements/ia-otp-form/ia-otp-form.ts +++ b/src/elements/ia-otp-form/ia-otp-form.ts @@ -1,12 +1,5 @@ -import { - html, - LitElement, - TemplateResult, - CSSResultGroup, - css, - nothing, - PropertyValues, -} from 'lit'; +import type { TemplateResult, CSSResultGroup, PropertyValues } from 'lit'; +import { html, LitElement, css, nothing } from 'lit'; import { msg } from '@lit/localize'; import { property, customElement, query } from 'lit/decorators.js'; diff --git a/src/elements/ia-otp-input/ia-otp-input.test.ts b/src/elements/ia-otp-input/ia-otp-input.test.ts index c2275d7..790a4cc 100644 --- a/src/elements/ia-otp-input/ia-otp-input.test.ts +++ b/src/elements/ia-otp-input/ia-otp-input.test.ts @@ -2,7 +2,7 @@ import { fixture, oneEvent } from '@open-wc/testing-helpers'; import { afterEach, describe, expect, test, vi } from 'vitest'; import { html } from 'lit'; -import { IAOTPInput } from './ia-otp-input'; +import type { IAOTPInput } from './ia-otp-input'; import './ia-otp-input'; describe('IA OTP Input', () => { diff --git a/src/elements/ia-otp-input/ia-otp-input.ts b/src/elements/ia-otp-input/ia-otp-input.ts index 4bff95b..26d6f20 100644 --- a/src/elements/ia-otp-input/ia-otp-input.ts +++ b/src/elements/ia-otp-input/ia-otp-input.ts @@ -1,11 +1,5 @@ -import { - html, - LitElement, - TemplateResult, - CSSResultGroup, - css, - PropertyValues, -} from 'lit'; +import type { TemplateResult, CSSResultGroup, PropertyValues } from 'lit'; +import { html, LitElement, css } from 'lit'; import { property, customElement, queryAll } from 'lit/decorators.js'; import themeStyles from '@src/themes/theme-styles'; diff --git a/src/elements/ia-status-indicator/ia-status-indicator.ts b/src/elements/ia-status-indicator/ia-status-indicator.ts index cb88fbc..a53281f 100644 --- a/src/elements/ia-status-indicator/ia-status-indicator.ts +++ b/src/elements/ia-status-indicator/ia-status-indicator.ts @@ -1,4 +1,5 @@ -import { css, CSSResultGroup, html, LitElement, TemplateResult } from 'lit'; +import type { CSSResultGroup, TemplateResult } from 'lit'; +import { css, html, LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { msg } from '@lit/localize'; import { choose } from 'lit/directives/choose.js'; diff --git a/src/util/lazy-load-template.ts b/src/util/lazy-load-template.ts index 8ea34b7..c30f4e8 100644 --- a/src/util/lazy-load-template.ts +++ b/src/util/lazy-load-template.ts @@ -1,12 +1,11 @@ import type { TemplateResult } from 'lit'; -import { - Directive, - directive, +import type { DirectiveParameters, DirectiveResult, Part, PartInfo, } from 'lit/directive.js'; +import { Directive, directive } from 'lit/directive.js'; const resolved = new WeakSet();