@open-dy/vibecoding-sdk-taro 0.1.11 → 0.1.12
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/dist/adapters/interest-card/eslint/taro.mjs +1 -0
- package/dist/eslint/card-profile.mjs +96 -0
- package/dist/eslint/eslint-plugin-post-card.mjs +82 -0
- package/dist/eslint/taro.mjs +10 -0
- package/package.json +1 -1
- package/release-provenance.json +4 -4
- package/src/adapters/interest-card/eslint/taro.mjs +1 -0
- package/src/eslint/card-profile.mjs +2 -0
- package/src/eslint/eslint-plugin-post-card.mjs +82 -0
- package/src/eslint/taro.mjs +10 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { pathToFileURL } from 'node:url'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @desc 校验卡片形态;缺少字段保留历史兴趣卡语义,不写回配置。
|
|
7
|
+
* @params {object} config 已解析的工程配置。
|
|
8
|
+
* @params {string} [source] 错误信息中的配置来源。
|
|
9
|
+
* @return {'interest'|'post'} 卡片形态。
|
|
10
|
+
* @example parseCardType({ cardType: 'post' })
|
|
11
|
+
*/
|
|
12
|
+
export function parseCardType(config, source = 'card-routing.json') {
|
|
13
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
14
|
+
throw new Error(`${source} must contain an object`)
|
|
15
|
+
}
|
|
16
|
+
if (!Object.hasOwn(config, 'cardType')) return 'interest'
|
|
17
|
+
if (config.cardType !== 'interest' && config.cardType !== 'post') {
|
|
18
|
+
throw new Error(`${source}: cardType must be interest or post; got ${JSON.stringify(config.cardType)}`)
|
|
19
|
+
}
|
|
20
|
+
return config.cardType
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @desc 从明确的宿主工程目录读取形态,供 CLI 与两端 SDK 共用。
|
|
25
|
+
* @params {{projectRoot: string}} options 宿主工程绝对路径;OpenCard 配置也传宿主目录。
|
|
26
|
+
* @return {{cardType: 'interest'|'post', configPath: string}} 形态及配置来源。
|
|
27
|
+
* @example readCardProfile({ projectRoot: '/workspace/project/card-app' })
|
|
28
|
+
*/
|
|
29
|
+
export function readCardProfile({ projectRoot }) {
|
|
30
|
+
if (typeof projectRoot !== 'string' || !path.isAbsolute(projectRoot)) {
|
|
31
|
+
throw new Error('readCardProfile requires an absolute projectRoot')
|
|
32
|
+
}
|
|
33
|
+
const configPath = path.join(projectRoot, 'config/card-routing.json')
|
|
34
|
+
let raw
|
|
35
|
+
try {
|
|
36
|
+
raw = readFileSync(configPath, 'utf8')
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (error.code === 'ENOENT') return { cardType: 'interest', configPath }
|
|
39
|
+
throw error
|
|
40
|
+
}
|
|
41
|
+
let config
|
|
42
|
+
try {
|
|
43
|
+
config = JSON.parse(raw)
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error(`${configPath} must contain valid JSON: ${error.message}`)
|
|
46
|
+
}
|
|
47
|
+
return { cardType: parseCardType(config, configPath), configPath }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function findEslintHost(filename, target) {
|
|
51
|
+
let directory = path.dirname(filename)
|
|
52
|
+
while (true) {
|
|
53
|
+
// Stop at the owning package/config instead of inheriting another project's
|
|
54
|
+
// routing. An OpenCard package belongs to its immediate host directory.
|
|
55
|
+
if (existsSync(path.join(directory, 'package.json')) ||
|
|
56
|
+
existsSync(path.join(directory, 'eslint.config.mjs')) ||
|
|
57
|
+
existsSync(path.join(directory, 'project.card.json'))) {
|
|
58
|
+
return target === 'opencard' ? path.dirname(directory) : directory
|
|
59
|
+
}
|
|
60
|
+
if (existsSync(path.join(directory, '.git'))) return undefined
|
|
61
|
+
const parent = path.dirname(directory)
|
|
62
|
+
if (parent === directory) return undefined
|
|
63
|
+
directory = parent
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @desc 创建作品卡文件匹配器;CLI 传入形态,直接 ESLint 从待检查文件定位工程。
|
|
69
|
+
* @params {{cardType?: 'interest'|'post', target: 'taro'|'opencard'}} options SDK 配置及目标端。
|
|
70
|
+
* @params {object} [environment] CLI 子进程环境。
|
|
71
|
+
* @return {(filename: string) => boolean} 是否启用作品卡配置。
|
|
72
|
+
* @example createPostCardMatcher({ target: 'taro' })('/workspace/app/src/index.ts')
|
|
73
|
+
*/
|
|
74
|
+
export function createPostCardMatcher(options, environment = process.env) {
|
|
75
|
+
const configured = options.cardType ?? environment.VIBECODING_CARD_TYPE
|
|
76
|
+
// An explicitly supplied null must not silently fall back to legacy rules.
|
|
77
|
+
const explicit = options.cardType !== undefined
|
|
78
|
+
if (explicit || configured !== undefined) {
|
|
79
|
+
const cardType = parseCardType({ cardType: explicit ? options.cardType : configured }, 'ESLint cardType')
|
|
80
|
+
return () => cardType === 'post'
|
|
81
|
+
}
|
|
82
|
+
return filename => {
|
|
83
|
+
const projectRoot = findEslintHost(filename, options.target)
|
|
84
|
+
return projectRoot ? readCardProfile({ projectRoot }).cardType === 'post' : false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// The legacy shell entry uses the same parser as the SDK-owned ESLint configs.
|
|
89
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
90
|
+
try {
|
|
91
|
+
console.log(readCardProfile({ projectRoot: path.resolve(process.argv[2]) }).cardType)
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(error.message)
|
|
94
|
+
process.exitCode = 1
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const TARO_SOURCES = new Set(['@tarojs/taro'])
|
|
2
|
+
const TAB_APIS = new Set([
|
|
3
|
+
'switchTab', 'setTabBarItem', 'setTabBarStyle', 'showTabBar', 'hideTabBar',
|
|
4
|
+
'setTabBarBadge', 'removeTabBarBadge', 'showTabBarRedDot', 'hideTabBarRedDot',
|
|
5
|
+
'getTabBar', 'onTabItemTap', 'useTabItemTap',
|
|
6
|
+
])
|
|
7
|
+
|
|
8
|
+
function key(node) {
|
|
9
|
+
return node?.type === 'Identifier' ? node.name
|
|
10
|
+
: node?.type === 'Literal' && typeof node.value === 'string' ? node.value : null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function platformObject(node, context, seen = new Set()) {
|
|
14
|
+
if (!node || seen.has(node)) return false
|
|
15
|
+
seen.add(node)
|
|
16
|
+
if (['TSAsExpression', 'TSNonNullExpression', 'ChainExpression'].includes(node.type)) {
|
|
17
|
+
return platformObject(node.expression, context, seen)
|
|
18
|
+
}
|
|
19
|
+
if (node.type === 'CallExpression') {
|
|
20
|
+
return node.callee.type === 'Identifier' && node.callee.name === 'require'
|
|
21
|
+
&& TARO_SOURCES.has(node.arguments[0]?.value)
|
|
22
|
+
}
|
|
23
|
+
if (node.type === 'MemberExpression') {
|
|
24
|
+
return ['globalThis', 'window'].includes(node.object?.name)
|
|
25
|
+
&& ['tt', 'Taro'].includes(key(node.property))
|
|
26
|
+
}
|
|
27
|
+
if (node.type !== 'Identifier') return false
|
|
28
|
+
let scope = context.sourceCode.getScope(node)
|
|
29
|
+
let variable
|
|
30
|
+
while (scope && !variable) { variable = scope.set.get(node.name); scope = scope.upper }
|
|
31
|
+
if (!variable?.defs.length) return ['tt', 'Taro'].includes(node.name)
|
|
32
|
+
return variable.defs.some(def => {
|
|
33
|
+
if (def.type === 'ImportBinding') {
|
|
34
|
+
return TARO_SOURCES.has(def.parent?.source?.value)
|
|
35
|
+
&& ['ImportDefaultSpecifier', 'ImportNamespaceSpecifier'].includes(def.node.type)
|
|
36
|
+
}
|
|
37
|
+
return def.type === 'Variable' && platformObject(def.node.init, context, seen)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
rules: {
|
|
43
|
+
'no-platform-tabbar': {
|
|
44
|
+
meta: { type: 'problem', schema: [], messages: {
|
|
45
|
+
config: '作品卡不支持平台 tabBar(含 custom 模式)。请用普通组件绘制导航并实现页面或状态切换。',
|
|
46
|
+
api: '作品卡不支持 {{name}}。请使用普通页面路由或组件状态切换,不调用平台 TabBar。',
|
|
47
|
+
} },
|
|
48
|
+
create(context) {
|
|
49
|
+
const filename = context.filename.replaceAll('\\', '/')
|
|
50
|
+
const isAppConfig = /\/app\.config\.[cm]?[jt]s$/.test(filename)
|
|
51
|
+
const report = (node, name) => {
|
|
52
|
+
if (TAB_APIS.has(name)) context.report({ node, messageId: 'api', data: { name } })
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
Property(node) {
|
|
56
|
+
if (isAppConfig && key(node.key) === 'tabBar') context.report({ node, messageId: 'config' })
|
|
57
|
+
if (node.parent.type === 'ObjectPattern' && platformObject(node.parent.parent?.init, context)) {
|
|
58
|
+
report(node, key(node.key))
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
ImportSpecifier(node) {
|
|
62
|
+
if (node.importKind !== 'type' && node.parent.importKind !== 'type'
|
|
63
|
+
&& TARO_SOURCES.has(node.parent.source.value)) report(node, key(node.imported))
|
|
64
|
+
},
|
|
65
|
+
ExportSpecifier(node) {
|
|
66
|
+
if (TARO_SOURCES.has(node.parent.source?.value)) report(node, key(node.local))
|
|
67
|
+
},
|
|
68
|
+
MemberExpression(node) {
|
|
69
|
+
const name = key(node.property)
|
|
70
|
+
if (platformObject(node.object, context) || (name === 'getTabBar' && node.object.type === 'ThisExpression')) {
|
|
71
|
+
report(node, name)
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
JSXAttribute(node) {
|
|
75
|
+
const value = node.value?.type === 'JSXExpressionContainer' ? node.value.expression?.value : node.value?.value
|
|
76
|
+
if (node.name.name === 'openType' && value === 'switchTab') report(node, 'switchTab')
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
}
|
package/dist/eslint/taro.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import postCard from './eslint-plugin-post-card.mjs';
|
|
2
|
+
import { createPostCardMatcher } from './card-profile.mjs';
|
|
3
|
+
export const CARD_TYPE_RULES_VERSION = 1;
|
|
1
4
|
import eslint from '@eslint/js';
|
|
2
5
|
import { fixupPluginRules } from '@eslint/compat';
|
|
3
6
|
import tseslint from 'typescript-eslint';
|
|
@@ -381,6 +384,7 @@ const indexPageRestrictedSyntaxRules = [
|
|
|
381
384
|
];
|
|
382
385
|
|
|
383
386
|
export function createMicroappTaroEslintConfig(options = {}) {
|
|
387
|
+
const matchesPostCard = createPostCardMatcher({ cardType: options.cardType, target: 'taro' });
|
|
384
388
|
const lucideColorCheckPlugin = createLucideColorCheckPlugin(options.lucideImportSources)
|
|
385
389
|
const pageRestrictedSyntaxRules = createPageRestrictedSyntaxRules(
|
|
386
390
|
options.preferredUiImportBase,
|
|
@@ -581,6 +585,12 @@ export function createMicroappTaroEslintConfig(options = {}) {
|
|
|
581
585
|
},
|
|
582
586
|
);
|
|
583
587
|
|
|
588
|
+
defaults.push({
|
|
589
|
+
files: [['src/**/*.{js,jsx,ts,tsx}', matchesPostCard]],
|
|
590
|
+
settings: { cardType: 'post', cardTypeRulesVersion: CARD_TYPE_RULES_VERSION },
|
|
591
|
+
plugins: { postCard },
|
|
592
|
+
rules: { 'postCard/no-platform-tabbar': 'error' },
|
|
593
|
+
});
|
|
584
594
|
const overrides = options.overrides
|
|
585
595
|
if (!overrides) return defaults
|
|
586
596
|
if (typeof overrides === 'function') return overrides(defaults) ?? defaults
|
package/package.json
CHANGED
package/release-provenance.json
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"producer": "vibecoding-sdk-taro",
|
|
4
4
|
"packageName": "@open-dy/vibecoding-sdk-taro",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.12",
|
|
6
6
|
"source": {
|
|
7
|
-
"digest": "sha256-
|
|
8
|
-
"files":
|
|
7
|
+
"digest": "sha256-343c13406654fbfb96b1ae78b83b481b68135affa5484b7230646fda91568c26",
|
|
8
|
+
"files": 162
|
|
9
9
|
},
|
|
10
|
-
"payload": "sha256-
|
|
10
|
+
"payload": "sha256-e969e73830ccf0dfe00b29542c2b992d78d6640c59b04031c7ab9f3f754731de",
|
|
11
11
|
"toolchain": {
|
|
12
12
|
"node": "v22.22.3",
|
|
13
13
|
"runtime": {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const TARO_SOURCES = new Set(['@tarojs/taro'])
|
|
2
|
+
const TAB_APIS = new Set([
|
|
3
|
+
'switchTab', 'setTabBarItem', 'setTabBarStyle', 'showTabBar', 'hideTabBar',
|
|
4
|
+
'setTabBarBadge', 'removeTabBarBadge', 'showTabBarRedDot', 'hideTabBarRedDot',
|
|
5
|
+
'getTabBar', 'onTabItemTap', 'useTabItemTap',
|
|
6
|
+
])
|
|
7
|
+
|
|
8
|
+
function key(node) {
|
|
9
|
+
return node?.type === 'Identifier' ? node.name
|
|
10
|
+
: node?.type === 'Literal' && typeof node.value === 'string' ? node.value : null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function platformObject(node, context, seen = new Set()) {
|
|
14
|
+
if (!node || seen.has(node)) return false
|
|
15
|
+
seen.add(node)
|
|
16
|
+
if (['TSAsExpression', 'TSNonNullExpression', 'ChainExpression'].includes(node.type)) {
|
|
17
|
+
return platformObject(node.expression, context, seen)
|
|
18
|
+
}
|
|
19
|
+
if (node.type === 'CallExpression') {
|
|
20
|
+
return node.callee.type === 'Identifier' && node.callee.name === 'require'
|
|
21
|
+
&& TARO_SOURCES.has(node.arguments[0]?.value)
|
|
22
|
+
}
|
|
23
|
+
if (node.type === 'MemberExpression') {
|
|
24
|
+
return ['globalThis', 'window'].includes(node.object?.name)
|
|
25
|
+
&& ['tt', 'Taro'].includes(key(node.property))
|
|
26
|
+
}
|
|
27
|
+
if (node.type !== 'Identifier') return false
|
|
28
|
+
let scope = context.sourceCode.getScope(node)
|
|
29
|
+
let variable
|
|
30
|
+
while (scope && !variable) { variable = scope.set.get(node.name); scope = scope.upper }
|
|
31
|
+
if (!variable?.defs.length) return ['tt', 'Taro'].includes(node.name)
|
|
32
|
+
return variable.defs.some(def => {
|
|
33
|
+
if (def.type === 'ImportBinding') {
|
|
34
|
+
return TARO_SOURCES.has(def.parent?.source?.value)
|
|
35
|
+
&& ['ImportDefaultSpecifier', 'ImportNamespaceSpecifier'].includes(def.node.type)
|
|
36
|
+
}
|
|
37
|
+
return def.type === 'Variable' && platformObject(def.node.init, context, seen)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
rules: {
|
|
43
|
+
'no-platform-tabbar': {
|
|
44
|
+
meta: { type: 'problem', schema: [], messages: {
|
|
45
|
+
config: '作品卡不支持平台 tabBar(含 custom 模式)。请用普通组件绘制导航并实现页面或状态切换。',
|
|
46
|
+
api: '作品卡不支持 {{name}}。请使用普通页面路由或组件状态切换,不调用平台 TabBar。',
|
|
47
|
+
} },
|
|
48
|
+
create(context) {
|
|
49
|
+
const filename = context.filename.replaceAll('\\', '/')
|
|
50
|
+
const isAppConfig = /\/app\.config\.[cm]?[jt]s$/.test(filename)
|
|
51
|
+
const report = (node, name) => {
|
|
52
|
+
if (TAB_APIS.has(name)) context.report({ node, messageId: 'api', data: { name } })
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
Property(node) {
|
|
56
|
+
if (isAppConfig && key(node.key) === 'tabBar') context.report({ node, messageId: 'config' })
|
|
57
|
+
if (node.parent.type === 'ObjectPattern' && platformObject(node.parent.parent?.init, context)) {
|
|
58
|
+
report(node, key(node.key))
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
ImportSpecifier(node) {
|
|
62
|
+
if (node.importKind !== 'type' && node.parent.importKind !== 'type'
|
|
63
|
+
&& TARO_SOURCES.has(node.parent.source.value)) report(node, key(node.imported))
|
|
64
|
+
},
|
|
65
|
+
ExportSpecifier(node) {
|
|
66
|
+
if (TARO_SOURCES.has(node.parent.source?.value)) report(node, key(node.local))
|
|
67
|
+
},
|
|
68
|
+
MemberExpression(node) {
|
|
69
|
+
const name = key(node.property)
|
|
70
|
+
if (platformObject(node.object, context) || (name === 'getTabBar' && node.object.type === 'ThisExpression')) {
|
|
71
|
+
report(node, name)
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
JSXAttribute(node) {
|
|
75
|
+
const value = node.value?.type === 'JSXExpressionContainer' ? node.value.expression?.value : node.value?.value
|
|
76
|
+
if (node.name.name === 'openType' && value === 'switchTab') report(node, 'switchTab')
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
}
|
package/src/eslint/taro.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import postCard from './eslint-plugin-post-card.mjs';
|
|
2
|
+
import { createPostCardMatcher } from './card-profile.mjs';
|
|
3
|
+
export const CARD_TYPE_RULES_VERSION = 1;
|
|
1
4
|
import eslint from '@eslint/js';
|
|
2
5
|
import { fixupPluginRules } from '@eslint/compat';
|
|
3
6
|
import tseslint from 'typescript-eslint';
|
|
@@ -381,6 +384,7 @@ const indexPageRestrictedSyntaxRules = [
|
|
|
381
384
|
];
|
|
382
385
|
|
|
383
386
|
export function createMicroappTaroEslintConfig(options = {}) {
|
|
387
|
+
const matchesPostCard = createPostCardMatcher({ cardType: options.cardType, target: 'taro' });
|
|
384
388
|
const lucideColorCheckPlugin = createLucideColorCheckPlugin(options.lucideImportSources)
|
|
385
389
|
const pageRestrictedSyntaxRules = createPageRestrictedSyntaxRules(
|
|
386
390
|
options.preferredUiImportBase,
|
|
@@ -581,6 +585,12 @@ export function createMicroappTaroEslintConfig(options = {}) {
|
|
|
581
585
|
},
|
|
582
586
|
);
|
|
583
587
|
|
|
588
|
+
defaults.push({
|
|
589
|
+
files: [['src/**/*.{js,jsx,ts,tsx}', matchesPostCard]],
|
|
590
|
+
settings: { cardType: 'post', cardTypeRulesVersion: CARD_TYPE_RULES_VERSION },
|
|
591
|
+
plugins: { postCard },
|
|
592
|
+
rules: { 'postCard/no-platform-tabbar': 'error' },
|
|
593
|
+
});
|
|
584
594
|
const overrides = options.overrides
|
|
585
595
|
if (!overrides) return defaults
|
|
586
596
|
if (typeof overrides === 'function') return overrides(defaults) ?? defaults
|