!128 fix: eslint error

Merge pull request !128 from 聪小陈/fix-eslint-error
This commit is contained in:
陈超涛 2024-01-31 01:58:17 +00:00 committed by Gitee
commit 63b82c2632
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
56 changed files with 232 additions and 182 deletions

View File

@ -57,6 +57,7 @@ module.exports = {
'no-constant-condition': 'off', 'no-constant-condition': 'off',
'no-function-declare-after-return/no-function-declare-after-return': 'error', 'no-function-declare-after-return/no-function-declare-after-return': 'error',
'@typescript-eslint/ban-ts-comment': 'warn'
}, },
globals: { globals: {
isDev: true, isDev: true,

View File

@ -3,7 +3,7 @@
**关联的 Issues:** [请列出与此 PR 相关的 issue 编号] **关联的 Issues:** [请列出与此 PR 相关的 issue 编号]
**检查项(无需修改,提交后界面上可勾选):** **检查项(无需修改,提交后界面上可勾选):**
- [ ] 代码已经被检视 - [ ] 代码已经被审查
- [ ] 代码符合项目的代码标准和最佳实践 - [ ] 代码符合项目的代码标准和最佳实践
- [ ] 代码已经通过所有测试用例 - [ ] 代码已经通过所有测试用例
- [ ] 代码不影响现有功能的正常使用 - [ ] 代码不影响现有功能的正常使用

View File

@ -70,6 +70,7 @@
"jest": "^25.5.4", "jest": "^25.5.4",
"jest-environment-jsdom-sixteen": "^1.0.3", "jest-environment-jsdom-sixteen": "^1.0.3",
"lint-staged": "^15.2.0", "lint-staged": "^15.2.0",
"openinula": "workspace:*",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"rollup": "^2.75.5", "rollup": "^2.75.5",
"rollup-plugin-execute": "^1.1.1", "rollup-plugin-execute": "^1.1.1",

17
packages/inula-cli/externals.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
declare module 'crequire';

View File

@ -57,7 +57,7 @@ export default (api: API) => {
api.applyHook({ name: 'afterStartDevServer' }); api.applyHook({ name: 'afterStartDevServer' });
}); });
} else { } else {
api.logger.error("Can't find config"); api.logger.error('Can\'t find config');
} }
break; break;
case 'vite': case 'vite':
@ -70,7 +70,7 @@ export default (api: API) => {
server.printUrls(); server.printUrls();
}); });
} else { } else {
api.logger.error("Can't find config"); api.logger.error('Can\'t find config');
} }
break; break;
default: default:

View File

@ -33,15 +33,17 @@ export default (api: API) => {
args._.shift(); args._.shift();
} }
if (args._.length === 0) { if (args._.length === 0) {
api.logger.warn("Can't find any generate options."); api.logger.warn('Can\'t find any generate options.');
return; return;
} }
switch (args._[0]) { switch (args._[0]) {
case 'jest': case 'jest':
args._.shift(); {
const isESM = api.packageJson['type'] === 'module'; args._.shift();
await generateJest(args, api.cwd, isESM); const isESM = api.packageJson['type'] === 'module';
await generateJest(args, api.cwd, isESM);
}
break; break;
default: default:
} }
@ -50,7 +52,7 @@ export default (api: API) => {
}; };
const generateJest = async (args: yargsParser.Arguments, cwd: string, isESM: boolean) => { const generateJest = async (args: yargsParser.Arguments, cwd: string, isESM: boolean) => {
let isTs: boolean = false; let isTs = false;
if (args['ts']) { if (args['ts']) {
isTs = true; isTs = true;
} else { } else {

View File

@ -25,7 +25,7 @@ export default (api: any) => {
initialState: api.userConfig.remoteProxy, initialState: api.userConfig.remoteProxy,
fn: async function (args: any, state: any) { fn: async function (args: any, state: any) {
if (!state) { if (!state) {
api.logger.error(`Invalid proxy config!`); api.logger.error('Invalid proxy config!');
return; return;
} }
const app = express(); const app = express();

View File

@ -61,7 +61,7 @@ export default async function run() {
break; break;
} }
let enableDebug: boolean = false; let enableDebug = false;
if (process.env.DEBUG === 'true') { if (process.env.DEBUG === 'true') {
enableDebug = true; enableDebug = true;

View File

@ -89,7 +89,7 @@ export default class Config {
getConfigFile(): string | null { getConfigFile(): string | null {
const configFileList: string[] = DEFAULT_CONFIG_FILES.map(f => join(this.cwd, f)); const configFileList: string[] = DEFAULT_CONFIG_FILES.map(f => join(this.cwd, f));
for (let configFile of configFileList) { for (const configFile of configFileList) {
if (existsSync(configFile)) { if (existsSync(configFile)) {
return configFile; return configFile;
} }

View File

@ -46,11 +46,11 @@ export default class Hub {
userConfig: UserConfig = {}; userConfig: UserConfig = {};
packageJson: PackageJSON; packageJson: PackageJSON;
stage: ServiceStage = ServiceStage.uninitialized; stage: ServiceStage = ServiceStage.uninitialized;
buildConfig: { name: string; config: object }[] = []; buildConfig: { name: string; config: Record<string, unknown> }[] = [];
pluginManager: Plugin; pluginManager: Plugin;
buildConfigPath: BuildConfig[] = []; buildConfigPath: BuildConfig[] = [];
devBuildConfig: object = {}; devBuildConfig: Record<string, unknown> = {};
compileMode: string = ''; compileMode = '';
builtInPlugins: string[] = []; builtInPlugins: string[] = [];
pluginPaths: string[] = []; pluginPaths: string[] = [];
devProxy: DevProxy | null = null; devProxy: DevProxy | null = null;
@ -150,14 +150,15 @@ export default class Hub {
async analyzeBuildConfig() { async analyzeBuildConfig() {
if (this.userConfig.devBuildConfig) { if (this.userConfig.devBuildConfig) {
let { name, path, env } = this.userConfig.devBuildConfig; let { path } = this.userConfig.devBuildConfig;
const { env } = this.userConfig.devBuildConfig;
path = isAbsolute(path) ? path : join(process.cwd(), path); path = isAbsolute(path) ? path : join(process.cwd(), path);
if (!existsSync(path)) { if (!existsSync(path)) {
this.logger.warn(`Cant't find dev build config. Path is ${path}`); this.logger.warn(`Cant't find dev build config. Path is ${path}`);
return; return;
} }
this.logger.debug(`Find dev build config. Path is ${path}`); this.logger.debug(`Find dev build config. Path is ${path}`);
let bc = await loadModule<object | Function>(path); const bc = await loadModule<Record<string, unknown> | ((...args: any[]) => any)>(path);
if (bc == undefined) { if (bc == undefined) {
return; return;
} }
@ -196,14 +197,15 @@ export default class Hub {
} }
this.buildConfigPath.forEach(async config => { this.buildConfigPath.forEach(async config => {
let { name, path } = config; let { path } = config;
const { name } = config;
path = isAbsolute(path) ? path : join(process.cwd(), path); path = isAbsolute(path) ? path : join(process.cwd(), path);
if (!existsSync(path)) { if (!existsSync(path)) {
this.logger.debug(`Cant't find build config. Path is ${path}`); this.logger.debug(`Cant't find build config. Path is ${path}`);
return; return;
} }
this.logger.debug(`Find build config. Path is ${path}`); this.logger.debug(`Find build config. Path is ${path}`);
let bc = await loadModule<object | Function>(path); const bc = await loadModule<Record<string, unknown> | ((...args: any[]) => any)>(path);
if (bc == undefined) { if (bc == undefined) {
return; return;
} }

View File

@ -36,7 +36,7 @@ export interface IPlugin {
id: string; id: string;
key: string; key: string;
path: string; path: string;
apply: Function; apply: (...args: any[]) => any;
} }
export default class Plugin { export default class Plugin {
@ -57,7 +57,7 @@ export default class Plugin {
} = {}; } = {};
hub: Hub; hub: Hub;
logger: Logger; logger: Logger;
registerFunction: Function[] = []; registerFunction: ((...args: any[]) => any)[] = [];
// 解决调用this[props]时ts提示属性未知 // 解决调用this[props]时ts提示属性未知
[key: string]: any; [key: string]: any;
@ -110,7 +110,7 @@ export default class Plugin {
}); });
for (const obj of objs) { for (const obj of objs) {
const module: Function | undefined = await loadModule(obj.path); const module: ((...args: any[]) => any) | undefined = await loadModule(obj.path);
if (module) { if (module) {
try { try {
module(obj.api); module(obj.api);

View File

@ -55,7 +55,7 @@ export default class PluginAPI {
this.register(hook); this.register(hook);
} }
registerMethod(fn: Function) { registerMethod(fn: (...args: any[]) => any) {
this.manager.registerFunction.push(fn); this.manager.registerFunction.push(fn);
} }

View File

@ -19,8 +19,8 @@ import { Logger } from '../utils/logger.js';
import type * as http from 'http'; import type * as http from 'http';
import type * as express from 'express'; import type * as express from 'express';
interface Request extends express.Request {} type Request = express.Request;
interface Response extends express.Response {} type Response = express.Response;
export interface IDep { export interface IDep {
[name: string]: string; [name: string]: string;
@ -37,7 +37,7 @@ export interface IPlugin {
id: string; id: string;
key: string; key: string;
path: string; path: string;
apply: Function; apply: (...args: any[]) => any;
config?: IPluginConfig; config?: IPluginConfig;
isPreset?: boolean; isPreset?: boolean;
@ -45,7 +45,7 @@ export interface IPlugin {
export interface IPluginConfig { export interface IPluginConfig {
default?: any; default?: any;
onChange?: string | Function; onChange?: string | ((...args: any[]) => any);
} }
export interface IHook { export interface IHook {
@ -94,7 +94,7 @@ export interface API {
(hook: IHook): void; (hook: IHook): void;
}; };
registerMethod: { registerMethod: {
(method: Function): void; (method: (...args: any[]) => any): void;
}; };
applyHook: { applyHook: {
(opts: applyHookConfig): void; (opts: applyHookConfig): void;
@ -133,8 +133,8 @@ export interface MockConfig {
export interface DevBuildConfig { export interface DevBuildConfig {
name: string; name: string;
path: string; path: string;
args?: object; args?: Record<string, unknown>;
env?: object; env?: Record<string, unknown>;
devProxy?: DevProxy; devProxy?: DevProxy;
} }
@ -147,8 +147,8 @@ export interface DevProxy {
export interface BuildConfig { export interface BuildConfig {
name: string; name: string;
path: string; path: string;
args?: object; args?: Record<string, unknown>;
env?: object; env?: Record<string, unknown>;
} }
export type ExportUserConfig = UserConfig | Promise<UserConfig>; export type ExportUserConfig = UserConfig | Promise<UserConfig>;

View File

@ -38,13 +38,15 @@ function getMocksFile() {
const mockFiles = globSync('**/*.js', { const mockFiles = globSync('**/*.js', {
cwd: mockDir, cwd: mockDir,
}); });
let ret = mockFiles.reduce((mocks: any, mockFile: string) => { const ret = mockFiles.reduce((mocks: any, mockFile: string) => {
if (!mockFile.startsWith('_')) { if (!mockFile.startsWith('_')) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const file = require(join(mockDir, mockFile));
mocks = { mocks = {
...mocks, ...mocks,
...require(join(mockDir, mockFile)), ...file,
}; };
console.log('mockFile', require(join(mockDir, mockFile))); console.log('mockFile', file);
} }
return mocks; return mocks;
@ -54,8 +56,8 @@ function getMocksFile() {
} }
function generateRoutes(app: any) { function generateRoutes(app: any) {
let mockStartIndex = app._router.stack.length, const mockStartIndex = app._router.stack.length;
mocks: Mock = {}; let mocks: Mock = {};
try { try {
mocks = getMocksFile(); mocks = getMocksFile();

View File

@ -16,7 +16,6 @@
import { dirname } from 'path'; import { dirname } from 'path';
import { readFileSync, writeFileSync } from 'fs'; import { readFileSync, writeFileSync } from 'fs';
import resolve from 'resolve'; import resolve from 'resolve';
// @ts-ignore
import crequire from 'crequire'; import crequire from 'crequire';
import { createRequire } from 'module'; import { createRequire } from 'module';
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);

View File

@ -11,9 +11,16 @@
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true, "esModuleInterop": true,
}, },
"include": ["src/**/*"], "include": [
"exclude": ["node_modules", "**/*.spec.ts", "./src/template/**/*"], "src/**/*",
"./externals.d.ts"
],
"exclude": [
"node_modules",
"**/*.spec.ts",
"./src/template/**/*"
],
"ts-node": { "ts-node": {
"esm": true, "esm": true,
}, },
} }

View File

@ -7,7 +7,7 @@
"types": "build/@types/index.d.ts", "types": "build/@types/index.d.ts",
"scripts": { "scripts": {
"demo-serve": "webpack serve --mode=development", "demo-serve": "webpack serve --mode=development",
"rollup-build": "rollup --config rollup.config.js && npm run build-types", "build": "rollup --config rollup.config.js && npm run build-types",
"build-types": "tsc -p tsconfig.json && rollup -c build-type.js", "build-types": "tsc -p tsconfig.json && rollup -c build-type.js",
"test": "jest --config jest.config.js", "test": "jest --config jest.config.js",
"test-c": "jest --coverage" "test-c": "jest --coverage"

View File

@ -117,7 +117,7 @@ export class I18n extends EventDispatcher<Events> {
formatMessage( formatMessage(
id: MessageDescriptor | string, id: MessageDescriptor | string,
values: Object | undefined = {}, values: Record<string, unknown> | undefined = {},
{ message, context, formatOptions }: MessageOptions = {} { message, context, formatOptions }: MessageOptions = {}
) { ) {
return getFormatMessage(this, id, values, { message, context, formatOptions }); return getFormatMessage(this, id, values, { message, context, formatOptions });

View File

@ -40,11 +40,11 @@ class Translation {
* @param values * @param values
* @param formatOptions * @param formatOptions
*/ */
translate(values: object, formatOptions: FormatOptions = {}): string { translate(values: Record<string, unknown>, formatOptions: FormatOptions = {}): string {
const createTextFormatter = ( const createTextFormatter = (
locale: Locale, locale: Locale,
locales: Locales, locales: Locales,
values: object, values: Record<string, unknown>,
formatOptions: FormatOptions, formatOptions: FormatOptions,
localeConfig: LocaleConfig localeConfig: LocaleConfig
) => { ) => {
@ -66,13 +66,13 @@ class Translation {
return textFormatter; return textFormatter;
}; };
let textFormatter = createTextFormatter(this.locale, this.locales, values, formatOptions, this.localeConfig); const textFormatter = createTextFormatter(this.locale, this.locales, values, formatOptions, this.localeConfig);
// 通过递归方法formatCore进行格式化处理 // 通过递归方法formatCore进行格式化处理
const result = this.formatMessage(this.compiledMessage, textFormatter); const result = this.formatMessage(this.compiledMessage, textFormatter);
return result; // 返回要格式化的结果 return result; // 返回要格式化的结果
} }
formatMessage(compiledMessage: CompiledMessage, textFormatter: Function) { formatMessage(compiledMessage: CompiledMessage, textFormatter: (...args: any[]) => any) {
if (!Array.isArray(compiledMessage)) { if (!Array.isArray(compiledMessage)) {
return compiledMessage; return compiledMessage;
} }

View File

@ -23,10 +23,11 @@ import creatI18nCache from './cache/cache';
export function getFormatMessage( export function getFormatMessage(
i18n: I18n, i18n: I18n,
id: MessageDescriptor | string, id: MessageDescriptor | string,
values: Object | undefined = {}, values: Record<string, unknown> | undefined = {},
options: MessageOptions = {} options: MessageOptions = {}
) { ) {
let { message, context, formatOptions } = options; let { message, context } = options;
const { formatOptions } = options;
const cache = i18n.cache ?? creatI18nCache(); const cache = i18n.cache ?? creatI18nCache();
if (typeof id !== 'string') { if (typeof id !== 'string') {
values = values || id.defaultValues; values = values || id.defaultValues;

View File

@ -19,19 +19,19 @@ import { LexerInterface } from '../types/interfaces';
class Lexer<T> implements LexerInterface<T> { class Lexer<T> implements LexerInterface<T> {
readonly startState: string; readonly startState: string;
readonly states: Record<string, any>; readonly states: Record<string, any>;
private buffer: string = ''; private buffer = '';
private stack: string[] = []; private stack: string[] = [];
private index: number = 0; private index = 0;
private line: number = 1; private line = 1;
private col: number = 1; private col = 1;
private queuedText: string = ''; private queuedText = '';
private state: string = ''; private state = '';
private groups: string[] = []; private groups: string[] = [];
private error: Record<string, any> | undefined; private error: Record<string, any> | undefined;
private regexp; private regexp;
private fast: object = {}; private fast: Record<string, unknown> = {};
private queuedGroup: string | null = ''; private queuedGroup: string | null = '';
private value: string = ''; private value = '';
constructor(unionReg: Record<string, any>, startState: string) { constructor(unionReg: Record<string, any>, startState: string) {
this.startState = startState; this.startState = startState;

View File

@ -14,11 +14,11 @@
*/ */
const body: Record<string, any> = { const body: Record<string, any> = {
doubleapos: { match: "''", value: () => "'" }, doubleapos: { match: '\'\'', value: () => '\'' },
quoted: { quoted: {
lineBreaks: true, lineBreaks: true,
match: /'[{}#](?:[^]*?[^'])?'(?!')/u, match: /'[{}#](?:[^]*?[^'])?'(?!')/u,
value: src => src.slice(1, -1).replace(/''/g, "'"), value: src => src.slice(1, -1).replace(/''/g, '\''),
}, },
argument: { argument: {
lineBreaks: true, lineBreaks: true,

View File

@ -23,8 +23,8 @@ const defaultErrorRule = ruleUtils.getRuleOptions('error', { lineBreaks: true, s
// 解析规则并生成词法分析器所需的数据结构,以便进行词法分析操作 // 解析规则并生成词法分析器所需的数据结构,以便进行词法分析操作
function parseRules(rules: Record<string, any>, hasStates: boolean): Record<string, any> { function parseRules(rules: Record<string, any>, hasStates: boolean): Record<string, any> {
let errorRule: Record<string, any> | null = null; let errorRule: Record<string, any> | null = null;
const fast: object = {}; const fast: Record<string, unknown> = {};
let enableFast: boolean = true; let enableFast = true;
let unicodeFlag: boolean | null = null; let unicodeFlag: boolean | null = null;
const groups: Record<string, any>[] = []; const groups: Record<string, any>[] = [];
const parts: string[] = []; const parts: string[] = [];
@ -148,6 +148,7 @@ function parseMappingRule(mappingRule: Record<string, any>, startState?: string)
} }
}); });
} }
// eslint-disable-next-line
rules.splice.apply(rules, splice); rules.splice.apply(rules, splice);
j--; j--;
} }
@ -176,7 +177,7 @@ function parseMappingRule(mappingRule: Record<string, any>, startState?: string)
return new Lexer(mappingAllRules, startState); return new Lexer(mappingAllRules, startState);
} }
function processFast(match, fast: {}, options) { function processFast(match, fast: Record<string, unknown>, options) {
while (match.length && typeof match[0] === 'string' && match[0].length === 1) { while (match.length && typeof match[0] === 'string' && match[0].length === 1) {
const word = match.shift(); const word = match.shift();
fast[word.charCodeAt(0)] = options; fast[word.charCodeAt(0)] = options;

View File

@ -42,7 +42,7 @@ class Parser {
isSelectKeyValid(token: RawToken, type: Select['type'], value: string) { isSelectKeyValid(token: RawToken, type: Select['type'], value: string) {
if (value[0] === '=') { if (value[0] === '=') {
if (type === 'select') { if (type === 'select') {
throw new Error(`The key value of the select type is invalid.`); throw new Error('The key value of the select type is invalid.');
} }
} else if (type !== 'select') { } else if (type !== 'select') {
const values = type === 'plural' ? this.cardinalKeys : this.ordinalKeys; const values = type === 'plural' ? this.cardinalKeys : this.ordinalKeys;
@ -132,7 +132,7 @@ class Parser {
if (checkSelectType(nextToken.value.toLowerCase())) { if (checkSelectType(nextToken.value.toLowerCase())) {
throw new Error(`Invalid parameter type: ${nextToken.value}`); throw new Error(`Invalid parameter type: ${nextToken.value}`);
} }
let param = this.parse(isPlural); const param = this.parse(isPlural);
return { return {
type: 'function', type: 'function',

View File

@ -19,7 +19,7 @@ import Lexer from '../parser/Lexer';
// FormattedMessage的参数定义 // FormattedMessage的参数定义
export interface FormattedMessageProps extends MessageDescriptor { export interface FormattedMessageProps extends MessageDescriptor {
values?: object; values?: Record<string, unknown>;
tagName?: string; tagName?: string;
children?(nodes: any[]): any; children?(nodes: any[]): any;
@ -203,7 +203,7 @@ export interface InjectedIntl {
// 信息格式化 // 信息格式化
formatMessage( formatMessage(
messageDescriptor: MessageDescriptor, messageDescriptor: MessageDescriptor,
values?: object, values?: Record<string, unknown>,
options?: MessageOptions, options?: MessageOptions,
useMemorize?: boolean useMemorize?: boolean
): string; ): string;

View File

@ -32,7 +32,7 @@ export type Locale = string;
export type Locales = Locale | Locale[]; export type Locales = Locale | Locale[];
export type LocaleConfig = { plurals?: Function }; export type LocaleConfig = { plurals?: (...arg: any) => any };
export type AllLocaleConfig = Record<Locale, LocaleConfig>; export type AllLocaleConfig = Record<Locale, LocaleConfig>;
@ -59,7 +59,7 @@ export type Token = Content | PlainArg | FunctionArg | Select | Octothorpe;
export type DatePool = Date | string; export type DatePool = Date | string;
export type SelectPool = string | object; export type SelectPool = string | Record<string, unknown>;
export type RawToken = { export type RawToken = {
type: string; type: string;
@ -76,9 +76,9 @@ export type I18nProviderProps = I18nContextProps & configProps;
export type IntlType = { export type IntlType = {
i18n: I18n; i18n: I18n;
formatMessage: Function; formatMessage: (...args: any[]) => any;
formatNumber: Function; formatNumber: (...args: any[]) => any;
formatDate: Function; formatDate: (...args: any[]) => any;
}; };
export interface InjectedIntlProps { export interface InjectedIntlProps {

View File

@ -73,7 +73,9 @@ function copyStaticProps<T, U>(targetComponent: T, sourceComponent: U): T {
try { try {
// 在一个已有的targetComponent对象上增加sourceComponent的属性 // 在一个已有的targetComponent对象上增加sourceComponent的属性
Object.defineProperty(targetComponent, key, Object.getOwnPropertyDescriptor(sourceComponent, key)!); Object.defineProperty(targetComponent, key, Object.getOwnPropertyDescriptor(sourceComponent, key)!);
} catch (e) {} } catch (e) {
console.log('Error occurred while copying static props:', e);
}
} }
}); });

View File

@ -32,12 +32,14 @@ const checkSticky = () => typeof new RegExp('')?.sticky === 'boolean';
// 转义正则表达式中的特殊字符 // 转义正则表达式中的特殊字符
function transferReg(s: string): string { function transferReg(s: string): string {
// eslint-disable-next-line
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
} }
// 计算正则表达式中捕获组的数量 // 计算正则表达式中捕获组的数量
function getRegGroups(s: string): number { function getRegGroups(s: string): number {
const re = new RegExp('|' + s); const re = new RegExp('|' + s);
// eslint-disable-next-line
return re.exec('')?.length! - 1; return re.exec('')?.length! - 1;
} }

View File

@ -90,19 +90,19 @@ describe('I18n', () => {
}); });
it('._ allow escaping syntax characters', () => { it('._ allow escaping syntax characters', () => {
const messages = { const messages = {
"My ''name'' is '{name}'": "Mi ''nombre'' es '{name}'", 'My \'\'name\'\' is \'{name}\'': 'Mi \'\'nombre\'\' es \'{name}\'',
}; };
const i18n = new I18n({ const i18n = new I18n({
locale: 'es', locale: 'es',
messages: { es: messages }, messages: { es: messages },
}); });
expect(i18n.formatMessage("My ''name'' is '{name}'")).toEqual("Mi 'nombre' es {name}"); expect(i18n.formatMessage('My \'\'name\'\' is \'{name}\'')).toEqual('Mi \'nombre\' es {name}');
}); });
it('._ should format message from catalog', function () { it('._ should format message from catalog', function () {
const messages = { const messages = {
Hello: 'Salut', Hello: 'Salut',
id: "Je m'appelle {name}", id: 'Je m\'appelle {name}',
}; };
const i18n = new I18n({ const i18n = new I18n({
locale: 'fr', locale: 'fr',
@ -110,7 +110,7 @@ describe('I18n', () => {
}); });
expect(i18n.locale).toEqual('fr'); expect(i18n.locale).toEqual('fr');
expect(i18n.formatMessage('Hello')).toEqual('Salut'); expect(i18n.formatMessage('Hello')).toEqual('Salut');
expect(i18n.formatMessage('id', { name: 'Fred' })).toEqual("Je m'appelle Fred"); expect(i18n.formatMessage('id', { name: 'Fred' })).toEqual('Je m\'appelle Fred');
}); });
it('should return the formatted date and time', () => { it('should return the formatted date and time', () => {

View File

@ -43,7 +43,7 @@ describe('eventEmitter', () => {
expect(listener).not.toBeCalled(); expect(listener).not.toBeCalled();
}); });
it("should do nothing when even doesn't exist", () => { it('should do nothing when even doesn\'t exist', () => {
const unknown = jest.fn(); const unknown = jest.fn();
const emitter = new EventEmitter(); const emitter = new EventEmitter();

View File

@ -38,7 +38,7 @@ const useIR = <T = unknown>(
Inula.useEffect(() => { Inula.useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
try { try {
let result = await irClient.query(url, config, options); const result = await irClient.query(url, config, options);
document.addEventListener('request', handleRequest(result)); document.addEventListener('request', handleRequest(result));
setData(result); // 未设置轮询查询时展示一次 setData(result); // 未设置轮询查询时展示一次

View File

@ -48,7 +48,7 @@ class InterceptorManager<V> implements IrInterceptorManager<V> {
this.handlers = []; this.handlers = [];
} }
forEach(func: Function) { forEach(func: (...arg: any) => any) {
utils.forEach(this.handlers, function forEachHandler(h: any) { utils.forEach(this.handlers, function forEachHandler(h: any) {
if (h !== null) { if (h !== null) {
func(h); func(h);

View File

@ -23,12 +23,10 @@ import CancelError from '../cancel/CancelError';
export const fetchRequest = (config: IrRequestConfig): Promise<IrResponse> => { export const fetchRequest = (config: IrRequestConfig): Promise<IrResponse> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let { const {
method = 'GET', method = 'GET',
baseURL, baseURL,
url,
params = null, params = null,
data = null,
headers = {}, headers = {},
responseType, responseType,
timeout = 0, timeout = 0,
@ -37,10 +35,10 @@ export const fetchRequest = (config: IrRequestConfig): Promise<IrResponse> => {
withCredentials = false, withCredentials = false,
onUploadProgress = null, onUploadProgress = null,
onDownloadProgress = null, onDownloadProgress = null,
signal,
} = config; } = config;
let { data = null, url, signal } = config;
let controller = new AbortController(); const controller = new AbortController();
if (!signal) { if (!signal) {
signal = controller.signal; signal = controller.signal;
} }

View File

@ -21,10 +21,9 @@ import { Method, ResponseType } from '../types/types';
export const ieFetchRequest = (config: IrRequestConfig): Promise<IrResponse> => { export const ieFetchRequest = (config: IrRequestConfig): Promise<IrResponse> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let { const {
method = 'get', method = 'get',
baseURL, baseURL,
url,
params = null, params = null,
data = null, data = null,
headers = {}, headers = {},
@ -34,7 +33,7 @@ export const ieFetchRequest = (config: IrRequestConfig): Promise<IrResponse> =>
cancelToken = null, cancelToken = null,
withCredentials = false, withCredentials = false,
} = config; } = config;
let { url } = config;
let controller: any; let controller: any;
let signal; let signal;

View File

@ -231,7 +231,7 @@ export interface IrInterceptorManager<T> {
clear(): void; clear(): void;
// 过滤跳过迭代器 // 过滤跳过迭代器
forEach(func: Function): void; forEach(func: (...arg: any) => any): void;
} }
export interface IrErrorInterface { export interface IrErrorInterface {

View File

@ -39,7 +39,7 @@ export type IrTransformer = (data: any, headers?: IrHeaders) => any;
// Headers // Headers
export type HeaderMap = Record<string, string | string[]>; export type HeaderMap = Record<string, string | string[]>;
export type HeaderMatcher = boolean | RegExp | Function; export type HeaderMatcher = boolean | RegExp | ((...args: any[]) => any);
// Promise 成功和拒绝类型 // Promise 成功和拒绝类型
export type FulfilledFn<T> = (value: T) => T | Promise<T>; // 泛型确保了拦截器链中各个环节之间的一致性,避免数据类型不匹配引发的错误 export type FulfilledFn<T> = (value: T) => T | Promise<T>; // 泛型确保了拦截器链中各个环节之间的一致性,避免数据类型不匹配引发的错误

View File

@ -23,7 +23,7 @@ import { Callback, FilterFunc, PropFilterFunc } from '../../types/types';
* *
* @returns {any} func 使 apply() thisArg func * @returns {any} func 使 apply() thisArg func
*/ */
function bind(func: Function, thisArg: any): (...args: any[]) => any { function bind(func: (...args: any[]) => any, thisArg: any): (...args: any[]) => any {
return (...args: any[]) => func.apply(thisArg, args); return (...args: any[]) => func.apply(thisArg, args);
} }
@ -107,7 +107,7 @@ const checkHTMLForm = createTypeChecker('HTMLFormElement');
*/ */
function forEach<T>( function forEach<T>(
input: T | T[] | Record<string, T> | null | undefined, input: T | T[] | Record<string, T> | null | undefined,
func: Function, func: (...args: any[]) => any,
options: { includeAll?: boolean } = {} options: { includeAll?: boolean } = {}
): void { ): void {
if (input === null || input === undefined) { if (input === null || input === undefined) {
@ -194,7 +194,7 @@ function extendObject(
* @returns {string[]} * @returns {string[]}
*/ */
function getAllPropertyNames(obj: Record<string, any>): string[] { function getAllPropertyNames(obj: Record<string, any>): string[] {
let result: string[] = []; const result: string[] = [];
let currentObj = obj; let currentObj = obj;
while (currentObj) { while (currentObj) {
const propNames = Object.getOwnPropertyNames(currentObj); const propNames = Object.getOwnPropertyNames(currentObj);
@ -406,6 +406,7 @@ function objectToQueryString(obj: Record<string, any>) {
const all = <T>(promises: Array<Promise<T>>): Promise<T[]> => Promise.all(promises); const all = <T>(promises: Array<Promise<T>>): Promise<T[]> => Promise.all(promises);
function spread<T>(callback: Callback<T>): (arr: any[]) => T { function spread<T>(callback: Callback<T>): (arr: any[]) => T {
// eslint-disable-next-line
return (arr: any[]): T => callback.apply(null, arr); return (arr: any[]): T => callback.apply(null, arr);
} }

View File

@ -17,7 +17,7 @@ import IrHeaders from '../../core/IrHeaders';
import defaultConfig from '../../config/defaultConfig'; import defaultConfig from '../../config/defaultConfig';
import { IrRequestConfig, IrResponse } from '../../types/interfaces'; import { IrRequestConfig, IrResponse } from '../../types/interfaces';
function transformData(inputConfig: IrRequestConfig, func: Function, response?: IrResponse) { function transformData(inputConfig: IrRequestConfig, func: (...arg: any) => any, response?: IrResponse) {
const config = inputConfig || defaultConfig; const config = inputConfig || defaultConfig;
const context = response || config; const context = response || config;
const headers = IrHeaders.from(context.headers); const headers = IrHeaders.from(context.headers);

View File

@ -20,9 +20,9 @@ function convertRawHeaders(rawHeaders: string): HeaderMap {
if (rawHeaders) { if (rawHeaders) {
rawHeaders.split('\n').forEach((item: string) => { rawHeaders.split('\n').forEach((item: string) => {
let i = item.indexOf(':'); const i = item.indexOf(':');
let key = item.substring(0, i).trim().toLowerCase(); const key = item.substring(0, i).trim().toLowerCase();
let val = item.substring(i + 1).trim(); const val = item.substring(i + 1).trim();
if (!key || (convertedHeaders[key] && key !== 'set-cookie')) { if (!key || (convertedHeaders[key] && key !== 'set-cookie')) {
return; return;

View File

@ -38,7 +38,7 @@ function processValueByParser(key: string, value: any, parser?: HeaderMatcher):
return parseKeyValuePairs(value); return parseKeyValuePairs(value);
} }
if (utils.checkFunction(parser)) { if (utils.checkFunction(parser)) {
return (parser as Function)(value, key); return (parser as (value: any, key: string) => any)(value, key);
} }
if (utils.checkRegExp(parser)) { if (utils.checkRegExp(parser)) {
return (parser as RegExp).exec(value)?.filter(item => item !== undefined); return (parser as RegExp).exec(value)?.filter(item => item !== undefined);

View File

@ -11,7 +11,10 @@
"declaration": true, "declaration": true,
"moduleResolution": "node", "moduleResolution": "node",
"sourceMap": true, "sourceMap": true,
"downlevelIteration": true "downlevelIteration": true,
"paths": {
"openinula": ["../packages/inula"],
}
}, },
"include": [ "include": [
"./src/**/*", "./src/**/*",

View File

@ -46,7 +46,6 @@
"@babel/plugin-transform-template-literals": "7.16.7", "@babel/plugin-transform-template-literals": "7.16.7",
"@babel/preset-env": "7.16.7", "@babel/preset-env": "7.16.7",
"@babel/preset-typescript": "^7.16.7", "@babel/preset-typescript": "^7.16.7",
"openinula": "^0.0.1",
"@rollup/plugin-babel": "^6.0.3", "@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-node-resolve": "^15.1.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",

View File

@ -18,14 +18,14 @@ import { createPath } from '../utils';
describe('createPath', () => { describe('createPath', () => {
describe('given only a pathname', () => { describe('given only a pathname', () => {
it('returns the pathname unchanged', () => { it('returns the pathname unchanged', () => {
let path = createPath({ pathname: 'https://google.com' }); const path = createPath({ pathname: 'https://google.com' });
expect(path).toBe('https://google.com'); expect(path).toBe('https://google.com');
}); });
}); });
describe('given a pathname and a search param', () => { describe('given a pathname and a search param', () => {
it('returns the constructed pathname', () => { it('returns the constructed pathname', () => {
let path = createPath({ const path = createPath({
pathname: 'https://google.com', pathname: 'https://google.com',
search: '?something=cool', search: '?something=cool',
}); });
@ -35,7 +35,7 @@ describe('createPath', () => {
describe('given a pathname and a search param without ?', () => { describe('given a pathname and a search param without ?', () => {
it('returns the constructed pathname', () => { it('returns the constructed pathname', () => {
let path = createPath({ const path = createPath({
pathname: 'https://google.com', pathname: 'https://google.com',
search: 'something=cool', search: 'something=cool',
}); });
@ -45,7 +45,7 @@ describe('createPath', () => {
describe('given a pathname and a hash param', () => { describe('given a pathname and a hash param', () => {
it('returns the constructed pathname', () => { it('returns the constructed pathname', () => {
let path = createPath({ const path = createPath({
pathname: 'https://google.com', pathname: 'https://google.com',
hash: '#section-1', hash: '#section-1',
}); });
@ -55,7 +55,7 @@ describe('createPath', () => {
describe('given a pathname and a hash param without #', () => { describe('given a pathname and a hash param without #', () => {
it('returns the constructed pathname', () => { it('returns the constructed pathname', () => {
let path = createPath({ const path = createPath({
pathname: 'https://google.com', pathname: 'https://google.com',
hash: 'section-1', hash: 'section-1',
}); });
@ -65,7 +65,7 @@ describe('createPath', () => {
describe('given a full location object', () => { describe('given a full location object', () => {
it('returns the constructed pathname', () => { it('returns the constructed pathname', () => {
let path = createPath({ const path = createPath({
pathname: 'https://google.com', pathname: 'https://google.com',
search: 'something=cool', search: 'something=cool',
hash: '#section-1', hash: '#section-1',

View File

@ -29,7 +29,7 @@ function useLocation() {
return useContext(RouterContext).location; return useContext(RouterContext).location;
} }
function useParams<P>(): Params<P> | {}; function useParams<P>(): Params<P> | Record<string, unknown>;
function useParams() { function useParams() {
const match = useContext(RouterContext).match; const match = useContext(RouterContext).match;
return match ? match.params : {}; return match ? match.params : {};

View File

@ -38,9 +38,9 @@ describe('path lexer Test', () => {
expect(tokens).toStrictEqual([{ type: 'delimiter', value: '/' }]); expect(tokens).toStrictEqual([{ type: 'delimiter', value: '/' }]);
}); });
it(`don't start with a slash`, () => { it('don\'t start with a slash', () => {
const func = () => lexer('abc.com'); const func = () => lexer('abc.com');
expect(func).toThrow(Error(`Url must start with "/".`)); expect(func).toThrow(Error('Url must start with "/".'));
}); });
it('dynamic params test', () => { it('dynamic params test', () => {

View File

@ -26,9 +26,9 @@ export function lexer(path: string): Token[] {
return tokens; return tokens;
} }
let urlPath = cleanPath(path); const urlPath = cleanPath(path);
if (urlPath !== '*' && !urlPath.startsWith('/')) { if (urlPath !== '*' && !urlPath.startsWith('/')) {
throw new Error(`Url must start with "/".`); throw new Error('Url must start with "/".');
} }
const getLiteral = () => { const getLiteral = () => {

View File

@ -84,7 +84,7 @@ export function createPathParser<P = unknown>(pathname: string, option: ParserOp
* @param currentIdx * @param currentIdx
*/ */
const lookToNextDelimiter = (currentIdx: number): boolean => { const lookToNextDelimiter = (currentIdx: number): boolean => {
let hasOptionalParam: boolean = false; let hasOptionalParam = false;
while (currentIdx < tokens.length && tokens[currentIdx].type !== TokenType.Delimiter) { while (currentIdx < tokens.length && tokens[currentIdx].type !== TokenType.Delimiter) {
if (tokens[currentIdx].value === '?' || tokens[currentIdx].value === '*') { if (tokens[currentIdx].value === '?' || tokens[currentIdx].value === '*') {
hasOptionalParam = true; hasOptionalParam = true;
@ -98,8 +98,10 @@ export function createPathParser<P = unknown>(pathname: string, option: ParserOp
const nextToken = tokens[tokenIdx + 1]; const nextToken = tokens[tokenIdx + 1];
switch (token.type) { switch (token.type) {
case TokenType.Delimiter: case TokenType.Delimiter:
const hasOptional = lookToNextDelimiter(tokenIdx + 1); {
pattern += `/${hasOptional ? '?' : ''}`; const hasOptional = lookToNextDelimiter(tokenIdx + 1);
pattern += `/${hasOptional ? '?' : ''}`;
}
break; break;
case TokenType.Static: case TokenType.Static:
pattern += token.value.replace(REGEX_CHARS_RE, '\\$&'); pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
@ -111,28 +113,30 @@ export function createPathParser<P = unknown>(pathname: string, option: ParserOp
scores.push(MatchScore.static); scores.push(MatchScore.static);
break; break;
case TokenType.Param: case TokenType.Param:
// 动态参数支持形如/:param、/:param*、/:param?、/:param(\\d+)的形式 {
let paramRegexp = ''; // 动态参数支持形如/:param、/:param*、/:param?、/:param(\\d+)的形式
if (nextToken) { let paramRegexp = '';
switch (nextToken.type) { if (nextToken) {
case TokenType.LBracket: switch (nextToken.type) {
// 跳过当前Token和左括号 case TokenType.LBracket:
tokenIdx += 2; // 跳过当前Token和左括号
while (tokens[tokenIdx].type !== TokenType.RBracket) { tokenIdx += 2;
paramRegexp += tokens[tokenIdx].value; while (tokens[tokenIdx].type !== TokenType.RBracket) {
paramRegexp += tokens[tokenIdx].value;
tokenIdx++;
}
paramRegexp = `(${paramRegexp})`;
break;
case TokenType.Pattern:
tokenIdx++; tokenIdx++;
} paramRegexp += `(${nextToken.value === '*' ? '.*' : BASE_PARAM_PATTERN})${nextToken.value}`;
paramRegexp = `(${paramRegexp})`; break;
break; }
case TokenType.Pattern:
tokenIdx++;
paramRegexp += `(${nextToken.value === '*' ? '.*' : BASE_PARAM_PATTERN})${nextToken.value}`;
break;
} }
pattern += paramRegexp ? `(?:${paramRegexp})` : `(${BASE_PARAM_PATTERN})`;
keys.push(token.value);
scores.push(MatchScore.param);
} }
pattern += paramRegexp ? `(?:${paramRegexp})` : `(${BASE_PARAM_PATTERN})`;
keys.push(token.value);
scores.push(MatchScore.param);
break; break;
case TokenType.WildCard: case TokenType.WildCard:
keys.push(token.value); keys.push(token.value);
@ -167,13 +171,13 @@ export function createPathParser<P = unknown>(pathname: string, option: ParserOp
return null; return null;
} }
const matchedPath = reMatch[0]; const matchedPath = reMatch[0];
let params: Params<P> = {}; const params: Params<P> = {};
let parseScore: number[] = Array.from(scores); const parseScore: number[] = Array.from(scores);
for (let i = 1; i < reMatch.length; i++) { for (let i = 1; i < reMatch.length; i++) {
let param = reMatch[i]; const param = reMatch[i];
let key = keys[i - 1]; const key = keys[i - 1];
if (key === '*' && param) { if (key === '*' && param) {
let value = param.split('/'); const value = param.split('/');
if (!Array.isArray(params['*'])) { if (!Array.isArray(params['*'])) {
params['*'] = value; params['*'] = value;
} else { } else {
@ -212,11 +216,13 @@ export function createPathParser<P = unknown>(pathname: string, option: ParserOp
path += params[token.value]; path += params[token.value];
break; break;
case TokenType.WildCard: case TokenType.WildCard:
let wildCard = params['*']; {
if (wildCard instanceof Array) { const wildCard = params['*'];
path += wildCard.join('/'); if (wildCard instanceof Array) {
} else { path += wildCard.join('/');
path += wildCard; } else {
path += wildCard;
}
} }
break; break;
case TokenType.Delimiter: case TokenType.Delimiter:
@ -250,7 +256,7 @@ export function matchPath<P = any>(
const patterns = Array.isArray(pattern) ? [...pattern] : [pattern]; const patterns = Array.isArray(pattern) ? [...pattern] : [pattern];
const matchedResults: Matched<P>[] = []; const matchedResults: Matched<P>[] = [];
for (const item of patterns) { for (const item of patterns) {
const parser = createPathParser(item, option); const parser = createPathParser<P>(item, option);
const matched = parser.parse(pathname); const matched = parser.parse(pathname);
if (matched) { if (matched) {
matchedResults.push(matched); matchedResults.push(matched);

View File

@ -56,7 +56,7 @@ type ParseParam<Param extends string> = Param extends `:${infer R}`
? { ? {
[K in R]: string; [K in R]: string;
} }
: {}; : Record<string, unknown>;
type MergeParams<OneParam extends Record<string, any>, OtherParam extends Record<string, any>> = { type MergeParams<OneParam extends Record<string, any>, OtherParam extends Record<string, any>> = {
readonly [Key in keyof OneParam | keyof OtherParam]?: string; readonly [Key in keyof OneParam | keyof OtherParam]?: string;

View File

@ -26,10 +26,10 @@
"types": "./build/@types/index.d.ts", "types": "./build/@types/index.d.ts",
"exports": { "exports": {
".": { ".": {
"default": "./index.js" "default": "./build/index.js"
}, },
"./package.json":"./package.json", "./package.json":"./package.json",
"./jsx-runtime": "./jsx-runtime.js", "./jsx-runtime": "./build/jsx-runtime.js",
"./jsx-dev-runtime": "./jsx-dev-runtime.js" "./jsx-dev-runtime": "./build/jsx-dev-runtime.js"
} }
} }

View File

@ -20,10 +20,10 @@ import { createWeakMapProxy } from './WeakMapProxy';
import { createMapProxy } from './MapProxy'; import { createMapProxy } from './MapProxy';
export function createCollectionProxy( export function createCollectionProxy(
rawObj: Object, rawObj: Record<string, unknown>,
listener: { current: (...args) => any }, listener: { current: (...args) => any },
hookObserver = true hookObserver = true
): Object { ): ProxyHandler<Record<string, unknown>> {
if (isWeakSet(rawObj)) { if (isWeakSet(rawObj)) {
return createWeakSetProxy(rawObj, listener, hookObserver); return createWeakSetProxy(rawObj, listener, hookObserver);
} }

View File

@ -311,7 +311,7 @@ export function createStore<S extends Record<string, any>, A extends UserActions
} }
// 函数组件中使用的hook // 函数组件中使用的hook
export function useStore<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>>( export function useStore<S extends Record<string, unknown>, A extends UserActions<S>, C extends UserComputedValues<S>>(
id: string id: string
): StoreObj<S, A, C> { ): StoreObj<S, A, C> {
const storeObj = storeMap.get(id); const storeObj = storeMap.get(id);

View File

@ -31,7 +31,7 @@ export interface IObserver {
clearByVNode: (vNode: any) => void; clearByVNode: (vNode: any) => void;
} }
export type StoreConfig<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = { export type StoreConfig<S extends Record<string, unknown>, A extends UserActions<S>, C extends UserComputedValues<S>> = {
id?: string; id?: string;
state?: S; state?: S;
actions?: A; actions?: A;
@ -41,22 +41,22 @@ export type StoreConfig<S extends object, A extends UserActions<S>, C extends Us
}; };
}; };
export type UserActions<S extends object> = { export type UserActions<S extends Record<string, unknown>> = {
[K: string]: ActionFunction<S>; [K: string]: ActionFunction<S>;
}; };
export type ActionFunction<S extends object> = (this: StoreObj<S, any, any>, state: S, ...args: any[]) => any; export type ActionFunction<S extends Record<string, unknown>> = (this: StoreObj<S, any, any>, state: S, ...args: any[]) => any;
export type StoreActions<S extends object, A extends UserActions<S>> = { export type StoreActions<S extends Record<string, unknown>, A extends UserActions<S>> = {
[K in keyof A]: Action<A[K], S>; [K in keyof A]: Action<A[K], S>;
}; };
type Action<T extends ActionFunction<any>, S extends object> = ( type Action<T extends ActionFunction<any>, S extends Record<string, unknown>> = (
this: StoreObj<S, any, any>, this: StoreObj<S, any, any>,
...args: RemoveFirstFromTuple<Parameters<T>> ...args: RemoveFirstFromTuple<Parameters<T>>
) => ReturnType<T>; ) => ReturnType<T>;
export type StoreObj<S extends object, A extends UserActions<S>, C extends UserComputedValues<S>> = { export type StoreObj<S extends Record<string, unknown>, A extends UserActions<S>, C extends UserComputedValues<S>> = {
$s: S; $s: S;
$a: StoreActions<S, A>; $a: StoreActions<S, A>;
$c: UserComputedValues<S>; $c: UserComputedValues<S>;
@ -66,7 +66,7 @@ export type StoreObj<S extends object, A extends UserActions<S>, C extends UserC
$unsubscribe: (listener: (mutation) => void) => void; $unsubscribe: (listener: (mutation) => void) => void;
} & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> }; } & { [K in keyof S]: S[K] } & { [K in keyof A]: Action<A[K], S> } & { [K in keyof C]: ReturnType<C[K]> };
export type PlannedAction<S extends object, F extends ActionFunction<S>> = { export type PlannedAction<S extends Record<string, unknown>, F extends ActionFunction<S>> = {
action: string; action: string;
payload: any[]; payload: any[];
resolve: ReturnType<F>; resolve: ReturnType<F>;
@ -78,21 +78,21 @@ type RemoveFirstFromTuple<T extends any[]> = T['length'] extends 0
? I ? I
: []; : [];
export type UserComputedValues<S extends object> = { export type UserComputedValues<S extends Record<string, unknown>> = {
[K: string]: ComputedFunction<S>; [K: string]: ComputedFunction<S>;
}; };
type ComputedFunction<S extends object> = (state: S) => any; type ComputedFunction<S extends Record<string, unknown>> = (state: S) => any;
export type AsyncAction<T extends ActionFunction<any>, S extends object> = ( export type AsyncAction<T extends ActionFunction<any>, S extends Record<string, unknown>> = (
this: StoreObj<S, any, any>, this: StoreObj<S, any, any>,
...args: RemoveFirstFromTuple<Parameters<T>> ...args: RemoveFirstFromTuple<Parameters<T>>
) => Promise<ReturnType<T>>; ) => Promise<ReturnType<T>>;
export type QueuedStoreActions<S extends object, A extends UserActions<S>> = { export type QueuedStoreActions<S extends Record<string, unknown>, A extends UserActions<S>> = {
[K in keyof A]: AsyncAction<A[K], S>; [K in keyof A]: AsyncAction<A[K], S>;
}; };
export type ComputedValues<S extends object, C extends UserComputedValues<S>> = { export type ComputedValues<S extends Record<string, unknown>, C extends UserComputedValues<S>> = {
[K in keyof C]: ReturnType<C[K]>; [K in keyof C]: ReturnType<C[K]>;
}; };

View File

@ -105,25 +105,27 @@ export function handleRenderThrowError(sourceVNode: VNode, error: any) {
return; return;
} }
case ClassComponent: case ClassComponent:
const ctor = vNode.type; {
const instance = vNode.realNode; const ctor = vNode.type;
if ( const instance = vNode.realNode;
(vNode.flags & DidCapture) === InitFlag && if (
(typeof ctor.getDerivedStateFromError === 'function' || (vNode.flags & DidCapture) === InitFlag &&
(instance !== null && typeof instance.componentDidCatch === 'function')) (typeof ctor.getDerivedStateFromError === 'function' ||
) { (instance !== null && typeof instance.componentDidCatch === 'function'))
FlagUtils.markShouldCapture(vNode); ) {
FlagUtils.markShouldCapture(vNode);
// Class捕捉到异常触发一次刷新 // Class捕捉到异常触发一次刷新
const update = createClassErrorUpdate(vNode, error); const update = createClassErrorUpdate(vNode, error);
pushUpdate(vNode, update); pushUpdate(vNode, update);
launchUpdateFromVNode(vNode); launchUpdateFromVNode(vNode);
// 有异常处理类把抛出异常的节点的Interrupted标志去掉继续走正常的绘制流程 // 有异常处理类把抛出异常的节点的Interrupted标志去掉继续走正常的绘制流程
FlagUtils.removeFlag(sourceVNode, Interrupted); FlagUtils.removeFlag(sourceVNode, Interrupted);
return; return;
}
} }
break; break;
default: default:

View File

@ -53,19 +53,25 @@ export function pushUpdate(vNode: VNode, update: Update) {
// 根据update获取新的state // 根据update获取新的state
function calcState(vNode: VNode, update: Update, inst: any, oldState: any, props: any): any { function calcState(vNode: VNode, update: Update, inst: any, oldState: any, props: any): any {
switch (update.type) { switch (update.type) {
case UpdateState.Override: case UpdateState.Override: {
const content = update.content; const content = update.content;
return typeof content === 'function' ? content.call(inst, oldState, props) : content; return typeof content === 'function' ? content.call(inst, oldState, props) : content;
}
case UpdateState.ForceUpdate: case UpdateState.ForceUpdate:
vNode.isForceUpdate = true; vNode.isForceUpdate = true;
return oldState; return oldState;
case UpdateState.Error: case UpdateState.Error: {
FlagUtils.removeFlag(vNode, ShouldCapture); FlagUtils.removeFlag(vNode, ShouldCapture);
FlagUtils.markDidCapture(vNode); FlagUtils.markDidCapture(vNode);
case UpdateState.Update:
const updateContent = update.content; const updateContent = update.content;
const newState = typeof updateContent === 'function' ? updateContent.call(inst, oldState, props) : updateContent; const newState = typeof updateContent === 'function' ? updateContent.call(inst, oldState, props) : updateContent;
return newState === null || newState === undefined ? oldState : { ...oldState, ...newState }; return newState === null || newState === undefined ? oldState : { ...oldState, ...newState };
}
case UpdateState.Update: {
const updateContent = update.content;
const newState = typeof updateContent === 'function' ? updateContent.call(inst, oldState, props) : updateContent;
return newState === null || newState === undefined ? oldState : { ...oldState, ...newState };
}
default: default:
return oldState; return oldState;
} }

View File

@ -40,8 +40,6 @@ function callRenderQueue() {
} }
renderQueue = null; renderQueue = null;
} catch (error) {
throw error;
} finally { } finally {
isCallingRenderQueue = false; isCallingRenderQueue = false;
} }

View File

@ -60,10 +60,11 @@ function newVirtualNode(tag: VNodeTag, key?: null | string, vNodeProps?: any, re
return new VNode(tag, vNodeProps, key as null | string, realNode); return new VNode(tag, vNodeProps, key as null | string, realNode);
} }
function isClassComponent(comp: Function) { function isClassComponent(comp: (...arg: any) => any) {
// 如果使用 getPrototypeOf 方法获取构造函数,不能兼容业务组组件继承组件的使用方式,会误认为是函数组件 // 如果使用 getPrototypeOf 方法获取构造函数,不能兼容业务组组件继承组件的使用方式,会误认为是函数组件
// 如果使用静态属性,部分函数高阶组件会将类组件的静态属性复制到自身,导致误判为类组件 // 如果使用静态属性,部分函数高阶组件会将类组件的静态属性复制到自身,导致误判为类组件
// 既然已经兼容使用了该标识符,那么继续使用 // 既然已经兼容使用了该标识符,那么继续使用
// @ts-ignore
return comp.prototype?.isReactComponent === true; return comp.prototype?.isReactComponent === true;
} }