@lark-apaas/fullstack-presets 1.1.17-alpha.4 → 1.1.17-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/custom-eslint-rules/index.d.ts +2 -0
- package/lib/custom-eslint-rules/index.js +4 -0
- package/lib/custom-eslint-rules/no-multiline-styled-jsx-classname.d.ts +29 -0
- package/lib/custom-eslint-rules/no-multiline-styled-jsx-classname.js +100 -0
- package/lib/custom-eslint-rules/no-styled-jsx-data-uri-url-ref.d.ts +20 -0
- package/lib/custom-eslint-rules/no-styled-jsx-data-uri-url-ref.js +89 -0
- package/lib/recommend/eslint/eslint-client.d.ts +2 -0
- package/lib/recommend/eslint/eslint-client.js +4 -0
- package/lib/recommend/eslint/index.d.ts +2 -0
- package/lib/recommend/eslint/index.js +1 -1
- package/lib/simple/recommend/eslint/eslint-client.js +4 -0
- package/lib/simple/recommend/eslint/index.d.ts +4 -0
- package/lib/simple/recommend/eslint/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const customRules: {
|
|
2
2
|
'no-nested-styled-jsx': import("eslint").Rule.RuleModule;
|
|
3
|
+
'no-styled-jsx-data-uri-url-ref': import("eslint").Rule.RuleModule;
|
|
4
|
+
'no-multiline-styled-jsx-classname': import("eslint").Rule.RuleModule;
|
|
3
5
|
'require-app-container': import("eslint").Rule.RuleModule;
|
|
4
6
|
'no-direct-capability-api': import("eslint").Rule.RuleModule;
|
|
5
7
|
'require-scroll-reveal-hook': import("eslint").Rule.RuleModule;
|
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.customRules = void 0;
|
|
7
7
|
const no_nested_styled_jsx_1 = __importDefault(require("./no-nested-styled-jsx"));
|
|
8
|
+
const no_styled_jsx_data_uri_url_ref_1 = __importDefault(require("./no-styled-jsx-data-uri-url-ref"));
|
|
9
|
+
const no_multiline_styled_jsx_classname_1 = __importDefault(require("./no-multiline-styled-jsx-classname"));
|
|
8
10
|
const require_app_container_1 = __importDefault(require("./require-app-container"));
|
|
9
11
|
const no_direct_capability_api_1 = __importDefault(require("./no-direct-capability-api"));
|
|
10
12
|
const require_scroll_reveal_hook_1 = __importDefault(require("./require-scroll-reveal-hook"));
|
|
@@ -16,6 +18,8 @@ const require_index_route_1 = __importDefault(require("./require-index-route"));
|
|
|
16
18
|
const no_duplicate_route_component_1 = __importDefault(require("./no-duplicate-route-component"));
|
|
17
19
|
exports.customRules = {
|
|
18
20
|
'no-nested-styled-jsx': no_nested_styled_jsx_1.default,
|
|
21
|
+
'no-styled-jsx-data-uri-url-ref': no_styled_jsx_data_uri_url_ref_1.default,
|
|
22
|
+
'no-multiline-styled-jsx-classname': no_multiline_styled_jsx_classname_1.default,
|
|
19
23
|
'require-app-container': require_app_container_1.default,
|
|
20
24
|
'no-direct-capability-api': no_direct_capability_api_1.default,
|
|
21
25
|
'require-scroll-reveal-hook': require_scroll_reveal_hook_1.default,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule: no-multiline-styled-jsx-classname
|
|
3
|
+
*
|
|
4
|
+
* Detects multi-line TemplateLiteral className attributes on JSX elements
|
|
5
|
+
* inside a component that uses `<style jsx>`.
|
|
6
|
+
*
|
|
7
|
+
* When styled-jsx's Babel plugin wraps the className, it preserves the
|
|
8
|
+
* TemplateLiteral in the output:
|
|
9
|
+
*
|
|
10
|
+
* className={"jsx-X" + " " + `\n bg-primary\n`}
|
|
11
|
+
*
|
|
12
|
+
* Some downstream transpilers (esbuild/swc/terser at certain target settings)
|
|
13
|
+
* lower simple TemplateLiterals (no ${}) to StringLiterals but fail to escape
|
|
14
|
+
* the embedded newlines, producing invalid JS:
|
|
15
|
+
*
|
|
16
|
+
* className={"jsx-X" + " " + "\n bg-primary\n"}
|
|
17
|
+
* // → "Unterminated string literal" build error
|
|
18
|
+
*
|
|
19
|
+
* The fix is simple: write className as a single-line string. CSS class lists
|
|
20
|
+
* treat any whitespace run as one separator, so collapsing is semantically
|
|
21
|
+
* equivalent.
|
|
22
|
+
*
|
|
23
|
+
* Observed: ~59 of 83 Unterminated cases in week 04-06~04-12.
|
|
24
|
+
*
|
|
25
|
+
* @see https://bytedance.larkoffice.com/wiki/TTNDwzgLzi9Q0skTUT5clFX3nNd
|
|
26
|
+
*/
|
|
27
|
+
import type { Rule } from 'eslint';
|
|
28
|
+
declare const rule: Rule.RuleModule;
|
|
29
|
+
export default rule;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const rule = {
|
|
4
|
+
meta: {
|
|
5
|
+
type: 'problem',
|
|
6
|
+
docs: {
|
|
7
|
+
description: 'Disallow multi-line template literal className in components using <style jsx>',
|
|
8
|
+
category: 'Possible Errors',
|
|
9
|
+
recommended: true,
|
|
10
|
+
},
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
schema: [],
|
|
13
|
+
messages: {
|
|
14
|
+
multilineClassName: '多行模板字符串 className 在 styled-jsx 编译后可能被下游构建工具截断为非法字符串' +
|
|
15
|
+
'("Unterminated string literal")。请改为单行写法。',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
create(context) {
|
|
19
|
+
// Track whether the current file/scope has any <style jsx>
|
|
20
|
+
let hasStyledJsx = false;
|
|
21
|
+
// Collect candidate className nodes to check (deferred until Program:exit
|
|
22
|
+
// so we know whether <style jsx> exists in the file)
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
const candidates = [];
|
|
25
|
+
return {
|
|
26
|
+
// First pass: detect <style jsx> anywhere in the file
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
JSXOpeningElement(node) {
|
|
29
|
+
if (hasStyledJsx)
|
|
30
|
+
return; // already found one
|
|
31
|
+
const name = node.name;
|
|
32
|
+
if (name?.type !== 'JSXIdentifier' || name.name !== 'style')
|
|
33
|
+
return;
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
const isJsx = node.attributes?.some(
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
(attr) => attr.type === 'JSXAttribute' &&
|
|
38
|
+
attr.name?.type === 'JSXIdentifier' &&
|
|
39
|
+
attr.name.name === 'jsx');
|
|
40
|
+
if (isJsx)
|
|
41
|
+
hasStyledJsx = true;
|
|
42
|
+
},
|
|
43
|
+
// Collect className attributes that contain newlines:
|
|
44
|
+
// Case A: className={`...\n...`} — TemplateLiteral without ${}
|
|
45
|
+
// Case B: className="...\n..." — JSX string literal with embedded newlines
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
JSXAttribute(node) {
|
|
48
|
+
if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'className')
|
|
49
|
+
return;
|
|
50
|
+
const val = node.value;
|
|
51
|
+
if (!val)
|
|
52
|
+
return;
|
|
53
|
+
// Case A: className={`...\n...`}
|
|
54
|
+
if (val.type === 'JSXExpressionContainer') {
|
|
55
|
+
const expr = val.expression;
|
|
56
|
+
if (!expr || expr.type !== 'TemplateLiteral')
|
|
57
|
+
return;
|
|
58
|
+
if (expr.expressions.length !== 0)
|
|
59
|
+
return;
|
|
60
|
+
const raw = expr.quasis[0]?.value?.raw ?? '';
|
|
61
|
+
if (!raw.includes('\n') && !raw.includes('\r'))
|
|
62
|
+
return;
|
|
63
|
+
candidates.push({ node, target: expr, raw, kind: 'template' });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Case B: className="...\n..." (JSX string attribute with literal newlines)
|
|
67
|
+
if (val.type === 'Literal' || val.type === 'StringLiteral') {
|
|
68
|
+
const str = val.value ?? '';
|
|
69
|
+
if (!str.includes('\n') && !str.includes('\r'))
|
|
70
|
+
return;
|
|
71
|
+
candidates.push({ node, target: val, raw: str, kind: 'string' });
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
// At the end, report if <style jsx> was present
|
|
75
|
+
'Program:exit'() {
|
|
76
|
+
if (!hasStyledJsx)
|
|
77
|
+
return;
|
|
78
|
+
for (const { target, raw, kind } of candidates) {
|
|
79
|
+
const text = kind === 'template'
|
|
80
|
+
? (target.quasis[0]?.value?.cooked ?? raw)
|
|
81
|
+
: raw;
|
|
82
|
+
const normalized = text.replace(/\s+/g, ' ').trim();
|
|
83
|
+
context.report({
|
|
84
|
+
node: target,
|
|
85
|
+
messageId: 'multilineClassName',
|
|
86
|
+
fix(fixer) {
|
|
87
|
+
if (kind === 'template') {
|
|
88
|
+
// Replace `\n flex items\n bg-primary\n` with "flex items bg-primary"
|
|
89
|
+
return fixer.replaceText(target, JSON.stringify(normalized));
|
|
90
|
+
}
|
|
91
|
+
// Replace "...\n..." with "collapsed" — keep the existing quotes style
|
|
92
|
+
return fixer.replaceText(target, JSON.stringify(normalized));
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
exports.default = rule;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule: no-styled-jsx-data-uri-url-ref
|
|
3
|
+
*
|
|
4
|
+
* Detects CSS `url("data:...url(...)...")` inside `<style jsx>` template
|
|
5
|
+
* literals. The stylis CSS parser (used by styled-jsx) misreads the inner
|
|
6
|
+
* `url(` as a new CSS url() function call, breaking bracket counting and
|
|
7
|
+
* causing "Nesting detected" build failures.
|
|
8
|
+
*
|
|
9
|
+
* This rule surfaces the problem at lint time (IDE red squiggly + CI gate)
|
|
10
|
+
* instead of letting it escape to build time.
|
|
11
|
+
*
|
|
12
|
+
* Observed: 107 cases in week 04-06~04-12, all from AI-generated SVG noise
|
|
13
|
+
* textures containing `filter='url(%23noise)'` inside data URIs.
|
|
14
|
+
*
|
|
15
|
+
* @see ~/docs/styled-jsx-nesting-bug-analysis-20260330.md
|
|
16
|
+
* @see https://bytedance.larkoffice.com/wiki/TTNDwzgLzi9Q0skTUT5clFX3nNd
|
|
17
|
+
*/
|
|
18
|
+
import type { Rule } from 'eslint';
|
|
19
|
+
declare const rule: Rule.RuleModule;
|
|
20
|
+
export default rule;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Detection strategy: instead of trying to parse the full url("data:...")
|
|
5
|
+
* atom with a single regex, we use a two-step check:
|
|
6
|
+
*
|
|
7
|
+
* 1. Does the CSS text contain a data URI? (`url("data:` or `url('data:`)
|
|
8
|
+
* 2. Does the CSS text also contain `url(%23` or `url(#` — the inner
|
|
9
|
+
* url() reference that triggers the stylis bracket-counting bug?
|
|
10
|
+
*
|
|
11
|
+
* If both are true → report. This is deliberately conservative: it may
|
|
12
|
+
* false-positive if the inner `url(%23` happens to appear in a completely
|
|
13
|
+
* separate CSS rule (not inside the data URI). In practice that never
|
|
14
|
+
* happens — `url(%23...)` is only ever seen inside SVG data URIs.
|
|
15
|
+
*
|
|
16
|
+
* Why not a single regex? The data URI body can contain both single AND
|
|
17
|
+
* double quotes (e.g. `url("data:...filter='url(%23n)'...")`) which makes
|
|
18
|
+
* `[^"']*` based approaches fail to match across inner quotes.
|
|
19
|
+
*/
|
|
20
|
+
const HAS_DATA_URI = /url\(\s*["']data:/i;
|
|
21
|
+
const HAS_URL_HASH_REF = /url\(\s*%23|url\(\s*#/i;
|
|
22
|
+
const rule = {
|
|
23
|
+
meta: {
|
|
24
|
+
type: 'problem',
|
|
25
|
+
docs: {
|
|
26
|
+
description: 'Disallow data URIs containing url() references inside <style jsx>, which crash the stylis CSS parser',
|
|
27
|
+
category: 'Possible Errors',
|
|
28
|
+
recommended: true,
|
|
29
|
+
},
|
|
30
|
+
schema: [],
|
|
31
|
+
messages: {
|
|
32
|
+
dataUriUrlRef: 'Data URI 内包含 url() 引用(如 SVG filter 的 url(#id)),会导致 styled-jsx 的 CSS 解析器(stylis)' +
|
|
33
|
+
'误判括号配对并报 "Nesting detected" 构建错误。请将 SVG 移到独立 .svg 文件通过 import 引用,' +
|
|
34
|
+
'或改用 CSS 变量 / 外链 background-image 代替内联 data URI。',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
create(context) {
|
|
38
|
+
return {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
JSXElement(node) {
|
|
41
|
+
// Only look at <style jsx> or <style jsx global>
|
|
42
|
+
const opening = node.openingElement;
|
|
43
|
+
if (!opening)
|
|
44
|
+
return;
|
|
45
|
+
const name = opening.name;
|
|
46
|
+
if (name?.type !== 'JSXIdentifier' || name.name !== 'style')
|
|
47
|
+
return;
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
const hasJsx = opening.attributes.some(
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
(attr) => attr.type === 'JSXAttribute' &&
|
|
52
|
+
attr.name?.type === 'JSXIdentifier' &&
|
|
53
|
+
attr.name.name === 'jsx');
|
|
54
|
+
if (!hasJsx)
|
|
55
|
+
return;
|
|
56
|
+
// Walk children looking for JSXExpressionContainer → TemplateLiteral
|
|
57
|
+
for (const child of node.children || []) {
|
|
58
|
+
if (child.type !== 'JSXExpressionContainer')
|
|
59
|
+
continue;
|
|
60
|
+
const expr = child.expression;
|
|
61
|
+
if (!expr)
|
|
62
|
+
continue;
|
|
63
|
+
// Handle both `{`...`}` and `{'...'}` forms
|
|
64
|
+
let cssText = null;
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
let targetNode = null;
|
|
67
|
+
if (expr.type === 'TemplateLiteral' && expr.quasis?.length > 0) {
|
|
68
|
+
cssText = expr.quasis.map((q) => q.value.raw).join('');
|
|
69
|
+
targetNode = expr;
|
|
70
|
+
}
|
|
71
|
+
else if (expr.type === 'Literal' && typeof expr.value === 'string') {
|
|
72
|
+
cssText = expr.value;
|
|
73
|
+
targetNode = expr;
|
|
74
|
+
}
|
|
75
|
+
if (!cssText || !targetNode)
|
|
76
|
+
continue;
|
|
77
|
+
if (HAS_DATA_URI.test(cssText) &&
|
|
78
|
+
HAS_URL_HASH_REF.test(cssText)) {
|
|
79
|
+
context.report({
|
|
80
|
+
node: targetNode,
|
|
81
|
+
messageId: 'dataUriUrlRef',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
exports.default = rule;
|
|
@@ -16,6 +16,8 @@ declare const _default: {
|
|
|
16
16
|
'@lark-apaas': {
|
|
17
17
|
rules: {
|
|
18
18
|
'no-nested-styled-jsx': import("eslint").Rule.RuleModule;
|
|
19
|
+
'no-styled-jsx-data-uri-url-ref': import("eslint").Rule.RuleModule;
|
|
20
|
+
'no-multiline-styled-jsx-classname': import("eslint").Rule.RuleModule;
|
|
19
21
|
'require-app-container': import("eslint").Rule.RuleModule;
|
|
20
22
|
'no-direct-capability-api': import("eslint").Rule.RuleModule;
|
|
21
23
|
'require-scroll-reveal-hook': import("eslint").Rule.RuleModule;
|
|
@@ -42,6 +42,10 @@ exports.default = {
|
|
|
42
42
|
'@lark-apaas/require-app-container': 'error',
|
|
43
43
|
// 平台规则:禁止直接调用 capability 内部 API,应使用 capabilityClient
|
|
44
44
|
'@lark-apaas/no-direct-capability-api': 'error',
|
|
45
|
+
// styled-jsx data URI + url(#) 会导致 stylis 误判括号配对 → "Nesting detected" 构建失败
|
|
46
|
+
'@lark-apaas/no-styled-jsx-data-uri-url-ref': 'error',
|
|
47
|
+
// styled-jsx + 多行模板 className → 下游 transpiler 可能产出 "Unterminated string literal"
|
|
48
|
+
'@lark-apaas/no-multiline-styled-jsx-classname': 'error',
|
|
45
49
|
// TypeScript 规则
|
|
46
50
|
'@typescript-eslint/no-unused-vars': 'off', // 未使用变量检查关闭
|
|
47
51
|
'@typescript-eslint/no-explicit-any': 'off', // 允许使用 any 类型
|
|
@@ -19,6 +19,8 @@ export declare const eslintPresets: {
|
|
|
19
19
|
'@lark-apaas': {
|
|
20
20
|
rules: {
|
|
21
21
|
'no-nested-styled-jsx': import("eslint").Rule.RuleModule;
|
|
22
|
+
'no-styled-jsx-data-uri-url-ref': import("eslint").Rule.RuleModule;
|
|
23
|
+
'no-multiline-styled-jsx-classname': import("eslint").Rule.RuleModule;
|
|
22
24
|
'require-app-container': import("eslint").Rule.RuleModule;
|
|
23
25
|
'no-direct-capability-api': import("eslint").Rule.RuleModule;
|
|
24
26
|
'require-scroll-reveal-hook': import("eslint").Rule.RuleModule;
|
|
@@ -20,7 +20,7 @@ const testFileIgnorePatterns = [
|
|
|
20
20
|
'**/*.spec.ts',
|
|
21
21
|
'**/*.spec.tsx',
|
|
22
22
|
];
|
|
23
|
-
const globalIgnoreServerPatterns = ['
|
|
23
|
+
const globalIgnoreServerPatterns = ['server/database/.introspect/**', ...testFileIgnorePatterns]; // 全局 eslint server ignore
|
|
24
24
|
const globalIgnoreClientPatterns = ['dist', 'node_modules', 'client/src/api/gen', ...testFileIgnorePatterns]; // 全局 eslint client Ignore
|
|
25
25
|
// 只导出纯净的规则配置,不包含基础配置
|
|
26
26
|
// 用户需要在项目配置中自己添加基础配置(eslintJs, tseslint, nestjs 等)
|
|
@@ -169,6 +169,10 @@ const baseConfig = {
|
|
|
169
169
|
// 平台规则:禁止直接调用 capability 内部 API,应使用 capabilityClient
|
|
170
170
|
'@lark-apaas/no-direct-capability-api': 'error',
|
|
171
171
|
'@lark-apaas/no-nested-styled-jsx': 'error',
|
|
172
|
+
// styled-jsx data URI + url(#) 会导致 stylis 误判括号配对 → "Nesting detected" 构建失败
|
|
173
|
+
'@lark-apaas/no-styled-jsx-data-uri-url-ref': 'error',
|
|
174
|
+
// styled-jsx + 多行模板 className → 下游 transpiler 可能产出 "Unterminated string literal"
|
|
175
|
+
'@lark-apaas/no-multiline-styled-jsx-classname': 'error',
|
|
172
176
|
// 自定义规则:HTTP 调用响应类型必须定义在 shared/(默认关闭,需 y 位升级后对新应用开启)
|
|
173
177
|
'@lark-apaas/require-shared-request-type': 'off',
|
|
174
178
|
// TypeScript 规则
|
|
@@ -46,6 +46,8 @@ export declare const eslintPresets: {
|
|
|
46
46
|
'@lark-apaas': {
|
|
47
47
|
rules: {
|
|
48
48
|
'no-nested-styled-jsx': import("eslint").Rule.RuleModule;
|
|
49
|
+
'no-styled-jsx-data-uri-url-ref': import("eslint").Rule.RuleModule;
|
|
50
|
+
'no-multiline-styled-jsx-classname': import("eslint").Rule.RuleModule;
|
|
49
51
|
'require-app-container': import("eslint").Rule.RuleModule;
|
|
50
52
|
'no-direct-capability-api': import("eslint").Rule.RuleModule;
|
|
51
53
|
'require-scroll-reveal-hook': import("eslint").Rule.RuleModule;
|
|
@@ -69,6 +71,8 @@ export declare const eslintPresets: {
|
|
|
69
71
|
'@lark-apaas': {
|
|
70
72
|
rules: {
|
|
71
73
|
'no-nested-styled-jsx': import("eslint").Rule.RuleModule;
|
|
74
|
+
'no-styled-jsx-data-uri-url-ref': import("eslint").Rule.RuleModule;
|
|
75
|
+
'no-multiline-styled-jsx-classname': import("eslint").Rule.RuleModule;
|
|
72
76
|
'require-app-container': import("eslint").Rule.RuleModule;
|
|
73
77
|
'no-direct-capability-api': import("eslint").Rule.RuleModule;
|
|
74
78
|
'require-scroll-reveal-hook': import("eslint").Rule.RuleModule;
|
|
@@ -21,7 +21,7 @@ const testFileIgnorePatterns = [
|
|
|
21
21
|
'**/*.spec.ts',
|
|
22
22
|
'**/*.spec.tsx',
|
|
23
23
|
];
|
|
24
|
-
const globalIgnoreServerPatterns = ['
|
|
24
|
+
const globalIgnoreServerPatterns = ['server/database/.introspect/**', ...testFileIgnorePatterns]; // 全局 eslint server ignore
|
|
25
25
|
const globalIgnoreClientPatterns = ['dist', 'node_modules', 'client/src/api/gen', ...testFileIgnorePatterns]; // 全局 eslint client Ignore
|
|
26
26
|
// @lark-apaas 插件只注册一次,client 和 server config 共享
|
|
27
27
|
const larkApaasPlugin = {
|