# HG changeset patch # User cin # Date 2022-09-14 12:05:31 # Node ID 4a375b9c654a64efcec6c2ea0d0f1e3d8d92023b # Parent d644dced936efba5917df7ce6e2c15e9a7a8eda9 linting diff --git a/.eslintignore b/.eslintignore new file mode 100644 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,1 @@ +.eslintrc.js \ No newline at end of file diff --git a/djx/.eslintrc.js b/djx/.eslintrc.js new file mode 100644 --- /dev/null +++ b/djx/.eslintrc.js @@ -0,0 +1,39 @@ +module.exports = { + root: true, + extends: [ + "plugin:react/recommended", + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaFeatures: { + jsx: true + }, + ecmaVersion: 5, + tsconfigRootDir: __dirname + "/src", + project: [ + "tsconfig.eslint.json", + "*/tsconfig.json" + ] + }, + plugins: [ + "react", + "@typescript-eslint" + ], + rules: { + "react/react-in-jsx-scope": "off", + "react/no-unknown-property": "off", + "@typescript-eslint/no-empty-function": "off", + "max-classes-per-file": [ + "error", + { "ignoreExpressions": true, "max": 1 } + ], + "@typescript-eslint/prefer-readonly": ["error"], + "semi": "off", + "@typescript-eslint/semi": ["error"] + + } +} diff --git a/djx/.eslintrc.json b/djx/.eslintrc.json deleted file mode 100644 --- a/djx/.eslintrc.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "root": true, - "extends": [ - "plugin:react/recommended", - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": 5, - "tsconfigRootDir": "djx/src", - "project": ["tsconfig.eslint.json", "*/tsconfig.json"] - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "react/react-in-jsx-scope": "off", - "react/no-unknown-property": "off", - "@typescript-eslint/no-empty-function": "off", - "max-classes-per-file": [ - "error", - { "ignoreExpressions": true, "max": 1 } - ], - "@typescript-eslint/prefer-readonly": ["error"], - "semi": "off", - "@typescript-eslint/semi": ["error"] - - } -} diff --git a/djx/package-lock.json b/djx/package-lock.json --- a/djx/package-lock.json +++ b/djx/package-lock.json @@ -10,7 +10,7 @@ "license": "BSD-2-Clause", "devDependencies": { "@implab/core-amd": "^1.4.0", - "@implab/dojo-typings": "1.0.0", + "@implab/dojo-typings": "1.0.3", "@types/chai": "4.1.3", "@types/requirejs": "2.1.31", "@types/tap": "15.0.7", @@ -458,9 +458,9 @@ "dev": true }, "node_modules/@implab/dojo-typings": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.0.tgz", - "integrity": "sha512-B2kvlKJgvyIQAl/k1pVyNmtp4ADvBDCs4Lw/qfBC+Sz/ft4c7EuRXmN/+2dhWrd6A5SMjEgex5oeq6Ja2ntrig==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.3.tgz", + "integrity": "sha512-oyCiuU5ay9MfvdQtZNJSeV30jKufdiLBAcq6rn360pww2hzdqvWEeoU9/New8fMzyNiaEumOlgbcS11EVIH+Jg==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -7030,9 +7030,9 @@ "dev": true }, "@implab/dojo-typings": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.0.tgz", - "integrity": "sha512-B2kvlKJgvyIQAl/k1pVyNmtp4ADvBDCs4Lw/qfBC+Sz/ft4c7EuRXmN/+2dhWrd6A5SMjEgex5oeq6Ja2ntrig==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.3.tgz", + "integrity": "sha512-oyCiuU5ay9MfvdQtZNJSeV30jKufdiLBAcq6rn360pww2hzdqvWEeoU9/New8fMzyNiaEumOlgbcS11EVIH+Jg==", "dev": true }, "@istanbuljs/load-nyc-config": { diff --git a/djx/package.json b/djx/package.json --- a/djx/package.json +++ b/djx/package.json @@ -25,7 +25,7 @@ "@types/yaml": "1.2.0", "@types/tap": "15.0.7", "dojo": "1.16.0", - "@implab/dojo-typings": "1.0.0", + "@implab/dojo-typings": "1.0.3", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", "eslint": "^8.23.0", diff --git a/djx/src/main/ts/NlsBundle.ts b/djx/src/main/ts/NlsBundle.ts --- a/djx/src/main/ts/NlsBundle.ts +++ b/djx/src/main/ts/NlsBundle.ts @@ -3,31 +3,18 @@ import { argumentNotEmptyString, isPromi export type LocaleProvider = () => PromiseOrValue; -type ResolveCallback = () => PromiseOrValue; - function when(value: PromiseOrValue, cb: (v: T) => PromiseOrValue): PromiseOrValue { return isPromise(value) ? value.then(cb) : cb(value); } -function isCallback(v: ResolveCallback | PromiseOrValue): v is ResolveCallback { - return typeof v === "function"; -} +const chainObjects = (o1: T, o2: T2) => + mixin(Object.create(o1) as T, o2); +export class NlsBundle { + private readonly _locales: MapOf>>; -function chainObjects(o1: T, o2: T) { - if (!o1) - return o2; - if (!o2) - return o1; - - return mixin(Object.create(o1) as T, o2); -} - -export class NlsBundle { - private _locales: MapOf>>; - - private _default: T; + private readonly _default: T; private _cache: MapOf>; @@ -65,9 +52,7 @@ export class NlsBundle }); } - _loadPackage(localeData: any) { - if (isCallback(localeData)) - return when(localeData(), data => data && "default" in data ? data.default : data); - return localeData; + _loadPackage(localeData: LocaleProvider>) { + return when(localeData(), data => data && "default" in data ? data.default : data); } } diff --git a/djx/src/main/ts/css.ts b/djx/src/main/ts/css.ts --- a/djx/src/main/ts/css.ts +++ b/djx/src/main/ts/css.ts @@ -1,8 +1,8 @@ import inject from "./dom-inject"; interface OnLoad { - (result?: any): void; - error(err: any): void; + (result?: unknown): void; + error(err: unknown): void; } const plugin = { diff --git a/djx/src/main/ts/declare.ts b/djx/src/main/ts/declare.ts --- a/djx/src/main/ts/declare.ts +++ b/djx/src/main/ts/declare.ts @@ -137,9 +137,10 @@ export function djclass unknown) => function (this: dojo._base.DeclareCreatedObject, ...args: unknown[]) { - const f = this.getInherited({ callee: m, ...args, length: args.length }); - return f ? f.apply(this, args) as unknown : undefined; + const proxy = (m: (...args: unknown[]) => unknown) => function (this: dojo._base.DeclareCreatedObject) { + const f = this.getInherited({ callee: m } as unknown as IArguments); + // eslint-disable-next-line prefer-rest-params + return f ? f.apply(this, arguments) as unknown : undefined; // так сделать можно только dojo 1.15+ // return this.inherited(m, arguments); @@ -153,7 +154,7 @@ export function djclass { if (typeof m === "function" && p !== "constructor" && - Object.prototype.hasOwnProperty.call(target, p) + Object.prototype.hasOwnProperty.call(target.prototype, p) ) { nbp[p] = proxy(m as (...args: unknown[]) => unknown); } @@ -224,18 +225,18 @@ export function bind(params: NodeBindSpe target[makeSetterName(name) as K /** hack to not go insane) */] = params; }; } else { - return void : unknown; }> (target: T, name: K) => { - target[name] = undefined as T[K]; - target[makeSetterName(name) as K] = function (this: T, v: unknown) { - this._set(name, v); - const inner = this[params.member] as Record; + return (target: Record, name: K) => { + target[name] = undefined; + + target[makeSetterName(name) as K] = function (this: typeof target, v: unknown) { + (this._set as (n: K, v: unknown) => void)(name, v); + const inner = this[params.member as K] as Record; if (typeof inner.set === "function") inner.set(params.property, v); - } as T[K]; + }; if (params.getter) - target[makeGetterName(name)] = function () { - const inner = this[params.member] as Record; + target[makeGetterName(name) as K] = function (this: typeof target) { + const inner = this[params.member as K] as Record; if (typeof inner.get === "function") return inner.get(params.property) as unknown; }; diff --git a/djx/src/main/ts/dom-inject.ts b/djx/src/main/ts/dom-inject.ts --- a/djx/src/main/ts/dom-inject.ts +++ b/djx/src/main/ts/dom-inject.ts @@ -6,7 +6,7 @@ import { mixin } from "@implab/core-amd/ const trace = TraceSource.get(mid); -function on(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => any): () => void { +function on(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => unknown): () => void { // Add an event listener to a DOM node node.addEventListener(eventName, handler, false); @@ -57,7 +57,7 @@ class DomInject { async injectScript(url: string) { let d = this._map[url]; - if (!d) { + if (d === undefined) { trace.log("js {0}", url); d = this._inject("script", { type: "text/javascript", @@ -77,7 +77,7 @@ class DomInject { async injectStylesheet(url: string) { let d = this._map[url]; - if (!d) { + if (d === undefined) { trace.log("js {0}", url); d = this._inject("link", { type: "text/css", diff --git a/djx/src/main/ts/i18n.ts b/djx/src/main/ts/i18n.ts --- a/djx/src/main/ts/i18n.ts +++ b/djx/src/main/ts/i18n.ts @@ -1,6 +1,6 @@ import { id as mid} from "module"; import { MapOf } from "@implab/core-amd/interfaces"; -import { NlsBundle } from "./NlsBundle"; +import { LocaleProvider, NlsBundle } from "./NlsBundle"; import { isPromise } from "@implab/core-amd/safe"; import { locale as sysLocale } from "dojo/_base/kernel"; import { TraceSource } from "@implab/core-amd/log/TraceSource"; @@ -10,11 +10,11 @@ const trace = TraceSource.get(mid); trace.debug("Current sysLocale: {0}", sysLocale); export interface OnLoad { - (result?: any): void; - error(err: any): void; + (result?: unknown): void; + error(err: unknown): void; } -export function bundle(nls: T, locales?: MapOf) { +export function bundle(nls: T, locales?: MapOf>) { const nlsBundle = new NlsBundle(nls, locales); const fn = (_locale?: string) => { @@ -28,7 +28,7 @@ export function bundle }; fn.define = (pack: Partial) => pack; - fn.load = async (id: string, require: Require, cb: OnLoad, config: any) => { + fn.load = async (id: string, require: Require, cb: OnLoad, config: {isBuild?: boolean}) => { const locale = id || sysLocale; if (config && config.isBuild) { cb(); diff --git a/djx/src/main/ts/observable.ts b/djx/src/main/ts/observable.ts --- a/djx/src/main/ts/observable.ts +++ b/djx/src/main/ts/observable.ts @@ -74,17 +74,17 @@ const sink = (consumer: Partial({ next, error, complete }: Sink) => { let done = false; return { - next: (value: T) => { !done && next(value) }, - error: (e: unknown) => { !done && (done = true, error(e)) }, - complete: () => { !done && (done = true, complete()) } - } -} + next: (value: T) => { !done && next(value); }, + error: (e: unknown) => { !done && (done = true, error(e)); }, + complete: () => { !done && (done = true, complete()); } + }; +}; const _observe = (producer: Producer): Observable => ({ subscribe: (consumer: Partial>) => ({ diff --git a/djx/src/main/ts/tsx.ts b/djx/src/main/ts/tsx.ts --- a/djx/src/main/ts/tsx.ts +++ b/djx/src/main/ts/tsx.ts @@ -12,7 +12,7 @@ import djAttr = require("dojo/dom-attr") import djClass = require("dojo/dom-class"); import { AnimationAttrs, WatchForRendition } from "./tsx/WatchForRendition"; -export function createElement Element)>(elementType: T, ...args: any[]): Rendition { +export function createElement Element)>(elementType: T, ...args: unknown[]): Rendition { if (typeof elementType === "string") { const ctx = new HtmlRendition(elementType); if (args) @@ -26,17 +26,17 @@ export function createElement Element); + const ctx = new FunctionRendition(elementType as (props: unknown) => Element); if (args) args.forEach(x => ctx.visitNext(x)); return ctx; } else { - throw new Error(`The element type '${elementType}' is unsupported`); + throw new Error(`The element type '${String(elementType)}' is unsupported`); } } -export interface EventDetails { +export interface EventDetails { detail: T; } @@ -56,7 +56,7 @@ export interface QueryResultUpdate { newIndex: number; } -export type DojoMouseEvent = MouseEvent & EventSelector & EventDetails; +export type DojoMouseEvent = MouseEvent & EventSelector & EventDetails; type StatefulProps = T extends Stateful ? A : T extends _WidgetBase ? T : never; @@ -73,7 +73,7 @@ type StatefulProps = T extends Statef export function watch( target: W, prop: K, - render: (model: W[K]) => any + render: (model: W[K]) => unknown ): Rendition; /** * Observers the property and calls render callback each change. @@ -86,7 +86,7 @@ export function watch>( target: T, prop: K, - render: (model: StatefulProps[K]) => any + render: (model: StatefulProps[K]) => unknown ): Rendition; export function watch(subj: Subscribable, render: (model: V) => unknown): Rendition; export function watch( @@ -98,7 +98,7 @@ export function watch( return new WatchRendition( render, observe(({next}) => { - const h = target.watch( + const h = target.watch( prop, (_prop, oldValue, newValue) => oldValue !== newValue && next(newValue) ); @@ -118,7 +118,7 @@ export const watchFor = (source: T[] subject: source, component: render }); -} +}; export const prop: { @@ -132,7 +132,7 @@ export const prop: { ); next(target.get(name)); return () => h.remove(); - }) + }); }; export const attach = (target: W, name: K) => (v: W[K]) => target.set(name, v); @@ -140,7 +140,7 @@ export const attach = (attr: K, subj: Subscribable) => { let h = { unsubscribe() { } }; - return (el: E | undefined) => { + return (el: Element | { set(name: K, value: T): void; } | undefined) => { if (el) { if (isElementNode(el)) { h = subj.subscribe({ @@ -154,7 +154,7 @@ export const bind = ) => { @@ -168,8 +168,8 @@ export const toggleClass = (className: s } else { h.unsubscribe(); } - } -} + }; +}; export const all = []>(...cbs: A): JSX.Ref => (arg: T | undefined) => cbs.forEach(cb => cb(arg)); @@ -185,13 +185,14 @@ export const all = (...eventNames: E[]) => , + T extends DjxWidgetBase, EV extends Event >( target: T, key: K, + // eslint-disable-next-line @typescript-eslint/no-unused-vars _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void> - ): any => { + ) => { const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key })); target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers; }; diff --git a/djx/src/main/ts/tsx/DjxWidgetBase.ts b/djx/src/main/ts/tsx/DjxWidgetBase.ts --- a/djx/src/main/ts/tsx/DjxWidgetBase.ts +++ b/djx/src/main/ts/tsx/DjxWidgetBase.ts @@ -17,7 +17,8 @@ export interface EventArgs { composed?: boolean; } -export interface DjxWidgetBase extends +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export interface DjxWidgetBase extends _WidgetBase { /** This property is declared only for type inference to work, it is never assigned @@ -32,7 +33,7 @@ export interface DjxWidgetBase; } @@ -43,7 +44,8 @@ type _super = { }; @djclass -export abstract class DjxWidgetBase extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export abstract class DjxWidgetBase extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) { private readonly _scope = new Scope(); buildRendering() { @@ -68,29 +70,29 @@ export abstract class DjxWidgetBase; + abstract render(): Rendition; private _connectEventHandlers() { if (this._eventHandlers) this._eventHandlers.forEach(({ eventName, handlerMethod }) => { const handler = this[handlerMethod as keyof this]; if (typeof handler === "function") - on(this.domNode, eventName, handler.bind(this)); + on(this.domNode, eventName, handler.bind(this) as (...args: unknown[]) => unknown); }); } _processTemplateNode( baseNode: T, - getAttrFunc: (baseNode: T, attr: string) => any, + getAttrFunc: (baseNode: T, attr: string) => string, // tslint:disable-next-line: ban-types - attachFunc: (node: T, type: string, func?: Function) => dojo.Handle + attachFunc: (node: T, type: string, func?: (...args: unknown[]) => unknown) => dojo.Handle ): boolean { if (isNode(baseNode)) { const w = registry.byNode(baseNode); if (w) { // from dijit/_WidgetsInTemplateMixin this._processTemplateNode(w, - (n, p) => n.get(p as any), // callback to get a property of a widget + (n, p) => String(n.get(p as keyof typeof n)), // callback to get a property of a widget (widget, type, callback) => { if (!callback) throw new Error("The callback must be specified"); @@ -101,7 +103,7 @@ export abstract class DjxWidgetBase dojo.Handle); } /** Starts current widget and all its supporting widgets (placed outside diff --git a/djx/src/main/ts/tsx/FunctionRendition.ts b/djx/src/main/ts/tsx/FunctionRendition.ts --- a/djx/src/main/ts/tsx/FunctionRendition.ts +++ b/djx/src/main/ts/tsx/FunctionRendition.ts @@ -3,19 +3,19 @@ import { getItemDom } from "./render"; import { RenditionBase } from "./RenditionBase"; export class FunctionRendition extends RenditionBase { - private _component: (...args: any[]) => any; + private readonly _component: (...args: unknown[]) => unknown; private _node: Node | undefined; - constructor(component: (...args: any[]) => any) { + constructor(component: (...args: unknown[]) => unknown) { super(); argumentNotNull(component, "component"); this._component = component; } - protected _create(attrs: object, children: any[]) { - const _attrs: any = attrs || {}; + protected _create(attrs: object, children: unknown[]) { + const _attrs = attrs || {}; const _children = children.map(x => getItemDom(x)); this._node = getItemDom( this._component.call(null, { ..._attrs, children: _children })); diff --git a/djx/src/main/ts/tsx/RenditionBase.ts b/djx/src/main/ts/tsx/RenditionBase.ts --- a/djx/src/main/ts/tsx/RenditionBase.ts +++ b/djx/src/main/ts/tsx/RenditionBase.ts @@ -3,11 +3,11 @@ import { isPlainObject, DojoNodePosition export abstract class RenditionBase implements Rendition { private _attrs = {}; - private _children = new Array(); + private _children: unknown[] = []; - private _created: boolean = false; + private _created = false; - visitNext(v: any) { + visitNext(v: unknown) { if (this._created) throw new Error("The Element is already created"); diff --git a/djx/src/main/ts/tsx/Scope.ts b/djx/src/main/ts/tsx/Scope.ts --- a/djx/src/main/ts/tsx/Scope.ts +++ b/djx/src/main/ts/tsx/Scope.ts @@ -30,7 +30,7 @@ export class Scope implements IDestroyab } catch { // guard } - } + }; this._cleanup.forEach(guard); this._cleanup.length = 0; diff --git a/djx/src/main/ts/tsx/WatchForRendition.ts b/djx/src/main/ts/tsx/WatchForRendition.ts --- a/djx/src/main/ts/tsx/WatchForRendition.ts +++ b/djx/src/main/ts/tsx/WatchForRendition.ts @@ -49,10 +49,10 @@ export interface WatchForRenditionAttrs< component: (arg: T, index: number) => unknown; } -const isObservable = (v: PromiseLike> | ArrayLike): v is ArrayLike & ObservableResults => - v && (typeof (v as any).observe === "function"); +const isObservable = (v: ArrayLike): v is ArrayLike & ObservableResults => + v && (typeof (v as { observe?: unknown; }).observe === "function"); -const noop = () => {}; +const noop = () => { }; const fadeIn = (nodes: Node[]) => Promise.all(nodes .filter(isElementNode) @@ -112,7 +112,7 @@ export class WatchForRendition extend if (isSubsribable>(result)) { let animate = false; const subscription = result.subscribe({ - next: ({item, prevIndex, newIndex}) => this._onItemUpdated({ item, prevIndex, newIndex, animate }) + next: ({ item, prevIndex, newIndex }) => this._onItemUpdated({ item, prevIndex, newIndex, animate }) }); scope.own(subscription); animate = this._animate; @@ -127,7 +127,7 @@ export class WatchForRendition extend this._ct = new Cancellation(cancel => scope.own(cancel)); } - private _onItemUpdated = (item: RenderTask) => { + private readonly _onItemUpdated = (item: RenderTask) => { if (!this._renderTasks.length) { // schedule a new job this._renderTasks.push(item); @@ -136,7 +136,7 @@ export class WatchForRendition extend // update existing job this._renderTasks.push(item); } - } + }; private async _render() { // fork @@ -149,7 +149,7 @@ export class WatchForRendition extend this._renderTasks.length = 0; } - _onRenderItem = ({ item, newIndex, prevIndex, animate: _animate }: RenderTask) => { + private readonly _onRenderItem = ({ item, newIndex, prevIndex, animate: _animate }: RenderTask) => { const animate = _animate && prevIndex !== newIndex; if (prevIndex > -1) { @@ -205,7 +205,7 @@ export class WatchForRendition extend if (animate) this._animateIn(nodes).catch(e => trace.error(e)); } - } + }; protected _getDomNode() { if (!this._node) diff --git a/djx/src/main/ts/tsx/WidgetRendition.ts b/djx/src/main/ts/tsx/WidgetRendition.ts --- a/djx/src/main/ts/tsx/WidgetRendition.ts +++ b/djx/src/main/ts/tsx/WidgetRendition.ts @@ -17,7 +17,7 @@ export interface _Widget { addChild?(widget: unknown, index?: number): void; } -export type _WidgetCtor = new (attrs: {}, srcNode?: string | Node) => _Widget; +export type _WidgetCtor = new (attrs: object, srcNode?: string | Node) => _Widget; export class WidgetRendition extends RenditionBase { readonly widgetClass: _WidgetCtor; diff --git a/djx/src/main/ts/tsx/render.ts b/djx/src/main/ts/tsx/render.ts --- a/djx/src/main/ts/tsx/render.ts +++ b/djx/src/main/ts/tsx/render.ts @@ -14,11 +14,11 @@ interface Context { let _context: Context = { scope: Scope.dummy -} +}; const guard = (cb: () => unknown) => { try { - const result = cb() + const result = cb(); if (isPromise(result)) { const warn = (ret: unknown) => trace.error("The callback {0} competed asynchronously. result = {1}", cb, ret); result.then(warn, warn); @@ -26,7 +26,7 @@ const guard = (cb: () => unknown) => { } catch (e) { trace.error(e); } -} +}; export const beginRender = (scope: IScope = getScope()) => { const prev = _context; @@ -35,7 +35,7 @@ export const beginRender = (scope: IScop hooks: [] }; return endRender(prev); -} +}; /** * Completes render operation @@ -46,7 +46,7 @@ const endRender = (prev: Context) => () hooks.forEach(guard); _context = prev; -} +}; export const renderHook = (hook: () => void) => { const { hooks } = _context; @@ -54,7 +54,7 @@ export const renderHook = (hook: () => v hooks.push(hook); else guard(hook); -} +}; export const refHook = (value: T, ref: JSX.Ref) => { const { hooks, scope } = _context; @@ -64,7 +64,7 @@ export const refHook = (value: T, ref guard(() => ref(value)); scope.own(() => ref(undefined)); -} +}; /** Returns the current scope */ export const getScope = () => _context.scope; @@ -80,7 +80,7 @@ export const render = (rendition: unknow } finally { complete(); } -} +}; /** Renders DOM element for different types of the argument. */ export const getItemDom = (v: unknown) => { @@ -107,6 +107,6 @@ export const getItemDom = (v: unknown) = return fragment; } else { // bug: explicit error otherwise - throw new Error("Invalid parameter: " + v); + throw new Error(`Invalid parameter: ${String(v)}`); } -} +}; diff --git a/djx/src/main/typings/index.d.ts b/djx/src/main/typings/index.d.ts --- a/djx/src/main/typings/index.d.ts +++ b/djx/src/main/typings/index.d.ts @@ -1,81 +1,89 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -declare namespace JSX { - - type Ref = ((value: T | undefined) => void); +import { Rendition } from "./tsx/traits"; - interface DjxIntrinsicAttributes { - /** alias for className */ - class: string; +declare global { + namespace JSX { + + type Ref = ((value: T | undefined) => void); + + type Element = Rendition; - /** specifies the name of the property in the widget where the the - * reference to the current object will be stored - */ - "data-dojo-attach-point": string; + interface DjxIntrinsicAttributes { + /** alias for className */ + class?: string; - /** specifies handlers map for the events */ - "data-dojo-attach-event": string; + /** specifies the name of the property in the widget where the the + * reference to the current object will be stored + */ + "data-dojo-attach-point"?: string; - ref: Ref; + /** specifies handlers map for the events */ + "data-dojo-attach-event"?: string; - /** @deprecated */ - [attr: string]: any; - } + ref?: Ref; - interface DjxIntrinsicElements { - } + /** @deprecated */ + [attr: string]: unknown; + } - type RecursivePartial = T extends string | number | boolean | null | undefined | Function ? - T : - { [k in keyof T]?: RecursivePartial }; + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface DjxIntrinsicElements { + } + + type RecursivePartial = T extends string | number | boolean | null | undefined | ((...args: unknown[]) => unknown) ? + T : + { [k in keyof T]?: RecursivePartial }; - type MatchingMemberKeys = { - [K in keyof T]: T[K] extends U ? K : never; - }[keyof T]; - type NotMatchingMemberKeys = { - [K in keyof T]: T[K] extends U ? never : K; - }[keyof T]; + type MatchingMemberKeys = { + [K in keyof T]: T[K] extends U ? K : never; + }[keyof T]; + type NotMatchingMemberKeys = { + [K in keyof T]: T[K] extends U ? never : K; + }[keyof T]; - type ExtractMembers = Pick>; + type ExtractMembers = Pick>; - type ExcludeMembers = Pick>; + type ExcludeMembers = Pick>; - type ElementAttrNames = NotMatchingMemberKeys any>; + type ElementAttrNames = NotMatchingMemberKeys unknown>; - type ElementAttrType = K extends keyof E ? RecursivePartial : string; + type ElementAttrType = K extends keyof E ? RecursivePartial : string; - type ElementAttrNamesBlacklist = "children" | "getRootNode" | keyof EventTarget; + type ElementAttrNamesBlacklist = "children" | "getRootNode" | keyof EventTarget; - /** This type extracts keys of the specified parameter E by the following rule: - * 1. skips all ElementAttrNamesBlacklist - * 2. skips all methods except with the signature of event handlers - */ - type AssignableElementAttrNames = { - [K in keyof E]: K extends ElementAttrNamesBlacklist ? never : - ((evt: Event) => any) extends E[K] ? K : - E[K] extends ((...args: any[]) => any) ? never : + /** This type extracts keys of the specified parameter E by the following rule: + * 1. skips all ElementAttrNamesBlacklist + * 2. skips all methods except with the signature of event handlers + */ + type AssignableElementAttrNames = { + [K in keyof E]: K extends ElementAttrNamesBlacklist ? never : + ((evt: Event) => unknown) extends E[K] ? K : + E[K] extends ((...args: unknown[]) => unknown) ? never : K; - }[keyof E]; + }[keyof E]; - type LaxElement = - Pick> & - DjxIntrinsicAttributes; + type LaxElement = + RecursivePartial>> & + DjxIntrinsicAttributes; - type LaxIntrinsicElementsMap = { - [tag in keyof HTMLElementTagNameMap]: LaxElement - } & DjxIntrinsicElements; + type LaxIntrinsicElementsMap = { + [tag in keyof HTMLElementTagNameMap]: LaxElement + } & DjxIntrinsicElements; - type IntrinsicElements = { - [tag in keyof LaxIntrinsicElementsMap]: RecursivePartial; - } + type IntrinsicElements = { + [tag in keyof LaxIntrinsicElementsMap]: LaxIntrinsicElementsMap[tag]; + }; - interface ElementChildrenAttribute { - children: {}; - } + interface ElementChildrenAttribute { + children: unknown; + } - interface IntrinsicClassAttributes { - ref?: (value: T) => void; - children?: unknown; + interface IntrinsicClassAttributes { + ref?: Ref; + children?: unknown; + } } -} +} \ No newline at end of file diff --git a/djx/src/test/ts/i18n/foo.ts b/djx/src/test/ts/i18n/foo.ts deleted file mode 100644 --- a/djx/src/test/ts/i18n/foo.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { bundle } from "../i18n"; - -export default bundle({ - greeting: (name: string) => `Hello, ${name}!`, - goodbye: (name: string) => `Bye, ${name}!` -}, { - ru: () => import("./ru/foo") -}); - diff --git a/djx/src/test/ts/i18n/ru/foo.ts b/djx/src/test/ts/i18n/ru/foo.ts deleted file mode 100644 --- a/djx/src/test/ts/i18n/ru/foo.ts +++ /dev/null @@ -1,6 +0,0 @@ -import foo from "../foo"; - -export default foo.define({ - greeting: (name: string) => `Привет, ${name}`, - goodbye: (name: string) => `Пока, ${name}` -}); diff --git a/djx/src/test/ts/view/MyWidget.tsx b/djx/src/test/ts/view/MyWidget.tsx --- a/djx/src/test/ts/view/MyWidget.tsx +++ b/djx/src/test/ts/view/MyWidget.tsx @@ -2,6 +2,7 @@ import { djbase, djclass, bind, prototyp import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; import { createElement, on } from "../tsx"; +import { argumentNotNull } from "@implab/core-amd/safe"; interface MyWidgetAttrs { title: string; @@ -19,6 +20,12 @@ interface MyWidgetEvents { }; } +interface FrameProps { + ref?: JSX.Ref; + children?: unknown[]; +} + +const Frame = ({children, ref}: FrameProps) =>
{children}
; @djclass export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor>) { @@ -29,31 +36,40 @@ export class MyWidget extends djbase(Djx @prototype(0) counter = 0; + frameNode?: HTMLDivElement; + render() { - const Frame = ({children, ref}: {ref: JSX.Ref, children: unknown[]}) =>
{children}
; - return
this._onSubmit(e)} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} > + + return

- {}}> - this._onIncClick(e)}>[+] + + [+] this._onDecClick()}>[-]
; } + private readonly _setFrameElement = (node?: HTMLDivElement) => { + this.frameNode = node; + }; + postCreate() { super.postCreate(); this.on("click", () => {}); } - _onSubmit(e: Event) { - } + private readonly _onSubmit = (evt: Event) => { + argumentNotNull(evt, "evt"); + }; - _onIncClick(e: MouseEvent) { + private readonly _onIncClick = (evt: MouseEvent) => { + argumentNotNull(evt, "evt"); + this.set("counter", this.counter + 1); this.emit("count-inc", { bubbles: false }); - } + }; _onDecClick() { this.emit("count-dec", { bubbles: false, detail: this.counter }); @@ -61,10 +77,11 @@ export class MyWidget extends djbase(Djx @on("count-inc") private _onCounterInc(evt: Event & { detail: number; x?: number; }) { + argumentNotNull(evt, "evt"); } @on("click", "keydown") - protected _onClick(event: MouseEvent | KeyboardEvent) { - + protected _onClick(evt: MouseEvent | KeyboardEvent) { + argumentNotNull(evt, "evt"); } } \ No newline at end of file diff --git a/djx/src/test/tsconfig.json b/djx/src/test/tsconfig.json --- a/djx/src/test/tsconfig.json +++ b/djx/src/test/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig", "compilerOptions": { + "baseUrl": ".", "noUnusedLocals": false, //"rootDir": "ts", "rootDirs": [ @@ -9,6 +10,11 @@ "../main/ts", "../main/typings" ], - "types": ["requirejs", "../main/typings", "@implab/dojo-typings"] + "types": [ + "requirejs", + "../main/typings", + "@implab/dojo-typings", + "@implab/dojo-typings/dojo/NodeList-fx" + ] } } \ No newline at end of file diff --git a/djx/src/tsconfig.eslint.json b/djx/src/tsconfig.eslint.json --- a/djx/src/tsconfig.eslint.json +++ b/djx/src/tsconfig.eslint.json @@ -1,10 +1,9 @@ { - "extends": "./tsconfig.json", + //"extends": "./tsconfig.json", "compilerOptions": { // ensure that nobody can accidentally use this config for a build - "noEmit": true + "noEmit": true, }, "include": [ - "ts" ] } \ No newline at end of file diff --git a/playground/.eslintrc.js b/playground/.eslintrc.js new file mode 100644 --- /dev/null +++ b/playground/.eslintrc.js @@ -0,0 +1,39 @@ +module.exports = { + root: true, + extends: [ + "plugin:react/recommended", + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaFeatures: { + jsx: true + }, + ecmaVersion: 5, + tsconfigRootDir: __dirname + "/src", + project: [ + "tsconfig.eslint.json", + "*/tsconfig.json" + ] + }, + plugins: [ + "react", + "@typescript-eslint" + ], + rules: { + "react/react-in-jsx-scope": "off", + "react/no-unknown-property": "off", + "@typescript-eslint/no-empty-function": "off", + "max-classes-per-file": [ + "error", + { "ignoreExpressions": true, "max": 1 } + ], + "@typescript-eslint/prefer-readonly": ["error"], + "semi": "off", + "@typescript-eslint/semi": ["error"], + "react/jsx-key": "off" + } +} diff --git a/playground/.eslintrc.json b/playground/.eslintrc.json deleted file mode 100644 --- a/playground/.eslintrc.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "root": true, - "extends": [ - "plugin:react/recommended", - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": 5, - "tsconfigRootDir": "src", - "project": ["tsconfig.eslint.json", "*/tsconfig.json"] - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "@typescript-eslint/no-empty-function": "off", - "max-classes-per-file": [ - "error", - { "ignoreExpressions": true, "max": 1 } - ], - "@typescript-eslint/prefer-readonly": ["error"], - "semi": "off", - "@typescript-eslint/semi": ["error"] - - } -} diff --git a/playground/src/main/ts/main.ts b/playground/src/main/ts/main.ts --- a/playground/src/main/ts/main.ts +++ b/playground/src/main/ts/main.ts @@ -1,7 +1,7 @@ import MainWidget from "./view/MainWidget"; -import "@implab/djx/css!dojo/resources/dojo.css" -import "@implab/djx/css!dijit/themes/dijit.css" -import "@implab/djx/css!dijit/themes/tundra/tundra.css" +import "@implab/djx/css!dojo/resources/dojo.css"; +import "@implab/djx/css!dijit/themes/dijit.css"; +import "@implab/djx/css!dijit/themes/tundra/tundra.css"; const w = new MainWidget(); w.placeAt(document.body); \ No newline at end of file diff --git a/playground/src/main/ts/model/MainContext.ts b/playground/src/main/ts/model/MainContext.ts --- a/playground/src/main/ts/model/MainContext.ts +++ b/playground/src/main/ts/model/MainContext.ts @@ -5,6 +5,7 @@ import { Contact } from "./Contact"; import { Uuid } from "@implab/core-amd/Uuid"; import { Observable as RxjsObservable } from "rxjs"; import { QueryResultUpdate } from "@implab/djx/tsx"; +import {isPromise} from "@implab/core-amd/safe"; type AppointmentRecord = Omit & {id: string}; @@ -22,13 +23,13 @@ export interface ObservableResults { } -export function isObservable(v: PromiseLike> | ArrayLike): v is ArrayLike & ObservableResults { - return v && (typeof (v as any).observe === "function"); +export function isObservable(v: unknown): v is ObservableResults { + return !!v && (typeof (v as {observe?: unknown}).observe === "function"); } export function observe(results: T[], includeObjectUpdates?: boolean): RxjsObservable>; export function observe(results: PromiseLike, includeObjectUpdates?: boolean): PromiseLike>>; -export function observe(results: any, includeObjectUpdates = true) { +export function observe(results: unknown[] | PromiseLike, includeObjectUpdates = true) { // results может быть асинхронным, т.е. до завершения // получения результатов store может быть обновлен. В любом // случае, если между подключением хотя бы одного наблюдателя @@ -41,7 +42,7 @@ export function observe(results: any, in // о необработанной ошибке в Promise при обращении к методам // обновления (add,put,remove) - const _subscribe = (items: any[]) => new RxjsObservable>(subscriber => { + const _subscribe = (items: unknown[]) => new RxjsObservable>(subscriber => { items .forEach((value, newIndex) => subscriber.next({ item: value, newIndex, prevIndex: -1})); @@ -72,11 +73,11 @@ export function observe(results: any, in export class MainContext { - private _appointments = new Observable(new Memory()); + private readonly _appointments = new Observable(new Memory()); - private _contacts = new Observable(new Memory()); + private readonly _contacts = new Observable(new Memory()); - private _members = new Observable(new Memory()); + private readonly _members = new Observable(new Memory()); createAppointment(title: string, startAt: Date, duration: number, members: Member[]) { const id = Uuid(); @@ -91,16 +92,16 @@ export class MainContext { this._members.add({ appointmentId: id, ...member - }, {id: Uuid()}) + }, {id: Uuid()}) as void ); } queryAppointments(dateFrom: Date, dateTo: Date) { - this._appointments.query().map() + //this._appointments.query().map() } - private _mapAppointment = ({startAt, title, duration, id}: AppointmentRecord) => ({ + private readonly _mapAppointment = ({startAt, title, duration, id}: AppointmentRecord) => ({ - }) + }); } diff --git a/playground/src/main/ts/view/MainWidget.tsx b/playground/src/main/ts/view/MainWidget.tsx --- a/playground/src/main/ts/view/MainWidget.tsx +++ b/playground/src/main/ts/view/MainWidget.tsx @@ -5,6 +5,8 @@ import ProgressBar from "./ProgressBar"; import Button = require("dijit/form/Button"); import { interval } from "rxjs"; +const Counter = ({ children }: { children: unknown[] }) => Counter: {children}; + @djclass export default class MainWidget extends djbase(DjxWidgetBase) { @@ -21,7 +23,6 @@ export default class MainWidget extends paused = false; render() { - const Counter = ({ children }: { children: unknown[] }) => Counter: {children}; return

Hi!

@@ -31,7 +32,6 @@ export default class MainWidget extends x/10) - .map(String) ), attach(this, "counterNode") )} /> s, @@ -71,11 +71,11 @@ export default class MainWidget extends }); } - private _onPauseClick = () => { + private readonly _onPauseClick = () => { this.set("paused", !this.paused); - } + }; - private _onToggleCounterClick = () => { + private readonly _onToggleCounterClick = () => { this.set("showCounter", !this.showCounter); - } + }; } diff --git a/playground/src/test/ts/index.ts b/playground/src/test/ts/index.ts --- a/playground/src/test/ts/index.ts +++ b/playground/src/test/ts/index.ts @@ -0,0 +1,1 @@ +/* noop */ \ No newline at end of file diff --git a/playground/src/test/tsconfig.json b/playground/src/test/tsconfig.json new file mode 100644 --- /dev/null +++ b/playground/src/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig", +} \ No newline at end of file diff --git a/playground/src/tsconfig.eslint.json b/playground/src/tsconfig.eslint.json --- a/playground/src/tsconfig.eslint.json +++ b/playground/src/tsconfig.eslint.json @@ -5,6 +5,5 @@ "noEmit": true }, "include": [ - "ts" ] } \ No newline at end of file