chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,868 @@
|
||||
import babelParser from "@babel/eslint-parser";
|
||||
import eslint from "@eslint/js";
|
||||
import reactHooks from "eslint-plugin-react-hooks";
|
||||
import globals from "globals";
|
||||
import type { Rule } from "eslint";
|
||||
|
||||
const sourceExtensions = "{ts,tsx,mts,cts}";
|
||||
|
||||
const layerPatterns = {
|
||||
domain: [
|
||||
"**/application/**",
|
||||
"**/presentation/**",
|
||||
"**/adapters/**",
|
||||
"**/bootstrap/**",
|
||||
"react",
|
||||
"react-dom",
|
||||
"@tanstack/**",
|
||||
],
|
||||
application: [
|
||||
"**/presentation/**",
|
||||
"**/adapters/**",
|
||||
"**/bootstrap/**",
|
||||
"react",
|
||||
"react-dom",
|
||||
"@tanstack/**",
|
||||
],
|
||||
presentation: [
|
||||
"**/adapters/**",
|
||||
"**/bootstrap/**",
|
||||
"**/application/ports/out/**",
|
||||
"@tanstack/**",
|
||||
],
|
||||
adapters: ["**/presentation/**", "**/bootstrap/**"],
|
||||
};
|
||||
|
||||
function restrictedImports(patterns: readonly string[]) {
|
||||
return ["error", { patterns }];
|
||||
}
|
||||
|
||||
const restrictedBrowserDataProperties = [
|
||||
"error",
|
||||
...(["globalThis", "window", "self"] as const).flatMap((object) =>
|
||||
[
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
"URL",
|
||||
].map((property) => ({
|
||||
object,
|
||||
property,
|
||||
message:
|
||||
"Browser file/storage APIs are adapter-owned; use the policy port.",
|
||||
})),
|
||||
),
|
||||
{
|
||||
object: "navigator",
|
||||
property: "storage",
|
||||
message: "StorageManager and OPFS access are adapter-owned.",
|
||||
},
|
||||
{
|
||||
object: "URL",
|
||||
property: "createObjectURL",
|
||||
message: "Object URL allocation is owned by the preview/download adapter.",
|
||||
},
|
||||
{
|
||||
object: "URL",
|
||||
property: "revokeObjectURL",
|
||||
message: "Object URL revocation is owned by the preview/download adapter.",
|
||||
},
|
||||
] as const;
|
||||
|
||||
const browserCapabilityRoots = new Set([
|
||||
"globalThis",
|
||||
"window",
|
||||
"self",
|
||||
"navigator",
|
||||
"URL",
|
||||
]);
|
||||
const browserCapabilityProperties = new Set([
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"storage",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
"URL",
|
||||
"createObjectURL",
|
||||
"revokeObjectURL",
|
||||
]);
|
||||
|
||||
/**
|
||||
* no-restricted-properties only recognizes the literal object identifier.
|
||||
* This rule follows local aliases of browser roots so `const host =
|
||||
* globalThis; host.indexedDB` cannot bypass the owned-adapter boundary.
|
||||
*/
|
||||
const noBrowserCapabilityAliasRule: Rule.RuleModule = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
schema: [],
|
||||
messages: {
|
||||
owned:
|
||||
"Browser file/storage capabilities are owned by the approved common adapters.",
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
const aliases = new Set<string>();
|
||||
|
||||
const unwrap = (input: any): any => {
|
||||
let node = input;
|
||||
while (
|
||||
node &&
|
||||
[
|
||||
"ChainExpression",
|
||||
"TSAsExpression",
|
||||
"TSNonNullExpression",
|
||||
"TSTypeAssertion",
|
||||
].includes(node.type)
|
||||
) {
|
||||
node = node.expression;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
const propertyName = (input: any): string | null => {
|
||||
const node = unwrap(input);
|
||||
if (!node) return null;
|
||||
if (!node.computed && node.property?.type === "Identifier") {
|
||||
return node.property.name;
|
||||
}
|
||||
if (
|
||||
node.computed &&
|
||||
node.property?.type === "Literal" &&
|
||||
typeof node.property.value === "string"
|
||||
) {
|
||||
return node.property.value;
|
||||
}
|
||||
if (
|
||||
node.computed &&
|
||||
node.property?.type === "StringLiteral"
|
||||
) {
|
||||
return node.property.value;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const isRootAlias = (input: any): boolean => {
|
||||
const node = unwrap(input);
|
||||
if (!node) return false;
|
||||
if (node.type === "Identifier") {
|
||||
return browserCapabilityRoots.has(node.name) ||
|
||||
aliases.has(node.name);
|
||||
}
|
||||
if (node.type === "MemberExpression") {
|
||||
return isRootAlias(node.object);
|
||||
}
|
||||
if (node.type === "LogicalExpression") {
|
||||
return isRootAlias(node.left) || isRootAlias(node.right);
|
||||
}
|
||||
if (node.type === "ConditionalExpression") {
|
||||
return (
|
||||
isRootAlias(node.consequent) ||
|
||||
isRootAlias(node.alternate)
|
||||
);
|
||||
}
|
||||
if (node.type === "SequenceExpression") {
|
||||
return node.expressions.some(isRootAlias);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isBareRootAlias = (input: any): boolean => {
|
||||
const node = unwrap(input);
|
||||
if (!node) return false;
|
||||
if (node.type === "Identifier") {
|
||||
return browserCapabilityRoots.has(node.name) ||
|
||||
aliases.has(node.name);
|
||||
}
|
||||
if (node.type === "LogicalExpression") {
|
||||
return (
|
||||
isBareRootAlias(node.left) ||
|
||||
isBareRootAlias(node.right)
|
||||
);
|
||||
}
|
||||
if (node.type === "ConditionalExpression") {
|
||||
return (
|
||||
isBareRootAlias(node.consequent) ||
|
||||
isBareRootAlias(node.alternate)
|
||||
);
|
||||
}
|
||||
if (node.type === "SequenceExpression") {
|
||||
return node.expressions.some(isBareRootAlias);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isDirectBrowserRoot = (input: any): boolean => {
|
||||
const node = unwrap(input);
|
||||
return (
|
||||
node?.type === "Identifier" &&
|
||||
browserCapabilityRoots.has(node.name)
|
||||
);
|
||||
};
|
||||
|
||||
const safeMethodThisBinding = (
|
||||
call: any,
|
||||
argumentIndex: number,
|
||||
): boolean => {
|
||||
if (argumentIndex !== 0) return false;
|
||||
const callee = unwrap(call.callee);
|
||||
if (
|
||||
callee?.type !== "MemberExpression" ||
|
||||
propertyName(callee) !== "bind"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const target = unwrap(callee.object);
|
||||
const targetName =
|
||||
target?.type === "MemberExpression"
|
||||
? propertyName(target)
|
||||
: null;
|
||||
return (
|
||||
targetName !== null &&
|
||||
!browserCapabilityProperties.has(targetName) &&
|
||||
isRootAlias(target.object)
|
||||
);
|
||||
};
|
||||
|
||||
const rememberPattern = (patternInput: any, source: any): void => {
|
||||
const pattern = unwrap(patternInput);
|
||||
if (!pattern || !isRootAlias(source)) return;
|
||||
if (pattern.type === "Identifier") {
|
||||
aliases.add(pattern.name);
|
||||
return;
|
||||
}
|
||||
if (pattern.type !== "ObjectPattern") return;
|
||||
for (const property of pattern.properties ?? []) {
|
||||
if (property.type !== "Property") continue;
|
||||
const name =
|
||||
property.computed
|
||||
? property.key?.value
|
||||
: property.key?.name ?? property.key?.value;
|
||||
if (
|
||||
typeof name === "string" &&
|
||||
browserCapabilityProperties.has(name)
|
||||
) {
|
||||
context.report({ node: property, messageId: "owned" });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
VariableDeclarator(node: any) {
|
||||
rememberPattern(node.id, node.init);
|
||||
},
|
||||
AssignmentExpression(node: any) {
|
||||
rememberPattern(node.left, node.right);
|
||||
const target = unwrap(node.left);
|
||||
if (
|
||||
target?.type !== "Identifier" &&
|
||||
target?.type !== "ObjectPattern" &&
|
||||
isBareRootAlias(node.right)
|
||||
) {
|
||||
context.report({ node: node.right, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
AssignmentPattern(node: any) {
|
||||
rememberPattern(node.left, node.right);
|
||||
},
|
||||
PropertyDefinition(node: any) {
|
||||
if (isBareRootAlias(node.value)) {
|
||||
context.report({ node: node.value, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
MemberExpression(node: any) {
|
||||
const name = propertyName(node);
|
||||
if (
|
||||
isRootAlias(node.object) &&
|
||||
((name !== null &&
|
||||
browserCapabilityProperties.has(name)) ||
|
||||
(name === null && node.computed))
|
||||
) {
|
||||
context.report({ node, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
CallExpression(node: any) {
|
||||
for (const [index, argument] of (
|
||||
node.arguments ?? []
|
||||
).entries()) {
|
||||
const candidate =
|
||||
argument.type === "SpreadElement"
|
||||
? argument.argument
|
||||
: argument;
|
||||
if (
|
||||
isDirectBrowserRoot(candidate) &&
|
||||
!safeMethodThisBinding(node, index)
|
||||
) {
|
||||
context.report({ node: argument, messageId: "owned" });
|
||||
}
|
||||
}
|
||||
const callee = unwrap(node.callee);
|
||||
if (
|
||||
callee?.type !== "MemberExpression" ||
|
||||
propertyName(callee) !== "get" ||
|
||||
unwrap(callee.object)?.type !== "Identifier" ||
|
||||
unwrap(callee.object).name !== "Reflect" ||
|
||||
!isRootAlias(node.arguments?.[0])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const key = unwrap(node.arguments?.[1]);
|
||||
if (
|
||||
(key?.type === "Literal" ||
|
||||
key?.type === "StringLiteral") &&
|
||||
typeof key.value === "string" &&
|
||||
browserCapabilityProperties.has(key.value)
|
||||
) {
|
||||
context.report({ node, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
NewExpression(node: any) {
|
||||
for (const argument of node.arguments ?? []) {
|
||||
const candidate =
|
||||
argument.type === "SpreadElement"
|
||||
? argument.argument
|
||||
: argument;
|
||||
if (isBareRootAlias(candidate)) {
|
||||
context.report({ node: argument, messageId: "owned" });
|
||||
}
|
||||
}
|
||||
},
|
||||
Property(node: any) {
|
||||
if (
|
||||
node.parent?.type === "ObjectExpression" &&
|
||||
isBareRootAlias(node.value)
|
||||
) {
|
||||
context.report({ node: node.value, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
ArrayExpression(node: any) {
|
||||
for (const element of node.elements ?? []) {
|
||||
if (isBareRootAlias(element)) {
|
||||
context.report({ node: element, messageId: "owned" });
|
||||
}
|
||||
}
|
||||
},
|
||||
ReturnStatement(node: any) {
|
||||
if (isBareRootAlias(node.argument)) {
|
||||
context.report({ node: node.argument, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
ArrowFunctionExpression(node: any) {
|
||||
if (
|
||||
node.expression === true &&
|
||||
isBareRootAlias(node.body)
|
||||
) {
|
||||
context.report({ node: node.body, messageId: "owned" });
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const browserDataBoundaryPlugin = {
|
||||
rules: {
|
||||
"no-capability-alias": noBrowserCapabilityAliasRule,
|
||||
},
|
||||
};
|
||||
|
||||
const commonLanguageOptions = {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
};
|
||||
|
||||
const commonSecurityRules = {
|
||||
"no-eval": "error",
|
||||
"no-new-func": "error",
|
||||
"no-script-url": "error",
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "JSXAttribute[name.name='dangerouslySetInnerHTML']",
|
||||
message: "Raw HTML injection is prohibited by FE-OC-019.",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"CallExpression[callee.object.name='document'][callee.property.name='createElement'][arguments.0.value='script']",
|
||||
message: "Runtime script construction is prohibited by FE-OC-019.",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const hookRules = {
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "error",
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
"artifacts/**",
|
||||
"tests/fixtures/typecheck/**",
|
||||
"tests/fixtures/architecture/forbidden/**",
|
||||
"tests/fixtures/diagnostics/forbidden/**",
|
||||
"tests/fixtures/i18n/forbidden/**",
|
||||
"tests/fixtures/security/forbidden/**",
|
||||
"tests/fixtures/optional-recipes/**",
|
||||
"tests/fixtures/browser-file-storage-boundaries/**",
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
{
|
||||
files: [`**/*.${sourceExtensions}`],
|
||||
languageOptions: {
|
||||
...commonLanguageOptions,
|
||||
parserOptions: {
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"react-hooks": reactHooks,
|
||||
},
|
||||
rules: {
|
||||
...commonSecurityRules,
|
||||
...hookRules,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.{ts,mts,cts}"],
|
||||
languageOptions: {
|
||||
...commonLanguageOptions,
|
||||
parser: babelParser,
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
plugins: [
|
||||
["@babel/plugin-syntax-typescript", { isTSX: false }],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.d.ts"],
|
||||
languageOptions: {
|
||||
...commonLanguageOptions,
|
||||
parser: babelParser,
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
plugins: [
|
||||
["@babel/plugin-syntax-typescript", { dts: true }],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.tsx"],
|
||||
languageOptions: {
|
||||
...commonLanguageOptions,
|
||||
parser: babelParser,
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
plugins: [
|
||||
[
|
||||
"@babel/plugin-syntax-typescript",
|
||||
{ allExtensions: true, isTSX: true },
|
||||
],
|
||||
"@babel/plugin-syntax-jsx",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
`src/domain/**/*.${sourceExtensions}`,
|
||||
`src/application/**/*.${sourceExtensions}`,
|
||||
`src/presentation/**/*.${sourceExtensions}`,
|
||||
`src/bootstrap/**/*.${sourceExtensions}`,
|
||||
`src/features/**/*.${sourceExtensions}`,
|
||||
`src/adapters/**/*.${sourceExtensions}`,
|
||||
],
|
||||
ignores: [
|
||||
`src/adapters/browser-file-storage/**/*.${sourceExtensions}`,
|
||||
`src/adapters/browser-files/**/*.${sourceExtensions}`,
|
||||
`src/adapters/browser-transfer/**/*.${sourceExtensions}`,
|
||||
`src/adapters/cache-storage/**/*.${sourceExtensions}`,
|
||||
`src/adapters/cross-context-invalidation/**/*.${sourceExtensions}`,
|
||||
`src/adapters/service-worker/**/*.${sourceExtensions}`,
|
||||
`src/bootstrap/register-service-worker.ts`,
|
||||
`src/adapters/platform/browser-lifecycle.ts`,
|
||||
`src/adapters/storage/**/*.${sourceExtensions}`,
|
||||
`src/adapters/storage/indexeddb/**/*.${sourceExtensions}`,
|
||||
`src/adapters/storage/opfs/**/*.${sourceExtensions}`,
|
||||
],
|
||||
plugins: {
|
||||
"browser-data-boundary": browserDataBoundaryPlugin,
|
||||
},
|
||||
rules: {
|
||||
"browser-data-boundary/no-capability-alias": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/domain/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports(layerPatterns.domain),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"window",
|
||||
"document",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"fetch",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/application/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports(layerPatterns.application),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"window",
|
||||
"document",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"fetch",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/presentation/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports(layerPatterns.presentation),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"fetch",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
`src/presentation/adapters/query/**/*.${sourceExtensions}`,
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports([
|
||||
"**/adapters/http/**",
|
||||
"**/adapters/storage/**",
|
||||
"**/adapters/auth/**",
|
||||
"**/bootstrap/**",
|
||||
"**/application/ports/out/**",
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/presentation/templates/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports([
|
||||
"**/application/**",
|
||||
"**/adapters/**",
|
||||
"**/bootstrap/**",
|
||||
"@tanstack/**",
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
`src/bootstrap/**/*.${sourceExtensions}`,
|
||||
`src/features/installed-feature-*.${sourceExtensions}`,
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/adapters/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports(layerPatterns.adapters),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
`src/adapters/browser-file-storage/**/*.${sourceExtensions}`,
|
||||
`src/adapters/browser-files/**/*.${sourceExtensions}`,
|
||||
`src/adapters/browser-transfer/**/*.${sourceExtensions}`,
|
||||
`src/adapters/cache-storage/**/*.${sourceExtensions}`,
|
||||
`src/adapters/cross-context-invalidation/**/*.${sourceExtensions}`,
|
||||
`src/adapters/service-worker/**/*.${sourceExtensions}`,
|
||||
`src/bootstrap/register-service-worker.ts`,
|
||||
`src/adapters/platform/browser-lifecycle.ts`,
|
||||
`src/adapters/storage/**/*.${sourceExtensions}`,
|
||||
`src/adapters/storage/indexeddb/**/*.${sourceExtensions}`,
|
||||
`src/adapters/storage/opfs/**/*.${sourceExtensions}`,
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-properties": "off",
|
||||
"no-restricted-globals": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/features/*/domain/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports(layerPatterns.domain),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"window",
|
||||
"document",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"fetch",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/features/*/application/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports(layerPatterns.application),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"window",
|
||||
"document",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"fetch",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/features/*/presentation/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports([
|
||||
"**/features/*/adapters/**",
|
||||
"**/adapters/http/**",
|
||||
"**/adapters/storage/**",
|
||||
"**/adapters/auth/**",
|
||||
"**/bootstrap/**",
|
||||
"**/application/ports/out/**",
|
||||
"@tanstack/**",
|
||||
]),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"fetch",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`src/features/*/adapters/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports([
|
||||
"**/presentation/**",
|
||||
"**/bootstrap/**",
|
||||
"@tanstack/**",
|
||||
]),
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"navigator",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`tests/**/*.${sourceExtensions}`],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`tests/support/browser/**/*.${sourceExtensions}`],
|
||||
rules: {
|
||||
"react-hooks/rules-of-hooks": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
`tests/fixtures/architecture/forbidden/**/*.${sourceExtensions}`,
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-imports": restrictedImports([
|
||||
"**/adapters/**",
|
||||
"**/application/ports/out/**",
|
||||
"@tanstack/**",
|
||||
"react",
|
||||
"react-dom",
|
||||
"**/application/**",
|
||||
]),
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"fetch",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
`tests/fixtures/browser-file-storage-boundaries/forbidden/**/*.${sourceExtensions}`,
|
||||
],
|
||||
plugins: {
|
||||
"browser-data-boundary": browserDataBoundaryPlugin,
|
||||
},
|
||||
rules: {
|
||||
"browser-data-boundary/no-capability-alias": "error",
|
||||
"no-restricted-properties": restrictedBrowserDataProperties,
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
"BroadcastChannel",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"caches",
|
||||
"File",
|
||||
"Blob",
|
||||
"FileSystemFileHandle",
|
||||
"FileSystemDirectoryHandle",
|
||||
"showOpenFilePicker",
|
||||
"showSaveFilePicker",
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user