* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
function getComponents() {
const componentsPath = path.join(__dirname, 'packages/react-native-harmony/Libraries/Components');
if (fs.existsSync(componentsPath)) {
return fs.readdirSync(componentsPath)
.filter(dir => fs.statSync(path.join(componentsPath, dir)).isDirectory());
}
return [];
}
const components = getComponents();
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'chore',
'deprecated',
'removed',
'security',
'revert'
]
],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'scope-enum': [
2,
'always',
[
...components,
'View',
'Text',
'Image',
'TextInput',
'ScrollView',
'FlatList',
'ListView',
'WebView',
'Modal',
'RefreshControl',
'ActivityIndicator',
'Flex',
'Absolute',
'SafeArea',
'KeyboardAvoidingView',
'Networking',
'Storage',
'AsyncStorage',
'Clipboard',
'Linking',
'Alert',
'Toast',
'Permissions',
'DeviceInfo',
'Animated',
'Reanimated',
'LayoutAnimation',
'GestureHandler',
'Navigation',
'ReactNavigation',
'StackNavigator',
'StyleSheet',
'StyleSheetOptimizer',
'Theme',
'Bridge',
'TurboModules',
'Fabric',
'LegacyComponents',
'Hermes',
'JSC',
'JSI',
'JSVM',
'BuildSystem',
'CMake',
'HAP',
'HAR',
'Symbols',
'DevTools',
'Debugger',
'Profiler',
'Flipper',
'Tests',
'E2E',
'UnitTests',
'Docs',
'Examples',
'Config',
'CLI',
'Scripts',
'Upgrade',
'Performance'
]
],
'scope-case': [2, 'always', 'pascal-case'],
'scope-empty': [0],
'subject-empty': [2, 'never'],
'subject-case': [0],
'subject-full-stop': [2, 'never', '.'],
'header-max-length': [2, 'always', 100],
'body-max-line-length': [2, 'always', 100],
'footer-max-line-length': [2, 'always', 100],
'signed-off-by': [2, 'always'],
'references-empty': [0]
},
plugins: [
{
rules: {
'rnoh-body-required': ({ type, body }) => {
const requiredTypes = ['feat', 'fix', 'refactor', 'perf', 'deprecated', 'removed', 'security'];
if (requiredTypes.includes(type) && !body) {
return [false, `${type} 类型commit必须提供body说明详细内容`];
}
return [true];
},
'rnoh-symbols-change-warning': ({ scope, body }) => {
if (scope === 'Symbols' && !body?.includes('符号') && !body?.includes('.sym') && !body?.includes('llvm-objcopy')) {
return [0, 'Symbols变更建议说明符号处理情况(如:符号分离、剥离、路径等)'];
}
return [true];
},
'rnoh-native-change-warning': ({ scope, body }) => {
const nativeScopes = ['Hermes', 'JSI', 'JSVM', 'Bridge', 'TurboModules', 'Fabric'];
if (nativeScopes.includes(scope) && !body?.includes('符号') && !body?.includes('.so')) {
return [0, 'Native层变更建议说明符号处理和.so文件影响'];
}
return [true];
},
'rnoh-build-change-warning': ({ scope, body }) => {
const buildScopes = ['BuildSystem', 'CMake', 'HAP', 'HAR'];
if (buildScopes.includes(scope) && !body?.includes('Debug') && !body?.includes('Release')) {
return [0, '构建系统变更建议说明对Debug/Release模式的影响'];
}
return [true];
},
'rnoh-component-change-warning': ({ scope, body }) => {
const componentScopes = ['View', 'Text', 'Image', 'ScrollView', 'FlatList', 'ListView', 'WebView', 'Modal'];
if (componentScopes.includes(scope) && !body?.includes('兼容性') && !body?.includes('API')) {
return [0, '组件变更建议说明兼容性和API变化'];
}
return [true];
}
}
}
]
};