askr analyze performs workspace-aware static checks for current Askr APIs.
It uses the TypeScript compiler API with each selected workspace's
tsconfig.json, when present, and also scans JavaScript and TypeScript source
files that are not listed by that config.
askr analyze
askr analyze --workspace "@example/web"
askr analyze --workspace "apps-*" --workspace "shared-*"
askr analyze --check
askr analyze --json --check
askr analyze --cwd ./apps/webThe command discovers the containing npm or pnpm workspace. It scans the root
and every declared workspace by default. Repeat --workspace to select workspace
names with minimatch patterns.
Every diagnostic has a stable rule ID, category, severity, message, workspace, workspace-relative file, one-based line and column, and optional remediation. Output is sorted by workspace, file, position, rule ID, and message.
The analyzer resolves named aliases and namespace imports from
@askrjs/askr, @askrjs/ui, @askrjs/themes/components, and their supported
public subpaths. A same-named function imported from another package or local
module is not treated as an Askr API.
askr/parse-errorreports malformed source before other results can be considered complete.askr/no-hardcoded-theme-tokenreports--ak-*token names in runtime JavaScript and TypeScript string, template, and JSX attribute literals. The exact@askrjs/themesworkspace is exempt because it owns those declarations.askr/stable-render-callenforces stable top-level calls for state, derived values, selectors, resources, lifecycle operations, actions, queries, and mutations where the AST establishes a component render context.askr/stable-control-boundaryreportsFor,Show,Case, and statically resolveddefineScope()boundaries created conditionally during rendering.askr/render-scope-requiredreports render-owned primitives created in statically non-render callbacks such as handlers, timers, Promise continuations, and task bodies. It also reports module-scope or non-render calls toreadScope(),getSignal(),routeData(), andErrorBoundary();readScope()remains valid in aresource()loader.askr/exhaustive-dependenciescompares direct same-component reactive reads with literalresourceandstreamdependency arrays. Dynamic arrays, spreads, and uninvoked nested functions are deliberately skipped.askr/for-row-closure-capturereports direct reactive reads and one-hop snapshots captured by aForrow renderer. Function-valued JSX props remain valid.askr/state-accessreports state getters used without calling them and setters called without a value or updater.askr/state-render-writereports state mutation during the owning component's render while allowing updates in event callbacks.askr/resource-cancellationandaskr/data-cancellationreport fetch-based resource, query, and mutation loaders that do not forward their cancellation signal.askr/for-contractrequireseach, an item renderer, and exactly one ofbyorbyIndex.askr/control-contractvalidates requiredShowandMatchconditions and directCase/Matchstructure;Caseotherwise permits only null, false, and whitespace children.askr/stable-module-identityreportslazy()anddefineScope()created directly during a proven component render.askr/no-async-componentreports async JSX components.askr/route-registrykeeps route DSL calls inside a synchronouscreateRouteRegistry()definition.askr/route-path-syntaxmirrors the runtime's static path validation, including leading and duplicate slashes, complete{name}interpolation, non-empty unique parameter names, final named splats, and non-empty page paths.askr/route-scope-structurefollows named definitions and groups to report nested pages, duplicate page indexes, and absolute child routes. A proven child-route leading slash has a safe fix.askr/link-contractrequires one unambiguous destination, rejects the runtime's unsafe URL schemes, and checks staticto(routeRef, { ... })parameter objects against workspace route declarations. Dynamic objects and spreads are deliberately skipped. Relative links plushttp,https,mailto,tel, andsmsremain valid.askr/no-slot-style-overriderejects non-emptyclassandclassNameprops on fully themed floating-layer content and overlay components. Customize the owning theme tokens instead. Components with a spread are skipped because the analyzer cannot prove the final prop set.askr/block-layout-authority-conflictwarns whenBlockcombines a non-emptyclassorclassNamewithdirection,align,justify,gap, orwrap. Choose either the class or the component props as the element's flex-layout authority. Components with a spread are skipped.askr/query-key-contractreports directly provable nondeterministic and Symbol query key/scope parts; dynamic values are left alone.askr/import-subpathgroups named root imports by their owning public Askr subpath in one transactional fix per declaration while retaining aliases, type modifiers, and valid root specifiers.askr/no-hardcoded-theme-tokenreports runtime color literals outside tests and the framework/theme owner packages.askr/no-effect-data-loadingreports direct fetch-to-component-state flows intask()callbacks; arbitrary service-call inference is intentionally out of scope.askr/testing-contractrequires a synchronousflush()orresult.flush()between canonical testingdispatch()and the next assertion in a block.askr/boot-registryrequires an explicit registry and an observed Promise forcreateSPA()andhydrateSPA().askr/ssr-browser-globalreports unguarded browser globals in SSR and SSG modules.
askr/prefer-forreports JSX.map()only when its receiver is proven to be an Askr state-backed reactive collection. Static array transforms remain valid.askr/stable-keyreports index-returningbyfunctions.askr/stable-dependenciesreports object, array, function, and constructor allocations in resource dependency arrays.
askr/framework-configvalidates the Askr JSX import source and detects a declared@askrjs/vitedependency that is absent fromvite.config.*.
The rule catalog is intentionally extensible. Current concepts inventory reactive state, lifecycle operations, queries, mutations, invalidation, control flow, route DSL and registries, SPA and island boot, SSR, SSG, actions, authorization, scopes, refs, and composition. Static analysis reports only patterns it can establish from source and configuration; it does not run the project's lint, tests, or build.
Without --check, the command applies only fixes whose intent is mechanical:
- convert route parameters such as
:idto{id}; - add a missing leading slash to a root route or collapse consecutive slashes;
- strip the leading slash from a statically proven child route;
- split misplaced named root imports into their documented public subpaths;
- add the Askr JSX runtime to a plain-JSON
tsconfig.json.
All changed files are staged and replaced as one transaction. If any replacement
fails, completed replacements are rolled back. JSONC, inherited TypeScript
configuration, .map() to <For>, conditional render-scoped calls, invalid
keys, and other semantic changes are report-only.
--check performs no writes. Fixable diagnostics remain in the result and
appear under skippedFixes with a check-mode reason.
The command exits 1 while error or warning diagnostics remain, and 0 when
only informational or no diagnostics remain.
The analyzer intentionally builds lightweight syntax programs: project source and local path aliases are resolved, while standard-library and external package declaration graphs are not loaded. Rules still distinguish canonical Askr imports from unrelated local functions, but analysis does not pay the cost of type-checking dependency declarations it never reports.
npm run bench:analyze runs the analyzer's Vitest benchmark suite. It covers a
50-file workspace, a 250-file workspace, and five workspaces containing 250
files in total. The benchmark reporter enforces mean-time budgets of 100 ms,
250 ms, and 300 ms respectively. The general npm run bench gate also checks a
cold installed-CLI scan of the 35-file startkit template against a 350 ms p95
budget.
Configure the analyzer in the workspace root package.json:
{
"askr": {
"analyze": {
"exclude": ["fixtures/**", "**/*.generated.ts"],
"rules": {
"askr/prefer-for": "error",
"askr/stable-dependencies": "info",
"askr/ssr-browser-global": "off"
}
}
}
}Rule values are error, warning, info, or off. Source discovery honors
.gitignore files from the project root through each selected workspace,
including nested rules and negation. askr.analyze.exclude adds analyzer-only
patterns relative to each workspace. The analyzer also always ignores dependency,
VCS, coverage, generated, and common build-output directories by default.
Use check mode so CI cannot change the checkout:
askr analyze --checkJSON output is one deterministic object containing schema version 1, the
project root, discovered and selected workspaces, per-workspace program details,
applied and skipped fixes, sorted diagnostics, and summary counts.