Skip to content

Instantly share code, notes, and snippets.

@peterpme
Created August 12, 2024 19:06
Show Gist options
  • Save peterpme/09dfc25f3606f421f9af3f9673475635 to your computer and use it in GitHub Desktop.
Save peterpme/09dfc25f3606f421f9af3f9673475635 to your computer and use it in GitHub Desktop.
React Native Eslint: Enforce Platform Shared Types
const path = require("path");
const fs = require("fs");
module.exports = {
meta: {
type: "problem",
docs: {
description: "Enforce shared types for multi-platform components",
category: "Possible Errors",
recommended: true,
},
fixable: null,
},
create(context) {
return {
Program(node) {
const filename = context.getFilename();
const ext = path.extname(filename);
const fullBasename = path.basename(filename, ext);
const dirname = path.dirname(filename);
if (!ext.endsWith("tsx")) {
return;
}
// Remove platform-specific extension if present
const basename = fullBasename.replace(
/\.(native|web|ios|android)$/,
""
);
// Check for the existence of any variant (including the base file)
const variants = [
path.join(dirname, `${basename}.tsx`),
...platformExts.map((ext) => path.join(dirname, `${basename}${ext}`)),
];
const existingVariants = variants.filter((v) => checkExists(v));
const isMultiPlatform = existingVariants.length > 1;
if (isMultiPlatform) {
// Check if types file exists
const typesFile = path.join(dirname, `${basename}.types.ts`);
if (checkExists(typesFile)) {
context.report({
node,
message: `Multi-platform component "${basename}" should have a separate types file "${basename}.types.ts"`,
});
} else {
// Check if types are imported in the current file
checkTypesImport(node, basename, context.report);
}
}
},
};
},
};
// Platform extensions to check
const platformExts = [".native.tsx", ".web.tsx", ".ios.tsx", ".android.tsx"];
function checkTypesImport(node, basename, report) {
const hasTypesImport = node.body.some(
(n) =>
n.type === "ImportDeclaration" && n.source.value === `./${basename}.types`
);
if (!hasTypesImport) {
report({
node,
message: `Component "${basename}" should import types from "${basename}.types.ts"`,
});
}
}
const cache = new Map();
function checkExists(path) {
if (cache.has(path)) {
return cache.get(path);
}
const exists = fs.existsSync(path);
cache.set(path, exists);
return exists;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment