style: 更新eslint版本,更新eslint配置

This commit is contained in:
roymondchen 2025-04-21 20:35:54 +08:00
parent 5e0e776d40
commit e7e9197ae3
88 changed files with 1878 additions and 1141 deletions

View File

@ -1,14 +0,0 @@
dist
coverage
node_modules
dest
types
public/entry
public/runtime
comp-entry.ts
config-entry.ts
value-entry.ts
magic-admin/web/public/runtime
magic-admin/server/static

View File

@ -1,71 +0,0 @@
module.exports = {
env: {
node: true,
browser: true,
es2021: true,
},
globals: {
describe: true,
it: true,
expect: true,
beforeEach: true,
},
extends: [
'eslint-config-tencent',
'eslint-config-tencent/ts',
'plugin:vue/vue3-essential',
'./prettier.cjs',
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
extraFileExtensions: ['.vue'],
sourceType: 'module',
},
plugins: [
'vue',
'@typescript-eslint',
'simple-import-sort'
],
ignorePatterns: ['.eslintrc.cjs'],
rules: {
'vue/no-mutating-props': 'off',
'vue/multi-word-component-names': 'off',
'no-param-reassign': 'off',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-require-imports': 'off',
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksVoidReturn": false
}
],
'simple-import-sort/imports': [
"error", {
groups: [
['./polyfills'],
// Node.js builtins. You could also generate this regex if you use a `.js` config.
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
[
"^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)",
],
["^(node)(:.*|$)"],
// Packages. `react|vue` related packages come first.
["^(react|vue|vite)", "^@?\\w"],
["^(@tmagic)(/.*|$)"],
// Internal packages.
["^(@|@editor|@data-source)(/.*|$)"],
// Side effect imports.
["^\\u0000"],
// Parent imports. Put `..` last.
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
// Other relative imports. Put same-folder imports and `.` last.
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
// Style imports.
["^.+\\.s?css$"],
],
}
]
},
};

2
.gitignore vendored
View File

@ -31,3 +31,5 @@ coverage
auto-imports.d.ts
components.d.ts
docs/.vitepress/cache
.eslintcache

View File

@ -17,11 +17,7 @@
<slot name="highlight"></slot>
</div>
</div>
<div
class="demo-block-control"
ref="control"
@click="isExpanded = !isExpanded"
>
<div class="demo-block-control" ref="control" @click="isExpanded = !isExpanded">
<transition name="arrow-slide">
<i :class="[iconClass, hovering]"></i>
</transition>
@ -30,23 +26,14 @@
</transition>
<el-tooltip effect="dark" :content="'前往 codepen.io 运行此示例'" placement="right">
<transition name="text-slide">
<el-button
size="small"
type="primary"
text
class="control-button"
@click.stop="goCodepen"
>
<el-button size="small" type="primary" text class="control-button" @click.stop="goCodepen">
{{ type === 'form' ? '查看结果' : '在线运行' }}
</el-button>
</transition>
</el-tooltip>
</div>
<el-dialog
v-model="resultVisible"
title="result"
>
<el-dialog v-model="resultVisible" title="result">
<pre><code class="language-javascript hljs" v-html="result"></code></pre>
</el-dialog>
</div>
@ -60,7 +47,9 @@
transition: 0.2s;
&.hover {
box-shadow: 0 0 8px 0 rgba(232, 237, 250, 0.6), 0 2px 4px 0 rgba(232, 237, 250, 0.5);
box-shadow:
0 0 8px 0 rgba(232, 237, 250, 0.6),
0 2px 4px 0 rgba(232, 237, 250, 0.5);
}
code {
@ -219,9 +208,7 @@ export function stripTemplate(content) {
}
export default {
props: [
'type', 'config'
],
props: ['type', 'config'],
data() {
return {
@ -293,12 +280,15 @@ export default {
},
text() {
return this.isStringConfig ?
hljs.highlight('js', this.config).value :
hljs.highlight('js', serialize(this.config, {
return this.isStringConfig
? hljs.highlight('js', this.config).value
: hljs.highlight(
'js',
serialize(this.config, {
space: 2,
unsafe: true,
}).replace(/"(\w+)":\s/g, '$1: ')).value;
}).replace(/"(\w+)":\s/g, '$1: '),
).value;
},
formConfig() {
@ -307,7 +297,7 @@ export default {
isStringConfig() {
return typeof this.config === 'string';
}
},
},
watch: {
@ -356,7 +346,7 @@ export default {
});
},
beforeDestroy() {
beforeUnmount() {
this.removeScrollHandler();
},
};

566
eslint-config/flat/base.mjs Normal file
View File

@ -0,0 +1,566 @@
export default {
rules: {
/**
* 不要在中括号中添加空格
*/
'array-bracket-spacing': ['error', 'never'],
/**
* 数组的方法除了 forEach 之外回调函数必须有返回值
*/
'array-callback-return': 'warn',
/**
* 要求箭头函数体使用大括号
*/
'arrow-body-style': ['warn', 'as-needed'],
/**
* 要求箭头函数的参数使用圆括号
*/
'arrow-parens': [
'warn',
'as-needed',
{
requireForBlockBody: true,
},
],
/**
* 强制箭头函数的箭头前后使用一致的空格
*/
'arrow-spacing': 'warn',
/**
* 要求打开的块标志和同一行上的标志拥有一致的间距此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距
*/
'block-spacing': 'error',
/**
* 强制在代码块中使用一致的大括号风格
*/
'brace-style': 'error',
/**
* 使用驼峰命名法camelCase命名对象函数和实例
*/
camelcase: [
'error',
{
ignoreDestructuring: true,
properties: 'never',
},
],
/**
* 添加尾随逗号
*/
'comma-dangle': ['warn', 'always-multiline'],
/**
* 强制在逗号前后使用一致的空格
*/
'comma-spacing': [
'error',
{
before: false,
after: true,
},
],
/**
* 强制使用一致的逗号风格
*/
'comma-style': ['error', 'last'],
/**
* 强制在计算的属性的方括号中使用一致的空格
*/
'computed-property-spacing': ['warn', 'never'],
/**
* 禁止使用 foo['bar']必须写成 foo.bar
*/
'dot-notation': 'warn',
/**
* 要求或禁止文件末尾存在空行
*/
'eol-last': ['error', 'always'],
/**
* 必须使用 === !==禁止使用 == !=
*/
eqeqeq: ['warn', 'always'],
/**
* 要求或禁止在函数标识符和其调用之间有空格
*/
'func-call-spacing': ['error', 'never'],
/**
* 必须只使用函数声明或只使用函数表达式
*/
'func-style': ['off', 'expression'],
/**
* 强制在函数括号内使用一致的换行
*/
'function-paren-newline': ['warn', 'multiline'],
/**
* 强制 generator 函数中 * 号周围使用一致的空格
*/
'generator-star-spacing': [
'warn',
{
before: false,
after: true,
},
],
/**
* 限制变量名长度
*/
'id-length': 'off',
/**
* 强制隐式返回的箭头函数体的位置
*/
'implicit-arrow-linebreak': ['warn', 'beside'],
/**
* 使用 2 个空格缩进
*/
indent: [
'warn',
2,
{
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
FunctionDeclaration: {
parameters: 1,
body: 1,
},
FunctionExpression: {
parameters: 1,
body: 1,
},
CallExpression: {
arguments: 1,
},
ArrayExpression: 1,
ObjectExpression: 1,
ImportDeclaration: 1,
flatTernaryExpressions: false,
ignoredNodes: [
'JSXElement',
'JSXElement > *',
'JSXAttribute',
'JSXIdentifier',
'JSXNamespacedName',
'JSXMemberExpression',
'JSXSpreadAttribute',
'JSXExpressionContainer',
'JSXOpeningElement',
'JSXClosingElement',
'JSXFragment',
'JSXOpeningFragment',
'JSXClosingFragment',
'JSXText',
'JSXEmptyExpression',
'JSXSpreadChild',
],
ignoreComments: false,
},
],
/**
* 强制在对象字面量的属性中键和值之间使用一致的间距
*/
'key-spacing': 'error',
/**
* 强制在关键字前后使用一致的空格
*/
'keyword-spacing': [
'error',
{
overrides: {
if: {
after: true,
},
for: {
after: true,
},
while: {
after: true,
},
else: {
after: true,
},
},
},
],
/**
* 只允许使用 unix LF 作为换行符Windows CRLF 不可以使用
*/
'linebreak-style': ['warn', 'unix'],
/**
* 强制一行的最大长度限制单行不能超过100个字符字符串和正则表达式除外
*/
'max-len': [
'off',
{
code: 120,
ignoreStrings: true,
ignoreUrls: true,
ignoreRegExpLiterals: true,
ignoreTemplateLiterals: true,
},
],
/**
* 只有在命名构造器或者类的时候才用帕斯卡拼命名法PascalCase即首字母大写
*/
'new-cap': [
'error',
{
newIsCap: true,
newIsCapExceptions: [],
capIsNew: false,
capIsNewExceptions: ['Immutable.Map', 'Immutable.Set', 'Immutable.List'],
properties: false,
},
],
/**
* 在编写多个方法链式调用(超过两个方法链式调用) 使用前导点强调这行是一个方法调用而不是一个语句
*/
'newline-per-chained-call': [
'warn',
{
ignoreChainWithDepth: 2,
},
],
/**
* 使用字面量语法创建数组
*/
'no-array-constructor': ['error'],
/**
* case default 的子句中如果存在声明 (例如. let, const, function, class)使用大括号来创建块级作用域
*/
'no-case-declarations': 'error',
/**
* 避免搞混箭头函数符号 (=>) 和比较运算符 (<=, >=)
*/
'no-confusing-arrow': 'warn',
/**
* 禁止对使用 const 定义的常量重新赋值
*/
'no-const-assign': 'error',
/**
* 禁止重复定义类的成员
*/
'no-dupe-class-members': 'error',
/**
* 禁止在 else 内使用 return必须改为提前结束
*/
'no-else-return': [
'warn',
{
allowElseIf: false,
},
],
/**
* 禁止使用 eval
*/
'no-eval': 'error',
/**
* 不要使用迭代器
* @reason 推荐使用 JavaScript 的高阶函数代替 for-in
*/
'no-iterator': 'warn',
/**
* 禁止在循环内的函数内部出现循环体条件语句中定义的变量
*/
'no-loop-func': 'error',
/**
* 禁止混合使用不同的操作符:
* - 禁止 `%`, `**` 之间混用
* - 禁止 `%` 与其它运算符之间混用
* - 禁止乘除运算符之间混用
* - 禁止位运算符之间的任何混用
* - 禁止比较运算符之间混用
* - 禁止 `&&`, `||` 之间混用
*/
'no-mixed-operators': [
'error',
{
groups: [
['%', '**'],
['%', '+'],
['%', '-'],
['%', '*'],
['%', '/'],
['&', '|', '<<', '>>', '>>>'],
['==', '!=', '===', '!=='],
['&&', '||'],
],
allowSamePrecedence: false,
},
],
/**
* 禁止连续赋值比如 foo = bar = 1
*/
'no-multi-assign': 'error',
/**
* 不要使用多个空行填充代码
*/
'no-multiple-empty-lines': 'error',
/**
* 禁止使用嵌套的三元表达式比如 a ? b : c ? d : e
*/
'no-nested-ternary': 'warn',
/**
* 禁止使用 new Function
* @reason 这和 eval 是等价的
*/
'no-new-func': 'error',
/**
* 禁止直接 new Object
*/
'no-new-object': 'error',
/**
* 禁止使用 new 来生成 String, Number Boolean
*/
'no-new-wrappers': 'warn',
/**
* 禁止对函数的参数重新赋值
*/
'no-param-reassign': [
'warn',
{
props: true,
ignorePropertyModificationsFor: [
'acc',
'accumulator',
'e',
'ctx',
'req',
'request',
'res',
'response',
'$scope',
'staticContext',
'state',
],
},
],
/**
* 禁止使用 ++ --
*/
'no-plusplus': [
'error',
{
allowForLoopAfterthoughts: true,
},
],
/**
* 禁止使用 hasOwnProperty, isPrototypeOf propertyIsEnumerable
*/
'no-prototype-builtins': 'error',
/**
* 计算指数时可以使用 ** 运算符
*/
'no-restricted-properties': [
'warn',
{
object: 'Math',
property: 'pow',
message: 'Please use ** instand',
},
],
/**
* 推荐使用 JavaScript 的高阶函数代替 for-in
*/
'no-restricted-syntax': [
'warn',
{
selector: 'ForInStatement',
message:
'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.',
},
{
selector: 'LabeledStatement',
message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
},
{
selector: 'WithStatement',
message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
},
],
/**
* 避免在行尾添加空格
*/
'no-trailing-spaces': 'error',
/**
* 变量应先声明再使用禁止引用任何未声明的变量除非你明确知道引用的变量存在于当前作用域链上
*/
'no-undef': ['error'],
/**
* 禁止变量名出现下划线
*/
'no-underscore-dangle': 'warn',
/**
* 必须使用 !a 替代 a ? false : true
*/
'no-unneeded-ternary': 'warn',
/**
* 已定义的变量必须使用
* 但不检查最后一个使用的参数之前的参数
* 也不检查 rest 属性的兄弟属性
*/
'no-unused-vars': [
'error',
{
args: 'after-used',
ignoreRestSiblings: true,
argsIgnorePattern: '^_.+',
varsIgnorePattern: '^_.+',
},
],
/**
* 禁止出现没必要的 constructor
*/
'no-useless-constructor': 'warn',
/**
* 禁止出现没必要的转义
*/
'no-useless-escape': 'error',
/**
* 禁止使用 var
*/
'no-var': 'error',
/**
* 禁止属性前有空白
*/
'no-whitespace-before-property': 'warn',
/**
* 强制单个语句的位置
*/
'nonblock-statement-body-position': ['error', 'beside'],
/**
* 强制在大括号中使用一致的空格
*/
'object-curly-spacing': ['warn', 'always'],
/**
* 将对象方法属性简写且间歇属性放在前面
*/
'object-shorthand': 'warn',
/**
* 禁止变量申明时用逗号一次申明多个
*/
'one-var': ['warn', 'never'],
/**
* 避免在赋值语句 = 前后换行如果你的代码单行长度超过了 max-len 定义的长度而不得不换行那么使用括号包裹
*/
'operator-linebreak': [
'error',
'before',
{
overrides: {
'=': 'none',
},
},
],
/**
* 要求或禁止块内填充
*/
'padded-blocks': ['error', 'never'],
/**
* 要求回调函数使用箭头函数
*/
'prefer-arrow-callback': 'warn',
/**
* 申明后不再被修改的变量必须使用 const 来申明
*/
'prefer-const': [
'error',
{
destructuring: 'any',
ignoreReadBeforeAssign: false,
},
],
/**
* 优先使用解构赋值
*/
'prefer-destructuring': [
'warn',
{
VariableDeclarator: {
array: false,
object: true,
},
AssignmentExpression: {
array: true,
object: false,
},
},
{
enforceForRenamedProperties: false,
},
],
/**
* 必须使用 ...args 而不是 arguments
*/
'prefer-rest-params': 'warn',
/**
* 必须使用 ... 而不是 apply比如 foo(...args)
*/
'prefer-spread': 'warn',
/**
* 必须使用模版字符串而不是字符串连接
*/
'prefer-template': 'error',
/**
* 要求对象字面量属性名称用引号括起来
*/
'quote-props': [
'error',
'as-needed',
{
keywords: false,
},
],
/**
* 使用单引号 '' 定义字符串
*/
quotes: [
'warn',
'single',
{
allowTemplateLiterals: false,
},
],
/**
* parseInt 必须传入第二个参数
*/
radix: 'warn',
/**
* 要加分号
*/
semi: ['error', 'always'],
/**
* 强制在块之前使用一致的空格
*/
'space-before-blocks': 'error',
/**
* 强制在 function 的左括号之前使用一致的空格
*/
'space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
},
],
/**
* 强制在圆括号内使用一致的空格
*/
'space-in-parens': ['error', 'never'],
/**
* 要求操作符周围有空格
*/
'space-infix-ops': 'error',
/**
* 注释的斜线或 * 后必须有空格
*/
'spaced-comment': ['error', 'always'],
/**
* 要求或禁止模板字符串中的嵌入表达式周围空格的使用
*/
'template-curly-spacing': ['error', 'never'],
/**
* 要求立即执行的函数使用括号括起来
*/
'wrap-iife': ['error', 'outside'],
},
};

View File

@ -0,0 +1,38 @@
import simpleImportSort from 'eslint-plugin-simple-import-sort';
export default {
files: ['**/*.{js,mjs,cjs,ts,vue}'],
plugins: {
'simple-import-sort': simpleImportSort,
},
rules: {
'simple-import-sort/imports': [
'error',
{
groups: [
['./polyfills'],
['^(node)(:.*|$)'],
// Node.js builtins. You could also generate this regex if you use a `.js` config.
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
[
'^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)',
],
// Packages. `react|vue` related packages come first.
['^(react|vue|vite|vitest)', '^@?\\w'],
['^(@tencent)(/.*|$)'],
['^(@tmagic)(/.*|$)'],
// Internal packages.
['^(@|@editor|@data-source)(/.*|$)'],
// Side effect imports.
['^\\u0000'],
// Parent imports. Put `..` last.
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
// Other relative imports. Put same-folder imports and `.` last.
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
// Style imports.
['^.+\\.s?css$'],
],
},
],
},
};

View File

@ -0,0 +1,37 @@
import { rules } from 'eslint-plugin-import';
export default {
plugins: {
import: {
meta: { name: 'eslint-plugin-import' },
rules,
},
},
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
languageOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
/**
* 导入语句前不允许有任何非导入语句
*/
'import/first': 'error',
/**
* 禁止重复导入模块
*/
'import/no-duplicates': 'error',
/**
* 禁止使用 let 导出
*/
'import/no-mutable-exports': 'warn',
/**
* 禁用导入的模块时使用 webpack 特有的语法感叹号
*/
'import/no-webpack-loader-syntax': 'warn',
/**
* 当只有一个导出时必须使用 export default 来导出
*/
'import/prefer-default-export': 'off',
},
};

View File

@ -1,5 +1,4 @@
module.exports = {
plugins: ['prettier'],
export default {
rules: {
'wrap-iife': 'off',
'template-curly-spacing': 'off',

376
eslint-config/flat/ts.mjs Normal file
View File

@ -0,0 +1,376 @@
export default {
files: ['**/*.ts', '**/*.tsx'],
rules: {
'brace-style': 'off',
'no-empty-function': 'off',
// https://github.com/typescript-eslint/typescript-eslint/issues/491
'no-invalid-this': 'off',
'no-magic-numbers': 'off',
'react/sort-comp': 'off',
'func-call-spacing': 'off',
'comma-spacing': 'off',
'dot-notation': 'off',
indent: 'off',
'keyword-spacing': 'off',
camelcase: 'off',
'no-underscore-dangle': 'off',
'no-array-constructor': 'off',
'no-dupe-class-members': 'off',
'no-undef': 'off',
'no-unused-vars': 'off',
'no-useless-constructor': 'off',
quotes: 'off',
semi: 'off',
'space-before-function-paren': 'off',
// https://github.com/typescript-eslint/typescript-eslint/issues/600
'spaced-comment': ['error', 'always', { markers: ['/'] }],
/**
* 重载的函数必须写在一起
* @reason 增加可读性
*/
'@typescript-eslint/adjacent-overload-signatures': 'error',
/** 同 JS 规则的 TS 版本 */
'@stylistic/ts/brace-style': 'error',
/** 同 JS 规则的 TS 版本 */
'@stylistic/comma-spacing': [
'error',
{
before: false,
after: true,
},
],
/**
* 类型断言必须使用 as Type禁止使用 <Type>禁止对对象字面量进行类型断言断言成 any 是允许的
* @reason <Type> 容易被理解为 jsx
*/
'@typescript-eslint/consistent-type-assertions': [
'error',
{
assertionStyle: 'as',
objectLiteralTypeAssertions: 'never',
},
],
/**
* 优先使用 interface 而不是 type
*/
'@typescript-eslint/consistent-type-definitions': 'off',
/** 同 JS 规则的 TS 版本 */
'@typescript-eslint/dot-notation': 'warn',
/**
* 必须设置类的成员的可访问性
* @reason 将不需要公开的成员设为私有的可以增强代码的可理解性对文档输出也很友好
*/
'@typescript-eslint/explicit-member-accessibility': 'off',
/**
* 要求或禁止在函数标识符和其调用之间有空格
*/
'@stylistic/ts/func-call-spacing': ['error', 'never'],
/** 同 JS 规则的 TS 版本 */
'@stylistic/ts/indent': [
'warn',
2,
{
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
FunctionDeclaration: {
parameters: 1,
body: 1,
},
FunctionExpression: {
parameters: 1,
body: 1,
},
CallExpression: {
arguments: 1,
},
ArrayExpression: 1,
ObjectExpression: 1,
ImportDeclaration: 1,
flatTernaryExpressions: false,
ignoredNodes: [
'JSXElement',
'JSXElement > *',
'JSXAttribute',
'JSXIdentifier',
'JSXNamespacedName',
'JSXMemberExpression',
'JSXSpreadAttribute',
'JSXExpressionContainer',
'JSXOpeningElement',
'JSXClosingElement',
'JSXFragment',
'JSXOpeningFragment',
'JSXClosingFragment',
'JSXText',
'JSXEmptyExpression',
'JSXSpreadChild',
],
ignoreComments: false,
},
],
/** 同 JS 规则的 TS 版本 */
'@stylistic/ts/keyword-spacing': [
'error',
{
overrides: {
if: {
after: true,
},
for: {
after: true,
},
while: {
after: true,
},
else: {
after: true,
},
},
before: true,
after: true,
},
],
/**
* 指定类成员的排序规则
* @reason 优先级
* 1. static > instance
* 2. field > constructor > method
* 3. public > protected > private
*/
'@typescript-eslint/member-ordering': [
'error',
{
default: [
'public-static-field',
'protected-static-field',
'private-static-field',
'static-field',
'public-static-method',
'protected-static-method',
'private-static-method',
'static-method',
'public-instance-field',
'protected-instance-field',
'private-instance-field',
'public-field',
'protected-field',
'private-field',
'instance-field',
'field',
'constructor',
'public-instance-method',
'protected-instance-method',
'private-instance-method',
'public-method',
'protected-method',
'private-method',
'instance-method',
'method',
],
},
],
/**
* 接口中的方法必须用属性的方式定义
*/
'@typescript-eslint/method-signature-style': 'off',
/** 代替 JS 规则 camelCase 的 TS 规则 */
'@typescript-eslint/naming-convention': [
'warn',
{
selector: 'function',
format: ['camelCase', 'PascalCase'],
},
{
selector: 'variable',
format: ['camelCase', 'UPPER_CASE'],
},
{
selector: 'variable',
modifiers: ['global'],
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
},
{
selector: 'variable',
format: ['camelCase', 'PascalCase'],
types: ['function'],
},
{
selector: 'variable',
modifiers: ['exported'],
format: ['UPPER_CASE'],
types: ['boolean', 'string', 'number', 'array'],
},
{
selector: 'variable',
modifiers: ['exported'],
format: ['camelCase', 'PascalCase'],
types: ['function'],
},
{
selector: ['class', 'typeLike'],
format: ['PascalCase'],
},
{
selector: ['classMethod', 'classProperty'],
leadingUnderscore: 'forbid',
trailingUnderscore: 'forbid',
format: ['camelCase'],
},
],
/** 同 JS 规则的 TS 版本 */
'@typescript-eslint/no-array-constructor': 'error',
/** 同 JS 规则的 TS 版本 */
'@typescript-eslint/no-dupe-class-members': 'error',
/**
* 禁止定义空的接口
*/
'@typescript-eslint/no-empty-interface': 'error',
/**
* 禁止给一个初始化时直接赋值为 number, string 的变量显式的声明类型
* @reason 可以简化代码
*/
'@typescript-eslint/no-inferrable-types': 'warn',
/**
* 禁止对 promise 的误用详见示例
*/
'@typescript-eslint/no-misused-promises': [
'error',
{
checksConditionals: true,
},
],
/**
* 禁止使用 namespace 来定义命名空间
* @reason 使用 es6 引入模块才是更标准的方式
* 但是允许使用 declare namespace ... {} 来定义外部命名空间
*/
'@typescript-eslint/no-namespace': [
'error',
{
allowDeclarations: true,
allowDefinitionFiles: true,
},
],
/**
* 禁止在 optional chaining 之后使用 non-null 断言感叹号
* @reason optional chaining 后面的属性一定是非空的
*/
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
/**
* 禁止给类的构造函数的参数添加修饰符
*/
'@typescript-eslint/no-parameter-properties': 'off',
/**
* 禁止使用 require
* @reason 统一使用 import 来引入模块特殊情况使用单行注释允许 require 引入
*/
'@typescript-eslint/no-require-imports': 'error',
/**
* 禁止将 this 赋值给其他变量除非是解构赋值
*/
'@typescript-eslint/no-this-alias': [
'error',
{
allowDestructuring: true,
},
],
/**
* 禁止无用的表达式
*/
'@typescript-eslint/no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
},
],
/** 同 JS 规则的 TS 版本 */
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'after-used',
ignoreRestSiblings: true,
argsIgnorePattern: '^_.+',
varsIgnorePattern: '^_.+',
},
],
/**
* 禁止出现没必要的 constructor
*/
'@typescript-eslint/no-useless-constructor': 'warn',
/**
* 使用 for 循环遍历数组时如果索引仅用于获取成员则必须使用 for of 循环替代 for 循环
* @reason for of 循环更加易读
*/
'@typescript-eslint/prefer-for-of': 'warn',
/**
* 使用函数类型别名替代包含函数调用声明的接口
*/
'@typescript-eslint/prefer-function-type': 'warn',
/**
* 禁止使用 module 来定义命名空间
* @reason module 已成为 js 的关键字
*/
'@typescript-eslint/prefer-namespace-keyword': 'error',
/**
* 使用 optional chaining 替代 &&
*/
'@typescript-eslint/prefer-optional-chain': 'error',
/** 同 JS 规则的 TS 版本 */
'@stylistic/ts/quotes': [
'warn',
'single',
{
allowTemplateLiterals: false,
},
],
/** 同 JS 规则的 TS 版本 */
'@stylistic/ts/semi': ['error', 'always'],
/** 同 JS 规则的 TS 版本 */
'@stylistic/ts/space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
},
],
/**
* 禁止使用三斜杠导入文件
* @reason 三斜杠是已废弃的语法但在类型声明文件中还是可以使用的
*/
'@typescript-eslint/triple-slash-reference': [
'error',
{
path: 'never',
types: 'always',
lib: 'always',
},
],
/**
* 在类型注释周围需要一致的间距
*/
'@stylistic/ts/type-annotation-spacing': 'error',
/**
* interface type 定义时必须声明成员的类型
*/
'@typescript-eslint/typedef': [
'error',
{
arrayDestructuring: false,
arrowParameter: false,
memberVariableDeclaration: false,
objectDestructuring: false,
parameter: false,
propertyDeclaration: true,
variableDeclaration: false,
},
],
/**
* 函数重载时若能通过联合类型将两个函数的类型声明合为一个则使用联合类型而不是两个函数声明
*/
'@typescript-eslint/unified-signatures': 'error',
},
};

39
eslint-config/index.mjs Normal file
View File

@ -0,0 +1,39 @@
import js from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
import stylisticTs from '@stylistic/eslint-plugin-ts';
import parserTs from '@typescript-eslint/parser';
import { defineConfig } from 'eslint/config';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import pluginVue from 'eslint-plugin-vue';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import tencentEslintBaseConfig from './flat/base.mjs';
import tencentEslintImportexport from './flat/import.mjs';
import ImportSortConfig from './flat/import-sort.mjs';
import tencentEslintPrettierConfig from './flat/prettier.mjs';
import tencentEslintTsConfig from './flat/ts.mjs';
export default (tsconfigRootDir) =>
defineConfig([
{ files: ['**/*.{js,mjs,cjs}'], plugins: { js }, extends: ['js/recommended'] },
{ files: ['**/*.{js,mjs,cjs,ts,vue}'], languageOptions: { globals: { ...globals.browser, ...globals.node } } },
...tseslint.config(tencentEslintBaseConfig, tencentEslintImportexport, tseslint.configs.base, {
plugins: {
'@stylistic': stylistic,
'@stylistic/ts': stylisticTs,
},
languageOptions: {
parser: parserTs,
parserOptions: {
project: tsconfigRootDir,
},
},
...tencentEslintTsConfig,
}),
pluginVue.configs['flat/essential'],
{ files: ['**/*.vue'], languageOptions: { parserOptions: { parser: tseslint.parser } } },
eslintPluginPrettierRecommended,
tencentEslintPrettierConfig,
ImportSortConfig,
]);

View File

@ -0,0 +1,29 @@
{
"name": "@tmagic/eslint-config",
"version": "0.0.1",
"main": "index.mjs",
"type": "module",
"repository": {
"directory": "eslint-config",
"type": "git",
"url": "https://github.com/Tencent/tmagic-editor.git"
},
"dependencies": {
"@eslint/js": "^9.24.0",
"@typescript-eslint/parser": "^8.30.1",
"@typescript-eslint/eslint-plugin": "^8.30.1",
"@stylistic/eslint-plugin": "^4.2.0",
"@stylistic/eslint-plugin-ts": "^4.2.0",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-vue": "^10.0.0",
"eslint-plugin-prettier": "^5.2.6",
"globals": "^16.0.0",
"typescript-eslint": "^8.30.1"
},
"peerDependencies": {
"eslint": ">=9.24.0",
"prettier": ">=3.5.3"
}
}

41
eslint.config.js Normal file
View File

@ -0,0 +1,41 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig, globalIgnores } from 'eslint/config';
import eslintConfig from '@tmagic/eslint-config';
export default defineConfig([
globalIgnores([
'./docs/**/*',
'./packages/cli/lib/**/*',
'*/**/auto-imports.d.ts',
'*/**/components.d.ts',
'*/**/dist/**/*',
'*/**/.tmagic/**/*',
'*/**/public/**/*',
'*/**/types/**/*',
'*/**/*.config.ts',
'vite-env.d.ts',
]),
...eslintConfig(path.join(path.dirname(fileURLToPath(import.meta.url)), 'tsconfig.json')),
{
files: ['**/*.vue'],
rules: {
'vue/no-mutating-props': 'off',
'vue/multi-word-component-names': 'off',
},
},
{
files: ['**/*.{js,mjs,cjs,ts,vue}'],
rules: {
'no-param-reassign': 'off',
},
},
{
files: ['**/*.tsx'],
rules: {
'@typescript-eslint/naming-convention': 'off',
},
},
]);

View File

@ -9,8 +9,8 @@
"clean:top": "rimraf */**/dist */**/types */dist coverage dwt* temp packages/cli/lib",
"clean:modules": "rimraf node_modules **/node_modules **/**/node_modules",
"clean:all": "pnpm clean:top && pnpm clean:modules",
"lint": "eslint . --ext .js,.vue,.ts,.tsx",
"lint-fix": "eslint . --fix --ext .vue,.js,.ts,.tsx",
"lint": "eslint --cache .",
"lint-fix": "eslint --fix --cache .",
"playground": "pnpm --filter \"runtime-vue3\" build:libs && pnpm --filter \"runtime-vue3\" --filter \"tmagic-playground\" dev",
"pg": "pnpm playground",
"playground:vue2": "pnpm --filter \"runtime-vue2\" build:libs && pnpm --filter \"runtime-vue2\" --filter \"tmagic-playground\" dev:vue2",
@ -46,9 +46,8 @@
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@rollup/plugin-alias": "^5.1.1",
"@tmagic/eslint-config": "workspace: ^*",
"@types/node": "18.19.61",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-vue": "^5.2.3",
"@vitest/coverage-v8": "^2.1.9",
"@vue/compiler-sfc": "^3.5.13",
@ -59,12 +58,7 @@
"cz-conventional-changelog": "^3.3.0",
"element-plus": "^2.9.7",
"enquirer": "^2.4.1",
"eslint": "^8.57.1",
"eslint-config-tencent": "^1.1.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-vue": "^9.33.0",
"eslint": "^9.25.0",
"execa": "^4.1.0",
"highlight.js": "^11.11.1",
"husky": "^7.0.4",
@ -72,7 +66,7 @@
"lint-staged": "^11.2.6",
"minimist": "^1.2.8",
"picocolors": "^1.1.1",
"prettier": "^2.8.8",
"prettier": "^3.5.3",
"recast": "^0.23.11",
"rimraf": "^3.0.2",
"rollup": "^4.38.0",
@ -93,7 +87,7 @@
}
},
"lint-staged": {
"*.{js,ts,vue}": "eslint --fix",
"*.{js,ts,vue}": "eslint --fix --cache",
"*.scss": "prettier --write"
}
}

View File

@ -1,4 +1,4 @@
import path from 'path';
import path from 'node:path';
import fs from 'fs-extra';
@ -6,6 +6,7 @@ import { ModuleMainFilePath, UserConfig } from './types';
import { prepareEntryFile, resolveAppPackages } from './utils';
export default class Core {
// eslint-disable-next-line @typescript-eslint/no-require-imports
public version = require('../package.json').version;
public options: UserConfig;

View File

@ -29,6 +29,7 @@ export const cli = (defaultAppConfig: UserConfig): void => {
const program = cac('tmagic');
// display core version and cli version
// eslint-disable-next-line @typescript-eslint/no-require-imports
const versionCli = require('../package.json').version;
program.version(`tmagic/cli@${versionCli}`);

View File

@ -1,4 +1,4 @@
import path from 'path';
import path from 'node:path';
import fs from 'fs-extra';

View File

@ -1,5 +1,5 @@
import fs from 'fs';
import path from 'path';
import fs from 'node:fs';
import path from 'node:path';
export const backupFile = (runtimeSource: string, file: string) => {
const filePath = path.join(runtimeSource, file);

View File

@ -10,6 +10,7 @@ export const hasExportDefault = <T = any>(mod: unknown): mod is { default: T } =
isPlainObject(mod) && !!mod.__esModule && Object.prototype.hasOwnProperty.call(mod, 'default');
export const loadUserConfigCjs: UserConfigLoader = async (userConfigPath) => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const required = require(userConfigPath);
return hasExportDefault(required) ? required.default : required;
};

View File

@ -95,7 +95,7 @@ export const prepareEntryFile = async (app: App) => {
app.writeTemp(fileName, content);
} else {
fileName = `${file}.js`;
app.writeTemp(`${file}.d.ts`, `const type: Record<string, any>;\n\nexport default type;`);
app.writeTemp(`${file}.d.ts`, 'const type: Record<string, any>;\n\nexport default type;');
}
app.writeTemp(fileName, content);
});
@ -136,6 +136,7 @@ export const generateContent = (
};
export const prettyCode = (code: string) =>
// eslint-disable-next-line @typescript-eslint/no-require-imports
recast.prettyPrint(recast.parse(code.replace(/\\/g, '/'), { parser: require('recast/parsers/typescript') }), {
tabWidth: 2,
trailingComma: true,

View File

@ -127,6 +127,7 @@ const typeAssertion = function ({
if (isFile(defaultFile)) {
const defaultCode = fs.readFileSync(defaultFile, { encoding: 'utf-8', flag: 'r' });
// eslint-disable-next-line @typescript-eslint/no-require-imports
const ast = recast.parse(defaultCode, { parser: require('recast/parsers/typescript') });
if (
isDatasource(
@ -518,6 +519,7 @@ const setPackages = (packages: ModuleMainFilePath, app: App, packagePath: string
.replace('\n', '');
const indexCode = fs.readFileSync(indexPath, { encoding: 'utf-8', flag: 'r' });
// eslint-disable-next-line @typescript-eslint/no-require-imports
const ast: Ast = recast.parse(indexCode, { parser: require('recast/parsers/typescript') });
const result = typeAssertion({ ast, indexPath, componentFileAffix, datasoucreSuperClass });

View File

@ -148,7 +148,7 @@ class Node extends EventEmitter {
}
private listenLifeSafe() {
this.once('created', async (instance: any) => {
this.once('created', (instance: any) => {
this.once('destroy', () => {
this.instance = null;
if (typeof this.data.destroy === 'function') {
@ -165,10 +165,11 @@ class Node extends EventEmitter {
}
}
await this.runHookCode('created');
this.runHookCode('created');
});
this.once('mounted', async (instance: any) => {
this.once('mounted', (instance: any) => {
const handler = async () => {
if (instance) {
this.registerMethod(instance);
if (instance.config) {
@ -182,7 +183,9 @@ class Node extends EventEmitter {
}
}
await this.runHookCode('mounted');
this.runHookCode('mounted');
};
handler();
});
}
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*

View File

@ -139,6 +139,7 @@ export const compliedDataSourceField = (value: any, data: DataSourceManagerData)
try {
return getValueByKeyPath(fields.join('.'), dsData);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return value;
}
@ -151,6 +152,7 @@ export const template = (value: string, data?: DataSourceManagerData) =>
value.replaceAll(dataSourceTemplateRegExp, (match, $1) => {
try {
return getValueByKeyPath($1, data);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e: any) {
return match;
}

View File

@ -125,7 +125,7 @@ describe('utils', () => {
test('isUseDataSourceField', () => {
expect(utils.isUseDataSourceField([`${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}dsID`, 'field'], 'dsID')).toBeTruthy();
expect(utils.isUseDataSourceField([`${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}dsID`, 'field'], 'dsID1')).toBeFalsy();
expect(utils.isUseDataSourceField([`abcdsID`, 'field'], 'dsID')).toBeFalsy();
expect(utils.isUseDataSourceField(['abcdsID', 'field'], 'dsID')).toBeFalsy();
expect(utils.isUseDataSourceField([123, 'field'], 123)).toBeFalsy();
});

View File

@ -27,9 +27,9 @@ import type { PopoverProps } from './types';
defineSlots<{
/** 触发 Popover 显示的 HTML 元素 */
reference(props: {}): any;
reference(_props: {}): any;
/** Popover 内嵌 HTML 文本 */
default(props: {}): any;
default(_props: {}): any;
}>();
defineOptions({
@ -130,7 +130,7 @@ const mouseenterHandler = () => {
popoverVisible.value = true;
};
let timer: NodeJS.Timeout | null = null;
let timer: ReturnType<typeof setTimeout> | null = null;
const mouseleaveHandler = () => {
if (props.disabled) return;

View File

@ -9,7 +9,6 @@ import './theme/index.scss';
export * from './types';
export * from './config';
/* eslint-disable @typescript-eslint/no-unused-vars */
export { default as TMagicAutocomplete } from './Autocomplete.vue';
export { default as TMagicBadge } from './Badge.vue';
export { default as TMagicButton } from './Button.vue';

View File

@ -142,7 +142,7 @@ import codeBlockService from './services/codeBlock';
import componentListService from './services/componentList';
import dataSourceService from './services/dataSource';
import depService from './services/dep';
import editorService, { type EditorService } from './services/editor';
import editorService from './services/editor';
import eventsService from './services/events';
import historyService from './services/history';
import keybindingService from './services/keybinding';
@ -153,25 +153,9 @@ import uiService from './services/ui';
import keybindingConfig from './utils/keybinding-config';
import { defaultEditorProps, EditorProps } from './editorProps';
import { initServiceEvents, initServiceState } from './initService';
import type {
EventBus,
FrameworkSlots,
PropsPanelSlots,
Services,
SidebarSlots,
StageOptions,
WorkspaceSlots,
} from './type';
import type { EditorSlots, EventBus, Services, StageOptions } from './type';
defineSlots<
FrameworkSlots &
WorkspaceSlots &
SidebarSlots &
PropsPanelSlots & {
workspace(props: { editorService: EditorService }): any;
'workspace-content'(props: { editorService: EditorService }): any;
}
>();
defineSlots<EditorSlots>();
defineOptions({
name: 'MEditor',

View File

@ -233,7 +233,7 @@ const changeHandler = (values: CodeBlockContent) => {
changedValue.value = values;
};
const beforeClose = (done: (cancel?: boolean) => void) => {
const beforeClose = (done: (_cancel?: boolean) => void) => {
if (!changedValue.value) {
done();
return;

View File

@ -39,7 +39,7 @@ const props = withDefaults(
defineProps<{
position?: Position;
title?: string;
beforeClose?: (done: (cancel?: boolean) => void) => void;
beforeClose?: (_done: (_cancel?: boolean) => void) => void;
}>(),
{
title: '',

View File

@ -93,7 +93,9 @@ const scrollBy = (delta: number) => {
position: absolute;
background-color: transparent;
opacity: 0.3;
transition: background-color 0.2s linear, opacity 0.2s linear;
transition:
background-color 0.2s linear,
opacity 0.2s linear;
.m-editor-scroll-bar-thumb {
background-color: #aaa;
@ -108,7 +110,9 @@ const scrollBy = (delta: number) => {
.m-editor-scroll-bar-thumb {
height: 6px;
transition: background-color 0.2s linear, height 0.2s ease-in-out;
transition:
background-color 0.2s linear,
height 0.2s ease-in-out;
bottom: 2px;
}
}
@ -120,7 +124,9 @@ const scrollBy = (delta: number) => {
.m-editor-scroll-bar-thumb {
width: 6px;
transition: background-color 0.2s linear, width 0.2s ease-in-out;
transition:
background-color 0.2s linear,
width 0.2s ease-in-out;
right: 2px;
}
}

View File

@ -27,7 +27,7 @@ const emit = defineEmits(['search']);
const filterText = ref('');
let timer: NodeJS.Timeout | null = null;
let timer: ReturnType<typeof setTimeout> | null = null;
const filterTextChangeHandler = () => {
timer && clearTimeout(timer);
timer = setTimeout(() => {

View File

@ -38,9 +38,9 @@ import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
import TreeNode from './TreeNode.vue';
defineSlots<{
'tree-node-content'(props: { data: TreeNodeData }): any;
'tree-node-label'(props: { data: TreeNodeData }): any;
'tree-node-tool'(props: { data: TreeNodeData }): any;
'tree-node-content'(_props: { data: TreeNodeData }): any;
'tree-node-label'(_props: { data: TreeNodeData }): any;
'tree-node-tool'(_props: { data: TreeNodeData }): any;
}>();
defineOptions({

View File

@ -72,9 +72,9 @@ import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
import { updateStatus } from '@editor/utils/tree';
defineSlots<{
'tree-node-label'(props: { data: TreeNodeData }): any;
'tree-node-tool'(props: { data: TreeNodeData }): any;
'tree-node-content'(props: { data: TreeNodeData }): any;
'tree-node-label'(_props: { data: TreeNodeData }): any;
'tree-node-tool'(_props: { data: TreeNodeData }): any;
'tree-node-content'(_props: { data: TreeNodeData }): any;
}>();
defineOptions({

View File

@ -179,7 +179,11 @@ const curCharIsDot = (dotIndex: number) => dotIndex > -1 && dotIndex === getSele
* @param leftCurlyBracketIndex 左大括号字符索引
* @param cb 建议的方法
*/
const dsQuerySearch = (queryString: string, leftCurlyBracketIndex: number, cb: (data: { value: string }[]) => void) => {
const dsQuerySearch = (
queryString: string,
leftCurlyBracketIndex: number,
cb: (_data: { value: string }[]) => void,
) => {
let result: DataSourceSchema[] = [];
if (curCharIsLeftCurlyBracket(leftCurlyBracketIndex)) {
@ -211,7 +215,7 @@ const fieldQuerySearch = (
queryString: string,
leftAngleIndex: number,
dotIndex: number,
cb: (data: { value: string }[]) => void,
cb: (_data: { value: string }[]) => void,
) => {
let result: DataSchema[] = [];
@ -272,7 +276,7 @@ const fieldQuerySearch = (
* @param queryString 当前输入框内的字符串
* @param cb 建议回调
*/
const querySearch = (queryString: string, cb: (data: { value: string }[]) => void) => {
const querySearch = (queryString: string, cb: (_data: { value: string }[]) => void) => {
inputText = queryString;
const selectionStart = getSelectionStart();

View File

@ -80,7 +80,7 @@ const methodColumns: ColumnConfig[] = [
{
text: '编辑',
handler: (method: CodeBlockContent, index: number) => {
let codeContent = method.content || `({ params, dataSource, app }) => {\n // place your code here\n}`;
let codeContent = method.content || '({ params, dataSource, app }) => {\n // place your code here\n}';
if (typeof codeContent !== 'string') {
codeContent = codeContent.toString();
@ -114,7 +114,7 @@ const methodColumns: ColumnConfig[] = [
const createCodeHandler = () => {
codeConfig.value = {
name: '',
content: `({ params, dataSource, app, flowState }) => {\n // place your code here\n}`,
content: '({ params, dataSource, app, flowState }) => {\n // place your code here\n}',
params: [],
};
@ -130,7 +130,7 @@ const submitCodeHandler = (value: CodeBlockContent, data: ContainerChangeEventDa
//
const parseDSL = getEditorConfig('parseDSL');
if (typeof value.content === 'string') {
value.content = parseDSL<(...args: any[]) => any>(value.content);
value.content = parseDSL<(..._args: any[]) => any>(value.content);
}
}
if (editIndex > -1) {

View File

@ -70,7 +70,7 @@ const uiSelectMode = ref(false);
const cancelHandler = () => {
uiService.set('uiSelectMode', false);
uiSelectMode.value = false;
globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener);
globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as any);
};
const clickHandler = ({ detail }: Event & { detail: HTMLElement | MNode }) => {
@ -97,7 +97,7 @@ const toName = computed(() => {
const startSelect = () => {
uiService.set('uiSelectMode', true);
uiSelectMode.value = true;
globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener);
globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as any);
};
const deleteHandler = () => {

View File

@ -16,7 +16,7 @@ export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService'])
const createCodeBlock = async () => {
codeConfig.value = {
name: '',
content: `({app, params, flowState}) => {\n // place your code here\n}`,
content: '({app, params, flowState}) => {\n // place your code here\n}',
params: [],
};

View File

@ -122,7 +122,6 @@ export default {
app.use(formPlugin, opt || {});
app.use(tablePlugin);
// eslint-disable-next-line no-param-reassign
app.config.globalProperties.$TMAGIC_EDITOR = option;
setEditorConfig(option);

View File

@ -269,13 +269,13 @@ export const initServiceEvents = (
}
};
const dsDepCollectedHandler = async () => {
const dsDepCollectedHandler = () => {
const root = editorService.get('root');
const app = await getTMagicApp();
getTMagicApp()?.then((app?: TMagicCore) => {
if (root && app?.dsl) {
app.dsl.dataSourceDeps = root.dataSourceDeps;
}
});
};
const collectIdle = (nodes: MComponent[], deep: boolean, type?: DepTargetType) =>
@ -317,7 +317,7 @@ export const initServiceEvents = (
depService.addTarget(createDataSourceCondTarget(ds, reactive({})));
};
const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
const rootChangeHandler = (value: MApp | null, preValue?: MApp | null) => {
if (!value) return;
value.codeBlocks = value.codeBlocks || {};
@ -347,12 +347,12 @@ export const initServiceEvents = (
delete value.dataSourceCondDeps;
}
const handler = async () => {
const nodeId = editorService.get('node')?.id || props.defaultSelected;
let node;
if (nodeId) {
node = editorService.getNodeById(nodeId);
}
if (node && node !== value) {
await editorService.select(node.id);
} else if (value.items?.length) {
@ -368,18 +368,19 @@ export const initServiceEvents = (
}
};
// 新增节点,收集依赖
const nodeAddHandler = async (nodes: MComponent[]) => {
await collectIdle(nodes, true);
handler();
};
// 新增节点,收集依赖
const nodeAddHandler = (nodes: MComponent[]) => {
collectIdle(nodes, true).then(() => {
updateStageNodes(nodes);
});
};
// 节点更新,收集依赖
// 仅当修改到数据源相关的才收集
const nodeUpdateHandler = async (
data: { newNode: MComponent; oldNode: MComponent; changeRecords?: ChangeRecord[] }[],
) => {
const nodeUpdateHandler = (data: { newNode: MComponent; oldNode: MComponent; changeRecords?: ChangeRecord[] }[]) => {
const needRecollectNodes: MComponent[] = [];
const normalNodes: MComponent[] = [];
for (const { newNode, oldNode, changeRecords } of data) {
@ -424,9 +425,12 @@ export const initServiceEvents = (
if (needRecollectNodes.length) {
// 有数据源依赖需要等依赖重新收集完才更新stage
const handler = async () => {
await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE);
await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE_COND);
updateStageNodes(needRecollectNodes);
};
handler();
} else {
updateStageNodes(normalNodes);
// 在上面判断是否需要收集数据源依赖中已经更新stage
@ -443,9 +447,10 @@ export const initServiceEvents = (
};
// 由于历史记录变化是更新整个page所以历史记录变化时需要重新收集依赖
const historyChangeHandler = async (page: MPage | MPageFragment) => {
await collectIdle([page], true);
const historyChangeHandler = (page: MPage | MPageFragment) => {
collectIdle([page], true).then(() => {
updateStageNode(page);
});
};
editorService.on('history-change', historyChangeHandler);
@ -454,7 +459,8 @@ export const initServiceEvents = (
editorService.on('remove', nodeRemoveHandler);
editorService.on('update', nodeUpdateHandler);
const dataSourceAddHandler = async (config: DataSourceSchema) => {
const dataSourceAddHandler = (config: DataSourceSchema) => {
const handler = async () => {
initDataSourceDepTarget(config);
const app = await getTMagicApp();
@ -471,10 +477,10 @@ export const initServiceEvents = (
}
};
const dataSourceUpdateHandler = async (
config: DataSourceSchema,
{ changeRecords }: { changeRecords: ChangeRecord[] },
) => {
handler();
};
const dataSourceUpdateHandler = (config: DataSourceSchema, { changeRecords }: { changeRecords: ChangeRecord[] }) => {
const updateDsData = async () => {
const app = await getTMagicApp();
@ -565,13 +571,14 @@ export const initServiceEvents = (
depService.removeTarget(id, DepTargetType.DATA_SOURCE_METHOD);
};
const dataSourceRemoveHandler = async (id: string) => {
const dataSourceRemoveHandler = (id: string) => {
const root = editorService.get('root');
if (!root) {
return;
}
const handler = async () => {
const nodeIds = Object.keys(root.dataSourceDeps?.[id] || {});
const nodes = getNodes(nodeIds, root.items);
@ -590,6 +597,9 @@ export const initServiceEvents = (
removeDataSourceTarget(id);
};
handler();
};
dataSourceService.on('add', dataSourceAddHandler);
dataSourceService.on('update', dataSourceUpdateHandler);
dataSourceService.on('remove', dataSourceRemoveHandler);

View File

@ -91,7 +91,7 @@ defineOptions({
defineProps<{
disabledPageFragment: boolean;
pageBarSortOptions?: PageBarSortOptions;
pageFilterFunction?: (page: MPage | MPageFragment, keyword: string) => boolean;
pageFilterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean;
}>();
const codeOptions = inject('codeOptions', {});

View File

@ -69,7 +69,7 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
type: 'button',
className: 'delete',
icon: markRaw(Delete),
tooltip: `刪除(Delete)`,
tooltip: '刪除(Delete)',
disabled: () => editorService.get('node')?.type === NodeType.PAGE,
handler: () => {
const node = editorService.get('node');

View File

@ -91,7 +91,7 @@ const props = withDefaults(
defineProps<{
disabledPageFragment: boolean;
pageBarSortOptions?: PageBarSortOptions;
filterFunction?: (page: MPage | MPageFragment, keyword: string) => boolean;
filterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean;
}>(),
{
filterFunction: (page, keyword) => page.name?.includes(keyword) || `${page.id}`.includes(keyword),

View File

@ -56,7 +56,7 @@ import { useServices } from '@editor/hooks/use-services';
import CodeEditor from '../CodeEditor.vue';
defineSlots<{
'props-form-panel-header'(props: {}): any;
'props-form-panel-header'(_props: {}): any;
}>();
defineOptions({
@ -70,7 +70,7 @@ const props = defineProps<{
labelWidth?: string;
codeValueKey?: string;
labelPosition?: string;
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
}>();
const emit = defineEmits<{

View File

@ -82,7 +82,7 @@ defineOptions({
defineProps<{
disabledShowSrc?: boolean;
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
}>();
const emit = defineEmits<{

View File

@ -80,7 +80,7 @@ const collapseValue = computed(() =>
.map((x, i) => `${i}`),
);
let timeout: NodeJS.Timeout | undefined;
let timeout: ReturnType<typeof setTimeout> | undefined;
let clientX: number;
let clientY: number;

View File

@ -55,7 +55,7 @@ defineOptions({
const props = defineProps<{
indent?: number;
nextLevelIndentIncrement?: number;
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
customError?: (_id: Id, _errorType: CodeDeleteErrorType) => any;
}>();
const emit = defineEmits<{

View File

@ -79,7 +79,7 @@ defineOptions({
const props = defineProps<{
indent?: number;
nextLevelIndentIncrement?: number;
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
customError?: (_id: Id, _errorType: CodeDeleteErrorType) => any;
customContentMenu: CustomContentMenuFunction;
}>();

View File

@ -149,7 +149,7 @@ watch(zoom, (zoom) => {
stage.setZoom(zoom);
});
let timeoutId: NodeJS.Timeout | null = null;
let timeoutId: ReturnType<typeof setTimeout> | null = null;
watch(page, (page) => {
if (runtime && page) {
editorService.set('stageLoading', true);

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*

View File

@ -1088,10 +1088,11 @@ class Editor extends BaseService {
this.isHistoryStateChange = true;
await this.update(value.data);
this.set('modifiedNodeIds', value.modifiedNodeIds);
setTimeout(async () => {
setTimeout(() => {
if (!value.nodeId) return;
await this.select(value.nodeId);
this.select(value.nodeId).then(() => {
this.get('stage')?.select(value.nodeId);
});
}, 0);
this.emit('history-change', value.data);
}

View File

@ -65,6 +65,15 @@ import type { StageOverlayService } from './services/stageOverlay';
import type { StorageService } from './services/storage';
import type { UiService } from './services/ui';
import type { UndoRedo } from './utils/undo-redo';
export type EditorSlots = FrameworkSlots &
WorkspaceSlots &
SidebarSlots &
PropsPanelSlots & {
workspace(props: { editorService: EditorService }): any;
'workspace-content'(props: { editorService: EditorService }): any;
};
export interface FrameworkSlots {
header(props: {}): any;
nav(props: {}): any;

View File

@ -179,13 +179,15 @@ export const fillConfig = (config: FormConfig = [], labelWidth = '80px'): FormCo
append: {
type: 'button',
text: '复制',
handler: async (vm, { model }) => {
try {
await navigator.clipboard.writeText(`${model.id}`);
handler: (vm, { model }) => {
navigator.clipboard
.writeText(`${model.id}`)
.then(() => {
tMagicMessage.success('已复制');
} catch (err) {
})
.catch(() => {
tMagicMessage.error('复制失败');
}
});
},
},
},

View File

@ -63,7 +63,7 @@ const props = withDefaults(
keyProp?: string;
popperClass?: string;
preventSubmitDefault?: boolean;
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
}>(),
{
config: () => [],
@ -101,7 +101,7 @@ const formState: FormState = reactive<FormState>({
parentValues: props.parentValues,
values,
lastValuesProcessed,
$emit: emit as (event: string, ...args: any[]) => void,
$emit: emit as (_event: string, ..._args: any[]) => void,
fields,
setField: (prop: string, field: any) => fields.set(prop, field),
getField: (prop: string) => fields.get(prop),

View File

@ -82,7 +82,7 @@ withDefaults(
labelPosition?: string;
preventSubmitDefault?: boolean;
/** 关闭前的回调,会暂停 Drawer 的关闭; done 是个 function type 接受一个 boolean 参数, 执行 done 使用 true 参数或不提供参数将会终止关闭 */
beforeClose?: (done: (cancel?: boolean) => void) => void;
beforeClose?: (_done: (_cancel?: boolean) => void) => void;
}>(),
{
closeOnPressEscape: true,

View File

@ -449,7 +449,6 @@ const onChangeHandler = async function (v: any, eventData: ContainerChangeEventD
valueProp = valueProp ? `${valueProp}.${eventData.modifyKey}` : eventData.modifyKey!;
// modifyKey
// eslint-disable-next-line no-param-reassign
delete eventData.modifyKey;
} else if (isValidName() && props.model !== value && (v !== value || props.model[name.value] !== value)) {
// fieldfield-linkmodel===value,

View File

@ -273,22 +273,18 @@ const isFullscreen = ref(false);
const modelName = computed(() => props.name || props.config.name || '');
const data = computed(() =>
props.config.pagination
? (props.model[modelName.value] || []).filter(
const getDataByPage = (data: any[] = []) =>
data.filter(
(item: any, index: number) =>
index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value,
)
: props.model[modelName.value],
);
const pageinationData = computed(() => getDataByPage(props.model[modelName.value]));
const data = computed(() => (props.config.pagination ? pageinationData.value : props.model[modelName.value]));
const lastData = computed(() =>
props.config.pagination
? (props.lastValues[modelName.value] || []).filter(
(item: any, index: number) =>
index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value,
)
: props.lastValues[modelName.value] || [],
props.config.pagination ? getDataByPage(props.lastValues[modelName.value]) : props.lastValues[modelName.value] || [],
);
const sortChange = ({ prop, order }: SortProp) => {

View File

@ -13,7 +13,6 @@ defineOptions({
const props = defineProps<FieldProps<DisplayConfig>>();
if (props.config.initValue && props.model) {
// eslint-disable-next-line vue/no-setup-props-destructure
props.model[props.name] = props.config.initValue;
}

View File

@ -21,7 +21,7 @@
>
<template v-if="config.group">
<component
v-for="(group, index) in (options as SelectGroupOption[])"
v-for="(group, index) in options as SelectGroupOption[]"
:key="index"
:is="optionGroupComponent?.component || 'el-option-group'"
v-bind="
@ -55,7 +55,7 @@
</template>
<template v-else>
<component
v-for="option in (options as SelectOption[])"
v-for="option in options as SelectOption[]"
class="tmagic-design-option"
:key="config.valueKey ? option.value[config.valueKey] : option.value"
:is="optionComponent?.component || 'el-option'"

View File

@ -31,7 +31,6 @@ const emit = defineEmits(['change']);
useAddField(props.prop);
// eslint-disable-next-line vue/no-setup-props-destructure
const { names } = props.config;
const value = ref<(Date | undefined)[] | null>([]);

View File

@ -101,7 +101,6 @@ export default {
install(app: App, opt: FormInstallOptions = {}) {
const option = Object.assign(defaultInstallOpt, opt);
// eslint-disable-next-line no-param-reassign
app.config.globalProperties.$MAGIC_FORM = option;
setConfig(option);

View File

@ -196,6 +196,7 @@ export interface OnChangeHandlerData {
config: any;
prop: string;
changeRecords: ChangeRecord[];
setModel: (prop: string, value: any) => void;
}
export type OnChangeHandler = (mForm: FormState | undefined, value: any, data: OnChangeHandlerData) => any;

View File

@ -16,7 +16,6 @@
* limitations under the License.
*/
/* eslint-disable no-param-reassign */
import { toRaw } from 'vue';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';

View File

@ -92,7 +92,8 @@ export default class ActionManager extends EventEmitter {
private disabledMultiSelect = false;
private config: ActionManagerConfig;
private mouseMoveHandler = throttle(async (event: MouseEvent): Promise<void> => {
private mouseMoveHandler = throttle((event: MouseEvent): void => {
const handler = async () => {
if ((event.target as HTMLDivElement)?.classList?.contains('moveable-direction')) {
return;
}
@ -106,6 +107,9 @@ export default class ActionManager extends EventEmitter {
this.emit('mousemove', event);
this.highlight(id);
};
handler();
}, throttleTime);
constructor(config: ActionManagerConfig) {
@ -299,6 +303,7 @@ export default class ActionManager extends EventEmitter {
el = this.getTargetElement(id);
} catch (error) {
this.clearHighlight();
console.warn('getTargetElement error:', error);
return;
}
@ -606,7 +611,8 @@ export default class ActionManager extends EventEmitter {
/**
* down事件中集中cpu处理画布中选中操作渲染up事件中再通知外面的编辑器更新
*/
private mouseDownHandler = async (event: MouseEvent): Promise<void> => {
private mouseDownHandler = (event: MouseEvent): void => {
const handler = async () => {
this.clearHighlight();
event.stopImmediatePropagation();
event.stopPropagation();
@ -631,6 +637,9 @@ export default class ActionManager extends EventEmitter {
getDocument().addEventListener('mouseup', this.mouseUpHandler);
};
handler();
};
private isStopTriggerSelect(event: MouseEvent): boolean {
if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT) return true;
if (!event.target) return true;

View File

@ -285,13 +285,18 @@ export default class DragResizeHelper {
getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) && getIdFromEl()(ev.target)?.endsWith(frameItem.id),
);
if (!frameSnapShot) return;
const targeEl = this.targetList.find(
(targetItem) =>
getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) &&
getIdFromEl()(targetItem) &&
getIdFromEl()(ev.target)?.endsWith(getIdFromEl()(targetItem)!),
);
const findTargetElFuctin = (targetItem: HTMLElement) => {
const getId = getIdFromEl();
const targetId = getId(ev.target);
const targetItemId = getId(targetItem);
return targetId?.startsWith(DRAG_EL_ID_PREFIX) && targetItemId && targetId?.endsWith(targetItemId);
};
const targeEl = this.targetList.find(findTargetElFuctin);
if (!targeEl) return;
// 元素与其所属组同时加入多选列表时,只更新父元素
const isParentIncluded = this.targetList.find(
(targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement),

View File

@ -16,7 +16,6 @@
* limitations under the License.
*/
/* eslint-disable no-param-reassign */
import Moveable, { MoveableOptions } from 'moveable';
import { getIdFromEl } from '@tmagic/core';

View File

@ -235,7 +235,8 @@ export default class StageRender extends EventEmitter {
}
}
private iframeLoadHandler = async () => {
private iframeLoadHandler = () => {
const handler = async () => {
if (!this.contentWindow?.magic) {
this.postTmagicRuntimeReady();
}
@ -253,4 +254,7 @@ export default class StageRender extends EventEmitter {
injectStyle(this.contentWindow.document, style);
};
handler();
};
}

View File

@ -61,7 +61,7 @@ Object.defineProperties(globalThis.HTMLElement.prototype, {
const createElement = () => {
const el = globalThis.document.createElement('div');
el.style.cssText = `width: 100px; height: 100px; position: absolute; left: 100px; top: 100px;`;
el.style.cssText = 'width: 100px; height: 100px; position: absolute; left: 100px; top: 100px;';
return el;
};
@ -78,7 +78,7 @@ describe('getOffset', () => {
});
test('没有offsetParent 没有left、top', () => {
div.style.cssText = `width: 100px; height: 100px`;
div.style.cssText = 'width: 100px; height: 100px';
root.appendChild(div);
const offset = util.getOffset(div);
expect(offset.left).toBe(0);
@ -95,7 +95,7 @@ describe('getOffset', () => {
test('有offsetParent 没有left、top', () => {
const parent = createElement();
div.style.cssText = `width: 100px; height: 100px`;
div.style.cssText = 'width: 100px; height: 100px';
parent.appendChild(div);
root.appendChild(parent);
@ -119,7 +119,7 @@ describe('getAbsolutePosition', () => {
test('有offsetParent', () => {
const parent = createElement();
div.style.cssText = `width: 100px; height: 100px`;
div.style.cssText = 'width: 100px; height: 100px';
parent.appendChild(div);
root.appendChild(parent);
const offset = util.getAbsolutePosition(div, { left: 100, top: 100 });

View File

@ -84,7 +84,7 @@ const props = withDefaults(
data: any[];
columns?: ColumnConfig[];
/** 合并行或列的计算方法 */
spanMethod?: (data: { row: any; column: any; rowIndex: number; columnIndex: number }) => [number, number];
spanMethod?: (_data: { row: any; column: any; rowIndex: number; columnIndex: number }) => [number, number];
loading?: boolean;
/** Table 的最大高度。合法的值为数字或者单位为 px 的高度 */
bodyHeight?: string | number;

View File

@ -25,12 +25,12 @@ export const formatter = (item: ColumnConfig, row: any, data: { index: number })
if (item.formatter) {
if (item.formatter === 'datetime') {
// eslint-disable-next-line no-param-reassign
item.formatter = (value: string) => datetimeFormatter(value);
}
try {
return item.formatter(row[item.prop], row, data);
} catch (e) {
console.error('Formatter error:', e);
return row[item.prop];
}
} else {

View File

@ -40,6 +40,7 @@ export * from './dom';
// for typeof global checks without @types/node
declare let global: {};
// eslint-disable-next-line @typescript-eslint/naming-convention
let _globalThis: any;
export const getGlobalThis = (): any =>
_globalThis ||
@ -410,6 +411,7 @@ export const getDefaultValueFromFields = (fields: DataSchema[]) => {
data[field.name] = JSON.parse(field.defaultValue);
} catch (e) {
data[field.name] = defaultValue.object;
console.warn('defaultValue 解析失败', field.defaultValue, e);
}
return;
}

View File

@ -172,8 +172,8 @@ describe('filterXSS', () => {
expect(value).toBe('&lt;div&gt;&lt;/div&gt;');
});
test(`'"`, () => {
const value = util.filterXSS(`'div'"span"`);
test('\'"', () => {
const value = util.filterXSS('\'div\'"span"');
expect(value).toBe('&apos;div&apos;&quot;span&quot;');
});
});

1104
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -4,3 +4,5 @@ packages:
- 'runtime/*'
- 'vue-components/*'
- 'react-components/*'
- 'eslint-config'

View File

@ -67,7 +67,7 @@ const IteratorContainer: React.FC<IteratorContainerProps> = ({
const configs: IteratorItemSchema[] = iteratorData.map((itemData: any, index: number) => {
const condResult =
app?.platform !== 'editor'
? app?.dataSourceManager?.compliedIteratorItemConds(itemData, itemConfig, dsField) ?? true
? (app?.dataSourceManager?.compliedIteratorItemConds(itemData, itemConfig, dsField) ?? true)
: true;
const newItems = app?.dataSourceManager?.compliedIteratorItems(itemData, items, dsField) ?? items;

View File

@ -22,7 +22,7 @@ const runtimes = readdirSync('temp/runtime');
const targets = process.env.TARGETS ? process.env.TARGETS.split(',') : null;
const targetPackages = targets ? packages.filter((pkg) => targets.includes(pkg)) : packages;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dirname = path.dirname(fileURLToPath(import.meta.url));
function rollupConfig(pkg, base) {
return {
@ -34,9 +34,9 @@ function rollupConfig(pkg, base) {
plugins: [
alias({
entries: [
{ find: /^@form/, replacement: path.join(__dirname, `./temp/packages/form/src`) },
{ find: /^@editor/, replacement: path.join(__dirname, `./temp/packages/editor/src`) },
{ find: /^@data-source/, replacement: path.join(__dirname, `./temp/packages/data-source/src`) },
{ find: /^@form/, replacement: path.join(dirname, './temp/packages/form/src') },
{ find: /^@editor/, replacement: path.join(dirname, './temp/packages/editor/src') },
{ find: /^@data-source/, replacement: path.join(dirname, './temp/packages/data-source/src') },
],
}),
dts(),
@ -57,5 +57,5 @@ export default [
];
function removeScss(path) {
writeFileSync(path, readFileSync(path).toString().replace(`import './theme/index.scss';`, ''));
writeFileSync(path, readFileSync(path).toString().replace("import './theme/index.scss';", ''));
}

View File

@ -37,13 +37,15 @@ export interface UseAppOptions<T extends MNodeInstance = MNodeInstance> {
};
}
export const useNode = (
export const useNode = <T extends TMagicNode = TMagicNode>(
props: Pick<UseAppOptions, 'config' | 'iteratorContainerId' | 'iteratorIndex'>,
app = useContext(AppContent),
) =>
isDslNode(props.config) && props.config.id
? app?.getNode(props.config.id, props.iteratorContainerId, props.iteratorIndex)
: undefined;
): T | undefined => {
if (isDslNode(props.config) && props.config.id) {
app?.getNode(props.config.id, props.iteratorContainerId, props.iteratorIndex);
}
return void 0;
};
export const registerNodeHooks = (node?: TMagicNode, methods: Methods = {}) => {
if (!node) {

View File

@ -48,6 +48,7 @@ const getLocalConfig = (): MApp[] => {
// eslint-disable-next-line no-eval
return [eval(`(${configStr})`)];
} catch (err) {
console.error('Error parsing localStorage magicDSL:', err);
return [];
}
};

View File

@ -33,13 +33,15 @@ interface UseAppOptions<T extends MNodeInstance = MNodeInstance> {
methods?: Methods;
}
export const useNode = (
export const useNode = <T extends TMagicNode = TMagicNode>(
props: Pick<UseAppOptions, 'config' | 'iteratorContainerId' | 'iteratorIndex'>,
app = inject<TMagicApp>('app'),
) =>
isDslNode(props.config) && props.config.id
? app?.getNode(props.config.id, props.iteratorContainerId, props.iteratorIndex)
: undefined;
): T | undefined => {
if (isDslNode(props.config) && props.config.id) {
return app?.getNode(props.config.id, props.iteratorContainerId, props.iteratorIndex);
}
return void 0;
};
export const registerNodeHooks = (node?: TMagicNode, methods: Methods = {}) => {
if (!node) {

View File

@ -8,7 +8,7 @@ export * from './hooks/use-app';
export * from './hooks/use-component-status';
export { useComponent } from './hooks/use-component';
export interface userRenderFunctionOptions {
export interface UserRenderFunctionOptions {
h: typeof h;
type: Parameters<typeof h>[0];
props?: {
@ -26,4 +26,4 @@ export interface userRenderFunctionOptions {
directives?: { name: string; value: any; modifiers: any }[];
}
export type UserRenderFunction = (options: userRenderFunctionOptions) => any;
export type UserRenderFunction = (options: UserRenderFunctionOptions) => any;

View File

@ -19,7 +19,7 @@
import Vue from 'vue';
import TMagicApp, { DataSourceManager, DeepObservedData, getUrlParam, registerDataSourceOnDemand } from '@tmagic/core';
import type { userRenderFunctionOptions } from '@tmagic/vue-runtime-help';
import type { UserRenderFunctionOptions } from '@tmagic/vue-runtime-help';
import asyncDataSources from '../.tmagic/async-datasource-entry';
import components from '../.tmagic/comp-entry';
@ -65,7 +65,7 @@ registerDataSourceOnDemand(dsl, asyncDataSources).then((dataSources) => {
const vueApp = new Vue({
provide: {
app,
userRender: ({ h, type, props, attrs, style, className }: userRenderFunctionOptions) =>
userRender: ({ h, type, props, attrs, style, className }: UserRenderFunctionOptions) =>
// class作为保留字符android 4.4以下不能直接使用, 需要加引号
// eslint-disable-next-line prettier/prettier
h(type, { props, attrs, style, 'class': className }),

View File

@ -25,6 +25,7 @@ export const getLocalConfig = (): MApp[] => {
// eslint-disable-next-line no-eval
return [eval(`(${configStr})`)];
} catch (err) {
console.error('Error parsing localStorage magicDSL:', err);
return [];
}
};

View File

@ -19,7 +19,7 @@
import Vue from 'vue';
import TMagicApp, { DataSourceManager, DeepObservedData } from '@tmagic/core';
import type { userRenderFunctionOptions } from '@tmagic/vue-runtime-help';
import type { UserRenderFunctionOptions } from '@tmagic/vue-runtime-help';
import App from './App.vue';
@ -62,10 +62,8 @@ Promise.all([
render: (h) => h(App),
provide: {
app,
userRender: ({ h, type, props, attrs, style, className }: userRenderFunctionOptions) =>
// class作为保留字符android 4.4以下不能直接使用, 需要加引号
// eslint-disable-next-line prettier/prettier
h(type, { props, attrs, style, 'class': className }),
userRender: ({ h, type, props, attrs, style, className }: UserRenderFunctionOptions) =>
h(type, { props, attrs, style, class: className }),
},
el: '#app',
});

View File

@ -1,5 +1,5 @@
<template>
<component :is="pageComponent" :config="(pageConfig as MPage)"></component>
<component :is="pageComponent" :config="pageConfig as MPage"></component>
</template>
<script lang="ts" setup>

View File

@ -25,6 +25,7 @@ export const getLocalConfig = (): MApp[] => {
// eslint-disable-next-line no-eval
return [eval(`(${configStr})`)];
} catch (err) {
console.error('Error parsing localStorage magicDSL:', err);
return [];
}
};

View File

@ -12,9 +12,9 @@ const args = minimist(process.argv.slice(2));
const toPascalCase = (str) => str.replace(/(^\w|-\w)/g, (text) => text.replace(/-/, '').toUpperCase());
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packagesDir = path.resolve(__dirname, '../packages');
const runtimeDir = path.resolve(__dirname, '../runtime');
const dirname = path.dirname(fileURLToPath(import.meta.url));
const packagesDir = path.resolve(dirname, '../packages');
const runtimeDir = path.resolve(dirname, '../runtime');
if (args.package) {
const pkgRoot = path.resolve(packagesDir, args.package);

View File

@ -23,7 +23,7 @@ let versionUpdated = false;
const { prompt } = enquirer;
const currentVersion = createRequire(import.meta.url)('../package.json').version;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dirname = path.dirname(fileURLToPath(import.meta.url));
const args = minimist(process.argv.slice(2), {
alias: {
skipBuild: 'skip-build',
@ -42,12 +42,13 @@ const { skipBuild } = args;
const skipPrompts = args.skipPrompts || args.canary;
const skipGit = args.skipGit || args.canary;
const packages = fs.readdirSync(path.resolve(__dirname, '../packages')).filter((p) => {
const pkgRoot = path.resolve(__dirname, '../packages', p);
const packages = fs.readdirSync(path.resolve(dirname, '../packages')).filter((p) => {
const pkgRoot = path.resolve(dirname, '../packages', p);
if (fs.statSync(pkgRoot).isDirectory()) {
const pkg = JSON.parse(fs.readFileSync(path.resolve(pkgRoot, 'package.json'), 'utf-8'));
return !pkg.private;
}
return false;
});
const keepThePackageName = (/** @type {string} */ pkgName) => pkgName;
@ -77,14 +78,14 @@ const dryRun = async (
const runIfNotDry = isDryRun ? dryRun : run;
const getPkgRoot = (/** @type {string} */ pkg) => path.resolve(__dirname, `../packages/${pkg}`);
const getRunTimeRoot = (pkg) => path.resolve(__dirname, `../runtime/${pkg}`);
const getPlayground = () => path.resolve(__dirname, `../playground`);
const getPlayground = () => path.resolve(__dirname, '../playground');
const step = (/** @type {string} */ msg) => console.log(pico.cyan(msg));
async function main() {
if (!(await isInSyncWithRemote())) {
return;
}
console.log(`${pico.green(``)} commit is up-to-date with remote.\n`);
console.log(`${pico.green('✓')} commit is up-to-date with remote.\n`);
let targetVersion = args._[0];
@ -141,7 +142,7 @@ async function main() {
const { yes: promptSkipTests } = await prompt({
type: 'confirm',
name: 'yes',
message: `CI for this commit passed. Skip local tests?`,
message: 'CI for this commit passed. Skip local tests?',
});
skipTests = promptSkipTests;
@ -153,7 +154,7 @@ async function main() {
if (!isDryRun) {
await run('pnpm', ['run', 'test', '--run']);
} else {
console.log(`Skipped (dry run)`);
console.log('Skipped (dry run)');
}
} else {
step('Tests skipped.');
@ -169,19 +170,19 @@ async function main() {
if (!skipBuild && !isDryRun) {
await run('pnpm', ['run', 'build']);
} else {
console.log(`(skipped)`);
console.log('(skipped)');
}
// generate changelog
step('\nGenerating changelog...');
await run(`pnpm`, ['run', 'changelog']);
await run('pnpm', ['run', 'changelog']);
if (!skipPrompts) {
/** @type {{ yes: boolean }} */
const { yes: changelogOk } = await prompt({
type: 'confirm',
name: 'yes',
message: `Changelog generated. Does it look good?`,
message: 'Changelog generated. Does it look good?',
});
if (!changelogOk) {
@ -223,7 +224,7 @@ async function main() {
// update pnpm-lock.yaml
step('\nUpdating lockfile...');
await run(`pnpm`, ['install', '--prefer-offline']);
await run('pnpm', ['install', '--prefer-offline']);
if (!skipGit) {
const { stdout } = await run('git', ['diff'], { stdio: 'pipe' });
@ -245,7 +246,7 @@ async function main() {
}
if (isDryRun) {
console.log(`\nDry run finished - run git diff to see package changes.`);
console.log('\nDry run finished - run git diff to see package changes.');
}
if (skippedPackages.length) {
@ -260,8 +261,7 @@ async function getCIResult() {
try {
const sha = await getSha();
const res = await fetch(
`https://api.github.com/repos/vuejs/core/actions/runs?head_sha=${sha}` +
`&status=success&exclude_pull_requests=true`,
`https://api.github.com/repos/vuejs/core/actions/runs?head_sha=${sha}&status=success&exclude_pull_requests=true`,
);
const data = await res.json();
return data.workflow_runs.length > 0;
@ -283,7 +283,7 @@ async function isInSyncWithRemote() {
const { yes } = await prompt({
type: 'confirm',
name: 'yes',
message: pico.red(`Local HEAD is not up-to-date with remote. Are you sure you want to continue?`),
message: pico.red('Local HEAD is not up-to-date with remote. Are you sure you want to continue?'),
});
return yes;
} catch {

View File

@ -79,7 +79,7 @@ export default defineComponent({
return iteratorData.map((itemData: any) => {
const condResult =
app?.platform !== 'editor'
? app?.dataSourceManager?.compliedIteratorItemConds(itemData, itemConfig, dsField) ?? true
? (app?.dataSourceManager?.compliedIteratorItemConds(itemData, itemConfig, dsField) ?? true)
: true;
const newItems = app?.dataSourceManager?.compliedIteratorItems(itemData, items, dsField) ?? items;