mirror of
https://github.com/facebook/react.git
synced 2026-02-24 04:33:04 +00:00
* Lint rule for unminified errors Add a lint rule that fails if an invariant message is not part of the error code map. The goal is to be more disciplined about adding and modifiying production error codes. Error codes should be consistent across releases even if their wording changes, for continuity in logs. Currently, error codes are added to the error code map via an automated script that runs right before release. The problem with this approach is that if someone modifies an error message in the source, but neglects to modify the corresponding message in the error code map, then the message will be assigned a new error code, instead of reusing the existing one. Because the error extraction script only runs before a release, people rarely modify the error code map in practice. By moving the extraction step to the PR stage, it forces the author to consider whether the message should be assigned a new error code. It also allows the reviewer to review the changes. The trade off is that it requires more effort and context to land new error messages, or to modify existing ones, particular for new contributors who are not familiar with our processes. Since we already expect users to lint their code, I would argue the additional burden is marginal. Even if they forget to run the lint command locally, they will get quick feedback from the CI lint job, which typically finishes within 2-3 minutes. * Add unreleased error messages to map
115 lines
3.2 KiB
JavaScript
115 lines
3.2 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
* @flow strict-local
|
|
*/
|
|
|
|
/* eslint-disable react-internal/warning-and-invariant-args */
|
|
|
|
'use strict';
|
|
|
|
import type {
|
|
ReactNativeBaseComponentViewConfig,
|
|
ViewConfigGetter,
|
|
} from './ReactNativeTypes';
|
|
|
|
const invariant = require('invariant');
|
|
|
|
// Event configs
|
|
const customBubblingEventTypes = {};
|
|
const customDirectEventTypes = {};
|
|
const eventTypes = {};
|
|
|
|
exports.customBubblingEventTypes = customBubblingEventTypes;
|
|
exports.customDirectEventTypes = customDirectEventTypes;
|
|
exports.eventTypes = eventTypes;
|
|
|
|
const viewConfigCallbacks = new Map();
|
|
const viewConfigs = new Map();
|
|
|
|
function processEventTypes(
|
|
viewConfig: ReactNativeBaseComponentViewConfig<>,
|
|
): void {
|
|
const {bubblingEventTypes, directEventTypes} = viewConfig;
|
|
|
|
if (__DEV__) {
|
|
if (bubblingEventTypes != null && directEventTypes != null) {
|
|
for (const topLevelType in directEventTypes) {
|
|
invariant(
|
|
bubblingEventTypes[topLevelType] == null,
|
|
'Event cannot be both direct and bubbling: %s',
|
|
topLevelType,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bubblingEventTypes != null) {
|
|
for (const topLevelType in bubblingEventTypes) {
|
|
if (customBubblingEventTypes[topLevelType] == null) {
|
|
eventTypes[topLevelType] = customBubblingEventTypes[topLevelType] =
|
|
bubblingEventTypes[topLevelType];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (directEventTypes != null) {
|
|
for (const topLevelType in directEventTypes) {
|
|
if (customDirectEventTypes[topLevelType] == null) {
|
|
eventTypes[topLevelType] = customDirectEventTypes[topLevelType] =
|
|
directEventTypes[topLevelType];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a native view/component by name.
|
|
* A callback is provided to load the view config from UIManager.
|
|
* The callback is deferred until the view is actually rendered.
|
|
* This is done to avoid causing Prepack deopts.
|
|
*/
|
|
exports.register = function(name: string, callback: ViewConfigGetter): string {
|
|
invariant(
|
|
!viewConfigCallbacks.has(name),
|
|
'Tried to register two views with the same name %s',
|
|
name,
|
|
);
|
|
viewConfigCallbacks.set(name, callback);
|
|
return name;
|
|
};
|
|
|
|
/**
|
|
* Retrieves a config for the specified view.
|
|
* If this is the first time the view has been used,
|
|
* This configuration will be lazy-loaded from UIManager.
|
|
*/
|
|
exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
|
|
let viewConfig;
|
|
if (!viewConfigs.has(name)) {
|
|
const callback = viewConfigCallbacks.get(name);
|
|
if (typeof callback !== 'function') {
|
|
invariant(
|
|
false,
|
|
'View config not found for name %s.%s',
|
|
name,
|
|
typeof name[0] === 'string' && /[a-z]/.test(name[0])
|
|
? ' Make sure to start component names with a capital letter.'
|
|
: '',
|
|
);
|
|
}
|
|
viewConfigCallbacks.set(name, null);
|
|
viewConfig = callback();
|
|
processEventTypes(viewConfig);
|
|
viewConfigs.set(name, viewConfig);
|
|
} else {
|
|
viewConfig = viewConfigs.get(name);
|
|
}
|
|
invariant(viewConfig, 'View config not found for name %s', name);
|
|
return viewConfig;
|
|
};
|