Skip to content

Commit 0d7b552

Browse files
committed
Change all new Error() calls to custom error classes, update linting rules, fix environment utility files and tests
1 parent 56c81a9 commit 0d7b552

65 files changed

Lines changed: 613 additions & 293 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"Onest",
7171
"optin",
7272
"pids",
73+
"pleroma",
7374
"Poslovski",
7475
"Qualys",
7576
"registrator",

_TODO.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# TODO
22

3+
## See the @TODO: about enabling the PWA to be used as a share target for other apps in src/pages/manifest.json.ts
4+
35
## Analytics
46

57
Vercel Analytics

eslint.config.ts

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,19 @@ const errorDefinitionIgnores = [
2121
'src/lib/errors/**/*',
2222
'src/components/scripts/errors/**/*',
2323
'test/errors/**/*',
24+
'src/pages/api/_errors/ApiFunctionError.ts',
2425
]
2526

27+
const baseErrorMessage = 'Use project-specific error classes instead of the base Error constructor.'
28+
2629
const newErrorSelector = {
2730
selector: 'NewExpression[callee.name="Error"]',
28-
message: 'Use project-specific error classes instead of the base Error constructor.',
31+
message: baseErrorMessage,
32+
}
33+
34+
const callErrorSelector = {
35+
selector: 'CallExpression[callee.name="Error"]',
36+
message: baseErrorMessage,
2937
}
3038

3139
const buildErrorSelector = {
@@ -63,6 +71,7 @@ const createRestrictedSyntaxRule = ({
6371
}: RestrictedSyntaxRuleOptions = {}) => {
6472
const selectors = [
6573
newErrorSelector,
74+
callErrorSelector,
6675
...(allowBuildError ? [] : [buildErrorSelector]),
6776
...(allowClientScriptError ? [] : [clientScriptErrorSelector]),
6877
...(allowTestError ? [] : [testErrorSelector]),
@@ -254,6 +263,18 @@ export default [
254263
'no-restricted-syntax': createRestrictedSyntaxRule({ allowBuildError: true }),
255264
},
256265
},
266+
{
267+
files: [
268+
'src/components/**/server/**/*',
269+
'src/layouts/**/server/**/*',
270+
'src/pages/**/server/**/*',
271+
'src/pages/api/**/*.ts',
272+
],
273+
ignores: errorDefinitionIgnores,
274+
rules: {
275+
'no-restricted-syntax': createRestrictedSyntaxRule({ allowBuildError: true, includeImportMetaEnv: true }),
276+
},
277+
},
257278
{
258279
files: [
259280
'src/components/**/client/**/*',
@@ -263,7 +284,24 @@ export default [
263284
],
264285
ignores: errorDefinitionIgnores,
265286
rules: {
266-
'no-restricted-syntax': createRestrictedSyntaxRule({ allowClientScriptError: true }),
287+
'no-restricted-syntax': createRestrictedSyntaxRule({ allowClientScriptError: true, includeImportMetaEnv: true }),
288+
},
289+
},
290+
{
291+
files: [
292+
'src/components/**/__tests__/**',
293+
'src/components/**/__tests__/**/*.ts',
294+
'src/components/**/__tests__/**/*.tsx',
295+
'src/components/**/__tests__/*.ts',
296+
'src/components/**/__tests__/*.tsx',
297+
],
298+
ignores: errorDefinitionIgnores,
299+
rules: {
300+
'no-restricted-syntax': createRestrictedSyntaxRule({
301+
allowClientScriptError: true,
302+
allowTestError: true,
303+
includeImportMetaEnv: true,
304+
}),
267305
},
268306
},
269307
{
@@ -275,6 +313,18 @@ export default [
275313
'no-restricted-syntax': createRestrictedSyntaxRule({ allowTestError: true }),
276314
},
277315
},
316+
{
317+
files: [
318+
'**/*.spec.ts',
319+
'**/*.spec.tsx',
320+
'**/*.test.ts',
321+
'**/*.test.tsx',
322+
],
323+
ignores: errorDefinitionIgnores,
324+
rules: {
325+
'no-restricted-syntax': createRestrictedSyntaxRule({ allowTestError: true }),
326+
},
327+
},
278328
{
279329
files: [
280330
'src/integrations/**/*.spec.ts',
@@ -414,6 +464,7 @@ export default [
414464
'.eslintrc.js',
415465
'astro.config.ts',
416466
'playwright.config.ts',
467+
'vitest.config.ts',
417468
'vitest.setup.ts',
418469
'src/components/scripts/utils/environmentClient.ts',
419470
'src/lib/config/**/*',
@@ -440,14 +491,20 @@ export default [
440491
'src/layouts/**/*',
441492
'src/pages/**/*',
442493
],
494+
ignores: [
495+
...errorDefinitionIgnores,
496+
'src/components/**/client/**/*',
497+
'src/components/**/server/**/*',
498+
'src/components/**/__tests__/**',
499+
'src/components/scripts/**/*',
500+
'src/layouts/**/client/**/*',
501+
'src/layouts/**/server/**/*',
502+
'src/pages/**/client/**/*',
503+
'src/pages/**/server/**/*',
504+
'src/pages/api/**/*',
505+
],
443506
rules: {
444-
'no-restricted-syntax': [
445-
level,
446-
{
447-
'selector': 'MemberExpression[property.name="env"] > MetaProperty[meta.name="import"][property.name="meta"]',
448-
'message': 'Do not use import.meta.env directly. See docs/ENVIRONMENT_VARIABLES.'
449-
}
450-
],
507+
'no-restricted-syntax': createRestrictedSyntaxRule({ includeImportMetaEnv: true }),
451508
},
452509
},
453510
{
@@ -461,12 +518,7 @@ export default [
461518
'src/pages/api/_environment/environmentApi.ts',
462519
],
463520
rules: {
464-
'no-restricted-syntax': [
465-
'off',
466-
{
467-
'selector': 'MemberExpression[property.name="env"] > MetaProperty[meta.name="import"][property.name="meta"]',
468-
}
469-
],
521+
'no-restricted-syntax': createRestrictedSyntaxRule({ allowBuildError: true, allowClientScriptError: true }),
470522
},
471523
},
472524

src/components/Animations/Computers/client/__tests__/index.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
44
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5+
import { TestError } from '@test/errors'
56
import ComputersAnimationAstro from '@components/Animations/Computers/index.astro'
67
import type { ComputersAnimationElement } from '@components/Animations/Computers/client'
78
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
@@ -39,7 +40,7 @@ const getComputersModule = () => {
3940
!registerComputersAnimationWebComponentFn ||
4041
!computersWebComponentModule
4142
) {
42-
throw new Error('Computers animation module was not initialized correctly')
43+
throw new TestError('Computers animation module was not initialized correctly')
4344
}
4445

4546
return {
@@ -229,7 +230,7 @@ describe('ComputersAnimationElement', () => {
229230
})
230231

231232
it('reports GSAP failures through the script error handler', async () => {
232-
const error = new Error('gsap failed')
233+
const error = new TestError('gsap failed')
233234
gsapMock.set.mockImplementationOnce(() => {
234235
throw error
235236
})
@@ -265,7 +266,7 @@ const renderComputersAnimation = async (
265266
selector: 'computers-animation',
266267
assert: async ({ element, module, window, renderResult }) => {
267268
if (!window) {
268-
throw new Error('Computers animation tests require a DOM-capable window environment')
269+
throw new TestError('Computers animation tests require a DOM-capable window environment')
269270
}
270271

271272
forceDocumentReady(window)

src/components/CallToAction/Newsletter/client/__tests__/index.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @vitest-environment node
22
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
33
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
4+
import { TestError } from '@test/errors'
45
import Newsletter from '@components/CallToAction/Newsletter/index.astro'
56
import type { NewsletterProps } from '@components/CallToAction/Newsletter/props'
67
import type { NewsletterFormElement } from '@components/CallToAction/Newsletter/client'
@@ -25,7 +26,7 @@ const getElements = (root: NewsletterFormElement) => {
2526
const selectElement = <T extends Element>(selector: string): T => {
2627
const element = root.querySelector(selector)
2728
if (!element) {
28-
throw new Error(`Failed to locate ${selector} within newsletter-form`)
29+
throw new TestError(`Failed to locate ${selector} within newsletter-form`)
2930
}
3031
return element as T
3132
}
@@ -160,7 +161,7 @@ describe('NewsletterFormElement web component', () => {
160161
})
161162

162163
test('shows a network error message when fetch rejects', async () => {
163-
fetchMock.mockRejectedValueOnce(new Error('Network error'))
164+
fetchMock.mockRejectedValueOnce(new TestError('Network error'))
164165

165166
await renderNewsletter(async ({ elements }) => {
166167
elements.emailInput.value = 'test@example.com'

src/components/Consent/Banner/client/__tests__/index.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { beforeEach, describe, expect, it, vi } from 'vitest'
44
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5+
import { TestError } from '@test/errors'
56
import ConsentBanner from '@components/Consent/Banner/index.astro'
67
import type { ConsentBannerElement } from '@components/Consent/Banner/client'
78
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
@@ -45,7 +46,7 @@ const waitForBannerReady = async (element: ConsentBannerElement) => {
4546
await new Promise<void>((resolve, reject) => {
4647
const timeoutId = setTimeout(() => {
4748
element.removeEventListener(BANNER_READY_EVENT, onReady)
48-
reject(new Error('Consent banner never finished initializing'))
49+
reject(new TestError('Consent banner never finished initializing'))
4950
}, CONSENT_READY_TIMEOUT_MS)
5051

5152
function onReady() {
@@ -72,7 +73,7 @@ const renderConsentBanner = async (
7273
waitForReady: waitForBannerReady,
7374
assert: async ({ element, window }) => {
7475
if (!window) {
75-
throw new Error('JSDOM window is not available for consent banner tests')
76+
throw new TestError('JSDOM window is not available for consent banner tests')
7677
}
7778

7879
await assertion({ element, window: window as JsdomWindow })
@@ -157,7 +158,7 @@ describe('ConsentBannerElement', () => {
157158
expect(customizeBtn).not.toBeNull()
158159

159160
const bannerCtor = element.constructor as typeof HTMLElement & {
160-
navigateToUrl: (url: string) => void
161+
navigateToUrl: (_url: string) => void
161162
}
162163
const navigateSpy = vi.spyOn(bannerCtor, 'navigateToUrl').mockImplementation(() => {})
163164

src/components/Consent/Banner/client/__tests__/selectors.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { beforeEach, describe, expect, it } from 'vitest'
44
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5+
import { TestError } from '@test/errors'
56
import ConsentBanner from '@components/Consent/Banner/index.astro'
67
import type { ConsentBannerElement } from '@components/Consent/Banner/client'
78
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
@@ -30,7 +31,7 @@ const waitForBannerReady = async (element: ConsentBannerElement) => {
3031
await new Promise<void>((resolve, reject) => {
3132
const timeoutId = setTimeout(() => {
3233
element.removeEventListener(BANNER_READY_EVENT, onReady)
33-
reject(new Error('Consent banner never finished initializing'))
34+
reject(new TestError('Consent banner never finished initializing'))
3435
}, CONSENT_READY_TIMEOUT_MS)
3536

3637
function onReady() {

src/components/Consent/Checkbox/client/__tests__/testUtils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expect } from 'vitest'
22
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
3+
import { TestError } from '@test/errors'
34
import CheckboxFixture from '@components/Consent/Checkbox/client/__fixtures__/checkbox.fixture.astro'
45
import type { CheckboxFixtureProps } from '@components/Consent/Checkbox/client/types'
56
import type { ConsentCheckboxElement } from '@components/Consent/Checkbox/client'
@@ -20,7 +21,7 @@ export const waitForConsentReady = async (element: ConsentCheckboxElement): Prom
2021
await new Promise<void>((resolve, reject) => {
2122
const timeoutId = setTimeout(() => {
2223
element.removeEventListener(consentCheckboxReadyEvent, onReady)
23-
reject(new Error('Consent checkbox never finished initializing'))
24+
reject(new TestError('Consent checkbox never finished initializing'))
2425
}, CONSENT_READY_TIMEOUT_MS)
2526

2627
function onReady() {
@@ -64,7 +65,7 @@ export const renderConsentCheckbox = async (
6465
waitForReady: waitForConsentReady,
6566
assert: async ({ element, window, module, renderResult }) => {
6667
if (!window) {
67-
throw new Error('Consent checkbox tests require a browser-like window environment')
68+
throw new TestError('Consent checkbox tests require a browser-like window environment')
6869
}
6970

7071
expect(renderResult).toContain(`<${module.registeredName}`)

src/components/Consent/Preferences/client/__tests__/index.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { beforeEach, describe, expect, it, vi } from 'vitest'
44
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5+
import { TestError } from '@test/errors'
56
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
67
import ConsentPreferencesComponent from '@components/Consent/Preferences/index.astro'
78
import type { ConsentPreferencesElement } from '@components/Consent/Preferences/client'
@@ -23,7 +24,7 @@ const waitForPreferencesReady = async (element: ConsentPreferencesElement) => {
2324
await new Promise<void>((resolve, reject) => {
2425
const timeoutId = setTimeout(() => {
2526
element.removeEventListener(CONSENT_PREFERENCES_READY_EVENT, onReady)
26-
reject(new Error('Consent preferences component never finished initializing'))
27+
reject(new TestError('Consent preferences component never finished initializing'))
2728
}, CONSENT_READY_TIMEOUT_MS)
2829

2930
function onReady() {
@@ -48,7 +49,7 @@ const renderConsentPreferences = async (
4849
waitForReady: waitForPreferencesReady,
4950
assert: async ({ element, window }) => {
5051
if (!window) {
51-
throw new Error('Consent preferences tests require a window instance')
52+
throw new TestError('Consent preferences tests require a window instance')
5253
}
5354

5455
await assertion({ element, window: window as JsdomWindow })

src/components/Consent/Preferences/client/__tests__/selectors.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { beforeEach, describe, expect, it } from 'vitest'
44
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5+
import { TestError } from '@test/errors'
56
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
67
import ConsentPreferencesComponent from '@components/Consent/Preferences/index.astro'
78
import type { ConsentPreferencesElement } from '@components/Consent/Preferences/client'
@@ -25,7 +26,7 @@ const waitForPreferencesReady = async (element: ConsentPreferencesElement) => {
2526
await new Promise<void>((resolve, reject) => {
2627
const timeoutId = setTimeout(() => {
2728
element.removeEventListener(CONSENT_PREFERENCES_READY_EVENT, onReady)
28-
reject(new Error('Consent preferences component never finished initializing'))
29+
reject(new TestError('Consent preferences component never finished initializing'))
2930
}, CONSENT_READY_TIMEOUT_MS)
3031

3132
function onReady() {
@@ -50,7 +51,7 @@ const renderConsentPreferences = async (
5051
waitForReady: waitForPreferencesReady,
5152
assert: async ({ element, window }) => {
5253
if (!window) {
53-
throw new Error('Consent preferences tests require a window instance')
54+
throw new TestError('Consent preferences tests require a window instance')
5455
}
5556

5657
await assertion({ element, window: window as JsdomWindow })

0 commit comments

Comments
 (0)