@lark-apaas/fullstack-presets 1.1.18 → 1.1.20

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.
@@ -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,36 @@
1
+ /**
2
+ * ESLint rule: no-multiline-styled-jsx-classname
3
+ *
4
+ * Detects multi-line className attributes (TemplateLiteral or JSX string attr
5
+ * with literal newlines) on JSX elements that belong to a function component
6
+ * which ALSO contains a `<style jsx>` tag.
7
+ *
8
+ * Why scope to the enclosing function component (not the whole file)?
9
+ * styled-jsx's Babel plugin only wraps className when the JSXElement tree
10
+ * actually contains a `<style jsx>` tag. A file can have multiple function
11
+ * components; flagging multi-line classNames in components that never use
12
+ * styled-jsx would be a false positive (their className never gets wrapped,
13
+ * so no downstream transpiler bug can bite them).
14
+ *
15
+ * When the rule DOES fire, styled-jsx wraps the className as:
16
+ *
17
+ * className={"jsx-X" + " " + `\n bg-primary\n`}
18
+ *
19
+ * Some downstream transpilers (esbuild/swc/terser at certain target settings)
20
+ * lower simple TemplateLiterals (no ${}) to StringLiterals but fail to escape
21
+ * the embedded newlines, producing invalid JS:
22
+ *
23
+ * className={"jsx-X" + " " + "\n bg-primary\n"}
24
+ * // → "Unterminated string literal" build error
25
+ *
26
+ * The fix is simple: write className as a single-line string. CSS class lists
27
+ * treat any whitespace run as one separator, so collapsing is semantically
28
+ * equivalent.
29
+ *
30
+ * Observed: ~59 of 83 Unterminated cases in week 04-06~04-12.
31
+ *
32
+ * @see https://bytedance.larkoffice.com/wiki/TTNDwzgLzi9Q0skTUT5clFX3nNd
33
+ */
34
+ import type { Rule } from 'eslint';
35
+ declare const rule: Rule.RuleModule;
36
+ export default rule;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /** Is this JSXOpeningElement a `<style jsx>` tag? */
4
+ function isStyledJsxOpening(node) {
5
+ if (!node)
6
+ return false;
7
+ if (node.type !== 'JSXOpeningElement')
8
+ return false;
9
+ const name = node.name;
10
+ if (name?.type !== 'JSXIdentifier' || name.name !== 'style')
11
+ return false;
12
+ return (node.attributes?.some((attr) => attr.type === 'JSXAttribute' &&
13
+ attr.name?.type === 'JSXIdentifier' &&
14
+ attr.name.name === 'jsx') ?? false);
15
+ }
16
+ const rule = {
17
+ meta: {
18
+ type: 'problem',
19
+ docs: {
20
+ description: 'Disallow multi-line className in components using <style jsx>',
21
+ category: 'Possible Errors',
22
+ recommended: true,
23
+ },
24
+ fixable: 'code',
25
+ schema: [],
26
+ messages: {
27
+ multilineClassName: '多行 className 在 styled-jsx 编译后可能被下游构建工具截断为非法字符串' +
28
+ '("Unterminated string literal")。请改为单行写法。',
29
+ },
30
+ },
31
+ create(context) {
32
+ // A "scope" is the OUTERMOST enclosing function component in the current
33
+ // traversal path (i.e., the first function after Program in the stack),
34
+ // or Program itself if no function is on the stack.
35
+ //
36
+ // Why "outermost" and not "nearest"?
37
+ // styled-jsx's Babel plugin wraps every className in the ENTIRE JSX tree
38
+ // of a component that contains `<style jsx>`. So a className inside
39
+ // `list.map((item) => <button className="..." />)` within PropsPage
40
+ // still gets wrapped — the arrow function is just a render helper, not
41
+ // a separate component. Scoping by nearest enclosing function would
42
+ // incorrectly exempt such inline callbacks.
43
+ //
44
+ // Implementation:
45
+ // - Stack entries are pushed for Program and every function node
46
+ // encountered (so nesting is preserved structurally).
47
+ // - `currentScope()` returns stack[1] — the first function below Program
48
+ // — if one exists, else stack[0] (Program). This effectively treats all
49
+ // nested functions as members of the outer function's scope.
50
+ // - Separate top-level function components (declared at Program level)
51
+ // get their own scope because only one of them is on the stack at a
52
+ // time (ComponentA pushes + pops before ComponentB pushes).
53
+ const scopeStack = [];
54
+ // WeakSet keeps references without preventing GC; matches by object identity.
55
+ const scopesWithStyledJsx = new WeakSet();
56
+ const candidates = [];
57
+ const currentScope = () => {
58
+ if (scopeStack.length === 0)
59
+ return null;
60
+ // stack[0] is Program; stack[1], if present, is the outermost function
61
+ // component. Everything deeper is considered part of stack[1]'s scope.
62
+ return scopeStack.length >= 2 ? scopeStack[1] : scopeStack[0];
63
+ };
64
+ const pushScope = (node) => scopeStack.push(node);
65
+ const popScope = () => {
66
+ scopeStack.pop();
67
+ };
68
+ return {
69
+ // --- Scope tracking (function components) ---
70
+ Program(node) {
71
+ pushScope(node);
72
+ },
73
+ 'Program:exit'() {
74
+ // Finalize: pop Program scope + run reports
75
+ popScope();
76
+ for (const { target, raw, kind, scope } of candidates) {
77
+ if (!scopesWithStyledJsx.has(scope))
78
+ continue;
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
+ // Both Case A (template) and Case B (string) collapse to a
88
+ // single-line StringLiteral. Safe because className list
89
+ // semantics treat any whitespace run as one separator.
90
+ return fixer.replaceText(target, JSON.stringify(normalized));
91
+ },
92
+ });
93
+ }
94
+ },
95
+ FunctionDeclaration(node) {
96
+ pushScope(node);
97
+ },
98
+ 'FunctionDeclaration:exit'() {
99
+ popScope();
100
+ },
101
+ FunctionExpression(node) {
102
+ pushScope(node);
103
+ },
104
+ 'FunctionExpression:exit'() {
105
+ popScope();
106
+ },
107
+ ArrowFunctionExpression(node) {
108
+ pushScope(node);
109
+ },
110
+ 'ArrowFunctionExpression:exit'() {
111
+ popScope();
112
+ },
113
+ // --- Mark scope as containing <style jsx> ---
114
+ JSXOpeningElement(node) {
115
+ if (!isStyledJsxOpening(node))
116
+ return;
117
+ const scope = currentScope();
118
+ if (scope)
119
+ scopesWithStyledJsx.add(scope);
120
+ },
121
+ // --- Collect multi-line className candidates ---
122
+ JSXAttribute(node) {
123
+ if (node.name?.type !== 'JSXIdentifier' ||
124
+ node.name.name !== 'className') {
125
+ return;
126
+ }
127
+ const val = node.value;
128
+ if (!val)
129
+ return;
130
+ const scope = currentScope();
131
+ if (!scope)
132
+ return; // no enclosing scope (shouldn't happen since Program is pushed)
133
+ // Case A: className={`...\n...`} — TemplateLiteral without ${}
134
+ if (val.type === 'JSXExpressionContainer') {
135
+ const expr = val.expression;
136
+ if (!expr || expr.type !== 'TemplateLiteral')
137
+ return;
138
+ if (expr.expressions.length !== 0)
139
+ return;
140
+ const raw = expr.quasis[0]?.value?.raw ?? '';
141
+ if (!raw.includes('\n') && !raw.includes('\r'))
142
+ return;
143
+ candidates.push({ target: expr, raw, kind: 'template', scope });
144
+ return;
145
+ }
146
+ // Case B: className="...\n..." — JSX string attribute with newlines
147
+ if (val.type === 'Literal' || val.type === 'StringLiteral') {
148
+ const str = val.value ?? '';
149
+ if (!str.includes('\n') && !str.includes('\r'))
150
+ return;
151
+ candidates.push({ target: val, raw: str, kind: 'string', scope });
152
+ }
153
+ },
154
+ };
155
+ },
156
+ };
157
+ exports.default = rule;
@@ -0,0 +1,39 @@
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
+ /**
20
+ * Precise detection: scan the CSS text character-by-character, looking for
21
+ * `url(<quote>data:...<quote>)` atoms. For each such atom, check whether the
22
+ * body (between the matching quotes) contains `url(`. Only flag if the
23
+ * offending `url(` is truly nested inside a data URI body — this eliminates
24
+ * the false-positive case where a data URI and a separate `url(#gradient)`
25
+ * coexist in the same CSS but are unrelated.
26
+ *
27
+ * Safety properties (can't hang / crash lint):
28
+ * - Single forward scan, O(n); `i` always advances past every candidate.
29
+ * - Bounded loop: hard limit on total iterations to protect against pathological
30
+ * input.
31
+ * - Handles escaped quotes (\") inside data URI bodies.
32
+ * - Bails safely (returns false) on unterminated strings.
33
+ * - Zero regex backtracking; no catastrophic-regex risk.
34
+ *
35
+ * @param cssText the full CSS source of a single <style jsx> block
36
+ */
37
+ export declare function hasDataUriWithNestedUrl(cssText: string): boolean;
38
+ declare const rule: Rule.RuleModule;
39
+ export default rule;
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasDataUriWithNestedUrl = hasDataUriWithNestedUrl;
4
+ /**
5
+ * Precise detection: scan the CSS text character-by-character, looking for
6
+ * `url(<quote>data:...<quote>)` atoms. For each such atom, check whether the
7
+ * body (between the matching quotes) contains `url(`. Only flag if the
8
+ * offending `url(` is truly nested inside a data URI body — this eliminates
9
+ * the false-positive case where a data URI and a separate `url(#gradient)`
10
+ * coexist in the same CSS but are unrelated.
11
+ *
12
+ * Safety properties (can't hang / crash lint):
13
+ * - Single forward scan, O(n); `i` always advances past every candidate.
14
+ * - Bounded loop: hard limit on total iterations to protect against pathological
15
+ * input.
16
+ * - Handles escaped quotes (\") inside data URI bodies.
17
+ * - Bails safely (returns false) on unterminated strings.
18
+ * - Zero regex backtracking; no catastrophic-regex risk.
19
+ *
20
+ * @param cssText the full CSS source of a single <style jsx> block
21
+ */
22
+ function hasDataUriWithNestedUrl(cssText) {
23
+ const len = cssText.length;
24
+ const MAX_ITERATIONS = len + 1; // guard against logic bugs
25
+ let iterations = 0;
26
+ let i = 0;
27
+ while (i < len) {
28
+ if (++iterations > MAX_ITERATIONS)
29
+ return false; // safety fuse
30
+ // Find the next literal "url(" atom
31
+ const urlIdx = cssText.indexOf('url(', i);
32
+ if (urlIdx === -1)
33
+ return false;
34
+ i = urlIdx + 4;
35
+ // Skip whitespace after "url("
36
+ while (i < len && (cssText[i] === ' ' || cssText[i] === '\t' || cssText[i] === '\n')) {
37
+ i++;
38
+ }
39
+ if (i >= len)
40
+ return false;
41
+ // Must be quoted
42
+ const quote = cssText[i];
43
+ if (quote !== '"' && quote !== "'")
44
+ continue;
45
+ // Must be "data:" scheme
46
+ if (cssText.slice(i + 1, i + 6) !== 'data:')
47
+ continue;
48
+ // Walk char-by-char to find matching close quote, honouring \" escapes
49
+ const bodyStart = i + 1;
50
+ let j = bodyStart;
51
+ let terminated = false;
52
+ while (j < len) {
53
+ const c = cssText[j];
54
+ if (c === '\\' && j + 1 < len) {
55
+ j += 2;
56
+ continue;
57
+ }
58
+ if (c === quote) {
59
+ terminated = true;
60
+ break;
61
+ }
62
+ j++;
63
+ }
64
+ if (!terminated)
65
+ return false; // unterminated data URI — bail safely
66
+ const body = cssText.slice(bodyStart, j);
67
+ // If the data URI body contains a literal `url(`, stylis will break.
68
+ if (body.indexOf('url(') !== -1)
69
+ return true;
70
+ // Move past this data URI and keep scanning for more url() atoms.
71
+ i = j + 1;
72
+ }
73
+ return false;
74
+ }
75
+ const rule = {
76
+ meta: {
77
+ type: 'problem',
78
+ docs: {
79
+ description: 'Disallow data URIs whose body contains url() references inside <style jsx>, which crash the stylis CSS parser',
80
+ category: 'Possible Errors',
81
+ recommended: true,
82
+ },
83
+ schema: [],
84
+ messages: {
85
+ dataUriUrlRef: '`url("data:...")` 的内容里嵌套了 `url(...)` 引用(如 SVG filter 的 `url(#id)`),会导致 styled-jsx 的 CSS 解析器(stylis)' +
86
+ '误判括号配对并报 "Nesting detected" 构建错误。请将 SVG 移到独立 .svg 文件通过 import 引用,' +
87
+ '或改用 CSS 变量 / 外链 background-image 代替内联 data URI。',
88
+ },
89
+ },
90
+ create(context) {
91
+ return {
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ JSXElement(node) {
94
+ // Only look at <style jsx> or <style jsx global>
95
+ const opening = node.openingElement;
96
+ if (!opening)
97
+ return;
98
+ const name = opening.name;
99
+ if (name?.type !== 'JSXIdentifier' || name.name !== 'style')
100
+ return;
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ const hasJsx = opening.attributes.some(
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
+ (attr) => attr.type === 'JSXAttribute' &&
105
+ attr.name?.type === 'JSXIdentifier' &&
106
+ attr.name.name === 'jsx');
107
+ if (!hasJsx)
108
+ return;
109
+ // Walk children looking for JSXExpressionContainer → TemplateLiteral / StringLiteral
110
+ for (const child of node.children || []) {
111
+ if (child.type !== 'JSXExpressionContainer')
112
+ continue;
113
+ const expr = child.expression;
114
+ if (!expr)
115
+ continue;
116
+ let cssText = null;
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
+ let targetNode = null;
119
+ if (expr.type === 'TemplateLiteral' && expr.quasis?.length > 0) {
120
+ cssText = expr.quasis
121
+ .map((q) => q.value.raw)
122
+ .join('');
123
+ targetNode = expr;
124
+ }
125
+ else if (expr.type === 'Literal' && typeof expr.value === 'string') {
126
+ cssText = expr.value;
127
+ targetNode = expr;
128
+ }
129
+ if (!cssText || !targetNode)
130
+ continue;
131
+ if (hasDataUriWithNestedUrl(cssText)) {
132
+ context.report({
133
+ node: targetNode,
134
+ messageId: 'dataUriUrlRef',
135
+ });
136
+ }
137
+ }
138
+ },
139
+ };
140
+ },
141
+ };
142
+ 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 = ['server/database/.introspect/**', ...testFileIgnorePatterns]; // 全局 eslint server ignore
23
+ const globalIgnoreServerPatterns = ['tmp/**', '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 = ['server/database/.introspect/**', ...testFileIgnorePatterns]; // 全局 eslint server ignore
24
+ const globalIgnoreServerPatterns = ['tmp/**', '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 = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-presets",
3
- "version": "1.1.18",
3
+ "version": "1.1.20",
4
4
  "files": [
5
5
  "lib"
6
6
  ],