Files
react-native/packages/eslint-plugin-specs/react-native-modules.js
T
Lorenzo Sciandra c40d270fdc fix npm lifecycle hook (#34273)
Summary:
In `0.70-stable` we started seeing `yarn run lint` fail; turns out, this is happening because the latest release of `react-native/eslint-plugin-specs` 0.0.4 (from https://github.com/facebook/react-native/commit/ea8d8e2f49ea3ce15faeab500b661a1cacacf8a8) is faulty: the underlying reason is that since 0.0.2 there was this local workaround in place https://github.com/facebook/react-native/commit/2d06e6a4c9261bb7790cf217b66145415301bc54 (that changes a flag from false to true) but in NPM 8 the lifecycle hook `prepublish` has [been deprecated](https://docs.npmjs.com/cli/v8/using-npm/scripts#life-cycle-scripts) so this pre publishing script to change the flag was not run (you can easily verify by checking the node_module and see `react-native-modules.js` the flag on L17 is set to false).

This PR addresses it by moving to the new lifecycle hook `prepack` (and modifies the other files accordingly).

After this PR is merged, we'll need to cherry pick it into 0.70 and do both a new release from the 0.70 branch and one from the main branch to have new versions of this module with the flag set correctly.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[General] [Fixed] - Fix eslint-plugin-specs prepack npm lifecycle hook now that we use npm 8

Pull Request resolved: https://github.com/facebook/react-native/pull/34273

Test Plan: Go in the `eslint-plugin-specs` folder, run `npm pack`, unzip the generated tar file, go into `package`, verify that L17 of `react-native-modules.js` is correctly changed to `const PACKAGE_USAGE = true;` (instead of false - which is what happen without this fix).

Reviewed By: GijsWeterings

Differential Revision: D38151433

Pulled By: dmitryrykun

fbshipit-source-id: 7c4f13dae70eb731d57cbafa90f7be05c9bb8576
2022-07-26 16:57:24 +01:00

172 lines
4.1 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.
*
* @emails react_native
* @format
*/
'use strict';
const path = require('path');
const withBabelRegister = require('./with-babel-register');
// 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;
function requireModuleParser() {
if (RNModuleParser == null || RNParserUtils == 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/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/flow/utils');
});
}
}
return {
buildModuleSchema: RNModuleParser.buildModuleSchema,
createParserErrorCapturer: RNParserUtils.createParserErrorCapturer,
};
}
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} =
requireModuleParser();
const flowParser = require('flow-parser');
const [parsingErrors, tryParse] = createParserErrorCapturer();
const sourceCode = context.getSourceCode().getText();
const ast = flowParser.parse(sourceCode);
tryParse(() => {
buildModuleSchema(hasteModuleName, ast, tryParse);
});
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;