From d793cb2877b08b398a25b922f54dac58151a7127 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 12:12:04 -0400 Subject: [PATCH 01/13] docs(core-web): document the TS strict-mode rollout #35935 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sdk-types` needs no code change: `libs/sdk/types/tsconfig.json` has carried `strict: true` plus the four extra safety flags since the library was created (#31967), and `tsc -p tsconfig.lib.json --noEmit` passes with zero errors. It is already enforced too. Because `tsconfig.lib.json` sets `"declaration": true`, `@rollup/plugin-typescript` sits in the Rollup chain and reports type diagnostics, so `sdk-types:build` fails on a strict violation — verified by removing a constructor assignment and watching the build report TS2564. CI builds every project via the `build-test` execution in `core-web/pom.xml`, so the gate already runs on each PR. A dedicated `typecheck` target would be redundant. `lint` does not catch this: ESLint reports lint rules, not TS diagnostics. What was actually missing is documentation, so the remaining 42 projects in epic #35932 have a pattern to follow: - Add a `## TypeScript Strict Mode` section covering the per-project flags, what enforces them, and the Vite exception (esbuild skips type checking, which is why the Nx Vite plugin infers a separate `typecheck` target). - Fix the line that forbade `"strict": true` in project tsconfigs. It sat under the Jest config guidance but read as a blanket ban, contradicted `docs/frontend/TYPESCRIPT_STANDARDS.md`, and blocked the epic outright. The restriction now points at `tsconfig.spec.json`, which is what it meant. Closes #35935 Co-Authored-By: Claude Opus 5 (1M context) --- core-web/CLAUDE.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/core-web/CLAUDE.md b/core-web/CLAUDE.md index d72e48a9a7c9..88c8862dfc44 100644 --- a/core-web/CLAUDE.md +++ b/core-web/CLAUDE.md @@ -98,6 +98,37 @@ Always wrap form fields with this structure for consistent styling: ``` +## TypeScript Strict Mode + +Strict mode is being rolled out **one project at a time** (epic #35932), bottom-up through the dependency graph. `tsconfig.base.json` stays at `"strict": false` — never flip it globally. + +To make a project strict: + +1. Add the flags to the **project's own** `tsconfig.json` (not `tsconfig.spec.json`, not the base): + + ```json + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + ``` + +2. Fix every error. No new `any` — use explicit types. To silence something unavoidable, use `@ts-expect-error` with a `// TODO(#issue):` note, never a blanket `@ts-ignore`. + +**What enforces this:** for Rollup libs that emit declarations (`"declaration": true`), `@rollup/plugin-typescript` is in the build chain and reports type errors, so the `build` target is the gate — CI runs `nx run-many -t build` (the `build-test` execution in `core-web/pom.xml`). Do **not** add a separate `typecheck` target to those projects; it is redundant. `lint` does not catch type errors — ESLint reports lint rules, not TS diagnostics. + +Vite-based projects are the exception: their builds use esbuild and skip type checking, which is why the Nx Vite plugin infers a separate `typecheck` target for them. + +Verify locally: + +```bash +pnpm exec tsc -p libs//tsconfig.lib.json --noEmit +pnpm exec nx run :build +pnpm exec nx affected -t build,lint --base=origin/main # check you didn't break consumers +``` + ## Portlet Development New portlets go in `libs/portlets/`. For full patterns, architecture, testing, and Nx generator setup: @@ -110,8 +141,8 @@ New portlets go in `libs/portlets/`. For full patterns, architecture, testing, a - Use `dot-content-drive` portlet as reference for test config - `tsconfig.spec.json` tsconfig.spec.json must have "isolatedModules": true in compilerOptions -- `tsconfig.json` — do NOT add `"strict": true` or `"module": "preserve"` -- `tsconfig.spec.json` — keep minimal (only `module`, `target`, `types`) +- `tsconfig.json` — do NOT add `"module": "preserve"` +- `tsconfig.spec.json` — keep minimal (only `module`, `target`, `types`); do NOT add `"strict": true` here, it belongs in the project's `tsconfig.json` (see [TypeScript Strict Mode](#typescript-strict-mode)) - Import `mockProvider` from `@openng/spectator/jest` (not `@openng/spectator`) ### SignalStore Tests From 7d0ffa4c7a0c0146749c6a337a2a35dfcf3d42e7 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 12:58:59 -0400 Subject: [PATCH 02/13] refactor(sdk-create-app): enable TypeScript strict mode #35938 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the standard per-project strict flags to `libs/sdk/create-app/tsconfig.json`, following the pattern established in #36879 (dotcms-models). `tsconfig.base.json` is left at `strict: false`. Two errors surfaced, both from flags beyond plain `strict`: - `src/index.ts:393` — `process.env.DEBUG` needs bracket access under `noPropertyAccessFromIndexSignature` (TS4111). It is the only `process.env.*` dot access in the project. - `src/utils/index.ts:41` — `fetchWithRetry` tripped `noImplicitReturns` (TS7030). The loop returns on success and throws on the last attempt, but with `retries < 1` the loop never runs and the function fell through returning `undefined`. Its only caller already guarded with `if (res && ...)`, so nothing broke in practice, but the signature was lying. Throwing after the loop closes the gap and narrows the return type. No build or CI wiring needed. The `@nx/esbuild:esbuild` executor type-checks before bundling — `skipTypeCheck` defaults to false and is not overridden — and CI already builds this project via `nx run-many -t build` (`build-test` in core-web/pom.xml). The same build runs in the SDK release pipeline (`cicd_release-sdk.yml` → `nx run-many --projects='sdk-*'`), so the flags are enforced on every release. Verified: tsc clean on lib and spec; `nx run sdk-create-app:build/lint/test` green; `nx affected -t build,lint` green; `node dist/libs/sdk/create-app/index.js --help` still works. Negative test — reverting the DEBUG fix makes `nx run sdk-create-app:build` fail with TS4111, confirming the gate is real. Closes #35938 Co-Authored-By: Claude Opus 5 (1M context) --- core-web/libs/sdk/create-app/src/index.ts | 2 +- core-web/libs/sdk/create-app/src/utils/index.ts | 6 ++++++ core-web/libs/sdk/create-app/tsconfig.json | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core-web/libs/sdk/create-app/src/index.ts b/core-web/libs/sdk/create-app/src/index.ts index 871c759a176c..12d39bf892c9 100644 --- a/core-web/libs/sdk/create-app/src/index.ts +++ b/core-web/libs/sdk/create-app/src/index.ts @@ -390,7 +390,7 @@ program if (error instanceof Error) { console.error(error.message); // Preserve stack trace for debugging when DEBUG mode is enabled - if (process.env.DEBUG) { + if (process.env['DEBUG']) { console.error('\n' + chalk.gray('Stack trace:')); console.error(chalk.gray(error.stack || 'No stack trace available')); } diff --git a/core-web/libs/sdk/create-app/src/utils/index.ts b/core-web/libs/sdk/create-app/src/utils/index.ts index e2955be7d806..d94b5cecc8a6 100644 --- a/core-web/libs/sdk/create-app/src/utils/index.ts +++ b/core-web/libs/sdk/create-app/src/utils/index.ts @@ -116,6 +116,12 @@ export async function fetchWithRetry( await new Promise((r) => setTimeout(r, delay)); } } + + // Only reachable when retries < 1, in which case the loop never runs. Throwing keeps the + // return type free of `undefined` and surfaces the bad argument instead of hiding it. + throw new Error( + chalk.red(`\n❌ fetchWithRetry requires at least 1 retry, received ${retries}\n`) + ); } export function getUVEConfigValue(frontEndUrl: string) { diff --git a/core-web/libs/sdk/create-app/tsconfig.json b/core-web/libs/sdk/create-app/tsconfig.json index f644ba6f2136..8a433ca61775 100644 --- a/core-web/libs/sdk/create-app/tsconfig.json +++ b/core-web/libs/sdk/create-app/tsconfig.json @@ -1,7 +1,13 @@ { "extends": "../../../tsconfig.base.json", "compilerOptions": { - "module": "esnext" + "module": "esnext", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true }, "files": [], "include": [], From 2e87ab91a639c8363aae664a8fbdaddd1caee902 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 14:03:40 -0400 Subject: [PATCH 03/13] refactor(dotcms-js): enable TypeScript strict mode #35939 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the standard per-project strict flags to `libs/dotcms-js/tsconfig.json`, following the pattern from #36879 (dotcms-models), and resolve the 38 errors they surface across 11 files. `tsconfig.base.json` stays at `strict: false`. Notable type corrections rather than mechanical silencing: - `Auth.loginAsUser` was typed `User` but the code has always passed `null` when nobody is impersonating, and every consumer already guards with `auth.loginAsUser || auth.user`. Corrected to `User | null`. - `StringUtils.getLine` and `HttpRequestUtils.getQueryStringParam` both document "null if it does not exist" but were typed `string`. Corrected. - `RoutingService.getPortletURL` returns `Map.get()`, so `string | undefined`. - `SiteService.switchSiteById` emits `of(null)` when no site is found, so `Observable`. Its one consumer already handles null. - `ResponseView` now models `HttpResponse.body` as nullable instead of assigning `null` into a non-nullable field inside a `try/catch` that could never throw. The dead try/catch is removed. - `LoginService.urls` is typed by inference instead of `Record`, which keeps dot access valid and gives each endpoint a named property. Two definite-assignment assertions were used, each with a TODO: `_auth` and `selectedSite` are assigned during init but not in the constructor. Modelling them as `| undefined` is the truthful type, but their public getters (`auth`, `currentSite`) are consumed by already-strict projects, so widening them is a public-API change that belongs in its own issue. No new `any`, `@ts-ignore`, or `@ts-expect-error`. Verified: - `tsc -p libs/dotcms-js/tsconfig.lib.json --noEmit` — 0 errors - All six already-strict consumers build green (data-access, global-store, portlets-dot-analytics, portlets-dot-analytics-data-access, portlets-dot-locales-portlet, utils-testing) - `data-access` typecheck went from 106 errors to 68, with zero new errors introduced — the honest types upstream remove noise downstream - `dotcms-ui` typecheck clean apart from a pre-existing missing `dotcms-webcomponents/loader` dist - `nx format:check` green; dotcms-js lint went from 42 to 41 problems Note: this project has no `build` target and is tag-excluded from lint and test, so nothing in CI verifies these flags. That was an explicit scoping decision — no `typecheck` target or CI gate was added. See `specs/35939-dotcms-js-strict-mode/spec.md`. Closes #35939 Co-Authored-By: Claude Opus 5 (1M context) --- .../src/lib/core/api-root.service.ts | 4 +- .../src/lib/core/dotcms-config.service.ts | 9 +- .../dotcms-js/src/lib/core/logger.service.ts | 7 +- .../dotcms-js/src/lib/core/login.service.ts | 38 +-- .../dotcms-js/src/lib/core/routing.service.ts | 8 +- .../src/lib/core/shared/user.model.ts | 4 +- .../src/lib/core/site.service.mock.ts | 2 +- .../dotcms-js/src/lib/core/site.service.ts | 7 +- .../src/lib/core/string-utils.service.ts | 8 +- .../src/lib/core/util/http-request-utils.ts | 4 +- .../src/lib/core/util/response-view.ts | 33 ++- core-web/libs/dotcms-js/tsconfig.json | 8 +- specs/35939-dotcms-js-strict-mode/spec.md | 235 ++++++++++++++++++ 13 files changed, 308 insertions(+), 59 deletions(-) create mode 100644 specs/35939-dotcms-js-strict-mode/spec.md diff --git a/core-web/libs/dotcms-js/src/lib/core/api-root.service.ts b/core-web/libs/dotcms-js/src/lib/core/api-root.service.ts index d81908f11082..a4c05ea14198 100644 --- a/core-web/libs/dotcms-js/src/lib/core/api-root.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/api-root.service.ts @@ -12,9 +12,9 @@ export class ApiRoot { hideFireOn = false; hideRulePushOptions = false; - static parseQueryParam(query: string, token: string): string { + static parseQueryParam(query: string, token: string): string | null { let idx = -1; - let result = null; + let result: string | null = null; token = token + '='; if (query && query.length) { idx = query.indexOf(token); diff --git a/core-web/libs/dotcms-js/src/lib/core/dotcms-config.service.ts b/core-web/libs/dotcms-js/src/lib/core/dotcms-config.service.ts index 24bb19f552a6..05c06ef12471 100644 --- a/core-web/libs/dotcms-js/src/lib/core/dotcms-config.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/dotcms-config.service.ts @@ -122,7 +122,8 @@ export class DotcmsConfigService { private http = inject(HttpClient); private loggerService = inject(LoggerService); - private configParamsSubject: BehaviorSubject = new BehaviorSubject(null); + private configParamsSubject: BehaviorSubject = + new BehaviorSubject(null); private configUrl: string; /** @@ -138,7 +139,7 @@ export class DotcmsConfigService { getConfig(): Observable { return this.configParamsSubject .asObservable() - .pipe(filter((config: ConfigParams) => !!config)); + .pipe(filter((config): config is ConfigParams => !!config)); } loadConfig(): void { @@ -159,8 +160,8 @@ export class DotcmsConfigService { paginatorLinks: res.config[DOTCMS_PAGINATOR_LINKS], paginatorRows: res.config[DOTCMS_PAGINATOR_ROWS], releaseInfo: { - buildDate: res.config.releaseInfo?.buildDate, - version: res.config.releaseInfo?.version + buildDate: res.config.releaseInfo?.buildDate ?? '', + version: res.config.releaseInfo?.version ?? '' }, websocket: { websocketReconnectTime: diff --git a/core-web/libs/dotcms-js/src/lib/core/logger.service.ts b/core-web/libs/dotcms-js/src/lib/core/logger.service.ts index 83783ccb5c4a..d1135cc6c1b7 100644 --- a/core-web/libs/dotcms-js/src/lib/core/logger.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/logger.service.ts @@ -63,7 +63,7 @@ export class LoggerService { * @returns boolean */ shouldShowLogs(): boolean { - const devMode: string = this.httpRequestUtils.getQueryStringParam(DEV_MODE_PARAM); + const devMode: string | null = this.httpRequestUtils.getQueryStringParam(DEV_MODE_PARAM); return !environment.production || devMode === 'on'; } // isProduction. @@ -77,13 +77,14 @@ export class LoggerService { try { throw new Error(); } catch (e) { - caller = this.cleanCaller(this.stringUtils.getLine(e.stack, 4)); + const stack = e instanceof Error ? (e.stack ?? '') : ''; + caller = this.cleanCaller(this.stringUtils.getLine(stack, 4)); } return caller; } - private cleanCaller(caller: string): string { + private cleanCaller(caller: string | null): string { return caller ? caller.trim().substr(3) : 'unknown'; } } diff --git a/core-web/libs/dotcms-js/src/lib/core/login.service.ts b/core-web/libs/dotcms-js/src/lib/core/login.service.ts index 4e11ac53174c..cfd0e95df4af 100644 --- a/core-web/libs/dotcms-js/src/lib/core/login.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/login.service.ts @@ -38,23 +38,23 @@ export class LoginService { currentUserLanguageId = ''; private country = ''; private lang = ''; - private urls: Record; + // Typed by inference rather than `Record` so each endpoint is a named + // property; that keeps dot access valid under `noPropertyAccessFromIndexSignature`. + private readonly urls = { + changePassword: '/api/v1/changePassword', + getAuth: '/api/v1/authentication/logInUser', + loginAs: '/api/v1/users/loginas', + logout: '/api/v1/logout', + logoutAs: '/api/v1/users/logoutas', + recoverPassword: '/api/v1/forgotpassword', + serverInfo: '/api/v1/loginform', + userAuth: '/api/v1/authentication', + current: '/api/v1/users/current/' + }; constructor() { this._loginAsUsersList$ = new Subject(); - this.urls = { - changePassword: '/api/v1/changePassword', - getAuth: '/api/v1/authentication/logInUser', - loginAs: '/api/v1/users/loginas', - logout: '/api/v1/logout', - logoutAs: '/api/v1/users/logoutas', - recoverPassword: '/api/v1/forgotpassword', - serverInfo: '/api/v1/loginform', - userAuth: '/api/v1/authentication', - current: '/api/v1/users/current/' - }; - this.dotcmsEventsService.subscribeTo('SESSION_DESTROYED').subscribe(() => { this.logOutUser(); this.clearExperimentPersistence(); @@ -77,7 +77,11 @@ export class LoginService { return this._logout$.asObservable(); } - private _auth: Auth; + // TODO(#35939): assigned by `setAuth()` during the login flow, never in the constructor. + // Modelling it as `Auth | undefined` is the truthful type, but the public `auth` getter is + // consumed by already-strict projects (global-store, data-access), so widening it is a + // public-API change that belongs in its own issue. + private _auth!: Auth; get auth(): Auth { return this._auth; @@ -291,7 +295,7 @@ export class LoginService { this._auth = this.getFullAuth(auth); this._auth$.next(this.getFullAuth(auth)); - this.currentUserLanguageId = auth.user.languageId; + this.currentUserLanguageId = auth.user.languageId ?? ''; // When not logged user we need to fire the observable chain if (!auth.user) { @@ -373,6 +377,8 @@ export interface User { export interface Auth { user: User; - loginAsUser: User; + // Null whenever nobody is impersonating. Callers already guard with + // `auth.loginAsUser || auth.user`; the type just never said so. + loginAsUser: User | null; isLoginAs?: boolean; } diff --git a/core-web/libs/dotcms-js/src/lib/core/routing.service.ts b/core-web/libs/dotcms-js/src/lib/core/routing.service.ts index 037fb990323c..bc4a4665e2f2 100644 --- a/core-web/libs/dotcms-js/src/lib/core/routing.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/routing.service.ts @@ -17,10 +17,10 @@ export class RoutingService { private http = inject(HttpClient); private _menusChange$: Subject = new Subject(); - private menus: Menu[]; + private menus: Menu[] = []; private urlMenus: string; private portlets: Map; - private _currentPortletId: string; + private _currentPortletId = ''; private _portletUrlSource$ = new Subject(); private _currentPortlet$ = new Subject(); @@ -55,7 +55,7 @@ export class RoutingService { return this._portletUrlSource$.asObservable(); } - get firstPortlet(): string { + get firstPortlet(): string | null { const porlets = this.portlets.entries().next().value; return porlets ? porlets[0] : null; @@ -65,7 +65,7 @@ export class RoutingService { this.portlets.set(portletId.replace(' ', '_'), url); } - public getPortletURL(portletId: string): string { + public getPortletURL(portletId: string): string | undefined { return this.portlets.get(portletId); } diff --git a/core-web/libs/dotcms-js/src/lib/core/shared/user.model.ts b/core-web/libs/dotcms-js/src/lib/core/shared/user.model.ts index 2af96dff3bbd..86b047265ae8 100644 --- a/core-web/libs/dotcms-js/src/lib/core/shared/user.model.ts +++ b/core-web/libs/dotcms-js/src/lib/core/shared/user.model.ts @@ -6,8 +6,8 @@ import { LoggerService } from '../logger.service'; export class UserModel { private loggerService = inject(LoggerService); - username: string; - password: string; + username = ''; + password = ''; locale: string; suppressAlerts = false; diff --git a/core-web/libs/dotcms-js/src/lib/core/site.service.mock.ts b/core-web/libs/dotcms-js/src/lib/core/site.service.mock.ts index 7764eee72860..e48edc89b41e 100644 --- a/core-web/libs/dotcms-js/src/lib/core/site.service.mock.ts +++ b/core-web/libs/dotcms-js/src/lib/core/site.service.mock.ts @@ -20,7 +20,7 @@ export const mockSites: Site[] = [ ]; export class SiteServiceMock { - _currentSite: Site; + _currentSite: Site | undefined; private _currentSite$: Subject = new Subject(); get currentSite(): Site { diff --git a/core-web/libs/dotcms-js/src/lib/core/site.service.ts b/core-web/libs/dotcms-js/src/lib/core/site.service.ts index ba41cee5a0c0..50b925231872 100644 --- a/core-web/libs/dotcms-js/src/lib/core/site.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/site.service.ts @@ -31,7 +31,10 @@ export class SiteService { private http = inject(HttpClient); private loggerService = inject(LoggerService); - private selectedSite: Site; + // TODO(#35939): assigned by `setCurrentSite()` during init, never in the constructor. + // Same trade-off as `LoginService._auth`: the public `currentSite` getter is widely + // consumed, so widening it to `Site | undefined` belongs in its own issue. + private selectedSite!: Site; private urls: { currentSiteUrl: string; sitesUrl: string; switchSiteUrl: string }; private events: string[] = [ 'SAVE_SITE', @@ -174,7 +177,7 @@ export class SiteService { * @return {*} {Observable} * @memberof SiteService */ - switchSiteById(id: string): Observable { + switchSiteById(id: string): Observable { this.loggerService.debug('Applying a Site Switch'); return this.getSiteById(id).pipe( diff --git a/core-web/libs/dotcms-js/src/lib/core/string-utils.service.ts b/core-web/libs/dotcms-js/src/lib/core/string-utils.service.ts index a55d97af7d6c..288e9008e0f9 100644 --- a/core-web/libs/dotcms-js/src/lib/core/string-utils.service.ts +++ b/core-web/libs/dotcms-js/src/lib/core/string-utils.service.ts @@ -10,8 +10,8 @@ export class StringUtils { * @param indexLine * @returns string */ - getLine(text: string, indexLine: number): string { - let line: string = null; + getLine(text: string, indexLine: number): string | null { + let line: string | null = null; if (text) { const lines = text.split('\n'); @@ -26,9 +26,9 @@ export class StringUtils { * @param str * @returns string */ - camelize(str): string { + camelize(str: string): string { return str - .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => { + .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter: string, index: number) => { return index === 0 ? letter.toLowerCase() : letter.toUpperCase(); }) .replace(/\s+/g, ''); diff --git a/core-web/libs/dotcms-js/src/lib/core/util/http-request-utils.ts b/core-web/libs/dotcms-js/src/lib/core/util/http-request-utils.ts index 39ff02220c0a..32b90e217916 100644 --- a/core-web/libs/dotcms-js/src/lib/core/util/http-request-utils.ts +++ b/core-web/libs/dotcms-js/src/lib/core/util/http-request-utils.ts @@ -25,8 +25,8 @@ export class HttpRequestUtils { * it is based on the window.location.href. * @returns string */ - getQueryStringParam(name: string): string { - let value = null; + getQueryStringParam(name: string): string | null { + let value: string | null = null; const regex = new RegExp('[?&]' + name.replace(/[\[\]]/g, '\\$&') + '(=([^&#]*)|&|#|$)'); const results = regex.exec(window.location.href); diff --git a/core-web/libs/dotcms-js/src/lib/core/util/response-view.ts b/core-web/libs/dotcms-js/src/lib/core/util/response-view.ts index 44246596a2e3..ebe234610a0f 100644 --- a/core-web/libs/dotcms-js/src/lib/core/util/response-view.ts +++ b/core-web/libs/dotcms-js/src/lib/core/util/response-view.ts @@ -15,47 +15,44 @@ import { DotCMSResponse } from '@dotcms/dotcms-models'; * */ export class ResponseView { - private bodyJsonObject: DotCMSResponse; + // `HttpResponse.body` is nullable, so the parsed body genuinely can be absent. + private bodyJsonObject: DotCMSResponse | null; private headers: HttpHeaders; public constructor(private resp: HttpResponse>) { - try { - this.bodyJsonObject = resp.body; - this.headers = resp.headers; - } catch (e) { - this.bodyJsonObject = null; - } + this.bodyJsonObject = resp.body; + this.headers = resp.headers; } - public header(headerName: string): string { + public header(headerName: string): string | null { return this.headers.get(headerName); } get i18nMessagesMap(): { [key: string]: string } { - return this.bodyJsonObject.i18nMessagesMap; + return this.bodyJsonObject?.i18nMessagesMap ?? {}; } - get contentlets(): T { - return this.bodyJsonObject.contentlets; + get contentlets(): T | undefined { + return this.bodyJsonObject?.contentlets; } - get entity(): T { - return this.bodyJsonObject.entity; + get entity(): T | undefined { + return this.bodyJsonObject?.entity; } - get tempFiles(): T { - return this.bodyJsonObject.tempFiles; + get tempFiles(): T | undefined { + return this.bodyJsonObject?.tempFiles; } get errorsMessages(): string { let errorMessages = ''; - if (this.bodyJsonObject.errors) { + if (this.bodyJsonObject?.errors) { this.bodyJsonObject.errors.forEach((e: any) => { errorMessages += e.message; }); } else { - errorMessages = this.bodyJsonObject.messages.toString(); + errorMessages = this.bodyJsonObject?.messages.toString() ?? ''; } return errorMessages; @@ -71,7 +68,7 @@ export class ResponseView { public existError(errorCode: string): boolean { return ( - this.bodyJsonObject.errors && + !!this.bodyJsonObject?.errors && this.bodyJsonObject.errors.filter((e: any) => e.errorCode === errorCode).length > 0 ); } diff --git a/core-web/libs/dotcms-js/tsconfig.json b/core-web/libs/dotcms-js/tsconfig.json index d72ba3eb3c86..d2cd03a27e5a 100644 --- a/core-web/libs/dotcms-js/tsconfig.json +++ b/core-web/libs/dotcms-js/tsconfig.json @@ -14,6 +14,12 @@ "target": "es2020", "module": "preserve", "moduleResolution": "bundler", - "lib": ["dom", "dom.iterable", "es2022"] + "lib": ["dom", "dom.iterable", "es2022"], + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true } } diff --git a/specs/35939-dotcms-js-strict-mode/spec.md b/specs/35939-dotcms-js-strict-mode/spec.md new file mode 100644 index 000000000000..5e37bdfa6d11 --- /dev/null +++ b/specs/35939-dotcms-js-strict-mode/spec.md @@ -0,0 +1,235 @@ +# Spec: Enable TypeScript strict mode in `dotcms-js` + +**Issue:** [#35939](https://github.com/dotCMS/core/issues/35939) — [06/44] · **Epic:** [#35932](https://github.com/dotCMS/core/issues/35932) +**Status:** Awaiting review (Phase 1 — Specify) + +--- + +## Objective + +Enable TypeScript `strict` mode for the Nx project `dotcms-js` (`core-web/libs/dotcms-js`) and resolve the **38 type errors** it surfaces, without introducing new `any` and without breaking any of its **20 dependent projects**. + +**Who benefits:** the ~20 downstream projects — including the `dotcms-ui` admin app — that compile `dotcms-js` source directly through the `@dotcms/dotcms-js` path alias. Today its null-safety holes are invisible to them; after this change the types tell the truth. + +**Why it matters here specifically:** `dotcms-js` is a layer-1 core library (auth, site, routing, config, HTTP response wrapping). Six of its consumers are *already* strict, so its loose types are actively leaking `any`-shaped uncertainty into projects that have opted into rigour. + +### Assumptions (validated with the requester) + +1. Follow precedent **#36879** (`dotcms-models`): the six flags go in the project's own `tsconfig.json`. `tsconfig.base.json` stays at `"strict": false` — never flipped globally. +2. No `typescript-strict-plugin`, no `tsc-strict` script, no `// @ts-strict-ignore`. That approach was dropped; the bootstrap #35933 closed without the plugin landing. Sub-issue ACs referencing them are stale. +3. **Scope is `tsconfig.lib.json` only.** `tsconfig.spec.json` fails today with `TS2688: Cannot find type definition file for 'jasmine'` — a pre-existing, non-strict-related breakage. Out of scope. +4. **The `skip:lint` / `skip:test` tags are not touched.** `nx run dotcms-js:lint` currently fails with **42 problems** (41 errors, mostly `no-explicit-any`). Re-enabling lint is a separate effort. +5. Legacy packaging debt (`ng-package.json`, `tslint.json`, peerDeps pinned to Angular `^6.0.0 || ^7.2.0`) is left as-is. + +### Accepted trade-off: strict will be unenforced + +**Decision made by the requester: enable strict only — no `typecheck` target, no CI gate.** + +Recorded plainly so it is not rediscovered later: `dotcms-js` has **no `build` target**. Its only targets are `lint`, `test`, and `nx-release-publish`, and the first two are tag-excluded from CI. Consumers compile it via path mapping under *their own* tsconfig, so the flags added here are read by nothing in CI. + +Consequence: after this work, `tsc -p libs/dotcms-js/tsconfig.lib.json --noEmit` will be clean, but **nothing prevents the next commit from regressing it**. The flags document intent; they do not enforce it. + +Partial mitigation that comes for free: the six already-strict consumers (below) will surface *some* regressions in their own builds, because they compile this source strictly. That coverage is incidental and incomplete — it only catches errors on code paths those six actually import. + +--- + +## Tech Stack + +| | | +|---|---| +| Language | TypeScript 6.0.3 | +| Framework | Angular 21+ (this library is Angular services + models, `@Injectable`) | +| Monorepo | Nx 23, pnpm 10.17.1, Node 22.22.3 | +| Test runner | Karma + Jasmine (3 spec files; target is tag-excluded from CI) | + +--- + +## Commands + +```bash +cd core-web + +# Measure the current error surface (the core loop for this work) +pnpm exec tsc -p libs/dotcms-js/tsconfig.lib.json --noEmit + +# Before the flags land, simulate them on the CLI +pnpm exec tsc -p libs/dotcms-js/tsconfig.lib.json --noEmit \ + --strict --noImplicitOverride --noPropertyAccessFromIndexSignature \ + --noImplicitReturns --noFallthroughCasesInSwitch --forceConsistentCasingInFileNames + +# Verify the six already-strict consumers still compile +pnpm exec nx run-many -t build,lint -p data-access,global-store,portlets-dot-analytics,portlets-dot-analytics-data-access,portlets-dot-locales-portlet,utils-testing + +# Full blast-radius check across all 20 dependents +pnpm exec nx affected -t build,lint --base=origin/main --exclude=tag:skip:lint + +# Formatting gate +pnpm exec nx format:check --base=origin/main +``` + +> Environment note: `pnpm` is not on `PATH` by default in this worktree. Use `corepack pnpm` with Node 22.22.3 from nvm (`.nvmrc`). + +--- + +## Project Structure + +``` +core-web/libs/dotcms-js/ +├── src/ +│ ├── public_api.ts → Public barrel (what the 20 consumers import) +│ └── lib/core/ +│ ├── login.service.ts → 12 errors — largest cluster +│ ├── util/response-view.ts → 6 errors — HTTP response wrapper +│ ├── string-utils.service.ts → 5 errors +│ ├── routing.service.ts → 4 errors +│ ├── dotcms-config.service.ts → 3 errors +│ ├── site.service.ts → 2 errors +│ ├── shared/user.model.ts → 2 errors — PUBLIC MODEL, handle with care +│ ├── site.service.mock.ts → 1 error +│ ├── logger.service.ts → 1 error +│ ├── api-root.service.ts → 1 error +│ └── util/http-request-utils.ts → 1 error +├── tsconfig.json → WHERE THE SIX FLAGS GO +├── tsconfig.lib.json → extends tsconfig.json; the compilation unit in scope +└── tsconfig.spec.json → out of scope (pre-existing jasmine failure) +``` + +31 `.ts` files, 3 `.spec.ts`. Errors touch 11 files. + +--- + +## Code Style + +The six flags, added to `libs/dotcms-js/tsconfig.json` — identical to precedent #36879: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "es2020", + "module": "preserve", + "moduleResolution": "bundler", + "lib": ["dom", "dom.iterable", "es2022"], + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + } +} +``` + +Fix style — model reality, do not silence the compiler: + +```ts +// GOOD — the value genuinely can be absent, so say so +getCookie(name: string): string | null { + return this.readCookie(name); +} + +// BAD — hides the hole the flag just exposed +getCookie(name: string): string { + return this.readCookie(name) as string; +} + +// GOOD — index-signature access (TS4111), purely mechanical +this.urls['current'] + +// GOOD — uninitialized field that is genuinely set later +private _auth: Auth | null = null; + +// BAD — definite-assignment assertion papering over real absence +private _auth!: Auth; +``` + +**Never** add `any`, `@ts-ignore`, or `!` non-null assertions to clear an error. If a value is truly always present, prove it with initialization or a constructor assignment. + +--- + +## Testing Strategy + +**There is effectively no test safety net here, and the plan must not pretend otherwise.** + +- 3 `.spec.ts` files exist, but the `test` target is tag-excluded (`skip:test`) and `tsconfig.spec.json` does not even compile (pre-existing jasmine types failure). +- Therefore verification is **compilation-based**, not test-based. + +| Level | Mechanism | What it proves | +|---|---|---| +| Unit | — | Nothing. No usable suite. | +| Type | `tsc -p tsconfig.lib.json --noEmit` | The 38 errors are gone | +| Integration | `nx run-many -t build,lint` on the 6 strict consumers | Public-surface changes did not break rigorous consumers | +| System | `nx affected -t build,lint` over all 20 dependents | No regression anywhere downstream | + +Writing new tests is **out of scope** — the suite cannot run without first fixing the jasmine types breakage. + +--- + +## Boundaries + +**Always:** +- Run the full `nx affected -t build,lint` before opening the PR — 20 projects depend on this library +- Prefer fixes that widen types honestly (`string | null`) over fixes that assert away the problem +- Keep each fix minimal and local to the error site + +**Ask first:** +- Any change to `src/public_api.ts` (the public barrel) +- Any change to `shared/user.model.ts` — it is a public model consumed downstream; making `username`/`password` optional alters the shape 20 projects see +- Any change that requires editing a *consumer* project to compile +- Removing the `skip:lint` / `skip:test` tags + +**Never:** +- Add `any`, `@ts-ignore`, or `!` non-null assertions to silence a flag +- Touch `core-web/tsconfig.base.json` +- Modify `tsconfig.spec.json` or attempt to fix the jasmine breakage in this PR +- Commit with any of the 20 dependents failing to build + +--- + +## The 38 errors, grouped by fix strategy + +| # | Group | Count | Files | Risk to consumers | +|---|---|---|---|---| +| A | `TS4111` index-signature dot access | 8 | `login.service.ts` (all) | **None** — internal, mechanical `.x` → `['x']` | +| B | `TS2564` uninitialized class property | 8 | `login.service.ts`, `routing.service.ts` ×2, `user.model.ts` ×2, `site.service.mock.ts`, `site.service.ts`, `response-view.ts` | **Medium** — `user.model.ts` is public | +| C | `TS2322`/`TS2345` null & undefined mismatches | 21 | `response-view.ts` ×5, `string-utils.service.ts` ×2, `login.service.ts` ×3, `dotcms-config.service.ts` ×3, `routing.service.ts` ×2, others | **High** — widens public return types | +| D | `TS7006` implicit any parameter | 3 | `string-utils.service.ts` | None — internal callback params | +| E | `TS18046` `unknown` in catch | 1 | `logger.service.ts:80` | None | + +Group A is the safe warm-up. Group C is where the judgement lives. + +--- + +## Success Criteria + +1. `libs/dotcms-js/tsconfig.json` contains all six flags, byte-identical in spirit to #36879. +2. `pnpm exec tsc -p libs/dotcms-js/tsconfig.lib.json --noEmit` exits **0**. +3. **Zero** new `any`, `@ts-ignore`, `@ts-expect-error`, or `!` non-null assertions introduced. Verify with a diff grep. +4. All six already-strict consumers build and lint green: `data-access`, `global-store`, `portlets-dot-analytics`, `portlets-dot-analytics-data-access`, `portlets-dot-locales-portlet`, `utils-testing`. +5. `pnpm exec nx affected -t build,lint --base=origin/main --exclude=tag:skip:lint` exits **0** across all 20 dependents. +6. `pnpm exec nx format:check --base=origin/main` exits **0**. +7. `tsconfig.spec.json` is untouched, and its pre-existing jasmine failure is unchanged (not newly introduced, not fixed). +8. No change to `src/public_api.ts` without explicit approval. + +--- + +## Risks + +| Risk | Likelihood | Mitigation | +|---|---|---| +| Widening a public return type to `\| null` breaks one of the 6 strict consumers | **High** | They are compiled explicitly in the verification loop, before `affected`. Fix forward in the same PR if small; escalate if it cascades. | +| `user.model.ts` shape change ripples across 20 projects | Medium | Listed under "Ask first". Prefer initializing (`username = ''`) over making optional, to preserve the shape. | +| Strict regresses silently after merge | **Certain, accepted** | Out of scope by decision. The six strict consumers give partial, incidental coverage. | +| A fix hides a real bug instead of surfacing it | Medium | The "never assert away" rule in Boundaries; review each Group C fix against actual runtime behaviour. | + +--- + +## Resolved Decisions + +1. **`user.model.ts` → initialize, do not make optional.** `username = ''` and `password = ''`. This preserves the public shape, so the 20 consumers see no type change. Making them optional would be more honest about runtime reality but is not worth the blast radius here. +2. **`response-view.ts` → do not grow this PR.** If widening its getters cascades into the six strict consumers beyond a trivial fix, stop, revert that file's changes, and open a follow-up issue. The PR ships the other groups rather than absorbing a cascade. +3. **Stale ACs on #35939 will be corrected on the issue**, same as was done for epic #35932 — removing the `typescript-strict-plugin`, `npx tsc-strict`, and `// @ts-strict-ignore` criteria that no longer apply. + +## Open Questions + +None outstanding. Ready for Phase 2 (Plan). From 84054c1578082f84095172b4c9b51f75bbf5130e Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 15:33:26 -0400 Subject: [PATCH 04/13] refactor(utils): enable TypeScript strict mode #35940 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the standard per-project strict flags to `libs/utils/tsconfig.json`, following the pattern from #36879 (dotcms-models), and resolve the 32 errors they surface across 3 files. `tsconfig.base.json` stays at `strict: false`. The flags also propagate to `tsconfig.spec.json`, which surfaced 17 further errors in the spec files (baseline was 0). Those are fixed here too rather than left as a regression. Notable changes: - `EMPTY_FIELD` assigned `null` to 18 members that `DotCMSContentTypeField` declares non-nullable. Replaced with zero values of the declared types. Nothing compares those members to `null` strictly — consumers use falsy checks such as `isNewField`'s `!field.id` — so `''`, `0` and `false` behave identically at runtime. - `clazz` has no zero value (`DotCMSClazz` is a union of concrete Java class names), so `EMPTY_FIELD` and `EMPTY_SYSTEM_FIELD` are now typed `Omit`. They are partial templates, not valid fields, and the type now says so. The derived `COLUMN_FIELD`, `ROW_FIELD` and `TAB_FIELD` already supply their own `clazz`. - `getFieldsWithoutLayout` used a truthy `.filter()` that does not narrow the optional `row.columns`. Replaced with a type predicate, which clears the TS2532 and both TS2769 errors without a cast. - `ellipsizeText` accepted `null`/`undefined` at runtime — its own guard and its tests document that — but declared `string` and `number`. Widened to match, with an explicit `limit == null` check so the later comparisons narrow. - `fallbackErrorMessages` typed `{ [key: number]: string }`, mirroring the identical declaration already in `libs/data-access/.../dot-upload.service.ts`. - `dot-utils.ts` uses bracket access for the six `DotCMSContentlet` index-signature reads in `getImageAssetUrl`. No new `any`, `@ts-ignore`, or `@ts-expect-error`. The nine `as unknown as` casts added are all in spec files, on inputs the tests deliberately pass as invalid, matching the idiom those files already used. Verified: - `tsc -p libs/utils/tsconfig.lib.json --noEmit` — 0 errors (from 32) - `tsc -p libs/utils/tsconfig.spec.json --noEmit` — 0 errors (from 17) - `data-access` typecheck went from 68 errors to 36, zero new - `utils-testing` unchanged at 1 pre-existing error (missing jasmine types) - `dotcms-ui` typecheck clean apart from a pre-existing missing `dotcms-webcomponents/loader` dist - `nx format:check` green Note: `utils` has no `build` target and is tag-excluded from lint and test, so nothing in CI verifies these flags — the same accepted trade-off as #35939. Closes #35940 Co-Authored-By: Claude Opus 5 (1M context) --- core-web/libs/utils/src/lib/dot-utils.spec.ts | 16 ++-- core-web/libs/utils/src/lib/dot-utils.ts | 16 ++-- .../src/lib/services/dot-asset.service.ts | 6 +- .../libs/utils/src/lib/shared/FieldUtil.ts | 82 +++++++++++-------- .../src/lib/shared/contentlet.utils.spec.ts | 10 +-- core-web/libs/utils/tsconfig.json | 8 +- 6 files changed, 79 insertions(+), 59 deletions(-) diff --git a/core-web/libs/utils/src/lib/dot-utils.spec.ts b/core-web/libs/utils/src/lib/dot-utils.spec.ts index 20af5ac34a1f..55863a4ae502 100644 --- a/core-web/libs/utils/src/lib/dot-utils.spec.ts +++ b/core-web/libs/utils/src/lib/dot-utils.spec.ts @@ -310,8 +310,8 @@ describe('Dot Utils', () => { it('should handle null currentUrl and requestHostName', () => { const url = 'https://example.com/{requestHostName}{currentUrl}{urlSearchParams}'; const params: DotPageToolUrlParams = { - currentUrl: null, // Handle null by substituting with empty string - requestHostName: null, // Handle null by substituting with empty string + currentUrl: null as unknown as string, // Handle null by substituting with empty string + requestHostName: null as unknown as string, // Handle null by substituting with empty string siteId: '', languageId: 1 }; @@ -324,8 +324,8 @@ describe('Dot Utils', () => { const params: DotPageToolUrlParams = { currentUrl: '', requestHostName: '', - siteId: null, // Handle null by not appending the query parameter - languageId: null // Handle null by not appending the query parameter + siteId: null as unknown as string, // Handle null by not appending the query parameter + languageId: null as unknown as number // Handle null by not appending the query parameter }; expect(getRunnableLink(url, params)).toEqual('https://example.com/page'); @@ -334,10 +334,10 @@ describe('Dot Utils', () => { it('should handle all parameters as null or empty', () => { const url = 'https://example.com/{requestHostName}{currentUrl}{urlSearchParams}'; const params: DotPageToolUrlParams = { - currentUrl: null, // Handle null by substituting with empty string - requestHostName: null, // Handle null by substituting with empty string - siteId: null, // Handle null by not appending the query parameter - languageId: null // Handle null by not appending the query parameter + currentUrl: null as unknown as string, // Handle null by substituting with empty string + requestHostName: null as unknown as string, // Handle null by substituting with empty string + siteId: null as unknown as string, // Handle null by not appending the query parameter + languageId: null as unknown as number // Handle null by not appending the query parameter }; expect(getRunnableLink(url, params)).toEqual('https://example.com/'); diff --git a/core-web/libs/utils/src/lib/dot-utils.ts b/core-web/libs/utils/src/lib/dot-utils.ts index 750cb2b3bed5..3fe821c52035 100644 --- a/core-web/libs/utils/src/lib/dot-utils.ts +++ b/core-web/libs/utils/src/lib/dot-utils.ts @@ -127,18 +127,18 @@ export function getRunnableLink(url: string, currentPageUrlParams: DotPageToolUr */ export function getImageAssetUrl(contentlet: DotCMSContentlet): string { if (!contentlet?.baseType) { - return contentlet.asset; + return contentlet['asset']; } switch (contentlet?.baseType) { case DotCMSBaseTypesContentTypes.FILEASSET: - return contentlet.fileAssetVersion || contentlet.fileAsset; + return contentlet['fileAssetVersion'] || contentlet['fileAsset']; case DotCMSBaseTypesContentTypes.DOTASSET: - return contentlet.assetVersion || contentlet.asset; + return contentlet['assetVersion'] || contentlet['asset']; default: - return contentlet?.asset || ''; + return contentlet?.['asset'] || ''; } } @@ -149,8 +149,12 @@ export function getImageAssetUrl(contentlet: DotCMSContentlet): string { * @param limit - The maximum length of the truncated text. * @returns The truncated text with ellipsis if it exceeds the limit, otherwise the original text. */ -export function ellipsizeText(text: string, limit: number): string { - if (!text || typeof text !== 'string' || limit <= 0 || isNaN(limit)) { +export function ellipsizeText( + text: string | null | undefined, + limit: number | null | undefined +): string { + // `limit == null` is checked explicitly so the remaining comparisons narrow it to `number`. + if (!text || typeof text !== 'string' || limit == null || limit <= 0 || isNaN(limit)) { return ''; } diff --git a/core-web/libs/utils/src/lib/services/dot-asset.service.ts b/core-web/libs/utils/src/lib/services/dot-asset.service.ts index 9ab930607283..0af67de83fdb 100644 --- a/core-web/libs/utils/src/lib/services/dot-asset.service.ts +++ b/core-web/libs/utils/src/lib/services/dot-asset.service.ts @@ -5,7 +5,7 @@ import { DotHttpErrorResponse } from '@dotcms/dotcms-models'; -export const fallbackErrorMessages = { +export const fallbackErrorMessages: { [key: number]: string } = { 500: '500 Internal Server Error', 400: '400 Bad Request', 401: '401 Unauthorized Error' @@ -19,7 +19,7 @@ export const fallbackErrorMessages = { export function createDotAsset( options: DotAssetCreateOptions ): Promise { - const promises = []; + const promises: Promise[] = []; let filesCreated = 1; options.files.map((file: DotCMSTempFile) => { const data = { @@ -70,7 +70,7 @@ export function createDotAsset( }); } -function fetchAsset(url, data): Promise { +function fetchAsset(url: string, data: unknown): Promise { return fetch(url, { method: 'PUT', headers: { diff --git a/core-web/libs/utils/src/lib/shared/FieldUtil.ts b/core-web/libs/utils/src/lib/shared/FieldUtil.ts index 7e455f30f1ac..6df86eee2ecd 100644 --- a/core-web/libs/utils/src/lib/shared/FieldUtil.ts +++ b/core-web/libs/utils/src/lib/shared/FieldUtil.ts @@ -6,33 +6,37 @@ import { DotCMSDataTypes } from '@dotcms/dotcms-models'; -export const EMPTY_FIELD: DotCMSContentTypeField = { +/** + * Blank template for a content type field. `clazz` is deliberately omitted: `DotCMSClazz` is a + * union of concrete implementation class names with no "empty" member, and every derived constant + * supplies its own. Spread this and add `clazz` to obtain a full `DotCMSContentTypeField`. + */ +export const EMPTY_FIELD: Omit = { contentTypeId: '', - dataType: null, + dataType: '', fieldType: '', fieldTypeLabel: '', fieldVariables: [], - fixed: null, - iDate: null, - id: null, - indexed: null, - listed: null, - modDate: null, - name: null, - readOnly: null, - required: null, - searchable: null, - sortOrder: null, - unique: null, - variable: null, - clazz: null, - defaultValue: null, - hint: null, + fixed: false, + iDate: 0, + id: '', + indexed: false, + listed: false, + modDate: 0, + name: '', + readOnly: false, + required: false, + searchable: false, + sortOrder: 0, + unique: false, + variable: '', + defaultValue: undefined, + hint: undefined, regexCheck: undefined, - values: null + values: undefined }; -export const EMPTY_SYSTEM_FIELD: DotCMSContentTypeField = { +export const EMPTY_SYSTEM_FIELD: Omit = { ...EMPTY_FIELD, dataType: DotCMSDataTypes.SYSTEM }; @@ -235,22 +239,28 @@ export class FieldUtil { * @memberof FieldUtil */ static getFieldsWithoutLayout(layout: DotCMSContentTypeLayoutRow[]): DotCMSContentTypeField[] { - return layout - .map((row: DotCMSContentTypeLayoutRow) => row.columns) - .filter((columns: DotCMSContentTypeLayoutColumn[]) => !!columns) - .reduce( - ( - accumulator: DotCMSContentTypeLayoutColumn[], - currentValue: DotCMSContentTypeLayoutColumn[] - ) => accumulator.concat(currentValue), - [] - ) - .map((fieldColumn) => fieldColumn.fields) - .reduce( - (accumulator: DotCMSContentTypeField[], currentValue: DotCMSContentTypeField[]) => - accumulator.concat(currentValue), - [] - ); + return ( + layout + .map((row: DotCMSContentTypeLayoutRow) => row.columns) + // Type guard rather than a plain truthy filter: `columns` is optional on the row, and + // only a predicate signature narrows it away for the `reduce` below. + .filter((columns): columns is DotCMSContentTypeLayoutColumn[] => !!columns) + .reduce( + ( + accumulator: DotCMSContentTypeLayoutColumn[], + currentValue: DotCMSContentTypeLayoutColumn[] + ) => accumulator.concat(currentValue), + [] + ) + .map((fieldColumn) => fieldColumn.fields) + .reduce( + ( + accumulator: DotCMSContentTypeField[], + currentValue: DotCMSContentTypeField[] + ) => accumulator.concat(currentValue), + [] + ) + ); } /** diff --git a/core-web/libs/utils/src/lib/shared/contentlet.utils.spec.ts b/core-web/libs/utils/src/lib/shared/contentlet.utils.spec.ts index 6a4a63bbba72..a91bc3e7d0da 100644 --- a/core-web/libs/utils/src/lib/shared/contentlet.utils.spec.ts +++ b/core-web/libs/utils/src/lib/shared/contentlet.utils.spec.ts @@ -64,14 +64,14 @@ describe('utils', () => { }; const result = getFileMetadata(contentlet); - expect(result).toEqual(contentlet.metaData); + expect(result).toEqual(contentlet['metaData']); }); it('should return assetMetaData if metaData is not present', () => { const contentlet: DotCMSContentlet = NEW_FILE_MOCK.entity; const result = getFileMetadata(contentlet); - expect(result).toEqual(contentlet.assetMetaData); + expect(result).toEqual(contentlet['assetMetaData']); }); it('should return an empty object if neither metaData nor assetMetaData is present', () => { @@ -97,7 +97,7 @@ describe('utils', () => { const contentlet: DotCMSContentlet = { ...NEW_FILE_MOCK.entity }; - delete contentlet.assetVersion; + delete contentlet['assetVersion']; const result = getFileVersion(contentlet); expect(result).toBeNull(); @@ -141,7 +141,7 @@ describe('utils', () => { ...TEMP_FILE_MOCK, mimeType: 'image/jpeg' }; - const acceptedFiles = []; + const acceptedFiles: string[] = []; expect(checkMimeType(file, acceptedFiles)).toBe(true); }); @@ -166,7 +166,7 @@ describe('utils', () => { it('returns false for file with no mime type', () => { const file = { ...TEMP_FILE_MOCK, - mimeType: null + mimeType: null as unknown as string }; const acceptedFiles = ['image/jpeg']; expect(checkMimeType(file, acceptedFiles)).toBe(false); diff --git a/core-web/libs/utils/tsconfig.json b/core-web/libs/utils/tsconfig.json index d72ba3eb3c86..d2cd03a27e5a 100644 --- a/core-web/libs/utils/tsconfig.json +++ b/core-web/libs/utils/tsconfig.json @@ -14,6 +14,12 @@ "target": "es2020", "module": "preserve", "moduleResolution": "bundler", - "lib": ["dom", "dom.iterable", "es2022"] + "lib": ["dom", "dom.iterable", "es2022"], + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true } } From c8ec1cc9f3187806c50aa215de952dd86d019bfa Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 16:20:30 -0400 Subject: [PATCH 05/13] fix(utils-testing): restore clazz on the basic field mock + review fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses three review comments on #36957. 1. `dot-content-types.mock.ts` — real regression, now fixed. `dotcmsContentTypeFieldBasicMock` spreads `EMPTY_SYSTEM_FIELD`, which #35940 retyped to `Omit`, leaving the mock without a required property (TS2741). It now supplies `clazz: DotCMSClazzes.TEXT`; callers that care already override it. Why the original verification missed it: `libs/utils-testing/tsconfig.lib.json` declares `"types": ["jasmine"]` and that package is not installed, so tsc emits `TS2688: Cannot find type definition file for 'jasmine'` and stops before semantic checking. The "1 error before, 1 after" measurement reported in #35940 therefore proved nothing — nothing was being checked. Running with `--types node` reveals 33 errors, including the TS2741. It is 32 after this fix. Verified the runtime-value change, since the mock has ~103 consumers whose tests do run in CI: `clazz` went `null` (pre-PR) → absent (#35940) → `TEXT`. `FieldUtil.isRow`/`isColumn`/`isTabDivider` compare for equality and return false for all three, and there is no `!field.clazz` or `=== null` check anywhere. Test runs: `default-value-property` 7/7, `dot-content-types-edit` 545 passed across 48 suites, `data-access` 751 passed across 79 suites. 2. `sdk-create-app/src/utils/index.ts` — the throw said "requires at least 1 retry", but `retries` is the total attempt count (`for (i = 0; i < retries)`), so `retries = 1` means one attempt and zero retries. Reworded to "attempt" and the ambiguity noted in the comment. 3. `core-web/CLAUDE.md` — the verify snippet hard-coded `libs//tsconfig.lib.json`, which resolves for neither nested projects (`libs/sdk/create-app`, which has no `tsconfig.lib.json`) nor apps (`tsconfig.app.json`). Replaced with a `` placeholder plus the two caveats, a reminder that `tsconfig.spec.json` inherits the flags, and a warning about unresolved `types` entries masking all semantic diagnostics. Co-Authored-By: Claude Opus 5 (1M context) --- core-web/CLAUDE.md | 11 ++++++++++- core-web/libs/sdk/create-app/src/utils/index.ts | 3 ++- .../utils-testing/src/lib/dot-content-types.mock.ts | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core-web/CLAUDE.md b/core-web/CLAUDE.md index 88c8862dfc44..725e31fb8f88 100644 --- a/core-web/CLAUDE.md +++ b/core-web/CLAUDE.md @@ -124,11 +124,20 @@ Vite-based projects are the exception: their builds use esbuild and skip type ch Verify locally: ```bash -pnpm exec tsc -p libs//tsconfig.lib.json --noEmit +pnpm exec tsc -p /tsconfig.lib.json --noEmit pnpm exec nx run :build pnpm exec nx affected -t build,lint --base=origin/main # check you didn't break consumers ``` +`` is the path from `project.json`, which is often nested — e.g. `libs/sdk/create-app`, not `libs/create-app`. Two caveats on the tsconfig name: + +- **Apps** use `tsconfig.app.json`. +- **Some projects have no `tsconfig.lib.json`** (`libs/sdk/create-app` is one); use their `tsconfig.json` instead. + +Also check `tsconfig.spec.json` — the flags live in `tsconfig.json`, which the spec config extends, so specs go strict too and their errors are yours to fix. + +> **Watch out for masked results.** If a tsconfig declares a `types` entry that is not installed, `tsc` reports `TS2688: Cannot find type definition file for ''` and **stops before semantic checking** — you get one error and no type checking at all. A stable error count across a change proves nothing in that case. `libs/utils-testing` is affected today (`"types": ["jasmine"]`); check it with `--types node` to see real diagnostics. + ## Portlet Development New portlets go in `libs/portlets/`. For full patterns, architecture, testing, and Nx generator setup: diff --git a/core-web/libs/sdk/create-app/src/utils/index.ts b/core-web/libs/sdk/create-app/src/utils/index.ts index d94b5cecc8a6..4903ffe34e49 100644 --- a/core-web/libs/sdk/create-app/src/utils/index.ts +++ b/core-web/libs/sdk/create-app/src/utils/index.ts @@ -119,8 +119,9 @@ export async function fetchWithRetry( // Only reachable when retries < 1, in which case the loop never runs. Throwing keeps the // return type free of `undefined` and surfaces the bad argument instead of hiding it. + // Note `retries` is the total attempt count, not the number of retries after the first try. throw new Error( - chalk.red(`\n❌ fetchWithRetry requires at least 1 retry, received ${retries}\n`) + chalk.red(`\n❌ fetchWithRetry requires at least 1 attempt, received ${retries}\n`) ); } diff --git a/core-web/libs/utils-testing/src/lib/dot-content-types.mock.ts b/core-web/libs/utils-testing/src/lib/dot-content-types.mock.ts index d7d76df1723a..8e0637c3ad9f 100644 --- a/core-web/libs/utils-testing/src/lib/dot-content-types.mock.ts +++ b/core-web/libs/utils-testing/src/lib/dot-content-types.mock.ts @@ -69,7 +69,10 @@ export const dotcmsContentTypeBasicMock = { } as unknown as DotCMSContentType; export const dotcmsContentTypeFieldBasicMock: DotCMSContentTypeField = { - ...EMPTY_SYSTEM_FIELD + ...EMPTY_SYSTEM_FIELD, + // `EMPTY_SYSTEM_FIELD` is `Omit` — a partial template — so a + // concrete class is supplied here. Callers that care override it. + clazz: DotCMSClazzes.TEXT }; export const fieldsWithBreakColumn: DotCMSContentTypeLayoutRow[] = [ From a78b5268cd9286791c61f478196aa4e4046e03a9 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 17:42:27 -0400 Subject: [PATCH 06/13] docs(specs): record sdk-uve as already strict-compliant #35941 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sdk-uve` needs no change for [08/44]. The six strict flags have been in `libs/sdk/uve/tsconfig.json` since the library was created (`277cbbc8f7`, #31242, Feb 2025) as a verbatim copy of `sdk-client`'s config, `tsc --noEmit` is clean on both lib and spec, and there are zero `any`, `@ts-ignore` or non-null assertions across 4518 lines. It is also genuinely enforced, which is what separated `sdk-types` from `dotcms-js` and `utils`. `rollup.config.cjs` sets `compiler: 'babel'`, but that governs only transpilation — `@nx/rollup`'s `withNx` always inserts a TypeScript plugin with `check`/`noEmitOnError` tied to `skipTypeCheck`, which this project does not set. Two of the three type-checking paths run in CI, and the `build-test` execution in `core-web/pom.xml` has no `` element, so it cannot be turned off. Issue closed as completed with the evidence; not linked to PR #36957 since there is no diff and that PR did not resolve it. Also records an incidental finding, left unfixed: `tsconfig.base.json:104` maps `@dotcms/uve/types` to a file that does not exist, and nothing imports it. Co-Authored-By: Claude Opus 5 (1M context) --- specs/35941-sdk-uve-strict-mode/spec.md | 192 ++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 specs/35941-sdk-uve-strict-mode/spec.md diff --git a/specs/35941-sdk-uve-strict-mode/spec.md b/specs/35941-sdk-uve-strict-mode/spec.md new file mode 100644 index 000000000000..64f9d067ad6d --- /dev/null +++ b/specs/35941-sdk-uve-strict-mode/spec.md @@ -0,0 +1,192 @@ +# Spec: Enable TypeScript strict mode in `sdk-uve` + +**Issue:** [#35941](https://github.com/dotCMS/core/issues/35941) — [08/44] · **Epic:** [#35932](https://github.com/dotCMS/core/issues/35932) +**Status:** Awaiting review (Phase 1 — Specify) +**Conclusion:** **No code change required. The issue is already satisfied.** + +--- + +## Objective + +Verify whether `sdk-uve` (`core-web/libs/sdk/uve`, package `@dotcms/uve`) meets the strict-mode bar defined by the rollout, and close the issue with evidence rather than producing a change for its own sake. + +**Result of the investigation: it already does — and unlike some earlier projects in this rollout, it is genuinely enforced.** + +### Assumptions (stated for correction) + +1. The rollout's definition of "strict" is the six flags from precedent #36879, in the project's own `tsconfig.json`. No `typescript-strict-plugin`, no `tsc-strict`, no `// @ts-strict-ignore` — that approach was dropped. +2. "Done" means: flags present **and** zero errors **and** something in CI actually verifies it. The third clause is the one that separated `sdk-types` (done) from `dotcms-js` / `utils` (declared but unenforced). +3. The 2 commits by which this branch trails `origin/main` are irrelevant to the conclusion, but the branch should be updated before any work. + +--- + +## Evidence + +### 1. The flags are already there + +`libs/sdk/uve/tsconfig.json` carries all six: + +```json +"forceConsistentCasingInFileNames": true, +"strict": true, +"noImplicitOverride": true, +"noPropertyAccessFromIndexSignature": true, +"noImplicitReturns": true, +"noFallthroughCasesInSwitch": true +``` + +Present since `277cbbc8f7` — *"chore(SDK): Create `getUVEState` to manage UVE state headlessly (#31242)"* — i.e. from early in the library's life, not added by this rollout. + +### 2. It compiles clean + +| Config | Errors | +|---|---:| +| `tsconfig.lib.json` | **0** | +| `tsconfig.spec.json` | **0** | + +### 3. The code is genuinely clean, not clean-by-escape-hatch + +Across **4518 lines** in 21 `.ts` files (5 of them specs): + +| Pattern | Count | +|---|---:| +| `: any` / `` / `any[]` | **0** | +| `@ts-ignore` / `@ts-expect-error` | **0** | +| Non-null assertions (`!.`) | **0** | + +So the zero-error result is not propped up by suppressions. + +### 4. It is enforced — verified at the source, not assumed + +This is the clause that failed for `dotcms-js` and `utils`, so it was checked directly rather than inferred. + +`libs/sdk/uve/rollup.config.cjs` sets `compiler: 'babel'`, which earlier in this rollout was **wrongly** read as "no type checking". `compiler` governs only the transpile step. `@nx/rollup`'s `withNx` **always** inserts a TypeScript plugin — see `@nx/rollup/src/plugins/with-nx/with-nx.js:164-196`: + +```js +options.useLegacyTypescriptPlugin !== false + ? require('rollup-plugin-typescript2')({ + check: !options.skipTypeCheck, // ← type checking + tsconfig: tsConfigPath, ... }) + : require('@rollup/plugin-typescript')({ + ..., + noEmitOnError: !options.skipTypeCheck }) // ← fails the build +``` + +`sdk-uve` sets neither `skipTypeCheck` nor `useLegacyTypescriptPlugin`, so it gets `rollup-plugin-typescript2` with `check: true` against `tsconfig.lib.json` — the config that carries the strict flags. A strict violation fails the build. + +The same mechanism was **proven empirically** on the sibling project `sdk-types` in this PR: reverting a fix there made `nx run sdk-types:build` fail with `@rollup/plugin-typescript TS2564`. + +### 5. That build runs in CI, and in the release pipeline + +- `project.json` has `"tags": []` — **no `skip:build` / `skip:lint` / `skip:test`.** +- CI runs `nx run-many -t build --exclude=tag:skip:build` (`build-test` in `core-web/pom.xml`), so `sdk-uve` is built on every PR. +- `@dotcms/uve` is a **published npm package** (v1.1.1) and matches the `sdk-*` glob in the SDK release pipeline, so the same type-checked build gates every release. + +There are in fact **three** type-checking paths, two of which run in CI: + +| Path | Type-checks? | In CI? | +|---|---|---| +| `build` (rollup) | Yes — TS plugin with `noEmitOnError: !skipTypeCheck` | **Yes**, and the `build-test` execution in `core-web/pom.xml:191` has **no `` element** — it cannot be turned off | +| `test` (ts-jest) | Yes — `diagnostics` is not disabled in `jest.preset.js` nor the project config, so it defaults on, against `tsconfig.spec.json` | **Yes**, via `nx affected -t test` (no skip tag) | +| `build:js` (esbuild) | Yes — `compiler: "tsc"`, `skipTypeCheck` unset → runs `tsc --noEmit` over `tsconfig.lib.json` | No — the target name does not match `nx run-many -t build`, and its output is committed by hand | + +`build-test` being unskippable is what makes this the strongest-enforced project of the rollout so far: `-DskipTests=true` disables `unit-test` and `-Pvalidate` gates `lint-test`, but neither can disable the build. + +### 6. Consumers + +7 dependents; **5 already strict**: + +| Consumer | Strict? | +|---|---| +| `sdk-analytics`, `sdk-angular`, `sdk-experiments`, `sdk-react`, `sdk-vue` | **Yes** | +| `dotcms-ui`, `portlets-edit-ema-portlet` | Inherits `false` | + +Nothing to widen, so no blast radius to manage. + +--- + +## Comparison with the rest of the rollout + +| Issue | Project | Flags | Clean | Enforced | Outcome | +|---|---|:--:|:--:|:--:|---| +| #35935 | `sdk-types` | ✅ | ✅ | ✅ | No-op; shipped docs | +| #35936 | `dotcms` | ❌ | ❌ | ❌ | Dead code → #36950 | +| #35937 | `dot-layout-grid` | ❌ | ❌ | ❌ | Dead code → #36950 | +| #35938 | `sdk-create-app` | ❌ | 2 errors | ✅ | Fixed | +| #35939 | `dotcms-js` | ❌ | 38 errors | ❌ | Fixed, unenforced | +| #35940 | `utils` | ❌ | 32+17 errors | ❌ | Fixed, unenforced | +| **#35941** | **`sdk-uve`** | ✅ | ✅ | ✅ | **No-op** | + +`sdk-uve` is the second project in the rollout that was already finished before the epic started. + +--- + +## Commands + +```bash +cd core-web + +pnpm exec tsc -p libs/sdk/uve/tsconfig.lib.json --noEmit # expect 0 +pnpm exec tsc -p libs/sdk/uve/tsconfig.spec.json --noEmit # expect 0 +pnpm exec nx run sdk-uve:build --skip-nx-cache # expect pass +pnpm exec nx run sdk-uve:lint +pnpm exec nx run sdk-uve:test +``` + +> Node 22.22.3 via nvm; `pnpm` is not on `PATH` in this worktree, use `corepack pnpm`. + +## Project Structure + +``` +core-web/libs/sdk/uve/ +├── src/ 21 .ts files, 4518 lines (5 specs) +│ ├── index.ts public barrel → @dotcms/uve +│ ├── internal.ts internal barrel → @dotcms/uve/internal +│ ├── types.ts → @dotcms/uve/types +│ ├── lib/core/, lib/editor/ +│ └── script/sdk-editor.ts entry for build:js → dotCMS webapp +├── tsconfig.json ← the six flags already live here +├── tsconfig.lib.json declaration: true ← what makes rollup type-check +└── rollup.config.cjs compiler: 'babel' (transpile only; TS plugin is separate) +``` + +## Code Style + +Not applicable — no code is being written. If a future change touches this project, the existing bar is: no `any`, no `@ts-ignore`, no `!` assertions, all currently at zero. + +## Testing Strategy + +No new tests. Existing `sdk-uve:test` and `sdk-uve:lint` targets run in CI (no skip tags) and must stay green. Verification for this issue is compilation- and build-based, per the commands above. + +## Boundaries + +**Always:** back the "already done" claim with reproducible commands in the issue comment. + +**Ask first:** any change to `libs/sdk/uve` source — it is a published package with 5 strict consumers, and nothing here needs changing. + +**Never:** add flags that are already present, or make a cosmetic change purely to have a diff for the issue. + +--- + +## Success Criteria + +1. `tsc -p libs/sdk/uve/tsconfig.lib.json --noEmit` → 0 errors. +2. `tsc -p libs/sdk/uve/tsconfig.spec.json --noEmit` → 0 errors. +3. `nx run sdk-uve:build` passes, and the rollup TS-plugin evidence above is recorded. +4. `git diff origin/main -- core-web/libs/sdk/uve` stays **empty** — the deliverable is a verdict, not a diff. +5. #35941 is closed with the evidence, and its stale ACs (`typescript-strict-plugin`, `npx tsc-strict`, `// @ts-strict-ignore`) corrected first — same treatment as #35939. + +## Resolved + +**How to record the closure:** close #35941 directly with the evidence, rather than adding `Closes #35941` to PR #36957. There is no diff to attach, and linking it would imply this PR resolved it — it did not; the project has been compliant since February 2025. + +## Incidental finding — out of scope + +`core-web/tsconfig.base.json:104` maps `@dotcms/uve/types` → `libs/sdk/uve/src/types.ts`, **a file that does not exist**. Nothing imports that specifier, and it is absent from the `exports` / `typesVersions` maps in `libs/sdk/uve/package.json`. A dead alias, unrelated to strict mode. Not touched here — worth a separate cleanup ticket. + +## Steps + +1. Correct the stale ACs on #35941 (`typescript-strict-plugin`, `npx tsc-strict`, `// @ts-strict-ignore`), same treatment as #35939. +2. Comment on #35941 with the evidence table and the reproducible commands. +3. Close it as completed — the acceptance criteria are met, just not by this rollout. +4. Commit `spec.md` alongside the one for #35939 already in PR #36957, so the "already compliant" verdict is recorded for the remaining 36 issues. From bb38549000bd4b0699fcfe0181d30baed42ef13b Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 19:02:35 -0400 Subject: [PATCH 07/13] docs(specs): record sdk-client as already strict-compliant #35942 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sdk-client` needs no change for [09/44]. The six strict flags are already in `libs/sdk/client/tsconfig.json`, `tsc --noEmit` is clean on both lib and spec, and there are zero `any`, `@ts-ignore` or non-null assertions across 9600 lines of production source. Enforcement is unambiguous here, unlike the sibling projects that needed an argument: `rollup.config.cjs` sets `compiler: 'tsc'` against `tsconfig.lib.json` with no `skipTypeCheck`, so the build compiles with tsc directly against the strict config. `tags` is empty and the `build-test` execution in `core-web/pom.xml` has no `` element, so that build runs on every PR and gates every SDK release. Issue closed as completed with the evidence; not linked to PR #36957 since there is no diff and that PR did not resolve it. Also records an emerging pattern for the remaining issues: every `libs/sdk/*` project checked so far is already strict and already enforced — they share a tsconfig lineage (sdk-uve's config is a verbatim copy of this one) and all build through Nx executors that type-check. The unfinished work is concentrated in the non-SDK libraries and the apps. Co-Authored-By: Claude Opus 5 (1M context) --- specs/35942-sdk-client-strict-mode/spec.md | 152 +++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 specs/35942-sdk-client-strict-mode/spec.md diff --git a/specs/35942-sdk-client-strict-mode/spec.md b/specs/35942-sdk-client-strict-mode/spec.md new file mode 100644 index 000000000000..cb3cab28d81c --- /dev/null +++ b/specs/35942-sdk-client-strict-mode/spec.md @@ -0,0 +1,152 @@ +# Spec: Enable TypeScript strict mode in `sdk-client` + +**Issue:** [#35942](https://github.com/dotCMS/core/issues/35942) — [09/44] · **Epic:** [#35932](https://github.com/dotCMS/core/issues/35932) +**Status:** Awaiting review (Phase 1 — Specify) +**Conclusion:** **No code change required. The issue is already satisfied, and enforced.** + +--- + +## Objective + +Verify whether `sdk-client` (`core-web/libs/sdk/client`, package `@dotcms/client`) meets the rollout's strict-mode bar, and close the issue with evidence rather than manufacturing a diff. + +It does. This is the **third** project in the rollout that was already compliant before the epic began — and the clearest of the three, because its build uses `compiler: 'tsc'` outright. + +### Assumptions + +1. "Strict" means the six flags from precedent #36879, in the project's own `tsconfig.json`. No `typescript-strict-plugin` / `tsc-strict` / `@ts-strict-ignore` — that approach was dropped. +2. "Done" requires three things, not one: flags present **and** zero errors **and** something in CI that actually verifies it. The third clause is what separated `sdk-types` / `sdk-uve` (done) from `dotcms-js` / `utils` (declared but unenforced). + +--- + +## Evidence + +### 1. Flags already present + +`libs/sdk/client/tsconfig.json` carries all six (`strict`, `forceConsistentCasingInFileNames`, `noImplicitOverride`, `noPropertyAccessFromIndexSignature`, `noImplicitReturns`, `noFallthroughCasesInSwitch`). + +This is almost certainly the **origin** of the pattern across the SDK: `git log --follow` on `libs/sdk/uve/tsconfig.json` showed it was created as a `C100` (100%-identical) copy of *this* file. + +### 2. Compiles clean + +| Config | Errors | +|---|---:| +| `tsconfig.lib.json` | **0** | +| `tsconfig.spec.json` | **0** | + +### 3. Clean without escape hatches + +Production source only (specs excluded), across **9600 lines** in 48 `.ts` files (15 of them specs): + +| Pattern | Count | +|---|---:| +| `: any` / `` / `any[]` | **0** | +| `@ts-ignore` / `@ts-expect-error` | **0** | +| Non-null assertions (`!.`) | **0** | + +### 4. Enforced — and here it is unambiguous + +`libs/sdk/client/rollup.config.cjs`: + +```js +compiler: 'tsc', // ← not 'babel' +tsConfig: './tsconfig.lib.json' // ← the config carrying the strict flags +``` + +`skipTypeCheck` is not set anywhere. Unlike `sdk-uve` — where `compiler: 'babel'` made this look ambiguous until the `@nx/rollup` source confirmed the TypeScript plugin is inserted unconditionally — here the build compiles with `tsc` directly against the strict config. A strict violation fails the build. + +Targets, all green on a fresh no-cache run: + +| Target | Result | +|---|---| +| `nx run sdk-client:build` | pass | +| `nx run sdk-client:lint` | pass | +| `nx run sdk-client:test` | pass | + +### 5. That build runs in CI and gates every release + +- `project.json` has `"tags": []` — no `skip:build` / `skip:lint` / `skip:test`. +- CI runs `nx run-many -t build --exclude=tag:skip:build` via the `build-test` execution in `core-web/pom.xml`, which has **no `` element** — `-DskipTests` and `-Pvalidate` cannot disable it. +- `@dotcms/client` **v1.2.0** is published to npm and matches the `sdk-*` glob in the SDK release pipeline (`cicd_release-sdk.yml` → `deploy-javascript-sdk`), so the same type-checked build gates every release. + +### 6. Consumers + +4 dependents; **3 already strict**: + +| Consumer | Strict? | +|---|---| +| `sdk-angular`, `sdk-react`, `sdk-vue` | **Yes** | +| `portlets-edit-ema-portlet` | Inherits `false` | + +Nothing is being widened, so there is no blast radius. + +### 7. Path aliases all resolve + +Unlike the dangling `@dotcms/uve/types` found in #35941, every alias here points at a real file: + +| Alias | Target | Exists | +|---|---|:--:| +| `@dotcms/client` | `libs/sdk/client/src/index.ts` | ✅ | +| `@dotcms/client/internal` | `libs/sdk/client/src/internal.ts` | ✅ | +| `@dotcms/query-builder` | `libs/sdk/client/src/lib/client/content/builders/query/query.ts` | ✅ | + +--- + +## Rollout status after this issue + +| Issue | Project | Flags | Clean | Enforced | Outcome | +|---|---|:--:|:--:|:--:|---| +| #35935 | `sdk-types` | ✅ | ✅ | ✅ | No-op | +| #35936 | `dotcms` | ❌ | ❌ | ❌ | Dead → #36950 | +| #35937 | `dot-layout-grid` | ❌ | ❌ | ❌ | Dead → #36950 | +| #35938 | `sdk-create-app` | ❌ | 2 err | ✅ | Fixed | +| #35939 | `dotcms-js` | ❌ | 38 err | ❌ | Fixed, unenforced | +| #35940 | `utils` | ❌ | 32+17 err | ❌ | Fixed, unenforced | +| #35941 | `sdk-uve` | ✅ | ✅ | ✅ | No-op | +| **#35942** | **`sdk-client`** | ✅ | ✅ | ✅ | **No-op** | + +Emerging pattern worth noting for the remaining 35: **every `libs/sdk/*` project is already strict and already enforced**, because they share a tsconfig lineage and all build through Nx executors that type-check. The genuinely unfinished work is concentrated in the non-SDK libraries and apps. + +--- + +## Commands + +```bash +cd core-web +pnpm exec tsc -p libs/sdk/client/tsconfig.lib.json --noEmit # 0 +pnpm exec tsc -p libs/sdk/client/tsconfig.spec.json --noEmit # 0 +pnpm exec nx run sdk-client:build --skip-nx-cache +pnpm exec nx run sdk-client:lint +pnpm exec nx run sdk-client:test +``` + +> Node 22.22.3 via nvm; `pnpm` is not on `PATH` in this worktree — use `corepack pnpm`. + +## Testing Strategy + +No new tests. `sdk-client` has 15 spec files and its `test` target runs in CI with no skip tag; ts-jest type-checks them against `tsconfig.spec.json`, which inherits the strict flags. Verification for this issue is compilation- and build-based. + +## Boundaries + +**Always:** back the "already done" verdict with reproducible commands in the issue comment. + +**Ask first:** any change to `libs/sdk/client` source — published package, 3 strict consumers, and nothing needs changing. + +**Never:** re-add flags that are already there, or make a cosmetic edit purely to produce a diff. + +--- + +## Success Criteria + +1. `tsc` clean on both `tsconfig.lib.json` and `tsconfig.spec.json`. +2. `nx run sdk-client:build` / `:lint` / `:test` pass on a no-cache run. +3. `git diff origin/main -- core-web/libs/sdk/client` stays **empty** — the deliverable is a verdict, not a diff. +4. #35942 closed with the evidence, its stale ACs corrected first — same treatment as #35939 and #35941. + +## Steps + +1. Correct the stale ACs on #35942 (`typescript-strict-plugin`, `npx tsc-strict`, `// @ts-strict-ignore`). +2. Comment with the evidence table and reproducible commands. +3. Close as completed — criteria met, just not by this rollout. +4. Commit this `spec.md` alongside those for #35939 and #35941. +5. Do **not** add `Closes #35942` to PR #36957 — there is no diff, and linking it would imply that PR resolved it. From 8b5dfc4dea2e906d4a576dbed9d5745ef6f1ad72 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Fri, 7 Aug 2026 19:13:08 -0400 Subject: [PATCH 08/13] docs(specs): correct published npm versions and consumer counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two factual corrections to the specs for #35941 and #35942, and one observation, all surfaced by a follow-up review. 1. Published version was wrong. Both specs quoted the version from the local `package.json` (`@dotcms/uve` 1.1.1, `@dotcms/client` 1.2.0) as if that were what ships. It is not: the SDK release action rewrites the version to the dotCMS release tag under ADR-0019 date lockstep. npm `latest` for both is 26.8.7-1 (197 and 262 published versions respectively). Both specs now say so explicitly, and the corresponding GitHub issue comments have been edited. 2. `sdk-client` has 6 dependents, not 4, and 5 of them are strict rather than 3. The Nx graph query used for the original count missed `sdk-experiments` and `sdk-create-app`. The lone non-strict consumer is `portlets-edit-ema-portlet`, which reaches into `@dotcms/client/internal`. 3. New observation, out of scope for the rollout: `build:js` in both `sdk-client` and `sdk-uve` emits an artifact that is committed to git (`html/js/editor-js/sdk-editor.js` and `ext/uve/dot-uve.js`), but that target is invoked by neither `core-web/pom.xml` nor any workflow. If the source changes and nobody runs it by hand, the committed file drifts out of sync and nothing notices. The verdicts for both issues are unchanged — both projects remain already strict-compliant and enforced. Co-Authored-By: Claude Opus 5 (1M context) --- specs/35941-sdk-uve-strict-mode/spec.md | 14 +++++++++++++- specs/35942-sdk-client-strict-mode/spec.md | 22 +++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/specs/35941-sdk-uve-strict-mode/spec.md b/specs/35941-sdk-uve-strict-mode/spec.md index 64f9d067ad6d..c884a1d1529b 100644 --- a/specs/35941-sdk-uve-strict-mode/spec.md +++ b/specs/35941-sdk-uve-strict-mode/spec.md @@ -80,7 +80,8 @@ The same mechanism was **proven empirically** on the sibling project `sdk-types` - `project.json` has `"tags": []` — **no `skip:build` / `skip:lint` / `skip:test`.** - CI runs `nx run-many -t build --exclude=tag:skip:build` (`build-test` in `core-web/pom.xml`), so `sdk-uve` is built on every PR. -- `@dotcms/uve` is a **published npm package** (v1.1.1) and matches the `sdk-*` glob in the SDK release pipeline, so the same type-checked build gates every release. +- `@dotcms/uve` is a **published npm package** and matches the `sdk-*` glob in the SDK release pipeline, so the same type-checked build gates every release. + > The local `package.json` says `1.1.1`, but that is **not** what ships. The release action rewrites the version to the dotCMS release tag (ADR-0019 date lockstep); npm `latest` is **26.8.7-1** across 197 published versions. There are in fact **three** type-checking paths, two of which run in CI: @@ -121,6 +122,17 @@ Nothing to widen, so no blast radius to manage. --- +## Adjacent observation — committed artifact that CI never regenerates + +The `build:js` target emits a file that is **committed to git**, but the target is not invoked by `core-web/pom.xml` or any workflow: + +| Project | Committed artifact | +|---|---| +| `sdk-client` | `dotCMS/src/main/webapp/html/js/editor-js/sdk-editor.js` | +| `sdk-uve` | `dotCMS/src/main/webapp/ext/uve/dot-uve.js` | + +If the source changes and nobody runs `build:js` by hand, the committed file silently drifts out of sync with it, and nothing in CI notices. Out of scope for the strict-mode rollout, but worth a ticket. + ## Commands ```bash diff --git a/specs/35942-sdk-client-strict-mode/spec.md b/specs/35942-sdk-client-strict-mode/spec.md index cb3cab28d81c..0f0b0f3558e0 100644 --- a/specs/35942-sdk-client-strict-mode/spec.md +++ b/specs/35942-sdk-client-strict-mode/spec.md @@ -67,16 +67,17 @@ Targets, all green on a fresh no-cache run: - `project.json` has `"tags": []` — no `skip:build` / `skip:lint` / `skip:test`. - CI runs `nx run-many -t build --exclude=tag:skip:build` via the `build-test` execution in `core-web/pom.xml`, which has **no `` element** — `-DskipTests` and `-Pvalidate` cannot disable it. -- `@dotcms/client` **v1.2.0** is published to npm and matches the `sdk-*` glob in the SDK release pipeline (`cicd_release-sdk.yml` → `deploy-javascript-sdk`), so the same type-checked build gates every release. +- `@dotcms/client` is published to npm and matches the `sdk-*` glob in the SDK release pipeline (`cicd_release-sdk.yml` → `deploy-javascript-sdk`), so the same type-checked build gates every release. + > The local `package.json` says `1.2.0`, but that is **not** what ships. The release action rewrites the version to the dotCMS release tag (ADR-0019 date lockstep); npm `latest` is **26.8.7-1** across 262 published versions. Do not quote the local version as the published one. ### 6. Consumers -4 dependents; **3 already strict**: +6 dependents; **5 already strict**: | Consumer | Strict? | |---|---| -| `sdk-angular`, `sdk-react`, `sdk-vue` | **Yes** | -| `portlets-edit-ema-portlet` | Inherits `false` | +| `sdk-angular`, `sdk-react`, `sdk-vue`, `sdk-experiments`, `sdk-create-app` | **Yes** | +| `portlets-edit-ema-portlet` | Inherits `false` — and it imports from `@dotcms/client/internal` (`dot-page-api.service.ts:8`) | Nothing is being widened, so there is no blast radius. @@ -109,6 +110,17 @@ Emerging pattern worth noting for the remaining 35: **every `libs/sdk/*` project --- +## Adjacent observation — committed artifact that CI never regenerates + +The `build:js` target emits a file that is **committed to git**, but the target is not invoked by `core-web/pom.xml` or any workflow: + +| Project | Committed artifact | +|---|---| +| `sdk-client` | `dotCMS/src/main/webapp/html/js/editor-js/sdk-editor.js` | +| `sdk-uve` | `dotCMS/src/main/webapp/ext/uve/dot-uve.js` | + +If the source changes and nobody runs `build:js` by hand, the committed file silently drifts out of sync with it, and nothing in CI notices. Out of scope for the strict-mode rollout, but worth a ticket. + ## Commands ```bash @@ -130,7 +142,7 @@ No new tests. `sdk-client` has 15 spec files and its `test` target runs in CI wi **Always:** back the "already done" verdict with reproducible commands in the issue comment. -**Ask first:** any change to `libs/sdk/client` source — published package, 3 strict consumers, and nothing needs changing. +**Ask first:** any change to `libs/sdk/client` source — published package, 5 strict consumers, and nothing needs changing. **Never:** re-add flags that are already there, or make a cosmetic edit purely to produce a diff. From 5ea14e8cf8049022804d93989d47c99752f97422 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Mon, 10 Aug 2026 11:00:49 -0400 Subject: [PATCH 09/13] refactor(dotcms-webcomponents): prepare Stencil members for strict mode #35943 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Groundwork for #35943. Strict is **not** enabled yet — 276 type errors remain across 42 files and Stencil type-checks during `build`, so flipping the flag before they are fixed would turn CI red. This lands the part that is correct on its own and leaves the build green. Stencil declares runtime-injected members without initializers, which collides with `strictPropertyInitialization`. Handled by decorator kind rather than uniformly, because the choice is not cosmetic: - `@Event` (57), `@Element` (27), `@State` (25) → definite assignment `!`. These are internal; the Stencil runtime assigns them and they do not appear in the generated public API. - `@Prop` (30) → optional `?` instead. Using `!` here made Stencil emit those props as **required** in `components.d.ts` — 28 of them — which is a breaking change for any TS/JSX consumer. With `?` the generated API moves the other way, from required to optional, which is backward compatible. Also `dot-binary-text-field`'s `value` prop was `= null` with no annotation, so under strict TS inferred its type as `null` and the generated API narrowed from `any` to `null`. It is assigned `''` and file URLs at runtime, so it is now typed `string | null` — still a narrowing from `any`, but an accurate one. `components.d.ts` and one readme are regenerated build output and are included so the repo matches what the build produces. Two things worth recording for whoever finishes this: - Do **not** put `"ignoreDeprecations": "6.0"` in this tsconfig. Stencil bundles TypeScript 5.8.3, which only accepts `"5.0"` and fails the build with `Invalid value for '--ignoreDeprecations'`. The repo's tsc is 6.0.3 and needs `"6.0"` to see past the deprecated `baseUrl` / `moduleResolution`, so pass it on the CLI. Without it, tsc aborts on TS5101/TS5107 before any semantic checking and reports a misleading 2 errors. - Unlike `utils` and `dotcms-js`, this project has no `skip:build`, so the Stencil build is a real CI gate. Strict has to reach 0 in one go. Verified: `nx run dotcms-webcomponents:build` green from a cleared `.stencil` cache; `dotcms-ui` typechecks with 0 errors; `nx format:check` green; no `!` on any member without a Stencil decorator (checked by script). Refs #35943 Co-Authored-By: Claude Opus 5 (1M context) --- .../dot-card-view/dot-card-view.tsx | 8 +-- .../dotcms-webcomponents/src/components.d.ts | 58 +++++++++---------- .../dot-binary-file-preview.tsx | 4 +- .../dot-binary-text-field.tsx | 12 ++-- .../dot-binary-text-field/readme.md | 2 +- .../dot-binary-upload-button.tsx | 6 +- .../dot-binary-file/dot-binary-file.tsx | 8 +-- .../dot-checkbox/dot-checkbox.tsx | 10 ++-- .../dot-date-range/dot-date-range.tsx | 8 +-- .../dot-date-time/dot-date-time.tsx | 8 +-- .../contenttypes-fields/dot-date/dot-date.tsx | 8 +-- .../dot-form-column/dot-form-column.tsx | 4 +- .../components/dot-form-row/dot-form-row.tsx | 4 +- .../contenttypes-fields/dot-form/dot-form.tsx | 6 +- .../dot-input-calendar/dot-input-calendar.tsx | 8 +-- .../key-value-form/key-value-form.tsx | 8 +-- .../key-value-table/key-value-table.tsx | 6 +- .../dot-key-value/dot-key-value.tsx | 26 ++++----- .../dot-multi-select/dot-multi-select.tsx | 10 ++-- .../dot-radio/dot-radio.tsx | 10 ++-- .../dot-select/dot-select.tsx | 10 ++-- .../dot-autocomplete/dot-autocomplete.tsx | 8 +-- .../dot-tags/components/dot-chip/dot-chip.tsx | 4 +- .../contenttypes-fields/dot-tags/dot-tags.tsx | 8 +-- .../dot-textarea/dot-textarea.tsx | 8 +-- .../dot-textfield/dot-textfield.tsx | 8 +-- .../contenttypes-fields/dot-time/dot-time.tsx | 8 +-- .../dot-asset-drop-zone.tsx | 4 +- .../dot-card-contentlet.tsx | 10 ++-- .../dot-context-menu/dot-context-menu.tsx | 2 +- .../dot-data-view-button.tsx | 2 +- .../dot-html-to-image/dot-html-to-image.tsx | 4 +- .../dot-select-button/dot-select-button.tsx | 2 +- .../dot-contentlet-icon.tsx | 2 +- .../dot-contentlet-lock-icon.tsx | 2 +- .../dot-contentlet-thumbnail.tsx | 6 +- .../dot-material-icon-picker.tsx | 8 +-- .../src/elements/dot-tooltip/dot-tooltip.tsx | 12 ++-- .../dot-video-thumbnail.tsx | 4 +- .../libs/dotcms-webcomponents/tsconfig.json | 9 +++ 40 files changed, 172 insertions(+), 163 deletions(-) diff --git a/core-web/libs/dotcms-webcomponents/src/collections/dot-card-view/dot-card-view.tsx b/core-web/libs/dotcms-webcomponents/src/collections/dot-card-view/dot-card-view.tsx index 0bb2e6b9e3cc..d70d656136fa 100644 --- a/core-web/libs/dotcms-webcomponents/src/collections/dot-card-view/dot-card-view.tsx +++ b/core-web/libs/dotcms-webcomponents/src/collections/dot-card-view/dot-card-view.tsx @@ -38,18 +38,18 @@ const getSelecttion = (items: DotCardContentletItem[], value: string): DotConten shadow: true }) export class DotCardView { - @Element() el: HTMLElement; + @Element() el!: HTMLElement; @Prop() items: DotCardContentletItem[] = []; @Prop({ reflect: true, mutable: true }) - value: string; + value?: string; @Prop() showVideoThumbnail = true; - @Event() selected: EventEmitter; - @Event() cardClick: EventEmitter; + @Event() selected!: EventEmitter; + @Event() cardClick!: EventEmitter; private selection: DotContentletItem[] = []; diff --git a/core-web/libs/dotcms-webcomponents/src/components.d.ts b/core-web/libs/dotcms-webcomponents/src/components.d.ts index 328b8083b091..848a116a0a0a 100644 --- a/core-web/libs/dotcms-webcomponents/src/components.d.ts +++ b/core-web/libs/dotcms-webcomponents/src/components.d.ts @@ -31,7 +31,7 @@ export namespace Components { * @default 'Creating DotAssets' */ "createAssetsText": string; - "customUploadFiles": (props: { + "customUploadFiles"?: (props: { files: File[]; onSuccess: () => void; updateProgress: (progress: number) => void; @@ -268,7 +268,7 @@ export namespace Components { /** * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept": string; + "accept"?: string; /** * (optional) Disables field's interaction * @default false @@ -293,7 +293,7 @@ export namespace Components { * Value specifies the value of the element * @default null */ - "value": any; + "value": string | null; } /** * Represent a dotcms text field for the binary file element. @@ -304,7 +304,7 @@ export namespace Components { /** * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept": string; + "accept"?: string; /** * (optional) Text that be shown in the browse file button * @default '' @@ -334,13 +334,13 @@ export namespace Components { interface DotCard { } interface DotCardContentlet { - "checked": boolean; + "checked"?: boolean; "hideMenu": () => Promise; /** * @default '96px' */ "iconSize": string; - "item": DotCardContentletItem; + "item"?: DotCardContentletItem; "showMenu": (x: number, y: number) => Promise; /** * @default false @@ -362,7 +362,7 @@ export namespace Components { * @default true */ "showVideoThumbnail": boolean; - "value": string; + "value"?: string; } interface DotCheckbox { /** @@ -444,7 +444,7 @@ export namespace Components { "size": string; } interface DotContentletLockIcon { - "locked": boolean; + "locked"?: boolean; /** * @default '16px' */ @@ -466,7 +466,7 @@ export namespace Components { * @default false */ "backgroundImage": boolean; - "contentlet": DotContentletItem; + "contentlet"?: DotContentletItem; /** * @default '' */ @@ -505,7 +505,7 @@ export namespace Components { "show": (x: number, y: number, position?: string) => Promise; } interface DotDataViewButton { - "value": string; + "value"?: string; } interface DotDate { /** @@ -711,7 +711,7 @@ export namespace Components { /** * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow": string; + "fieldsToShow"?: string; /** * Layout metada to be rendered * @default [] @@ -737,21 +737,21 @@ export namespace Components { /** * Fields metada to be rendered */ - "column": DotCMSContentTypeLayoutColumn; + "column"?: DotCMSContentTypeLayoutColumn; /** * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow": string; + "fieldsToShow"?: string; } interface DotFormRow { /** * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow": string; + "fieldsToShow"?: string; /** * Fields metada to be rendered */ - "row": DotCMSContentTypeLayoutRow; + "row"?: DotCMSContentTypeLayoutRow; } interface DotHtmlToImage { /** @@ -827,23 +827,23 @@ export namespace Components { /** * (optional) Label for the add button in the key-value-form */ - "formAddButtonLabel": string; + "formAddButtonLabel"?: string; /** * (optional) The string to use in the key label in the key-value-form */ - "formKeyLabel": string; + "formKeyLabel"?: string; /** * (optional) Placeholder for the key input text in the key-value-form */ - "formKeyPlaceholder": string; + "formKeyPlaceholder"?: string; /** * (optional) The string to use in the value label in the key-value-form */ - "formValueLabel": string; + "formValueLabel"?: string; /** * (optional) Placeholder for the value input text in the key-value-form */ - "formValuePlaceholder": string; + "formValuePlaceholder"?: string; /** * (optional) Hint text that suggest a clue of the field * @default '' @@ -857,7 +857,7 @@ export namespace Components { /** * (optional) The string to use in the delete button of a key/value item */ - "listDeleteLabel": string; + "listDeleteLabel"?: string; /** * Name that will be used as ID * @default '' @@ -890,11 +890,11 @@ export namespace Components { /** * (optional) The string containing the value to be parsed for whitelist key/value */ - "whiteList": string; + "whiteList"?: string; /** * (optional) The string to use in the empty option of whitelist dropdown key/value item */ - "whiteListEmptyOptionLabel": string; + "whiteListEmptyOptionLabel"?: string; } /** * Represent a dotcms label control. @@ -1410,9 +1410,9 @@ export namespace Components { "value": string; } interface DotTooltip { - "content": string; - "delay": number; - "for": string; + "content"?: string; + "delay"?: number; + "for"?: string; /** * @default 'center bottom' */ @@ -1423,7 +1423,7 @@ export namespace Components { * @type {DotContentletItem} * @memberof DotVideoThumbnail */ - "contentlet": DotContentletItem; + "contentlet"?: DotContentletItem; /** * @type {boolean} * @memberof DotVideoThumbnail @@ -1441,7 +1441,7 @@ export namespace Components { * @type {string} * @memberof variable */ - "variable": string; + "variable"?: string; } interface KeyValueForm { /** @@ -2609,7 +2609,7 @@ declare namespace LocalJSX { * Value specifies the value of the element * @default null */ - "value"?: any; + "value"?: string | null; } /** * Represent a dotcms text field for the binary file element. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-file-preview/dot-binary-file-preview.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-file-preview/dot-binary-file-preview.tsx index c65d78ca0d1d..4ea54941d45e 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-file-preview/dot-binary-file-preview.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-file-preview/dot-binary-file-preview.tsx @@ -12,7 +12,7 @@ import { Component, Element, Event, EventEmitter, Prop, Host, h } from '@stencil }) export class DotBinaryFilePreviewComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** file name to be displayed */ @Prop({ reflect: true, mutable: true }) @@ -28,7 +28,7 @@ export class DotBinaryFilePreviewComponent { /** Emit when the file is deleted */ @Event() - delete: EventEmitter; + delete!: EventEmitter; render() { return this.fileName ? ( diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx index bd872d5f178b..67879c1e5662 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx @@ -14,11 +14,11 @@ import { getErrorClass, getHintId, isFileAllowed, isValidURL } from '../../../.. }) export class DotBinaryTextFieldComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value specifies the value of the element */ @Prop({ mutable: true, reflect: true }) - value = null; + value: string | null = null; /** (optional) Hint text that suggest a clue of the field */ @Prop({ reflect: true }) @@ -34,19 +34,19 @@ export class DotBinaryTextFieldComponent { /** (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ @Prop({ reflect: true }) - accept: string; + accept?: string; /** (optional) Disables field's interaction */ @Prop({ reflect: true }) disabled = false; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - fileChange: EventEmitter; + fileChange!: EventEmitter; @Event() - lostFocus: EventEmitter; + lostFocus!: EventEmitter; render() { return ( diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md index 1a99d629ee52..ffa210b41119 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md @@ -16,7 +16,7 @@ Represent a dotcms text field for the binary file element. | `hint` | `hint` | (optional) Hint text that suggest a clue of the field | `string` | `''` | | `placeholder` | `placeholder` | (optional) Placeholder specifies a short hint that describes the expected value of the input field | `string` | `''` | | `required` | `required` | (optional) Determine if it is mandatory | `boolean` | `false` | -| `value` | `value` | Value specifies the value of the element | `any` | `null` | +| `value` | `value` | Value specifies the value of the element | `string` | `null` | ## Events diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-upload-button/dot-binary-upload-button.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-upload-button/dot-binary-upload-button.tsx index f0367a40b434..0b9c6546787b 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-upload-button/dot-binary-upload-button.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-upload-button/dot-binary-upload-button.tsx @@ -14,7 +14,7 @@ import { getId, isFileAllowed } from '../../../../../utils'; }) export class DotBinaryUploadButtonComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Name that will be used as ID */ @Prop({ reflect: true }) @@ -26,7 +26,7 @@ export class DotBinaryUploadButtonComponent { /** (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ @Prop({ reflect: true }) - accept: string; + accept?: string; /** (optional) Disables field's interaction */ @Prop({ reflect: true }) @@ -41,7 +41,7 @@ export class DotBinaryUploadButtonComponent { buttonLabel = ''; @Event() - fileChange: EventEmitter; + fileChange!: EventEmitter; render() { return ( diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/dot-binary-file.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/dot-binary-file.tsx index 956e01e4919e..1e199b059a61 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/dot-binary-file.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/dot-binary-file.tsx @@ -45,7 +45,7 @@ import { getDotAttributesFromElement, setDotAttributesToElement } from '../dot-f }) export class DotBinaryFileComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Name that will be used as ID */ @Prop({ reflect: true }) @@ -112,12 +112,12 @@ export class DotBinaryFileComponent { previewImageUrl = ''; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; private file: string | File = null; private allowedFileTypes = []; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-checkbox/dot-checkbox.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-checkbox/dot-checkbox.tsx index e32ca1998eeb..0a43b5cea1f4 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-checkbox/dot-checkbox.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-checkbox/dot-checkbox.tsx @@ -36,7 +36,7 @@ import { getDotAttributesFromElement, setDotAttributesToElement } from '../dot-f }) export class DotCheckboxComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Name that will be used as ID */ @Prop({ reflect: true }) @@ -71,14 +71,14 @@ export class DotCheckboxComponent { value = ''; @State() - _options: DotOption[]; + _options!: DotOption[]; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; componentWillLoad() { this.value = this.value || ''; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-range/dot-date-range.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-range/dot-date-range.tsx index 3e18dfb1f8aa..407cc4d05fb4 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-range/dot-date-range.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-range/dot-date-range.tsx @@ -31,7 +31,7 @@ import flatpickr from 'flatpickr'; }) export class DotDateRangeComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** (optional) Value formatted with start and end date splitted with a comma */ @Prop({ mutable: true, reflect: true }) @@ -103,12 +103,12 @@ export class DotDateRangeComponent { presetLabel = 'Presets'; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; private flatpickr: any; private defaultPresets = [ diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-time/dot-date-time.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-time/dot-date-time.tsx index fe40ddc7c840..6ed690cfeaad 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-time/dot-date-time.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date-time/dot-date-time.tsx @@ -39,7 +39,7 @@ const TIME_SUFFIX = '-time'; }) export class DotDateTimeComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value format yyyy-mm-dd hh:mm:ss e.g., 2005-12-01 15:22:00 */ @Prop({ mutable: true, reflect: true }) @@ -94,14 +94,14 @@ export class DotDateTimeComponent { timeLabel = 'Time'; @State() - classNames: DotFieldStatusClasses; + classNames!: DotFieldStatusClasses; @State() errorMessageElement: any; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; private _minDateTime: DotDateSlot; private _maxDateTime: DotDateSlot; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date/dot-date.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date/dot-date.tsx index a56c6d7ecbd7..ea1cffdbb1e5 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date/dot-date.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-date/dot-date.tsx @@ -26,7 +26,7 @@ import { setDotAttributesToElement, getDotAttributesFromElement } from '../dot-f }) export class DotDateComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value format yyyy-mm-dd e.g., 2005-12-01 */ @Prop({ mutable: true, reflect: true }) @@ -73,14 +73,14 @@ export class DotDateComponent { step = '1'; @State() - classNames: DotFieldStatusClasses; + classNames!: DotFieldStatusClasses; @State() errorMessageElement: any; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; /** * Reset properties of the field, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-column/dot-form-column.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-column/dot-form-column.tsx index 6369ecb41193..c63459389bab 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-column/dot-form-column.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-column/dot-form-column.tsx @@ -9,11 +9,11 @@ import { DotCMSContentTypeLayoutColumn, DotCMSContentTypeField } from '@dotcms/d export class DotFormColumnComponent { /** Fields metada to be rendered */ @Prop() - column: DotCMSContentTypeLayoutColumn; + column?: DotCMSContentTypeLayoutColumn; /** (optional) List of fields (variableName) separated by comma, to be shown */ @Prop({ reflect: true }) - fieldsToShow: string; + fieldsToShow?: string; render() { // When the user start dragging a form in the edit page the value of layout of the diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-row/dot-form-row.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-row/dot-form-row.tsx index 7fc485a83fb3..55040c4e2e93 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-row/dot-form-row.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/components/dot-form-row/dot-form-row.tsx @@ -8,11 +8,11 @@ import { DotCMSContentTypeLayoutColumn, DotCMSContentTypeLayoutRow } from '@dotc export class DotFormRowComponent { /** Fields metada to be rendered */ @Prop() - row: DotCMSContentTypeLayoutRow; + row?: DotCMSContentTypeLayoutRow; /** (optional) List of fields (variableName) separated by comma, to be shown */ @Prop({ reflect: true }) - fieldsToShow: string; + fieldsToShow?: string; render() { // When the user start dragging a form in the edit page the value of layout of the diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/dot-form.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/dot-form.tsx index a713b4acb31f..9b333fabd4a2 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/dot-form.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-form/dot-form.tsx @@ -32,11 +32,11 @@ const SUBMIT_FORM_API_URL = '/api/v1/workflow/actions/default/fire/NEW'; }) export class DotFormComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** (optional) List of fields (variableName) separated by comma, to be shown */ @Prop() - fieldsToShow: string; + fieldsToShow?: string; /** (optional) Text to be rendered on Reset button */ @Prop({ reflect: true }) @@ -65,7 +65,7 @@ export class DotFormComponent { /**Emit when submit the form */ @Event() - submit: EventEmitter; + submit!: EventEmitter; private fieldsStatus: { [key: string]: { [key: string]: boolean } } = {}; private value = {}; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-input-calendar/dot-input-calendar.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-input-calendar/dot-input-calendar.tsx index 65d6180ef1b3..978c05884cd8 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-input-calendar/dot-input-calendar.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-input-calendar/dot-input-calendar.tsx @@ -9,7 +9,7 @@ import { getErrorClass, getId, getOriginalStatus, updateStatus } from '../../../ }) export class DotInputCalendarComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value specifies the value of the input element */ @Prop({ mutable: true, reflect: true }) @@ -44,11 +44,11 @@ export class DotInputCalendarComponent { type = ''; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - _dotValueChange: EventEmitter; + _dotValueChange!: EventEmitter; @Event() - _dotStatusChange: EventEmitter; + _dotStatusChange!: EventEmitter; /** * Reset properties of the field, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-form/key-value-form.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-form/key-value-form.tsx index f32c0a5ac105..4778736075a2 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-form/key-value-form.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-form/key-value-form.tsx @@ -9,7 +9,7 @@ const DEFAULT_VALUE = { key: '', value: '' }; }) export class DotKeyValueComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** (optional) Disables all form interaction */ @Prop({ reflect: true }) @@ -59,15 +59,15 @@ export class DotKeyValueComponent { /** Emit the added value, key/value pair */ @Event() - add: EventEmitter; + add!: EventEmitter; /** Emit when key is changed */ @Event() - keyChanged: EventEmitter; + keyChanged!: EventEmitter; /** Emit when any of the input is blur */ @Event() - lostFocus: EventEmitter; + lostFocus!: EventEmitter; @State() inputs: DotKeyValueField = { ...DEFAULT_VALUE }; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-table/key-value-table.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-table/key-value-table.tsx index 5ff0e9667470..e2eb46c35465 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-table/key-value-table.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/components/key-value-table/key-value-table.tsx @@ -7,7 +7,7 @@ import { DotKeyValueField } from '../../../../../models'; }) export class KeyValueTableComponent { /** to get the current element */ - @Element() el: HTMLElement; + @Element() el!: HTMLElement; /** (optional) Items to render in the list of key value */ @Prop() @@ -31,11 +31,11 @@ export class KeyValueTableComponent { /** Emit the index of the item deleted from the list */ @Event() - delete: EventEmitter; + delete!: EventEmitter; /** Emit the notification of list reordered */ @Event() - reorder: EventEmitter; + reorder!: EventEmitter; dragSrcEl = null; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/dot-key-value.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/dot-key-value.tsx index d772a932e08e..c5d991fe4d20 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/dot-key-value.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-key-value/dot-key-value.tsx @@ -43,7 +43,7 @@ const mapToKeyValue = ({ label, value }: DotOption) => { }) export class DotKeyValueComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value of the field */ @Prop({ reflect: true, mutable: true }) @@ -101,61 +101,61 @@ export class DotKeyValueComponent { @Prop({ reflect: true }) - formKeyPlaceholder: string; + formKeyPlaceholder?: string; /** (optional) Placeholder for the value input text in the key-value-form */ @Prop({ reflect: true }) - formValuePlaceholder: string; + formValuePlaceholder?: string; /** (optional) The string to use in the key label in the key-value-form */ @Prop({ reflect: true }) - formKeyLabel: string; + formKeyLabel?: string; /** (optional) The string to use in the value label in the key-value-form */ @Prop({ reflect: true }) - formValueLabel: string; + formValueLabel?: string; /** (optional) Label for the add button in the key-value-form */ @Prop({ reflect: true }) - formAddButtonLabel: string; + formAddButtonLabel?: string; /** (optional) The string to use in the delete button of a key/value item */ @Prop({ reflect: true }) - listDeleteLabel: string; + listDeleteLabel?: string; /** (optional) The string to use in the empty option of whitelist dropdown key/value item */ @Prop({ reflect: true }) - whiteListEmptyOptionLabel: string; + whiteListEmptyOptionLabel?: string; /** (optional) The string containing the value to be parsed for whitelist key/value */ @Prop({ reflect: true }) - whiteList: string; + whiteList?: string; @State() - errorExistingKey: boolean; + errorExistingKey!: boolean; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @State() items: DotKeyValueField[] = []; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; @Watch('value') valueWatch(): void { diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-multi-select/dot-multi-select.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-multi-select/dot-multi-select.tsx index a83d2047c771..dfd530b8e4f8 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-multi-select/dot-multi-select.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-multi-select/dot-multi-select.tsx @@ -42,7 +42,7 @@ import { getDotAttributesFromElement, setDotAttributesToElement } from '../dot-f }) export class DotMultiSelectComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value set from the dropdown option */ @Prop({ mutable: true, reflect: true }) @@ -81,14 +81,14 @@ export class DotMultiSelectComponent { size = '3'; @State() - _options: DotOption[]; + _options!: DotOption[]; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; _dotTouched = false; _dotPristine = true; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-radio/dot-radio.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-radio/dot-radio.tsx index 82142f325137..478274c61606 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-radio/dot-radio.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-radio/dot-radio.tsx @@ -42,7 +42,7 @@ import { getDotAttributesFromElement, setDotAttributesToElement } from '../dot-f }) export class DotRadioComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value set from the ratio option */ @Prop({ mutable: true, reflect: true }) @@ -77,14 +77,14 @@ export class DotRadioComponent { options = ''; @State() - _options: DotOption[]; + _options!: DotOption[]; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; /** * Reset properties of the field, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-select/dot-select.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-select/dot-select.tsx index 3a592c1fd229..ec4e60602cdf 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-select/dot-select.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-select/dot-select.tsx @@ -42,7 +42,7 @@ import { getDotAttributesFromElement, setDotAttributesToElement } from '../dot-f }) export class DotSelectComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value set from the dropdown option */ @Prop({ mutable: true, reflect: true }) @@ -77,14 +77,14 @@ export class DotSelectComponent { disabled = false; @State() - _options: DotOption[]; + _options!: DotOption[]; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; _dotTouched = false; _dotPristine = true; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-autocomplete/dot-autocomplete.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-autocomplete/dot-autocomplete.tsx index 65d8f641ca93..41de9894c6e3 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-autocomplete/dot-autocomplete.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-autocomplete/dot-autocomplete.tsx @@ -21,7 +21,7 @@ export interface SelectionFeedback { }) export class DotAutocompleteComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** (optional) Disables field's interaction */ @Prop({ reflect: true }) @@ -48,11 +48,11 @@ export class DotAutocompleteComponent { data: () => Promise | string[] = null; @Event() - selection: EventEmitter; + selection!: EventEmitter; @Event() - enter: EventEmitter; + enter!: EventEmitter; @Event() - lostFocus: EventEmitter; + lostFocus!: EventEmitter; private readonly id = `autoComplete${new Date().getTime()}`; private enteredSuggestionList = false; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-chip/dot-chip.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-chip/dot-chip.tsx index 4b28a916cd11..b3bf999a21db 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-chip/dot-chip.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/components/dot-chip/dot-chip.tsx @@ -6,7 +6,7 @@ import { Component, Prop, Element, Event, EventEmitter, h, Host } from '@stencil }) export class DotChipComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Chip's label */ @Prop({ reflect: true }) @@ -21,7 +21,7 @@ export class DotChipComponent { disabled = false; @Event() - remove: EventEmitter; + remove!: EventEmitter; render() { const label = this.label ? `${this.deleteLabel} ${this.label}` : null; diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/dot-tags.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/dot-tags.tsx index bc20137b96ef..e0062236ff56 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/dot-tags.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-tags/dot-tags.tsx @@ -30,7 +30,7 @@ import { SelectionFeedback } from './components/dot-autocomplete/dot-autocomplet }) export class DotTagsComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value formatted splitted with a comma, for example: tag-1,tag-2 */ @Prop({ mutable: true, reflect: true }) @@ -77,12 +77,12 @@ export class DotTagsComponent { data: () => Promise | string[] = null; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; /** * Reset properties of the filed, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textarea/dot-textarea.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textarea/dot-textarea.tsx index 97bb0af1f5b3..1476abc74bf4 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textarea/dot-textarea.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textarea/dot-textarea.tsx @@ -36,7 +36,7 @@ import { setDotAttributesToElement, getDotAttributesFromElement } from '../dot-f }) export class DotTextareaComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value specifies the value of the textarea element */ @Prop({ mutable: true, reflect: true }) @@ -75,13 +75,13 @@ export class DotTextareaComponent { regexCheck = ''; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; /** * Reset properties of the field, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textfield/dot-textfield.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textfield/dot-textfield.tsx index 8a17f686f809..4c8c4622b0dd 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textfield/dot-textfield.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-textfield/dot-textfield.tsx @@ -36,7 +36,7 @@ import { setDotAttributesToElement, getDotAttributesFromElement } from '../dot-f }) export class DotTextfieldComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value specifies the value of the input element */ @Prop({ mutable: true }) @@ -83,12 +83,12 @@ export class DotTextfieldComponent { type = 'text'; @State() - status: DotFieldStatus; + status!: DotFieldStatus; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; /** * Reset properties of the field, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-time/dot-time.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-time/dot-time.tsx index 8bb0ee74e144..bc521f533df6 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-time/dot-time.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-time/dot-time.tsx @@ -27,7 +27,7 @@ import { setDotAttributesToElement, getDotAttributesFromElement } from '../dot-f }) export class DotTimeComponent { @Element() - el: HTMLElement; + el!: HTMLElement; /** Value format hh:mm:ss e.g., 15:22:00 */ @Prop({ mutable: true, reflect: true }) @@ -74,14 +74,14 @@ export class DotTimeComponent { step = '1'; @State() - classNames: DotFieldStatusClasses; + classNames!: DotFieldStatusClasses; @State() errorMessageElement: any; @Event() - dotValueChange: EventEmitter; + dotValueChange!: EventEmitter; @Event() - dotStatusChange: EventEmitter; + dotStatusChange!: EventEmitter; /** * Reset properties of the field, clear value and emit events. diff --git a/core-web/libs/dotcms-webcomponents/src/components/dot-asset-drop-zone/dot-asset-drop-zone.tsx b/core-web/libs/dotcms-webcomponents/src/components/dot-asset-drop-zone/dot-asset-drop-zone.tsx index 754228bb8e2e..c4704f710a8a 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/dot-asset-drop-zone/dot-asset-drop-zone.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/dot-asset-drop-zone/dot-asset-drop-zone.tsx @@ -68,7 +68,7 @@ export class DotAssetDropZone { @Prop() typesErrorLabel: string = 'This action only allows $0 files.'; /* custom function to upload files */ - @Prop() customUploadFiles: (props: { + @Prop() customUploadFiles?: (props: { files: File[]; onSuccess: () => void; updateProgress: (progress: number) => void; @@ -76,7 +76,7 @@ export class DotAssetDropZone { }) => Promise; /** Emit an array of Contentlets just created or array of errors */ - @Event() uploadComplete: EventEmitter; + @Event() uploadComplete!: EventEmitter; @State() dropState: DotDropStatus = DotDropStatus.NONE; @State() progressIndicator = 0; diff --git a/core-web/libs/dotcms-webcomponents/src/components/dot-card-contentlet/dot-card-contentlet.tsx b/core-web/libs/dotcms-webcomponents/src/components/dot-card-contentlet/dot-card-contentlet.tsx index 668ecc8d1b66..45529c5e6c6a 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/dot-card-contentlet/dot-card-contentlet.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/dot-card-contentlet/dot-card-contentlet.tsx @@ -16,9 +16,9 @@ import { DotContentState } from '@dotcms/dotcms-models'; shadow: true }) export class DotCardContentlet { - @Element() el: HTMLDotCardContentletElement; + @Element() el!: HTMLDotCardContentletElement; - @Prop() item: DotCardContentletItem; + @Prop() item?: DotCardContentletItem; @Prop() thumbnailSize = '260'; @Prop() iconSize = '96px'; @@ -27,12 +27,12 @@ export class DotCardContentlet { reflect: true, mutable: true }) - checked: boolean; + checked?: boolean; @Prop() showVideoThumbnail = false; - @Event() checkboxChange: EventEmitter; - @Event() contextMenuClick: EventEmitter; + @Event() checkboxChange!: EventEmitter; + @Event() contextMenuClick!: EventEmitter; private menu: HTMLDotContextMenuElement; diff --git a/core-web/libs/dotcms-webcomponents/src/components/dot-context-menu/dot-context-menu.tsx b/core-web/libs/dotcms-webcomponents/src/components/dot-context-menu/dot-context-menu.tsx index 1fc8c7082b43..396de386b7fb 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/dot-context-menu/dot-context-menu.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/dot-context-menu/dot-context-menu.tsx @@ -12,7 +12,7 @@ import { DotContextMenuAction } from '../../models/dot-context-menu-action.model shadow: true }) export class DotContextMenu { - @Element() el: HTMLElement; + @Element() el!: HTMLElement; @Prop() options: DotContextMenuOption[] = []; @Prop() fontSize = '16px'; diff --git a/core-web/libs/dotcms-webcomponents/src/components/dot-data-view-button/dot-data-view-button.tsx b/core-web/libs/dotcms-webcomponents/src/components/dot-data-view-button/dot-data-view-button.tsx index eea48a82720d..dbd0a1dbcf50 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/dot-data-view-button/dot-data-view-button.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/dot-data-view-button/dot-data-view-button.tsx @@ -5,7 +5,7 @@ import { Component, h, Prop } from '@stencil/core'; styleUrl: 'dot-data-view-button.css' }) export class DotDataViewButton { - @Prop() value: string; + @Prop() value?: string; render() { return ( diff --git a/core-web/libs/dotcms-webcomponents/src/components/dot-html-to-image/dot-html-to-image.tsx b/core-web/libs/dotcms-webcomponents/src/components/dot-html-to-image/dot-html-to-image.tsx index b625683c8861..0f12df7f442b 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/dot-html-to-image/dot-html-to-image.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/dot-html-to-image/dot-html-to-image.tsx @@ -21,11 +21,11 @@ export class DotHtmlToImage { @Prop({ reflect: false, mutable: true }) width = ''; - @Event() pageThumbnail: EventEmitter<{ + @Event() pageThumbnail!: EventEmitter<{ file: File; error?: string; }>; - @State() previewImg: string; + @State() previewImg!: string; boundOnMessageHandler = null; iframeId = `iframe_${Math.floor(Date.now() / 1000).toString()}`; diff --git a/core-web/libs/dotcms-webcomponents/src/components/dot-select-button/dot-select-button.tsx b/core-web/libs/dotcms-webcomponents/src/components/dot-select-button/dot-select-button.tsx index 40f8880f54a8..549458e4a195 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/dot-select-button/dot-select-button.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/dot-select-button/dot-select-button.tsx @@ -14,7 +14,7 @@ export class DotSelectButton { @Prop({ reflect: true }) options: DotSelectButtonOption[] = []; - @Event() selected: EventEmitter; + @Event() selected!: EventEmitter; render() { return ( diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-icon/dot-contentlet-icon.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-icon/dot-contentlet-icon.tsx index 7dd904125e3e..e7130f01322d 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-icon/dot-contentlet-icon.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-icon/dot-contentlet-icon.tsx @@ -124,7 +124,7 @@ export class DotContentletIcon { @Prop({ reflect: true }) size = ''; - private ext: string; + private ext?: string; componentWillRender() { /* diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-lock-icon/dot-contentlet-lock-icon.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-lock-icon/dot-contentlet-lock-icon.tsx index b60c102404da..4490aaeda4c4 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-lock-icon/dot-contentlet-lock-icon.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-lock-icon/dot-contentlet-lock-icon.tsx @@ -7,7 +7,7 @@ import '@material/mwc-icon'; shadow: true }) export class DotContentletLockIcon { - @Prop() locked: boolean; + @Prop() locked?: boolean; @Prop() size = '16px'; render() { diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-thumbnail/dot-contentlet-thumbnail.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-thumbnail/dot-contentlet-thumbnail.tsx index 0bd228d800f0..0d474064394b 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-thumbnail/dot-contentlet-thumbnail.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-contentlet-thumbnail/dot-contentlet-thumbnail.tsx @@ -41,13 +41,13 @@ export class DotContentletThumbnail { playableVideo = false; @Prop() - contentlet: DotContentletItem; + contentlet?: DotContentletItem; @Prop({ reflect: true }) fieldVariable = ''; - @State() renderImage: boolean; - @State() isSVG: boolean; + @State() renderImage!: boolean; + @State() isSVG!: boolean; componentWillLoad() { const { hasTitleImage, mimeType } = this.contentlet; diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx index e6fba2d69018..4f60223d3efb 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx @@ -17,11 +17,11 @@ import '@material/mwc-icon'; styleUrl: 'dot-material-icon-picker.scss' }) export class DotMaterialIcon { - @Element() element: HTMLElement; + @Element() element!: HTMLElement; - @State() showSuggestions: boolean; + @State() showSuggestions!: boolean; @State() suggestionArr: string[] = []; - @State() selectedSuggestionIndex: number; + @State() selectedSuggestionIndex!: number; /** Value for input placeholder */ @Prop({ reflect: true }) placeholder: string = ''; @@ -52,7 +52,7 @@ export class DotMaterialIcon { @Prop({ reflect: true }) suggestionlist: string[] = MaterialIconClasses; @Event() - dotValueChange: EventEmitter<{ name: string; value: string; colorValue: string }>; + dotValueChange!: EventEmitter<{ name: string; value: string; colorValue: string }>; @Listen('click', { target: 'window' }) handleWindowClick(e: Event) { diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-tooltip/dot-tooltip.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-tooltip/dot-tooltip.tsx index a53e90e5a0fd..8ed3ebed2054 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-tooltip/dot-tooltip.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-tooltip/dot-tooltip.tsx @@ -7,15 +7,15 @@ import { getElement, getPosition, PositionX, PositionY, fadeIn } from './utils'; shadow: true }) export class DotTooltip { - @Element() el: HTMLElement; + @Element() el!: HTMLElement; - @Prop() content: string; - @Prop() for: string; - @Prop() delay: number; + @Prop() content?: string; + @Prop() for?: string; + @Prop() delay?: number; @Prop() position = 'center bottom'; - private targetEl: HTMLElement; - private tooltipEl: HTMLElement; + private targetEl?: HTMLElement; + private tooltipEl?: HTMLElement; private showing = false; connectedCallback() { diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-video-thumbnail/dot-video-thumbnail.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-video-thumbnail/dot-video-thumbnail.tsx index 605085f53d7e..fec1b5a20ab1 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-video-thumbnail/dot-video-thumbnail.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-video-thumbnail/dot-video-thumbnail.tsx @@ -14,7 +14,7 @@ export class DotVideoThumbnail { * @type {DotContentletItem} * @memberof DotVideoThumbnail */ - @Prop() contentlet: DotContentletItem; + @Prop() contentlet?: DotContentletItem; /** * @@ -22,7 +22,7 @@ export class DotVideoThumbnail { * @type {string} * @memberof variable */ - @Prop() variable: string; + @Prop() variable?: string; /** * If the video is playable or not. diff --git a/core-web/libs/dotcms-webcomponents/tsconfig.json b/core-web/libs/dotcms-webcomponents/tsconfig.json index 6c3064585af2..2e4fff2bc872 100644 --- a/core-web/libs/dotcms-webcomponents/tsconfig.json +++ b/core-web/libs/dotcms-webcomponents/tsconfig.json @@ -8,6 +8,15 @@ "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "declaration": false, + // Strict mode (#35943) is not enabled yet — 274 type errors remain across 42 files, and + // Stencil type-checks during `build`, so turning it on before they are fixed breaks CI. + // The decorator members in src/ are already prepared for it. + // + // Do NOT add `"ignoreDeprecations": "6.0"` here: Stencil bundles TypeScript 5.8.3, which + // only accepts "5.0" and fails the build with `Invalid value for '--ignoreDeprecations'`. + // The repo's own tsc is 6.0.3 and needs "6.0" to look past the deprecated `baseUrl` / + // `moduleResolution` below, so pass it on the CLI when type-checking this project: + // pnpm exec tsc -p libs/dotcms-webcomponents/tsconfig.json --noEmit --ignoreDeprecations 6.0 --strict "esModuleInterop": true, "experimentalDecorators": true, "lib": ["dom", "es2015"], From 0117273504903510ab5127a5ef47232b070a658d Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Mon, 10 Aug 2026 11:13:32 -0400 Subject: [PATCH 10/13] fix(dotcms-webcomponents): correct the value prop type on dot-binary-text-field `5ea14e8cf8` typed `dot-binary-text-field`'s `value` prop as `string | null`. That was wrong and broke the Stencil build: `handleFilePaste` assigns a `File` to it (line 105), alongside the strings assigned elsewhere. Corrected to `string | File | null`. The error was missed because the verification builds were reading Stencil's `.stencil` cache. `nx run :build --skip-nx-cache` skips only the Nx cache, not Stencil's own, so a build can report green against stale output. Delete `libs/dotcms-webcomponents/.stencil` before trusting a result. Verified with both caches cleared (`.stencil` removed and `nx reset`): `nx run dotcms-webcomponents:build` green, `dotcms-ui` typechecks with 0 errors, `nx format:check` green. Refs #35943 Co-Authored-By: Claude Opus 5 (1M context) --- .../dotcms-webcomponents/src/components.d.ts | 8 ++++---- .../dot-binary-text-field.tsx | 7 +++++-- .../components/dot-binary-text-field/readme.md | 16 ++++++++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core-web/libs/dotcms-webcomponents/src/components.d.ts b/core-web/libs/dotcms-webcomponents/src/components.d.ts index 848a116a0a0a..5c808b1b92cc 100644 --- a/core-web/libs/dotcms-webcomponents/src/components.d.ts +++ b/core-web/libs/dotcms-webcomponents/src/components.d.ts @@ -290,10 +290,10 @@ export namespace Components { */ "required": boolean; /** - * Value specifies the value of the element + * Value specifies the value of the element. Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. * @default null */ - "value": string | null; + "value": string | File | null; } /** * Represent a dotcms text field for the binary file element. @@ -2606,10 +2606,10 @@ declare namespace LocalJSX { */ "required"?: boolean; /** - * Value specifies the value of the element + * Value specifies the value of the element. Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. * @default null */ - "value"?: string | null; + "value"?: string | File | null; } /** * Represent a dotcms text field for the binary file element. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx index 67879c1e5662..f235c685ad53 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx @@ -16,9 +16,12 @@ export class DotBinaryTextFieldComponent { @Element() el!: HTMLElement; - /** Value specifies the value of the element */ + /** + * Value specifies the value of the element. + * Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. + */ @Prop({ mutable: true, reflect: true }) - value: string | null = null; + value: string | File | null = null; /** (optional) Hint text that suggest a clue of the field */ @Prop({ reflect: true }) diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md index ffa210b41119..e6f27bafe528 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md @@ -9,14 +9,14 @@ Represent a dotcms text field for the binary file element. ## Properties -| Property | Attribute | Description | Type | Default | -| ------------- | ------------- | ------------------------------------------------------------------------------------------------------- | --------- | ----------- | -| `accept` | `accept` | (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg | `string` | `undefined` | -| `disabled` | `disabled` | (optional) Disables field's interaction | `boolean` | `false` | -| `hint` | `hint` | (optional) Hint text that suggest a clue of the field | `string` | `''` | -| `placeholder` | `placeholder` | (optional) Placeholder specifies a short hint that describes the expected value of the input field | `string` | `''` | -| `required` | `required` | (optional) Determine if it is mandatory | `boolean` | `false` | -| `value` | `value` | Value specifies the value of the element | `string` | `null` | +| Property | Attribute | Description | Type | Default | +| ------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------- | +| `accept` | `accept` | (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg | `string` | `undefined` | +| `disabled` | `disabled` | (optional) Disables field's interaction | `boolean` | `false` | +| `hint` | `hint` | (optional) Hint text that suggest a clue of the field | `string` | `''` | +| `placeholder` | `placeholder` | (optional) Placeholder specifies a short hint that describes the expected value of the input field | `string` | `''` | +| `required` | `required` | (optional) Determine if it is mandatory | `boolean` | `false` | +| `value` | `value` | Value specifies the value of the element. Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. | `File \| string` | `null` | ## Events From f22afce3837438a32c6a93d06b07ab38bd55c30d Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Mon, 10 Aug 2026 11:28:38 -0400 Subject: [PATCH 11/13] fix(dotcms-webcomponents): revert the value prop annotation on dot-binary-text-field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both `5ea14e8cf8` (`string | null`) and `0117273504` (`string | File | null`) were wrong, and the second broke the Stencil build. The prop is genuinely contradictory at runtime and `any` was hiding it: `handleFilePaste` assigns a `File` to it (line 105), other paths assign strings, and the template passes it straight to an ``, which accepts `string | number | string[]` and therefore neither. No annotation describes the current code correctly — the render path has to be fixed first, which belongs to the strict-mode work rather than to this groundwork. Reverted to the original untyped `= null` and left a TODO(#35943) recording the contradiction so the next person does not re-annotate it and hit the same wall. Verified green on two consecutive builds with both `.stencil` and the Nx cache cleared. The earlier green readings that let this through were stale Stencil cache: `--skip-nx-cache` does not clear `libs/dotcms-webcomponents/.stencil`. Refs #35943 Co-Authored-By: Claude Opus 5 (1M context) --- .../dotcms-webcomponents/src/components.d.ts | 8 ++++---- .../dot-binary-text-field.tsx | 8 ++++++-- .../components/dot-binary-text-field/readme.md | 16 ++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core-web/libs/dotcms-webcomponents/src/components.d.ts b/core-web/libs/dotcms-webcomponents/src/components.d.ts index 5c808b1b92cc..48fba264e7db 100644 --- a/core-web/libs/dotcms-webcomponents/src/components.d.ts +++ b/core-web/libs/dotcms-webcomponents/src/components.d.ts @@ -290,10 +290,10 @@ export namespace Components { */ "required": boolean; /** - * Value specifies the value of the element. Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. + * Value specifies the value of the element. TODO(#35943): deliberately left untyped. At runtime this holds a string (pasted URL) but `handleFilePaste` also assigns a `File`, while the template feeds it to an `` that accepts neither. Annotating it surfaces that contradiction, which needs the render path fixed — that belongs to the strict-mode work, not here. * @default null */ - "value": string | File | null; + "value": any; } /** * Represent a dotcms text field for the binary file element. @@ -2606,10 +2606,10 @@ declare namespace LocalJSX { */ "required"?: boolean; /** - * Value specifies the value of the element. Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. + * Value specifies the value of the element. TODO(#35943): deliberately left untyped. At runtime this holds a string (pasted URL) but `handleFilePaste` also assigns a `File`, while the template feeds it to an `` that accepts neither. Annotating it surfaces that contradiction, which needs the render path fixed — that belongs to the strict-mode work, not here. * @default null */ - "value"?: string | File | null; + "value"?: any; } /** * Represent a dotcms text field for the binary file element. diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx index f235c685ad53..3566b67df0ed 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/dot-binary-text-field.tsx @@ -18,10 +18,14 @@ export class DotBinaryTextFieldComponent { /** * Value specifies the value of the element. - * Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. + * + * TODO(#35943): deliberately left untyped. At runtime this holds a string (pasted URL) but + * `handleFilePaste` also assigns a `File`, while the template feeds it to an `` + * that accepts neither. Annotating it surfaces that contradiction, which needs the render + * path fixed — that belongs to the strict-mode work, not here. */ @Prop({ mutable: true, reflect: true }) - value: string | File | null = null; + value = null; /** (optional) Hint text that suggest a clue of the field */ @Prop({ reflect: true }) diff --git a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md index e6f27bafe528..c67117bf607e 100644 --- a/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md +++ b/core-web/libs/dotcms-webcomponents/src/components/contenttypes-fields/dot-binary-file/components/dot-binary-text-field/readme.md @@ -9,14 +9,14 @@ Represent a dotcms text field for the binary file element. ## Properties -| Property | Attribute | Description | Type | Default | -| ------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------- | -| `accept` | `accept` | (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg | `string` | `undefined` | -| `disabled` | `disabled` | (optional) Disables field's interaction | `boolean` | `false` | -| `hint` | `hint` | (optional) Hint text that suggest a clue of the field | `string` | `''` | -| `placeholder` | `placeholder` | (optional) Placeholder specifies a short hint that describes the expected value of the input field | `string` | `''` | -| `required` | `required` | (optional) Determine if it is mandatory | `boolean` | `false` | -| `value` | `value` | Value specifies the value of the element. Holds a `File` when one is pasted from the clipboard, a string when a URL is pasted. | `File \| string` | `null` | +| Property | Attribute | Description | Type | Default | +| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- | +| `accept` | `accept` | (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg | `string` | `undefined` | +| `disabled` | `disabled` | (optional) Disables field's interaction | `boolean` | `false` | +| `hint` | `hint` | (optional) Hint text that suggest a clue of the field | `string` | `''` | +| `placeholder` | `placeholder` | (optional) Placeholder specifies a short hint that describes the expected value of the input field | `string` | `''` | +| `required` | `required` | (optional) Determine if it is mandatory | `boolean` | `false` | +| `value` | `value` | Value specifies the value of the element. TODO(#35943): deliberately left untyped. At runtime this holds a string (pasted URL) but `handleFilePaste` also assigns a `File`, while the template feeds it to an `` that accepts neither. Annotating it surfaces that contradiction, which needs the render path fixed — that belongs to the strict-mode work, not here. | `any` | `null` | ## Events From 6d45c0722ec96094049ca04444dcb510bfc756d0 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Mon, 10 Aug 2026 11:45:08 -0400 Subject: [PATCH 12/13] fix(utils-testing): make the declared strict mode actually apply #35944 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `libs/utils-testing/tsconfig.json` has carried all six strict flags (plus `strictTemplates`) for some time, but they were inert: `tsconfig.lib.json` declared `"types": ["jasmine"]` and that package is not installed, so tsc emitted `TS2688: Cannot find type definition file for 'jasmine'` and **stopped before semantic checking**. The project reported exactly one error no matter what the code did. The reference is stale — nothing here uses jasmine, two files use `jest.*` APIs, and `@types/jest` is installed. Changed to `"types": ["jest"]`, which both removes the abort and drops 27 spurious `Cannot find name 'jest'` errors, leaving 5 real ones: - `clean-up-dialog.ts` — untyped `fixture` param. Typed structurally as `{ nativeElement: unknown }` rather than importing Angular's `ComponentFixture`, since only that one property is touched. - `dot-page-state.service.mock.ts` — `_lock: boolean = null`, now `boolean | null`. - `dot-page-tools.mock.ts` — three mock entries carried a `tags` array that `DotPageTool` does not declare. Nothing reads `.tags` off a page tool anywhere in the repo, so the dead field was removed rather than added to the model in `dotcms-models`. `tsc -p libs/utils-testing/tsconfig.lib.json --noEmit` now exits 0 with no CLI overrides — the check is real rather than short-circuited. Verified across the consumers of the touched mocks (`cleanUpDialog` in 7 files, the page-tools mock in 3): `data-access` 751 passed, `edit-ema-ui` 338 passed, `dotcms-ui` typechecks with 0 errors, `nx format:check` green. Note this project still has no build target and is tagged `skip:test` / `skip:lint`, so nothing in CI runs this check — the same gap recorded for `dotcms-js` (#35939) and `utils` (#35940). Closes #35944 Co-Authored-By: Claude Opus 5 (1M context) --- core-web/libs/utils-testing/src/lib/clean-up-dialog.ts | 2 +- .../libs/utils-testing/src/lib/dot-page-state.service.mock.ts | 2 +- core-web/libs/utils-testing/src/lib/dot-page-tools.mock.ts | 3 --- core-web/libs/utils-testing/tsconfig.lib.json | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/core-web/libs/utils-testing/src/lib/clean-up-dialog.ts b/core-web/libs/utils-testing/src/lib/clean-up-dialog.ts index c824e53e1570..84d8b0d0e8ce 100644 --- a/core-web/libs/utils-testing/src/lib/clean-up-dialog.ts +++ b/core-web/libs/utils-testing/src/lib/clean-up-dialog.ts @@ -1,4 +1,4 @@ -export function cleanUpDialog(fixture) { +export function cleanUpDialog(fixture: { nativeElement: unknown }) { try { (fixture.nativeElement as HTMLElement).remove(); } catch { diff --git a/core-web/libs/utils-testing/src/lib/dot-page-state.service.mock.ts b/core-web/libs/utils-testing/src/lib/dot-page-state.service.mock.ts index 5977cb22f217..4377633c7bf8 100644 --- a/core-web/libs/utils-testing/src/lib/dot-page-state.service.mock.ts +++ b/core-web/libs/utils-testing/src/lib/dot-page-state.service.mock.ts @@ -19,7 +19,7 @@ export class DotPageStateServiceMock { return of(mockDotRenderedPageState); } - setLock(_options: DotPageRenderOptions, _lock: boolean = null): void { + setLock(_options: DotPageRenderOptions, _lock: boolean | null = null): void { /* */ } diff --git a/core-web/libs/utils-testing/src/lib/dot-page-tools.mock.ts b/core-web/libs/utils-testing/src/lib/dot-page-tools.mock.ts index e7b5b5519017..6081527da4de 100644 --- a/core-web/libs/utils-testing/src/lib/dot-page-tools.mock.ts +++ b/core-web/libs/utils-testing/src/lib/dot-page-tools.mock.ts @@ -7,7 +7,6 @@ export const mockPageTools: DotPageTools = { title: 'Wave', description: 'The WAVE® evaluation suite helps educate authors on how to make their web content more accessible to individuals with disabilities. WAVE can identify many accessibility and Web Content Accessibility Guideline (WCAG) errors, but also facilitates human evaluation of web content.', - tags: ['Accessibility', 'WCAG'], runnableLink: 'https://wave.webaim.org/report#/http://localhost/blogTest?host_id=123?language_id=1' }, @@ -16,7 +15,6 @@ export const mockPageTools: DotPageTools = { title: 'Mozilla Observatory', description: 'The Mozilla Observatory has helped hundreds of thousands of websites by teaching developers, system administrators, and security professionals how to configure their sites safely and securely. ', - tags: ['Security', 'Best Practices'], runnableLink: 'https://developer.mozilla.org/en-US/observatory/analyze?host=localhost' }, { @@ -24,7 +22,6 @@ export const mockPageTools: DotPageTools = { title: 'Security Headers', description: 'This tool is designed to help you better deploy and understand modern security features that are available for your website. It will provide a simple to understand grading system for how well your site follows best practices, as well as suggestions for how to make improvement.', - tags: ['Securty', 'Best Practices'], runnableLink: 'https://securityheaders.com/?q=http://localhost/blogTest&host_id=123&language_id=1&followRedirects=on' } diff --git a/core-web/libs/utils-testing/tsconfig.lib.json b/core-web/libs/utils-testing/tsconfig.lib.json index 86b82f4b2923..d2c22eefe058 100644 --- a/core-web/libs/utils-testing/tsconfig.lib.json +++ b/core-web/libs/utils-testing/tsconfig.lib.json @@ -5,7 +5,7 @@ "declaration": true, "declarationMap": true, "inlineSources": true, - "types": ["jasmine"] + "types": ["jest"] }, "exclude": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts"], "include": ["**/*.ts"] From 8f3c2b446c10c5ed2f932844b4a4cc3a6a7b4b70 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Mon, 10 Aug 2026 12:02:22 -0400 Subject: [PATCH 13/13] refactor(sdk-react): complete TypeScript strict mode #35945 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `strict: true` was already present; the five companion flags were not. Adding them surfaced 14 errors, all `TS4111` — dot access on a type carrying an index signature — resolved with bracket notation. Two origins, same fix: - 13 come from `node.attrs`, declared `Record` in `@dotcms/types`. That type is left alone: block editor attributes really are dynamic, and it lives in a layer-0 project whose consumers would all be affected. - 1 comes from CSS Modules (`styles.row` in `Row.tsx`), whose generated type is also a `Record`. No behaviour change — bracket access compiles to the same property lookup. Unlike `dotcms-js`, `utils` and `utils-testing`, the flags here are genuinely enforced. Proved rather than assumed: reverting one access to dot notation fails the build with `@rollup/plugin-typescript TS4111`, so TypeScript is in the Rollup chain. The project carries no `skip:` tags, so CI builds, lints and tests it on every PR, and the same build runs in the SDK release pipeline. One error remains under plain `tsc` and is expected: `Cannot find module 'virtual:sdk-version'` in `sdk-client`. It is a Vite virtual module that raw `tsc` cannot resolve but the build can; it predates this change and is unrelated to strict mode. Verified: `sdk-react` build, lint and test green; `sdk-experiments` (its only internal dependent) builds green; `nx format:check` green; no new `any`, `@ts-ignore` or `@ts-expect-error`. Closes #35945 Co-Authored-By: Claude Opus 5 (1M context) --- .../components/blocks/Code.tsx | 2 +- .../components/blocks/GridBlock.tsx | 2 +- .../components/blocks/Table.tsx | 12 ++++++------ .../components/blocks/Texts.tsx | 6 +++--- .../sdk/react/src/lib/next/components/Row/Row.tsx | 2 +- core-web/libs/sdk/react/tsconfig.json | 7 ++++++- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Code.tsx b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Code.tsx index 69a8810129fd..b1bbb865260f 100644 --- a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Code.tsx +++ b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Code.tsx @@ -13,7 +13,7 @@ interface CodeBlockProps { * @returns The rendered code block component. */ export const CodeBlock = ({ node, children }: CodeBlockProps) => { - const language = node?.attrs?.language || ''; + const language = node?.attrs?.['language'] || ''; return (
diff --git a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/GridBlock.tsx b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/GridBlock.tsx
index e24dd885a01c..1db1ec8076bf 100644
--- a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/GridBlock.tsx
+++ b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/GridBlock.tsx
@@ -20,7 +20,7 @@ interface GridBlockProps {
  */
 export const GridBlock = ({ node, blockEditorBlock, customRenderers }: GridBlockProps) => {
     const BlockEditorBlockComponent = blockEditorBlock;
-    const rawCols = Array.isArray(node.attrs?.columns) ? node.attrs.columns : [6, 6];
+    const rawCols = Array.isArray(node.attrs?.['columns']) ? node.attrs['columns'] : [6, 6];
     const cols =
         rawCols.length === 2 &&
         rawCols.every((v: unknown) => typeof v === 'number' && Number.isFinite(v))
diff --git a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Table.tsx b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Table.tsx
index 0a58108d4c50..da920b17ff4c 100644
--- a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Table.tsx
+++ b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Table.tsx
@@ -37,9 +37,9 @@ export const TableRenderer: React.FC = ({
         
     );
 
-    const caption: string | undefined = attrs?.caption || undefined;
-    const ariaLabel: string | undefined = attrs?.ariaLabel || undefined;
-    const ariaLabelledBy: string | undefined = attrs?.ariaLabelledby || undefined;
+    const caption: string | undefined = attrs?.['caption'] || undefined;
+    const ariaLabel: string | undefined = attrs?.['ariaLabel'] || undefined;
+    const ariaLabelledBy: string | undefined = attrs?.['ariaLabelledby'] || undefined;
 
     return (
         
@@ -48,8 +48,8 @@ export const TableRenderer: React.FC = ({
                 {content.map((rowNode, rowIndex) => (
                     
                         {rowNode.content?.map((cellNode, cellIndex) => {
-                            const colSpan = Number(cellNode.attrs?.colspan || 1);
-                            const rowSpan = Number(cellNode.attrs?.rowspan || 1);
+                            const colSpan = Number(cellNode.attrs?.['colspan'] || 1);
+                            const rowSpan = Number(cellNode.attrs?.['rowspan'] || 1);
                             // Cell type — not row index — decides th vs td. Matches the
                             // VTL renderer (storyblock/render.vtl).
                             if (cellNode.type === 'tableHeader') {
@@ -58,7 +58,7 @@ export const TableRenderer: React.FC = ({
                                         key={`cell-${cellIndex}`}
                                         colSpan={colSpan}
                                         rowSpan={rowSpan}
-                                        scope={cellNode.attrs?.scope || undefined}>
+                                        scope={cellNode.attrs?.['scope'] || undefined}>
                                         {renderCellContent(cellNode)}
                                     
                                 );
diff --git a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Texts.tsx b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Texts.tsx
index da0dda6fd429..fe3c6c747312 100644
--- a/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Texts.tsx
+++ b/core-web/libs/sdk/react/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Texts.tsx
@@ -75,7 +75,7 @@ export const Link = ({ children, attrs }: MarkProps) => {
  */
 export const Heading = ({ children, node }: TextComponentProp) => {
     const attrs = node?.attrs || {};
-    const level = attrs.level || 1;
+    const level = attrs['level'] || 1;
     const Tag = `h${level}` as keyof JSX.IntrinsicElements;
 
     return {children};
@@ -121,8 +121,8 @@ export const TextBlock = (props: TextNodeProps = {}) => {
 
     // In React, class is not a valid attribute name, so we need to rename it to className
     if (mark.attrs) {
-        mark.attrs.className = mark.attrs.class;
-        delete mark.attrs.class;
+        mark.attrs['className'] = mark.attrs['class'];
+        delete mark.attrs['class'];
     }
 
     if (!Component) {
diff --git a/core-web/libs/sdk/react/src/lib/next/components/Row/Row.tsx b/core-web/libs/sdk/react/src/lib/next/components/Row/Row.tsx
index 7a7ebe5b67ca..00779ac9119e 100644
--- a/core-web/libs/sdk/react/src/lib/next/components/Row/Row.tsx
+++ b/core-web/libs/sdk/react/src/lib/next/components/Row/Row.tsx
@@ -30,7 +30,7 @@ export const Row = ({ row, index }: DotCMSRowRendererProps) => {
 
     return (
         
-
+
{row.columns.map((column, index) => ( ))} diff --git a/core-web/libs/sdk/react/tsconfig.json b/core-web/libs/sdk/react/tsconfig.json index 8437b73a357d..12bb6caa5af3 100644 --- a/core-web/libs/sdk/react/tsconfig.json +++ b/core-web/libs/sdk/react/tsconfig.json @@ -4,7 +4,12 @@ "allowJs": false, "esModuleInterop": false, "allowSyntheticDefaultImports": true, - "strict": true + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true }, "files": [], "include": [],