Skip to content

Instantly share code, notes, and snippets.

@sillvva
Created November 15, 2025 14:10
Show Gist options
  • Select an option

  • Save sillvva/ecb57b6b4d21c16d20d121f693f36062 to your computer and use it in GitHub Desktop.

Select an option

Save sillvva/ecb57b6b4d21c16d20d121f693f36062 to your computer and use it in GitHub Desktop.
export default {
meta: {
type: "problem",
docs: {
description:
"Enforce that exports in .remote.ts files use query(), command(), or form(), and only allow type exports",
category: "Best Practices",
recommended: true
},
messages: {
mustBeRemoteFunction: 'Export "{{name}}" must be a remote function (query(), command(), or form()).',
onlyTypesAllowed:
'Only remote functions and type exports are allowed in .remote.ts files. Export "{{name}}" is not allowed.'
},
schema: []
},
create(context) {
const filename = context.getFilename();
// Only apply this rule to files ending in .remote.ts
if (!filename.endsWith(".remote.ts")) {
return {};
}
const remoteFunctions = new Set(["query", "command", "form"]);
function isRemoteFunction(node) {
return node.type === "CallExpression" && node.callee.type === "Identifier" && remoteFunctions.has(node.callee.name);
}
function checkExportDeclaration(node) {
// Allow type-only exports
if (node.exportKind === "type") {
return;
}
if (node.type === "ExportNamedDeclaration") {
// Handle type-only named exports: export type { Foo }
if (node.exportKind === "type") {
return;
}
// Handle re-exports without declaration
if (!node.declaration && node.specifiers) {
for (const specifier of node.specifiers) {
// Allow individual type specifiers: export { type Foo }
if (specifier.exportKind === "type") {
continue;
}
context.report({
node: specifier,
messageId: "onlyTypesAllowed",
data: {
name: specifier.exported.name
}
});
}
return;
}
if (node.declaration && node.declaration.type === "VariableDeclaration") {
for (const declarator of node.declaration.declarations) {
if (declarator.init) {
if (!isRemoteFunction(declarator.init)) {
context.report({
node: declarator,
messageId: "mustBeRemoteFunction",
data: {
name: declarator.id.name
}
});
}
}
}
} else if (node.declaration) {
// Block any other declaration types (functions, classes, etc.) unless they're type declarations
const name = node.declaration.id?.name || "unknown";
context.report({
node: node.declaration,
messageId: "onlyTypesAllowed",
data: { name }
});
}
}
}
return {
ExportNamedDeclaration: checkExportDeclaration,
// Block default exports entirely (they can't be types in the same way)
ExportDefaultDeclaration(node) {
context.report({
node: node.declaration || node,
loc: node.loc,
messageId: "onlyTypesAllowed",
data: { name: "default" }
});
},
// Block export * statements
ExportAllDeclaration(node) {
context.report({
node,
loc: node.loc,
messageId: "onlyTypesAllowed",
data: { name: "*" }
});
}
};
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment