oxlint-plugin-arch 0.6.3 → 0.7.0
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 +15 -0
- package/README.md +100 -528
- package/dist/constants/preset.constants.d.ts +6 -0
- package/dist/index.js +83 -11
- package/dist/presets/index.d.ts +3 -1
- package/dist/presets/index.js +247 -69
- package/dist/presets/next-react-modules-preset.d.ts +4 -0
- package/dist/presets/next-react-modules-preset.js +1570 -0
- package/dist/presets/react-modules-preset.d.ts +4 -0
- package/dist/presets/react-modules-preset.js +1565 -0
- package/dist/presets/tanstack-start-react-modules-preset.d.ts +1 -0
- package/dist/presets/tanstack-start-react-modules-preset.js +239 -68
- package/dist/rules/module-policy/index.js +202 -34
- package/dist/rules/module-policy/next-runtime.d.ts +7 -0
- package/dist/types/module-policy.types.d.ts +5 -0
- package/dist/types/preset.types.d.ts +29 -0
- package/dist/utils/helpers/declarations.d.ts +1 -1
- package/dist/utils/helpers/index.d.ts +1 -0
- package/dist/utils/helpers/layout.d.ts +1 -1
- package/dist/utils/helpers/naming.d.ts +1 -1
- package/dist/utils/helpers/preset.d.ts +1 -0
- package/dist/utils/helpers/runtime.d.ts +14 -0
- package/package.json +29 -20
|
@@ -18,6 +18,12 @@ export declare const presetTailwindRules: {
|
|
|
18
18
|
export declare const presetImportRules: PresetRules;
|
|
19
19
|
export declare const presetNamingRules: PresetRules;
|
|
20
20
|
export declare const presetTestRules: PresetRules;
|
|
21
|
+
/** HTTP methods a Next `route.ts` handler may export. */
|
|
22
|
+
export declare const presetNextHandlerMethods: string[];
|
|
23
|
+
/** Next route segment config exports, shared by pages, layouts and route handlers. */
|
|
24
|
+
export declare const presetNextSegmentConfig: string[];
|
|
25
|
+
/** Exports a Next page or layout may add next to its default component. */
|
|
26
|
+
export declare const presetNextSegmentExports: string[];
|
|
21
27
|
export declare const presetExportPrefix: {
|
|
22
28
|
stem: string;
|
|
23
29
|
trailingRoles: string[];
|
package/dist/index.js
CHANGED
|
@@ -894,6 +894,49 @@ function routesInspectFunction(context, item, bannedHooks, banIntrinsicJsx) {
|
|
|
894
894
|
}
|
|
895
895
|
});
|
|
896
896
|
}
|
|
897
|
+
// src/utils/helpers/runtime.ts
|
|
898
|
+
import { builtinModules } from "node:module";
|
|
899
|
+
var runtimeServerGlobals = ["process", "Bun", "Deno", "Buffer", "__dirname", "__filename", "require"];
|
|
900
|
+
var runtimeBrowserGlobals = [
|
|
901
|
+
"window",
|
|
902
|
+
"document",
|
|
903
|
+
"navigator",
|
|
904
|
+
"localStorage",
|
|
905
|
+
"sessionStorage",
|
|
906
|
+
"location",
|
|
907
|
+
"history",
|
|
908
|
+
"screen",
|
|
909
|
+
"HTMLElement",
|
|
910
|
+
"Element",
|
|
911
|
+
"HTMLInputElement",
|
|
912
|
+
"ResizeObserver",
|
|
913
|
+
"MutationObserver",
|
|
914
|
+
"IntersectionObserver",
|
|
915
|
+
"FileReader",
|
|
916
|
+
"matchMedia",
|
|
917
|
+
"getComputedStyle",
|
|
918
|
+
"requestAnimationFrame",
|
|
919
|
+
"cancelAnimationFrame",
|
|
920
|
+
"alert",
|
|
921
|
+
"confirm",
|
|
922
|
+
"prompt"
|
|
923
|
+
];
|
|
924
|
+
function runtimeMatchesImport(values, source) {
|
|
925
|
+
return Boolean(values?.some((value) => source === value || source.startsWith(value + "/")));
|
|
926
|
+
}
|
|
927
|
+
function runtimeIsBackendImport(options, target, source) {
|
|
928
|
+
return options.backend.some((path) => target.startsWith(path + "/")) || options.backendPackages.some((name) => source === name || source.startsWith(name + "/") && /\/(?:server|rpc)(?:\/|$)/.test(source.slice(name.length)));
|
|
929
|
+
}
|
|
930
|
+
function runtimeIsServerPlatformImport(source) {
|
|
931
|
+
return builtinModules.includes(source) || source === "@orpc/server" || /^(?:node:|bun(?::|$)|cloudflare:)/.test(source) || /^react-dom\/server(?:\.|\/|$)/.test(source);
|
|
932
|
+
}
|
|
933
|
+
function runtimeMemberName(node) {
|
|
934
|
+
if (node?.type !== "MemberExpression")
|
|
935
|
+
return null;
|
|
936
|
+
if (!node.computed && node.property.type === "Identifier")
|
|
937
|
+
return node.property.name;
|
|
938
|
+
return node.computed && node.property.type === "Literal" && typeof node.property.value === "string" ? node.property.value : null;
|
|
939
|
+
}
|
|
897
940
|
// src/utils/helpers/sanitizers.ts
|
|
898
941
|
function sanitizersCallsConfigured(node, names) {
|
|
899
942
|
let found = false;
|
|
@@ -2378,8 +2421,10 @@ var noFileLevelHelpers = defineRule16({
|
|
|
2378
2421
|
} = optionsFirst(context, {});
|
|
2379
2422
|
const allowed = optionsOptionalPatterns(allowPattern);
|
|
2380
2423
|
const hooks = optionsPatterns(hookPattern);
|
|
2424
|
+
const exported = exportsCollect(program);
|
|
2425
|
+
const allowedExport = (name) => exported.some((binding) => binding.localName === name && optionsPatternsTest(allowed, binding.exportedName));
|
|
2381
2426
|
for (const candidate of declarationsFileLevelFunctionCandidates(program)) {
|
|
2382
|
-
if (detectComponents && reactComponentsIsLike(program, candidate) || optionsPatternsTest(hooks, candidate.name) || optionsPatternsTest(allowed, candidate.name)) {
|
|
2427
|
+
if (detectComponents && reactComponentsIsLike(program, candidate) || optionsPatternsTest(hooks, candidate.name) || optionsPatternsTest(allowed, candidate.name) || allowedExport(candidate.name)) {
|
|
2383
2428
|
continue;
|
|
2384
2429
|
}
|
|
2385
2430
|
context.report({
|
|
@@ -4271,6 +4316,7 @@ var onlyExportComponents = defineRule36({
|
|
|
4271
4316
|
additionalProperties: false,
|
|
4272
4317
|
properties: {
|
|
4273
4318
|
matchFileName: { type: "boolean" },
|
|
4319
|
+
trailingRoles: { type: "array", items: { type: "string" } },
|
|
4274
4320
|
allowTypeExports: { type: "boolean" },
|
|
4275
4321
|
denyTypePattern: optionsPatternSchema
|
|
4276
4322
|
}
|
|
@@ -4288,10 +4334,15 @@ var onlyExportComponents = defineRule36({
|
|
|
4288
4334
|
Program(program) {
|
|
4289
4335
|
const {
|
|
4290
4336
|
matchFileName = true,
|
|
4337
|
+
trailingRoles = [],
|
|
4291
4338
|
allowTypeExports = true,
|
|
4292
4339
|
denyTypePattern
|
|
4293
4340
|
} = optionsFirst(context, {});
|
|
4294
|
-
const
|
|
4341
|
+
const basename = namingFileBasename(context.filename);
|
|
4342
|
+
const extension = basename.slice(basename.lastIndexOf("."));
|
|
4343
|
+
const role = trailingRoles.find((candidate) => basename.endsWith(`.${candidate}${extension}`));
|
|
4344
|
+
const expected = reactComponentsPascalFromBasename(role ? basename.replace(`.${role}${extension}`, extension) : basename);
|
|
4345
|
+
const expectedKeys = [expected, ...role ? [reactComponentsPascalFromBasename(basename)] : []].map((name) => name.toLowerCase());
|
|
4295
4346
|
const denied = optionsOptionalPatterns(denyTypePattern);
|
|
4296
4347
|
const components = [];
|
|
4297
4348
|
for (const candidate of reactComponentsExportCandidates(program)) {
|
|
@@ -4324,10 +4375,9 @@ var onlyExportComponents = defineRule36({
|
|
|
4324
4375
|
if (components.length === 0 || !matchFileName) {
|
|
4325
4376
|
return;
|
|
4326
4377
|
}
|
|
4327
|
-
const
|
|
4328
|
-
const
|
|
4329
|
-
|
|
4330
|
-
if (!exact && !allPrefixed) {
|
|
4378
|
+
const names = components.map((candidate) => candidate.name.toLowerCase());
|
|
4379
|
+
const matches = expectedKeys.some((expectedKey) => names.includes(expectedKey) || names.every((name) => name.startsWith(expectedKey)));
|
|
4380
|
+
if (!matches) {
|
|
4331
4381
|
for (const candidate of components) {
|
|
4332
4382
|
context.report({
|
|
4333
4383
|
node: candidate.node,
|
|
@@ -4994,13 +5044,21 @@ var routeSurface = defineRule44({
|
|
|
4994
5044
|
additionalProperties: false,
|
|
4995
5045
|
properties: {
|
|
4996
5046
|
exportName: { type: "string" },
|
|
5047
|
+
allowedExports: { type: "array", items: { type: "string" } },
|
|
5048
|
+
inspectExports: { type: "boolean" },
|
|
4997
5049
|
bannedHooks: { type: "array", items: { type: "string" } },
|
|
4998
5050
|
banIntrinsicJsx: { type: "boolean" }
|
|
4999
5051
|
}
|
|
5000
5052
|
}
|
|
5001
5053
|
],
|
|
5002
5054
|
defaultOptions: [
|
|
5003
|
-
{
|
|
5055
|
+
{
|
|
5056
|
+
exportName: "Route",
|
|
5057
|
+
allowedExports: [],
|
|
5058
|
+
inspectExports: false,
|
|
5059
|
+
bannedHooks: ["useState", "useEffect", "useMutation"],
|
|
5060
|
+
banIntrinsicJsx: true
|
|
5061
|
+
}
|
|
5004
5062
|
],
|
|
5005
5063
|
messages: {
|
|
5006
5064
|
extraExport: "Route files may only export '{{expected}}'; found '{{actual}}'.",
|
|
@@ -5011,20 +5069,34 @@ var routeSurface = defineRule44({
|
|
|
5011
5069
|
createOnce(context) {
|
|
5012
5070
|
return {
|
|
5013
5071
|
Program(program) {
|
|
5014
|
-
const { exportName, bannedHooks, banIntrinsicJsx } = optionsFirst(context);
|
|
5015
|
-
|
|
5016
|
-
|
|
5072
|
+
const { exportName, allowedExports = [], inspectExports = false, bannedHooks, banIntrinsicJsx } = optionsFirst(context);
|
|
5073
|
+
const allowed = new Set([exportName, ...allowedExports]);
|
|
5074
|
+
const bindings = exportsCollect(program);
|
|
5075
|
+
for (const binding of bindings) {
|
|
5076
|
+
if (!allowed.has(binding.exportedName)) {
|
|
5017
5077
|
context.report({
|
|
5018
5078
|
node: binding.node,
|
|
5019
5079
|
messageId: "extraExport",
|
|
5020
|
-
data: { expected:
|
|
5080
|
+
data: { expected: [...allowed].join("', '"), actual: binding.exportedName }
|
|
5021
5081
|
});
|
|
5022
5082
|
}
|
|
5023
5083
|
}
|
|
5024
5084
|
const banned = new Set(bannedHooks);
|
|
5085
|
+
const inspected = new Set;
|
|
5025
5086
|
for (const item of declarationsTopLevelUnexportedFunctions(program)) {
|
|
5087
|
+
inspected.add(item.node.start);
|
|
5026
5088
|
routesInspectFunction(context, item, banned, banIntrinsicJsx);
|
|
5027
5089
|
}
|
|
5090
|
+
if (!inspectExports)
|
|
5091
|
+
return;
|
|
5092
|
+
const names = new Set(bindings.filter((binding) => binding.exportedName === exportName).map((binding) => binding.localName ?? binding.exportedName));
|
|
5093
|
+
const defaultExport = program.body.find((statement) => statement.type === "ExportDefaultDeclaration");
|
|
5094
|
+
const declaration = defaultExport?.declaration;
|
|
5095
|
+
const defaultFunctions = declaration?.type === "ArrowFunctionExpression" || declaration?.type === "FunctionExpression" ? [{ name: "default", node: declaration }] : [];
|
|
5096
|
+
for (const item of [...exportsCollectFunctions(program), ...defaultFunctions]) {
|
|
5097
|
+
if (names.has(item.name) && !inspected.has(item.node.start))
|
|
5098
|
+
routesInspectFunction(context, item, banned, banIntrinsicJsx);
|
|
5099
|
+
}
|
|
5028
5100
|
}
|
|
5029
5101
|
};
|
|
5030
5102
|
}
|
package/dist/presets/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
export { default as reactModulesPreset } from './react-modules-preset.ts';
|
|
2
|
+
export { default as nextReactModulesPreset } from './next-react-modules-preset.ts';
|
|
1
3
|
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, PresetTestsOptions, PresetPolicyOptions, PresetPolicy, PresetPolicyName, PresetTailwindOptions, PresetShadcnOptions, } from '../types/preset.types.ts';
|
|
4
|
+
export type { PresetOptions, PresetArchitecture, PresetArchitectureEntry, PresetFolderLayout, PresetFormattingOptions, PresetFramework, PresetNextOptions, PresetTanstackStartOptions, PresetLimitsOptions, PresetModulesOptions, PresetImportsOptions, PresetOrpcOptions, PresetSqlOptions, PresetFormsOptions, PresetReactOptions, PresetTestsOptions, PresetPolicyOptions, PresetPolicy, PresetPolicyName, PresetTailwindOptions, PresetShadcnOptions, } from '../types/preset.types.ts';
|