Skip to content

feat: Add TypeScript infrastructure to client app#2218

Draft
wreality wants to merge 22 commits intomasterfrom
feature/client-typescript
Draft

feat: Add TypeScript infrastructure to client app#2218
wreality wants to merge 22 commits intomasterfrom
feature/client-typescript

Conversation

@wreality
Copy link
Contributor

@wreality wreality commented Feb 6, 2026

This pull request introduces TypeScript support and integrates GraphQL code generation into the client project. It also improves ESLint configuration for TypeScript, adds automation for GraphQL schema/codegen updates, and performs several file cleanups and conversions. The most significant changes are grouped below.

TypeScript Integration and Improvements:

  • Converted several files from JavaScript to TypeScript, including quasar.config.ts, src/boot/vue-apollo.ts, and src/boot/i18n.ts. This includes updating imports, type annotations, and bootstrapping code for better type safety. [1] [2] [3] [4]
  • Added TypeScript-related dev dependencies (typescript, typescript-eslint, vue-tsc) and updated the Quasar build configuration to support TypeScript. [1] [2]

GraphQL Code Generation Automation:

  • Added GraphQL codegen configuration (codegen.ts), scripts to fetch schema and generate types, and related dev dependencies. The .gitignore and ESLint ignore patterns were updated to exclude generated code. [1] [2] [3] [4] [5]
  • Integrated vite-plugin-graphql-codegen and a custom Vite plugin to automatically regenerate types on backend schema changes. [1] [2]

ESLint and Formatting Enhancements:

  • Improved ESLint configuration for TypeScript and Vue files, including specific rules for unused variables and test file patterns. Formatting script now includes .ts files. [1] [2] [3] [4]

Cleanup and Refactoring:

  • Removed unused or legacy files (src/boot/axios.js, src/boot/i18n.js, jsconfig.json) and updated imports to match new file extensions. [1] [2] [3] [4]
  • Refactored components and composables to use improved TypeScript-based i18n helpers and type annotations. [1] [2] [3] [4]

Apollo and Error Handling Improvements:

  • Improved error handling in Apollo links for XSRF token refresh and updated Apollo provider initialization for TypeScript. [1] [2]

These changes lay the groundwork for a more robust, type-safe, and maintainable frontend codebase with automated GraphQL type generation and improved developer tooling.

Set up TypeScript so the project compiles, new files can be written
in TS, and existing JS files continue to work via allowJs.

- Add typescript as a devDependency
- Rename jsconfig.json to tsconfig.json
- Rename quasar.config.js to quasar.config.ts with build.typescript config
- Update ESLint to lint .ts files instead of ignoring them
- Add .ts to prettier format and vite-plugin-checker patterns

Commit includes content generated with LLM assistance
Rename 86 .js files to .ts across source, composables, graphql, tiptap
extensions, router, apollo, and vitest test/utility files. Add
typescript-eslint parser to ESLint config for .ts file support. Apply
minimal type fixes: non-null assertion on getCurrentInstance() in
vQWrap, networkError type cast in apollo-links, and remove stale .js
import extensions.

Commit includes content generated with LLM assistance
Fix type errors in converted TS files: apollo-links, tiptap extensions,
router routes, userValidation, timeAgo, submission composable, and
test utilities. Update eslint and tsconfig for TS support.

Commit includes content generated with LLM assistance
Add lang="ts" to 27 Vue component script blocks and convert to
type-based props/emits syntax. Convert VQWrap and TagList from
Options API to script-setup. Fix bitwise OR operators to logical OR
in acceptMore computed properties. Fix Date subtraction to use
.getTime(). Update ESLint config to parse TypeScript in Vue SFCs.

Commit includes content generated with LLM assistance
The spec files were prematurely renamed to .ts in the non-component
conversion commit. Rename them back to .js since test migration is
a separate step that requires proper typing of the components first.

Commit includes content generated with LLM assistance
Declare $t on ComponentCustomProperties so vue-tsc recognizes it
in template expressions of TypeScript Vue components.

Commit includes content generated with LLM assistance
Commit includes content generated with LLM assistance
Remove pwa, cordova, capacitor, electron, and bex config blocks
that are not used by this project. Fixes the vue-tsc error caused
by the pwa.workboxMode casing mismatch.

Commit includes content generated with LLM assistance
…afety

Add reusable useI18nPrefix composable that provides pt (prefixed translate)
and pte (prefixed translate-exists) helpers with full ComposerTranslation
type passthrough. Refactor both AssignedPublicationUsers and
AssignedSubmissionUsers to use it, replacing inline tp$/tPrefix patterns.

Also add proper mutation variable interfaces and DocumentNode typing to
both components, replacing untyped useMutation calls.

Commit includes content generated with LLM assistance
…elect

- Add useI18nPrefix composable with pt/pte helpers for prefixed i18n
- Convert FindUserSelect to TypeScript with exported FoundUser interface
  and FindUserSelectValue type
- Refactor AssignedPublicationUsers and AssignedSubmissionUsers to use
  shared types and composable, replacing inline tp$/tPrefix patterns
- Add proper mutation variable interfaces with DocumentNode typing
- Pass narrowed types through function parameters instead of re-reading
  the union ref

Commit includes content generated with LLM assistance
Add @graphql-codegen/cli with typescript and typescript-operations
plugins to generate TypeScript types from the backend GraphQL schema
and the client's centrally-defined operations.

- Add codegen.ts config with introspection as primary schema source
- Add vite-plugin-graphql-codegen for automatic regeneration during dev
- Add custom watch-backend-schema Vite plugin to detect backend schema
  changes and re-run codegen automatically
- Add schema-ast plugin to keep committed schema.graphql in sync
- Builds use committed schema file (configOverrideOnBuild), no backend needed
- Add graphql:codegen, graphql:fetch-schema, graphql:codegen-offline scripts
- Fix 3 duplicate GraphQL operation names in mutations.ts
- Remove dead CREATE_SUBMISSION mutation (references non-existent field)
- Add developer documentation for backend and client GraphQL workflows
- Add generated/ directory to eslint ignores

Commit includes content generated with LLM assistance
@netlify
Copy link

netlify bot commented Feb 13, 2026

Deploy Preview for pilcrow-docs ready!

Name Link
🔨 Latest commit 675069f
🔍 Latest deploy log https://app.netlify.com/projects/pilcrow-docs/deploys/698f63bb0e358a0008a2c7ce
😎 Deploy Preview https://deploy-preview-2218--pilcrow-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Migrate all implicit $q template references to use the explicit
useQuasar() composable, improving type safety and Composition API
consistency. Also converts EmailVerificationBanner from Options API
to script setup.

Commit includes content generated with LLM assistance
…text composable

Replace string-keyed provide/inject for submission review state with typed
InjectionKey symbols and composable helpers. This ensures types are defined
once, provide sites always use Refs, and TypeScript catches mismatches.

- Create src/use/submissionContext.ts with typed keys and composables
- Update 5 provide sites to use provideSubmissionReviewContext() / submissionKey
- Update 13 inject sites to use useSubmission(), useActiveComment(), etc.
- Fix forExport .value access in OverallComment and InlineComment
- Update 4 test specs to use Symbol-based injection keys
- Clean up let → const for ref declarations

Commit includes content generated with LLM assistance
- PublicationStyleCriteria: pass both publication_id and id to delete mutation
- SubmissionContent: use querySelectorAll<HTMLElement> for dataset access
- StyleCriteriaForm: use $errors.some() instead of possibly-undefined maxLength
- EditableList: fix number|false usage in slice() and string/boolean comparison
- annotation.ts: convert addOptions from object literal to function

Commit includes content generated with LLM assistance
import FormActions from "../molecules/FormActions.vue"
import { useI18n } from "vue-i18n"
const { dirty, setError } = inject("formState")
const { dirty, setError } = inject("formState") as any
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Need to address this and type the formState injection.

default: () => ({})
const props = withDefaults(
defineProps<{
criteria?: Record<string, any>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is Record<string, any> the best we can do? We should import the GraphQL types that it should expect

import { computed, inject } from "vue"

const { state, errorMessage } = inject("formState")
const { state, errorMessage } = inject("formState") as any
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needs typing on formstae.


<component
:is="comment.new ? NewInlineComment : InlineComment"
:is="'new' in comment ? NewInlineComment : InlineComment"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

can't this use the isActiveCommentNew computed?

return comments
.filter((c) => {
return c.deleted_at === null || c.replies?.length > 0
return c.deleted_at === null || ("replies" in c && c.replies?.length > 0)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why isn't optional chaining ok here?

}
})
const props = defineProps<{
publication: Record<string, any>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should be using the graphql types here.

}
})
const props = defineProps<{
publication: Record<string, any>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needs graphql types;

import NotificationListItem from "src/components/atoms/NotificationListItem.vue"

const filterMode = ref(null)
const filterMode = ref<string | null>(null)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't this be refactored to ref<"Unread" | "Read" | null>?

})

let viewType = ref("review")
const viewType = ref("review")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Types would clarify what this ref is up to.

.flat()
.sort((a, b) => {
return new Date(b.updated_at) - new Date(a.updated_at)
return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We re-use this pattern multiple times. Should this get extracted into a utility?

Replace string-based formState provide/inject with typed InjectionKey,
use GraphQL generated types instead of Record<string, any>, add proper
types to tiptap annotation extension, extract date sort utility, and
convert NewPasswordInputMeter to script setup.

Commit includes content generated with LLM assistance
Extract inline type parameters from defineProps into named Props
interfaces for consistency and readability across all components.

Commit includes content generated with LLM assistance
Enforce type-based defineProps declarations over runtime options-object
style in Vue components.

Commit includes content generated with LLM assistance
Enable tseslint.configs.recommended via extends in ESLint config for
.ts, .vue, and test file blocks. Fix violations caught by new rules:
remove dead code (no-unused-expressions), replace Function type
(no-unsafe-function-type), fix var scoping (no-var/prefer-const), and
restructure appAuth.ts error handling. Downgrade no-explicit-any to
warn for incremental cleanup.

Commit includes content generated with LLM assistance
- Container props: use generated Publication/Submission GraphQL types
- VQInput/VQWrap: add VuelidateValidator interface (src/types/vuelidate.ts)
  for component props (BaseValidation is too complex for Vue's type resolution)
- Vuelidate rules props: use ValidationRuleCollection from @vuelidate/core
- NewPasswordInputAnalysis: define ZxcvbnComplexity interface for password
  complexity prop
- InlineComments: type commentRefs as InlineComment component instances
- FeedPage: Record<string, unknown> for query variables
- formState: Ref<Error | null> for mutationError
- userValidation: properly typed mutation/rules/variables options
- Dialog test mock: use unknown instead of any
- Remove no-explicit-any warn overrides from eslint config (now errors)

Commit includes content generated with LLM assistance
@wreality wreality force-pushed the feature/client-typescript branch from 4892e01 to 1dc0764 Compare February 14, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant