oxlint-plugin-arch 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/README.md +92 -5
- package/dist/constants/test.constants.d.ts +1 -0
- package/dist/index.js +205 -83
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.js +79 -33
- package/dist/presets/tanstack-start-react-modules-preset.js +79 -33
- package/dist/rules/module-policy/index.js +321 -11
- package/dist/types/preset.types.d.ts +8 -0
- package/package.json +1 -1
package/dist/presets/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as tanstackStartReactModulesPreset } from './tanstack-start-react-modules-preset.ts';
|
|
2
|
-
export type { PresetOptions, PresetArchitecture, PresetArchitectureEntry, PresetFolderLayout, PresetTanstackStartOptions, PresetLimitsOptions, PresetModulesOptions, PresetImportsOptions, PresetOrpcOptions, PresetSqlOptions, PresetFormsOptions, PresetReactOptions, PresetPolicyOptions, PresetPolicy, PresetPolicyName, PresetTailwindOptions, PresetShadcnOptions, } from '../types/preset.types.ts';
|
|
2
|
+
export type { PresetOptions, PresetArchitecture, PresetArchitectureEntry, PresetFolderLayout, PresetTanstackStartOptions, PresetLimitsOptions, PresetModulesOptions, PresetImportsOptions, PresetOrpcOptions, PresetSqlOptions, PresetFormsOptions, PresetReactOptions, PresetTestsOptions, PresetPolicyOptions, PresetPolicy, PresetPolicyName, PresetTailwindOptions, PresetShadcnOptions, } from '../types/preset.types.ts';
|
package/dist/presets/index.js
CHANGED
|
@@ -30,6 +30,8 @@ var presetPolicyOrder = [
|
|
|
30
30
|
"shadcn"
|
|
31
31
|
];
|
|
32
32
|
var presetCommonRules = {
|
|
33
|
+
"vitest/expect-expect": "off",
|
|
34
|
+
"vitest/no-standalone-expect": "off",
|
|
33
35
|
"jsx-a11y/no-noninteractive-tabindex": "error",
|
|
34
36
|
"no-case-declarations": "error",
|
|
35
37
|
"no-empty": "error",
|
|
@@ -413,18 +415,7 @@ var presetTestRules = {
|
|
|
413
415
|
"jest/no-export": "off",
|
|
414
416
|
"jest/no-standalone-expect": "off",
|
|
415
417
|
"jest/require-to-throw-message": "off",
|
|
416
|
-
"vitest/no-
|
|
417
|
-
"error",
|
|
418
|
-
{
|
|
419
|
-
additionalTestBlockFunctions: ["test"]
|
|
420
|
-
}
|
|
421
|
-
],
|
|
422
|
-
"vitest/expect-expect": [
|
|
423
|
-
"error",
|
|
424
|
-
{
|
|
425
|
-
additionalTestBlockFunctions: ["test"]
|
|
426
|
-
}
|
|
427
|
-
],
|
|
418
|
+
"vitest/no-conditional-expect": "error",
|
|
428
419
|
"vitest/require-mock-type-parameters": "error"
|
|
429
420
|
};
|
|
430
421
|
var presetExportPrefix = {
|
|
@@ -589,6 +580,7 @@ function presetTypesConfig(context) {
|
|
|
589
580
|
}
|
|
590
581
|
]
|
|
591
582
|
}, [...ui, ...testFiles]),
|
|
583
|
+
...options.react?.componentProps === false ? [] : [presetOverride(components, { "arch/component-props": ["error", { filePrefix: true }] }, [...ui, ...testFiles])],
|
|
592
584
|
presetOverride(presetScopes(app, "**/*.types.ts"), {
|
|
593
585
|
"arch/no-restricted-files": ["error", { message: "Domain types belong in src/types." }]
|
|
594
586
|
}, [...types, ...ui]),
|
|
@@ -679,6 +671,7 @@ function presetTypesConfig(context) {
|
|
|
679
671
|
function presetModulesConfig(context) {
|
|
680
672
|
const { web, backend, app, services, utilities, ui, testFiles, components, hooks, options } = context;
|
|
681
673
|
const conceptFiles = (options.modules?.customFileRoles ?? []).map((role) => `**/*.${role}.ts`);
|
|
674
|
+
const migrations = options.modules?.migrationFiles ?? [];
|
|
682
675
|
const frontendEntries = [
|
|
683
676
|
...presetScopes(web, "services/**/*.{client,server,rsc}.ts"),
|
|
684
677
|
...presetScopes(web, "services/**/*.rsc.tsx")
|
|
@@ -696,7 +689,7 @@ function presetModulesConfig(context) {
|
|
|
696
689
|
message: mode === "flat" ? "This folder uses a flat layout; subfolders are forbidden." : "This folder uses exactly one owning domain directory; loose and deeper files are forbidden."
|
|
697
690
|
}
|
|
698
691
|
]
|
|
699
|
-
}),
|
|
692
|
+
}, migrations),
|
|
700
693
|
...mode === "domain" ? [
|
|
701
694
|
presetOverride([`${path}/*/${presetExtensions}`], {
|
|
702
695
|
"arch/folder-prefix": [
|
|
@@ -740,7 +733,7 @@ function presetModulesConfig(context) {
|
|
|
740
733
|
minMembers: 1
|
|
741
734
|
}
|
|
742
735
|
]
|
|
743
|
-
}, [...utilities, ...testFiles, ...conceptFiles]),
|
|
736
|
+
}, [...utilities, ...testFiles, ...conceptFiles, ...migrations]),
|
|
744
737
|
presetOverride(frontendEntries, {
|
|
745
738
|
"modules/service-functions": ["error", { frontend: true }]
|
|
746
739
|
}, testFiles),
|
|
@@ -792,7 +785,10 @@ function presetModulesConfig(context) {
|
|
|
792
785
|
]
|
|
793
786
|
}, testFiles),
|
|
794
787
|
presetOverride(presetScopes(app, "services/**/{actions,queries}/**/*.ts"), {
|
|
795
|
-
"arch/no-restricted-files": [
|
|
788
|
+
"arch/no-restricted-files": [
|
|
789
|
+
"error",
|
|
790
|
+
{ message: "Use domain-action.name.ts or domain-query.name.ts." }
|
|
791
|
+
]
|
|
796
792
|
})
|
|
797
793
|
];
|
|
798
794
|
policies.naming = [
|
|
@@ -814,7 +810,10 @@ function presetModulesConfig(context) {
|
|
|
814
810
|
]
|
|
815
811
|
}, testFiles),
|
|
816
812
|
presetOverride(presetScopes(web, "services/**/*.constants.ts"), {
|
|
817
|
-
"arch/export-file-prefix": [
|
|
813
|
+
"arch/export-file-prefix": [
|
|
814
|
+
"error",
|
|
815
|
+
{ ...presetExportPrefix, allDeclarations: true, allowPattern: "^\\*$" }
|
|
816
|
+
]
|
|
818
817
|
}, testFiles),
|
|
819
818
|
presetOverride(presetScopes(app, "**/*.utils.ts"), {
|
|
820
819
|
"arch/export-file-prefix": [
|
|
@@ -843,7 +842,10 @@ function presetModulesConfig(context) {
|
|
|
843
842
|
presetOverride(presetScopes(web.filter((path) => !context.folders.some((scope) => scope.path === path + "/components")), "components/**/*.tsx"), {
|
|
844
843
|
"arch/folder-prefix": ["error", { singularize: "trailing-s", separators: ["-"] }]
|
|
845
844
|
}, [...ui, ...testFiles]),
|
|
846
|
-
...["action", "query"].map((role) => presetOverride([
|
|
845
|
+
...["action", "query"].map((role) => presetOverride([
|
|
846
|
+
...presetScopes(app, `services/**/*-${role}.*.ts`),
|
|
847
|
+
...presetScopes(web, `services/**/*-${role}.*.rsc.tsx`)
|
|
848
|
+
], {
|
|
847
849
|
"arch/filename-match": [
|
|
848
850
|
"error",
|
|
849
851
|
{
|
|
@@ -897,29 +899,53 @@ function presetModulesConfig(context) {
|
|
|
897
899
|
|
|
898
900
|
// src/configs/tests.ts
|
|
899
901
|
function presetTestsConfig(context) {
|
|
900
|
-
const { backend, testFiles, baseImports } = context;
|
|
902
|
+
const { backend, testFiles, baseImports, options } = context;
|
|
903
|
+
const profile = options.tests?.profile ?? "fixtures";
|
|
904
|
+
if (profile !== "standard" && profile !== "fixtures")
|
|
905
|
+
throw new Error("Unknown test profile.");
|
|
906
|
+
const testFunctions = ["test", "it", ...options.tests?.additionalTestFunctions ?? []];
|
|
907
|
+
const assertionFunctions = options.tests?.additionalAssertionFunctions ?? [];
|
|
908
|
+
if (testFunctions.some((name) => !/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(name)))
|
|
909
|
+
throw new Error("Additional test functions must be literal identifiers or dotted names.");
|
|
910
|
+
if (assertionFunctions.some((name) => !/^[A-Za-z_$][\w$]*(?:\.[\w$*]+)*$/.test(name)))
|
|
911
|
+
throw new Error("Assertion functions must be rooted dotted names or patterns.");
|
|
901
912
|
const policies = {};
|
|
902
913
|
policies.tests = [
|
|
903
914
|
presetOverride(testFiles, {
|
|
904
915
|
...presetTestRules,
|
|
905
916
|
"dillon-anti-slop/no-module-mocking": "error",
|
|
906
|
-
"modules/test-modifiers":
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
"modules/
|
|
911
|
-
"no-restricted-imports": [
|
|
917
|
+
"modules/test-modifiers": [
|
|
918
|
+
"error",
|
|
919
|
+
{ additionalTestFunctions: options.tests?.additionalTestFunctions ?? [] }
|
|
920
|
+
],
|
|
921
|
+
"modules/test-assertions": [
|
|
912
922
|
"error",
|
|
913
923
|
{
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
{
|
|
917
|
-
group: ["**/helpers/**"],
|
|
918
|
-
message: "Use named fixtures or tests/support."
|
|
919
|
-
}
|
|
920
|
-
]
|
|
924
|
+
additionalTestFunctions: options.tests?.additionalTestFunctions ?? [],
|
|
925
|
+
additionalAssertionFunctions: assertionFunctions
|
|
921
926
|
}
|
|
927
|
+
],
|
|
928
|
+
"arch/test-title-pattern": [
|
|
929
|
+
"error",
|
|
930
|
+
{ callees: ["describe", ...testFunctions], forbid: "^should\\b", flags: "i" }
|
|
922
931
|
]
|
|
932
|
+
}),
|
|
933
|
+
presetOverride(presetScopes(backend.map((path) => path.replace(/\/src$/, "")), "tests/**/*.ts"), {
|
|
934
|
+
"modules/concurrent-db": "error",
|
|
935
|
+
...profile === "fixtures" ? {
|
|
936
|
+
"no-restricted-imports": [
|
|
937
|
+
"error",
|
|
938
|
+
{
|
|
939
|
+
patterns: [
|
|
940
|
+
...baseImports,
|
|
941
|
+
{
|
|
942
|
+
group: ["**/helpers/**"],
|
|
943
|
+
message: "Use named fixtures or tests/support."
|
|
944
|
+
}
|
|
945
|
+
]
|
|
946
|
+
}
|
|
947
|
+
]
|
|
948
|
+
} : {}
|
|
923
949
|
})
|
|
924
950
|
];
|
|
925
951
|
return policies;
|
|
@@ -1016,15 +1042,35 @@ function presetReactConfig(context) {
|
|
|
1016
1042
|
]))));
|
|
1017
1043
|
}
|
|
1018
1044
|
if (Object.keys(css).length && options.shadcn !== false) {
|
|
1045
|
+
const allow = ["layout", "opacity", "truncate"];
|
|
1046
|
+
const padding = ["p", "px", "py", "ps", "pe", "pt", "pr", "pb", "pl"];
|
|
1047
|
+
const gap = ["gap", "gap-x", "gap-y"];
|
|
1048
|
+
const safeArea = padding.flatMap((group) => ["top", "right", "bottom", "left"].flatMap((side) => [
|
|
1049
|
+
`${group}-[env(safe-area-inset-${side})]`,
|
|
1050
|
+
...group === "p" ? [] : [`${group}-[max(--spacing(*),env(safe-area-inset-${side}))]`]
|
|
1051
|
+
]));
|
|
1019
1052
|
policies.shadcn = [
|
|
1020
1053
|
presetOverride(webFiles, {
|
|
1021
|
-
"shadcn/no-restyle": [
|
|
1022
|
-
|
|
1054
|
+
"shadcn/no-restyle": [
|
|
1055
|
+
"error",
|
|
1056
|
+
{
|
|
1057
|
+
allow,
|
|
1058
|
+
contracts: [
|
|
1059
|
+
{ pattern: "^(CardContent|PopoverContent)$", allow: [...allow, ...padding] },
|
|
1060
|
+
{ pattern: "^TabsContent$", allow: [...allow, ...gap, ...padding] },
|
|
1061
|
+
{ pattern: "^(Tabs|HoverCardContent|SidebarHeader|BreadcrumbList)$", allow: [...allow, ...gap] }
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
],
|
|
1065
|
+
"shadcn/no-arbitrary-values": ["error", { allow: ["layout", ...safeArea] }],
|
|
1023
1066
|
"shadcn/require-static-classes": "error",
|
|
1024
1067
|
"modules/dynamic-classes": "error"
|
|
1025
1068
|
}, ui),
|
|
1026
1069
|
presetOverride(webFiles, {
|
|
1027
1070
|
"shadcn/no-unknown-classes": ["error", { allow: [] }]
|
|
1071
|
+
}),
|
|
1072
|
+
presetOverride(presetScopes(web, "components/ui/sonner.tsx"), {
|
|
1073
|
+
"shadcn/no-unknown-classes": ["error", { allow: ["toaster", "toast"] }]
|
|
1028
1074
|
})
|
|
1029
1075
|
];
|
|
1030
1076
|
}
|
|
@@ -30,6 +30,8 @@ var presetPolicyOrder = [
|
|
|
30
30
|
"shadcn"
|
|
31
31
|
];
|
|
32
32
|
var presetCommonRules = {
|
|
33
|
+
"vitest/expect-expect": "off",
|
|
34
|
+
"vitest/no-standalone-expect": "off",
|
|
33
35
|
"jsx-a11y/no-noninteractive-tabindex": "error",
|
|
34
36
|
"no-case-declarations": "error",
|
|
35
37
|
"no-empty": "error",
|
|
@@ -413,18 +415,7 @@ var presetTestRules = {
|
|
|
413
415
|
"jest/no-export": "off",
|
|
414
416
|
"jest/no-standalone-expect": "off",
|
|
415
417
|
"jest/require-to-throw-message": "off",
|
|
416
|
-
"vitest/no-
|
|
417
|
-
"error",
|
|
418
|
-
{
|
|
419
|
-
additionalTestBlockFunctions: ["test"]
|
|
420
|
-
}
|
|
421
|
-
],
|
|
422
|
-
"vitest/expect-expect": [
|
|
423
|
-
"error",
|
|
424
|
-
{
|
|
425
|
-
additionalTestBlockFunctions: ["test"]
|
|
426
|
-
}
|
|
427
|
-
],
|
|
418
|
+
"vitest/no-conditional-expect": "error",
|
|
428
419
|
"vitest/require-mock-type-parameters": "error"
|
|
429
420
|
};
|
|
430
421
|
var presetExportPrefix = {
|
|
@@ -589,6 +580,7 @@ function presetTypesConfig(context) {
|
|
|
589
580
|
}
|
|
590
581
|
]
|
|
591
582
|
}, [...ui, ...testFiles]),
|
|
583
|
+
...options.react?.componentProps === false ? [] : [presetOverride(components, { "arch/component-props": ["error", { filePrefix: true }] }, [...ui, ...testFiles])],
|
|
592
584
|
presetOverride(presetScopes(app, "**/*.types.ts"), {
|
|
593
585
|
"arch/no-restricted-files": ["error", { message: "Domain types belong in src/types." }]
|
|
594
586
|
}, [...types, ...ui]),
|
|
@@ -679,6 +671,7 @@ function presetTypesConfig(context) {
|
|
|
679
671
|
function presetModulesConfig(context) {
|
|
680
672
|
const { web, backend, app, services, utilities, ui, testFiles, components, hooks, options } = context;
|
|
681
673
|
const conceptFiles = (options.modules?.customFileRoles ?? []).map((role) => `**/*.${role}.ts`);
|
|
674
|
+
const migrations = options.modules?.migrationFiles ?? [];
|
|
682
675
|
const frontendEntries = [
|
|
683
676
|
...presetScopes(web, "services/**/*.{client,server,rsc}.ts"),
|
|
684
677
|
...presetScopes(web, "services/**/*.rsc.tsx")
|
|
@@ -696,7 +689,7 @@ function presetModulesConfig(context) {
|
|
|
696
689
|
message: mode === "flat" ? "This folder uses a flat layout; subfolders are forbidden." : "This folder uses exactly one owning domain directory; loose and deeper files are forbidden."
|
|
697
690
|
}
|
|
698
691
|
]
|
|
699
|
-
}),
|
|
692
|
+
}, migrations),
|
|
700
693
|
...mode === "domain" ? [
|
|
701
694
|
presetOverride([`${path}/*/${presetExtensions}`], {
|
|
702
695
|
"arch/folder-prefix": [
|
|
@@ -740,7 +733,7 @@ function presetModulesConfig(context) {
|
|
|
740
733
|
minMembers: 1
|
|
741
734
|
}
|
|
742
735
|
]
|
|
743
|
-
}, [...utilities, ...testFiles, ...conceptFiles]),
|
|
736
|
+
}, [...utilities, ...testFiles, ...conceptFiles, ...migrations]),
|
|
744
737
|
presetOverride(frontendEntries, {
|
|
745
738
|
"modules/service-functions": ["error", { frontend: true }]
|
|
746
739
|
}, testFiles),
|
|
@@ -792,7 +785,10 @@ function presetModulesConfig(context) {
|
|
|
792
785
|
]
|
|
793
786
|
}, testFiles),
|
|
794
787
|
presetOverride(presetScopes(app, "services/**/{actions,queries}/**/*.ts"), {
|
|
795
|
-
"arch/no-restricted-files": [
|
|
788
|
+
"arch/no-restricted-files": [
|
|
789
|
+
"error",
|
|
790
|
+
{ message: "Use domain-action.name.ts or domain-query.name.ts." }
|
|
791
|
+
]
|
|
796
792
|
})
|
|
797
793
|
];
|
|
798
794
|
policies.naming = [
|
|
@@ -814,7 +810,10 @@ function presetModulesConfig(context) {
|
|
|
814
810
|
]
|
|
815
811
|
}, testFiles),
|
|
816
812
|
presetOverride(presetScopes(web, "services/**/*.constants.ts"), {
|
|
817
|
-
"arch/export-file-prefix": [
|
|
813
|
+
"arch/export-file-prefix": [
|
|
814
|
+
"error",
|
|
815
|
+
{ ...presetExportPrefix, allDeclarations: true, allowPattern: "^\\*$" }
|
|
816
|
+
]
|
|
818
817
|
}, testFiles),
|
|
819
818
|
presetOverride(presetScopes(app, "**/*.utils.ts"), {
|
|
820
819
|
"arch/export-file-prefix": [
|
|
@@ -843,7 +842,10 @@ function presetModulesConfig(context) {
|
|
|
843
842
|
presetOverride(presetScopes(web.filter((path) => !context.folders.some((scope) => scope.path === path + "/components")), "components/**/*.tsx"), {
|
|
844
843
|
"arch/folder-prefix": ["error", { singularize: "trailing-s", separators: ["-"] }]
|
|
845
844
|
}, [...ui, ...testFiles]),
|
|
846
|
-
...["action", "query"].map((role) => presetOverride([
|
|
845
|
+
...["action", "query"].map((role) => presetOverride([
|
|
846
|
+
...presetScopes(app, `services/**/*-${role}.*.ts`),
|
|
847
|
+
...presetScopes(web, `services/**/*-${role}.*.rsc.tsx`)
|
|
848
|
+
], {
|
|
847
849
|
"arch/filename-match": [
|
|
848
850
|
"error",
|
|
849
851
|
{
|
|
@@ -897,29 +899,53 @@ function presetModulesConfig(context) {
|
|
|
897
899
|
|
|
898
900
|
// src/configs/tests.ts
|
|
899
901
|
function presetTestsConfig(context) {
|
|
900
|
-
const { backend, testFiles, baseImports } = context;
|
|
902
|
+
const { backend, testFiles, baseImports, options } = context;
|
|
903
|
+
const profile = options.tests?.profile ?? "fixtures";
|
|
904
|
+
if (profile !== "standard" && profile !== "fixtures")
|
|
905
|
+
throw new Error("Unknown test profile.");
|
|
906
|
+
const testFunctions = ["test", "it", ...options.tests?.additionalTestFunctions ?? []];
|
|
907
|
+
const assertionFunctions = options.tests?.additionalAssertionFunctions ?? [];
|
|
908
|
+
if (testFunctions.some((name) => !/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(name)))
|
|
909
|
+
throw new Error("Additional test functions must be literal identifiers or dotted names.");
|
|
910
|
+
if (assertionFunctions.some((name) => !/^[A-Za-z_$][\w$]*(?:\.[\w$*]+)*$/.test(name)))
|
|
911
|
+
throw new Error("Assertion functions must be rooted dotted names or patterns.");
|
|
901
912
|
const policies = {};
|
|
902
913
|
policies.tests = [
|
|
903
914
|
presetOverride(testFiles, {
|
|
904
915
|
...presetTestRules,
|
|
905
916
|
"dillon-anti-slop/no-module-mocking": "error",
|
|
906
|
-
"modules/test-modifiers":
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
"modules/
|
|
911
|
-
"no-restricted-imports": [
|
|
917
|
+
"modules/test-modifiers": [
|
|
918
|
+
"error",
|
|
919
|
+
{ additionalTestFunctions: options.tests?.additionalTestFunctions ?? [] }
|
|
920
|
+
],
|
|
921
|
+
"modules/test-assertions": [
|
|
912
922
|
"error",
|
|
913
923
|
{
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
{
|
|
917
|
-
group: ["**/helpers/**"],
|
|
918
|
-
message: "Use named fixtures or tests/support."
|
|
919
|
-
}
|
|
920
|
-
]
|
|
924
|
+
additionalTestFunctions: options.tests?.additionalTestFunctions ?? [],
|
|
925
|
+
additionalAssertionFunctions: assertionFunctions
|
|
921
926
|
}
|
|
927
|
+
],
|
|
928
|
+
"arch/test-title-pattern": [
|
|
929
|
+
"error",
|
|
930
|
+
{ callees: ["describe", ...testFunctions], forbid: "^should\\b", flags: "i" }
|
|
922
931
|
]
|
|
932
|
+
}),
|
|
933
|
+
presetOverride(presetScopes(backend.map((path) => path.replace(/\/src$/, "")), "tests/**/*.ts"), {
|
|
934
|
+
"modules/concurrent-db": "error",
|
|
935
|
+
...profile === "fixtures" ? {
|
|
936
|
+
"no-restricted-imports": [
|
|
937
|
+
"error",
|
|
938
|
+
{
|
|
939
|
+
patterns: [
|
|
940
|
+
...baseImports,
|
|
941
|
+
{
|
|
942
|
+
group: ["**/helpers/**"],
|
|
943
|
+
message: "Use named fixtures or tests/support."
|
|
944
|
+
}
|
|
945
|
+
]
|
|
946
|
+
}
|
|
947
|
+
]
|
|
948
|
+
} : {}
|
|
923
949
|
})
|
|
924
950
|
];
|
|
925
951
|
return policies;
|
|
@@ -1016,15 +1042,35 @@ function presetReactConfig(context) {
|
|
|
1016
1042
|
]))));
|
|
1017
1043
|
}
|
|
1018
1044
|
if (Object.keys(css).length && options.shadcn !== false) {
|
|
1045
|
+
const allow = ["layout", "opacity", "truncate"];
|
|
1046
|
+
const padding = ["p", "px", "py", "ps", "pe", "pt", "pr", "pb", "pl"];
|
|
1047
|
+
const gap = ["gap", "gap-x", "gap-y"];
|
|
1048
|
+
const safeArea = padding.flatMap((group) => ["top", "right", "bottom", "left"].flatMap((side) => [
|
|
1049
|
+
`${group}-[env(safe-area-inset-${side})]`,
|
|
1050
|
+
...group === "p" ? [] : [`${group}-[max(--spacing(*),env(safe-area-inset-${side}))]`]
|
|
1051
|
+
]));
|
|
1019
1052
|
policies.shadcn = [
|
|
1020
1053
|
presetOverride(webFiles, {
|
|
1021
|
-
"shadcn/no-restyle": [
|
|
1022
|
-
|
|
1054
|
+
"shadcn/no-restyle": [
|
|
1055
|
+
"error",
|
|
1056
|
+
{
|
|
1057
|
+
allow,
|
|
1058
|
+
contracts: [
|
|
1059
|
+
{ pattern: "^(CardContent|PopoverContent)$", allow: [...allow, ...padding] },
|
|
1060
|
+
{ pattern: "^TabsContent$", allow: [...allow, ...gap, ...padding] },
|
|
1061
|
+
{ pattern: "^(Tabs|HoverCardContent|SidebarHeader|BreadcrumbList)$", allow: [...allow, ...gap] }
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
],
|
|
1065
|
+
"shadcn/no-arbitrary-values": ["error", { allow: ["layout", ...safeArea] }],
|
|
1023
1066
|
"shadcn/require-static-classes": "error",
|
|
1024
1067
|
"modules/dynamic-classes": "error"
|
|
1025
1068
|
}, ui),
|
|
1026
1069
|
presetOverride(webFiles, {
|
|
1027
1070
|
"shadcn/no-unknown-classes": ["error", { allow: [] }]
|
|
1071
|
+
}),
|
|
1072
|
+
presetOverride(presetScopes(web, "components/ui/sonner.tsx"), {
|
|
1073
|
+
"shadcn/no-unknown-classes": ["error", { allow: ["toaster", "toast"] }]
|
|
1028
1074
|
})
|
|
1029
1075
|
];
|
|
1030
1076
|
}
|