mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
b344aec2ae
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51778 Adds `noflow` to a bunch of ESLint and Babel files that are expected to be evaluated using Node.js without Babel. Additioanlly, these files tend to depend on ESLint and Babel type definitions that are not currently readily available. In the future, these files could be migrated to use `flow strict-local` or `flow strict` using comment syntax for type annotations. But for now, adding `noflow` makes it explicit that these are known to not be typechecked. Changelog: [Internal] Reviewed By: SamChou19815 Differential Revision: D75883642 fbshipit-source-id: 54236d123ca8773de42bce81189dfb5c0671563e
198 lines
5.0 KiB
JavaScript
198 lines
5.0 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
* @noflow
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const withBabelRegister = require('./with-babel-register');
|
|
const path = require('path');
|
|
|
|
// We use the prepack hook before publishing package to set this value to true
|
|
const PACKAGE_USAGE = false;
|
|
const ERRORS = {
|
|
misnamedHasteModule(hasteModuleName) {
|
|
return `Module ${hasteModuleName}: All files using TurboModuleRegistry must start with Native.`;
|
|
},
|
|
};
|
|
|
|
let RNModuleParser;
|
|
let RNParserUtils;
|
|
let RNFlowParser;
|
|
let RNParserCommons;
|
|
let RNFlowParserUtils;
|
|
|
|
function requireModuleParser() {
|
|
if (
|
|
RNModuleParser == null ||
|
|
RNParserUtils == null ||
|
|
RNFlowParser == null ||
|
|
RNParserCommons == null ||
|
|
RNFlowParserUtils == null
|
|
) {
|
|
// If using this externally, we leverage @react-native/codegen as published form
|
|
if (!PACKAGE_USAGE) {
|
|
const config = {
|
|
only: [/react-native-codegen\/src\//],
|
|
plugins: [require('@babel/plugin-transform-flow-strip-types').default],
|
|
};
|
|
|
|
withBabelRegister(config, () => {
|
|
RNModuleParser = require('@react-native/codegen/src/parsers/flow/modules');
|
|
RNParserUtils = require('@react-native/codegen/src/parsers/utils');
|
|
RNFlowParser = require('@react-native/codegen/src/parsers/flow/parser');
|
|
RNParserCommons = require('@react-native/codegen/src/parsers/parsers-commons');
|
|
RNFlowParserUtils = require('@react-native/codegen/src/parsers/flow/utils');
|
|
});
|
|
} else {
|
|
const config = {
|
|
only: [/@react-native\/codegen\/lib\//],
|
|
plugins: [require('@babel/plugin-transform-flow-strip-types').default],
|
|
};
|
|
|
|
withBabelRegister(config, () => {
|
|
RNModuleParser = require('@react-native/codegen/lib/parsers/flow/modules');
|
|
RNParserUtils = require('@react-native/codegen/lib/parsers/utils');
|
|
RNFlowParser = require('@react-native/codegen/lib/parsers/flow/parser');
|
|
RNParserCommons = require('@react-native/codegen/lib/parsers/parsers-commons');
|
|
RNFlowParserUtils = require('@react-native/codegen/lib/parsers/flow/utils');
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
buildModuleSchema: RNParserCommons.buildModuleSchema,
|
|
createParserErrorCapturer: RNParserUtils.createParserErrorCapturer,
|
|
parser: new RNFlowParser.FlowParser(),
|
|
translateTypeAnnotation: RNModuleParser.flowTranslateTypeAnnotation,
|
|
};
|
|
}
|
|
|
|
const VALID_SPEC_NAMES = /^Native\S+$/;
|
|
|
|
function isModuleRequire(node) {
|
|
if (node.type !== 'CallExpression') {
|
|
return false;
|
|
}
|
|
|
|
const callExpression = node;
|
|
|
|
if (callExpression.callee.type !== 'MemberExpression') {
|
|
return false;
|
|
}
|
|
|
|
const memberExpression = callExpression.callee;
|
|
if (
|
|
!(
|
|
memberExpression.object.type === 'Identifier' &&
|
|
memberExpression.object.name === 'TurboModuleRegistry'
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
!(
|
|
memberExpression.property.type === 'Identifier' &&
|
|
(memberExpression.property.name === 'get' ||
|
|
memberExpression.property.name === 'getEnforcing')
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function isGeneratedFile(context) {
|
|
return (
|
|
context
|
|
.getSourceCode()
|
|
.getText()
|
|
.indexOf('@' + 'generated SignedSource<<') !== -1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* A lint rule to guide best practices in writing type safe React NativeModules.
|
|
*/
|
|
function rule(context) {
|
|
const filename = context.getFilename();
|
|
const hasteModuleName = path.basename(filename).replace(/\.js$/, '');
|
|
|
|
if (isGeneratedFile(context)) {
|
|
return {};
|
|
}
|
|
|
|
let isModule = false;
|
|
|
|
return {
|
|
'Program:exit': function (node) {
|
|
if (!isModule) {
|
|
return;
|
|
}
|
|
|
|
// Report invalid file names
|
|
if (!VALID_SPEC_NAMES.test(hasteModuleName)) {
|
|
context.report({
|
|
node,
|
|
message: ERRORS.misnamedHasteModule(hasteModuleName),
|
|
});
|
|
}
|
|
|
|
const {
|
|
buildModuleSchema,
|
|
createParserErrorCapturer,
|
|
parser,
|
|
translateTypeAnnotation,
|
|
} = requireModuleParser();
|
|
|
|
const [parsingErrors, tryParse] = createParserErrorCapturer();
|
|
|
|
const sourceCode = context.getSourceCode().getText();
|
|
const ast = parser.getAst(sourceCode, filename);
|
|
|
|
tryParse(() => {
|
|
buildModuleSchema(
|
|
hasteModuleName,
|
|
ast,
|
|
tryParse,
|
|
parser,
|
|
translateTypeAnnotation,
|
|
);
|
|
});
|
|
|
|
parsingErrors.forEach(error => {
|
|
error.nodes.forEach(flowNode => {
|
|
context.report({
|
|
loc: flowNode.loc,
|
|
message: error.message,
|
|
});
|
|
});
|
|
});
|
|
},
|
|
CallExpression(node) {
|
|
if (!isModuleRequire(node)) {
|
|
return;
|
|
}
|
|
|
|
isModule = true;
|
|
},
|
|
InterfaceExtends(node) {
|
|
if (node.id.name !== 'TurboModule') {
|
|
return;
|
|
}
|
|
|
|
isModule = true;
|
|
},
|
|
};
|
|
}
|
|
|
|
rule.errors = ERRORS;
|
|
|
|
module.exports = rule;
|