@blogic-cz/oxc-config 1.3.0 → 1.4.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/.oxlintrc.ts.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/niconiconainu/oxc/HEAD/npm/oxc-types/src/generated/schema/oxlintrc.json",
3
3
  "jsPlugins": [
4
- "./plugins/effect-ts.js",
5
- "./plugins/enforce-props-type-name.js",
6
- "./plugins/max-file-lines.js"
4
+ "./dist/plugins/effect-ts.js",
5
+ "./dist/plugins/enforce-props-type-name.js",
6
+ "./dist/plugins/max-file-lines.js"
7
7
  ],
8
8
  "plugins": [
9
9
  "import",
@@ -85,6 +85,7 @@
85
85
  "naming/no-console-in-server": "error",
86
86
  "naming/no-dynamic-type-import": "error",
87
87
  "naming/no-server-logger-in-client": "error",
88
+ "naming/no-process-env": "error",
88
89
  "naming/no-undefined-parameter-union": "error",
89
90
  "effect-ts/no-direct-tag-check": "error"
90
91
  },
@@ -126,6 +127,7 @@
126
127
  "import/no-relative-parent-imports": "off",
127
128
  "naming/enforce-props-type-name": "off",
128
129
  "naming/no-default-parameter-values": "off",
130
+ "naming/no-process-env": "off",
129
131
  "naming/no-undefined-parameter-union": "off",
130
132
  "typescript/no-unsafe-type-assertion": "off",
131
133
  "unicorn/no-array-sort": "off"
@@ -0,0 +1,141 @@
1
+ import effectTs from "./plugins/effect-ts";
2
+ import enforcePropsTypeName from "./plugins/enforce-props-type-name";
3
+ import maxFileLines from "./plugins/max-file-lines";
4
+ export { effectTs, enforcePropsTypeName, maxFileLines, };
5
+ export declare const oxlintTsConfigPath = "@blogic-cz/oxc-config/oxlint/ts";
6
+ export declare const oxlintTsReactConfigPath = "@blogic-cz/oxc-config/oxlint/ts-react";
7
+ export declare const oxfmtBaseConfigPath = "@blogic-cz/oxc-config/oxfmt/base";
8
+ export declare const oxlintPlugins: {
9
+ effectTs: {
10
+ meta: {
11
+ name: string;
12
+ };
13
+ rules: {
14
+ "no-direct-tag-check": {
15
+ meta: {
16
+ schema: any[];
17
+ };
18
+ create(context: any): {
19
+ BinaryExpression(node: any): void;
20
+ SwitchStatement(node: any): void;
21
+ };
22
+ };
23
+ };
24
+ };
25
+ enforcePropsTypeName: {
26
+ meta: {
27
+ name: string;
28
+ };
29
+ rules: {
30
+ "enforce-props-type-name": {
31
+ meta: {
32
+ schema: any[];
33
+ };
34
+ create(context: any): {
35
+ TSTypeAliasDeclaration(node: any): void;
36
+ };
37
+ };
38
+ "no-dynamic-type-import": {
39
+ meta: {
40
+ schema: any[];
41
+ };
42
+ create(context: any): {
43
+ TSImportType(node: any): void;
44
+ };
45
+ };
46
+ "no-server-logger-in-client": {
47
+ meta: {
48
+ schema: any[];
49
+ };
50
+ create(context: any): {
51
+ ImportDeclaration(node: any): void;
52
+ };
53
+ };
54
+ "no-console-in-server": {
55
+ meta: {
56
+ schema: any[];
57
+ };
58
+ create(context: any): {
59
+ MemberExpression(node: any): void;
60
+ };
61
+ };
62
+ "no-default-parameter-values": {
63
+ meta: {
64
+ schema: any[];
65
+ };
66
+ create(context: any): {
67
+ AssignmentPattern(node: any): void;
68
+ };
69
+ };
70
+ "no-process-env": {
71
+ meta: {
72
+ schema: any[];
73
+ };
74
+ create(context: any): {
75
+ MemberExpression(node: any): void;
76
+ };
77
+ };
78
+ "no-undefined-parameter-union": {
79
+ meta: {
80
+ schema: any[];
81
+ };
82
+ create(context: any): {
83
+ TSUndefinedKeyword(node: any): void;
84
+ };
85
+ };
86
+ };
87
+ };
88
+ maxFileLines: {
89
+ meta: {
90
+ name: string;
91
+ };
92
+ rules: {
93
+ "max-file-lines": {
94
+ meta: {
95
+ defaultOptions: {
96
+ maxLines: number;
97
+ extensions: string[];
98
+ }[];
99
+ schema: {
100
+ type: string;
101
+ default: {};
102
+ properties: {
103
+ maxLines: {
104
+ type: string;
105
+ default: number;
106
+ };
107
+ extensions: {
108
+ type: string;
109
+ items: {
110
+ type: string;
111
+ };
112
+ default: string[];
113
+ };
114
+ };
115
+ additionalProperties: boolean;
116
+ }[];
117
+ };
118
+ create(context: any): {
119
+ Program(): void;
120
+ };
121
+ };
122
+ "no-barrel-files": {
123
+ meta: {
124
+ schema: any[];
125
+ };
126
+ create(context: any): {
127
+ Program(node: any): void;
128
+ };
129
+ };
130
+ "kebab-case-filename": {
131
+ meta: {
132
+ schema: any[];
133
+ };
134
+ create(context: any): {
135
+ Program(node: any): void;
136
+ };
137
+ };
138
+ };
139
+ };
140
+ };
141
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,oBAAoB,MAAM,mCAAmC,CAAC;AACrE,OAAO,YAAY,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,YAAY,GACb,CAAC;AAEF,eAAO,MAAM,kBAAkB,oCACI,CAAC;AACpC,eAAO,MAAM,uBAAuB,0CACK,CAAC;AAC1C,eAAO,MAAM,mBAAmB,qCACI,CAAC;AAErC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIzB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,381 @@
1
+ // src/plugins/effect-ts.ts
2
+ var EFFECT_TAGS = {
3
+ Some: "Use `Option.isSome(value)` or `Option.match(value, { onSome, onNone })`.",
4
+ None: "Use `Option.isNone(value)` or `Option.match(value, { onSome, onNone })`.",
5
+ Left: "Use `Either.isLeft(value)` or `Either.match(value, { onLeft, onRight })`.",
6
+ Right: "Use `Either.isRight(value)` or `Either.match(value, { onLeft, onRight })`.",
7
+ Success: "Use `Exit.isSuccess(value)` or `Exit.match(value, { onSuccess, onFailure })`.",
8
+ Failure: "Use `Exit.isFailure(value)` or `Exit.match(value, { onSuccess, onFailure })`."
9
+ };
10
+ var TAG_PROPERTY = "_tag";
11
+ var isMemberAccessOnTag = (node) => node.type === "MemberExpression" && !node.computed && node.property?.type === "Identifier" && node.property.name === TAG_PROPERTY;
12
+ var isEffectTagLiteral = (node) => {
13
+ if (node.type === "StringLiteral" && typeof node.value === "string") {
14
+ return node.value in EFFECT_TAGS ? node.value : undefined;
15
+ }
16
+ return;
17
+ };
18
+ var plugin = {
19
+ meta: {
20
+ name: "effect-ts"
21
+ },
22
+ rules: {
23
+ "no-direct-tag-check": {
24
+ meta: {
25
+ schema: []
26
+ },
27
+ create(context) {
28
+ return {
29
+ BinaryExpression(node) {
30
+ if (node.operator !== "===" && node.operator !== "!==" && node.operator !== "==" && node.operator !== "!=") {
31
+ return;
32
+ }
33
+ let tag;
34
+ if (isMemberAccessOnTag(node.left)) {
35
+ tag = isEffectTagLiteral(node.right);
36
+ } else if (isMemberAccessOnTag(node.right)) {
37
+ tag = isEffectTagLiteral(node.left);
38
+ }
39
+ if (tag === undefined) {
40
+ return;
41
+ }
42
+ const hint = EFFECT_TAGS[tag];
43
+ context.report({
44
+ message: `Do not check \`._tag\` directly for "${tag}". ${hint}`,
45
+ node
46
+ });
47
+ },
48
+ SwitchStatement(node) {
49
+ if (!isMemberAccessOnTag(node.discriminant)) {
50
+ return;
51
+ }
52
+ const effectCases = node.cases.filter((c) => c.test && c.test.type === "StringLiteral" && typeof c.test.value === "string" && (c.test.value in EFFECT_TAGS));
53
+ if (effectCases.length === 0) {
54
+ return;
55
+ }
56
+ const tags = effectCases.map((c) => `"${c.test.value}"`).join(", ");
57
+ context.report({
58
+ message: `Do not switch on \`._tag\` with Effect tags (${tags}). Use the \`.pipe(T.match(...))\` API instead.`,
59
+ node: node.discriminant
60
+ });
61
+ }
62
+ };
63
+ }
64
+ }
65
+ }
66
+ };
67
+ var effect_ts_default = plugin;
68
+
69
+ // src/plugins/enforce-props-type-name.ts
70
+ var isFunctionNode = (type) => type === "FunctionDeclaration" || type === "FunctionExpression" || type === "ArrowFunctionExpression" || type === "TSDeclareFunction";
71
+ var plugin2 = {
72
+ meta: {
73
+ name: "naming"
74
+ },
75
+ rules: {
76
+ "enforce-props-type-name": {
77
+ meta: {
78
+ schema: []
79
+ },
80
+ create(context) {
81
+ return {
82
+ TSTypeAliasDeclaration(node) {
83
+ if (!context.filename.endsWith(".tsx")) {
84
+ return;
85
+ }
86
+ const name = node.id.name;
87
+ if (name.endsWith("Props") && name !== "Props") {
88
+ context.report({
89
+ message: `Props type must be named "Props", not "${name}". Use \`type Props = { ... }\` instead.`,
90
+ node: node.id
91
+ });
92
+ }
93
+ }
94
+ };
95
+ }
96
+ },
97
+ "no-dynamic-type-import": {
98
+ meta: {
99
+ schema: []
100
+ },
101
+ create(context) {
102
+ return {
103
+ TSImportType(node) {
104
+ context.report({
105
+ message: 'Use regular `import type` instead of dynamic `import("...")` type syntax.',
106
+ node
107
+ });
108
+ }
109
+ };
110
+ }
111
+ },
112
+ "no-server-logger-in-client": {
113
+ meta: {
114
+ schema: []
115
+ },
116
+ create(context) {
117
+ return {
118
+ ImportDeclaration(node) {
119
+ const source = node.source.value;
120
+ if ((source.endsWith("/logger") || source === "pino") && context.filename.endsWith(".tsx")) {
121
+ context.report({
122
+ message: `Do not import "${source}" in client-side (.tsx) files. Use console.log/console.error instead.`,
123
+ node
124
+ });
125
+ }
126
+ }
127
+ };
128
+ }
129
+ },
130
+ "no-console-in-server": {
131
+ meta: {
132
+ schema: []
133
+ },
134
+ create(context) {
135
+ return {
136
+ MemberExpression(node) {
137
+ const filename = context.filename;
138
+ const isServerFile = filename.endsWith(".ts") && !filename.endsWith(".tsx");
139
+ const isWebAppServer = filename.includes("apps/web-app/src/");
140
+ const isExcluded = filename.includes(".test.ts") || filename.includes("/jobs/") || filename.includes("/scripts/");
141
+ if (isServerFile && isWebAppServer && !isExcluded && node.object.type === "Identifier" && node.object.name === "console") {
142
+ context.report({
143
+ message: "Do not use console.* in server-side code. Use the project logger package instead.",
144
+ node
145
+ });
146
+ }
147
+ }
148
+ };
149
+ }
150
+ },
151
+ "no-default-parameter-values": {
152
+ meta: {
153
+ schema: []
154
+ },
155
+ create(context) {
156
+ return {
157
+ AssignmentPattern(node) {
158
+ const parentType = node.parent.type;
159
+ const isFunctionParamDefault = parentType === "FunctionDeclaration" || parentType === "FunctionExpression" || parentType === "ArrowFunctionExpression" || parentType === "TSParameterProperty";
160
+ if (!isFunctionParamDefault) {
161
+ return;
162
+ }
163
+ context.report({
164
+ message: "Default parameter values are not allowed. Require the caller to pass the value explicitly.",
165
+ node
166
+ });
167
+ }
168
+ };
169
+ }
170
+ },
171
+ "no-process-env": {
172
+ meta: {
173
+ schema: []
174
+ },
175
+ create(context) {
176
+ return {
177
+ MemberExpression(node) {
178
+ if (node.object.type !== "Identifier" || node.object.name !== "process") {
179
+ return;
180
+ }
181
+ const isEnvAccess = !node.computed && node.property.type === "Identifier" && node.property.name === "env" || node.computed && node.property.type === "StringLiteral" && node.property.value === "env";
182
+ if (!isEnvAccess) {
183
+ return;
184
+ }
185
+ const filename = context.filename;
186
+ const parts = filename.split("/");
187
+ const basename = parts.at(-1) ?? "";
188
+ if (basename === "env.ts" || basename.startsWith("env.") || basename === "public-env.ts" || parts.includes("env")) {
189
+ return;
190
+ }
191
+ context.report({
192
+ message: 'Do not access `process.env` directly. Import the typed `env` object from the env module instead (e.g., `import { env } from "@/env/env.server"`).',
193
+ node
194
+ });
195
+ }
196
+ };
197
+ }
198
+ },
199
+ "no-undefined-parameter-union": {
200
+ meta: {
201
+ schema: []
202
+ },
203
+ create(context) {
204
+ return {
205
+ TSUndefinedKeyword(node) {
206
+ if (node.parent.type !== "TSUnionType") {
207
+ return;
208
+ }
209
+ if (node.parent.parent.type !== "TSTypeAnnotation") {
210
+ return;
211
+ }
212
+ const annotationTarget = node.parent.parent.parent;
213
+ const parentType = annotationTarget.parent.type;
214
+ const isDirectFunctionParam = parentType && isFunctionNode(parentType);
215
+ const isTsParameterProperty = annotationTarget.type === "Identifier" && parentType === "TSParameterProperty" && annotationTarget.parent.parent.type === "FunctionExpression" && annotationTarget.parent.parent.parent.type === "MethodDefinition";
216
+ if (!isDirectFunctionParam && !isTsParameterProperty) {
217
+ return;
218
+ }
219
+ context.report({
220
+ message: "Avoid `| undefined` in parameter types. Use explicit `| null` (or an optional parameter when intended).",
221
+ node
222
+ });
223
+ }
224
+ };
225
+ }
226
+ }
227
+ }
228
+ };
229
+ var enforce_props_type_name_default = plugin2;
230
+
231
+ // src/plugins/max-file-lines.ts
232
+ function parseOptions(value) {
233
+ if (typeof value !== "object" || value === null) {
234
+ return { maxLines: 500, extensions: [".tsx"] };
235
+ }
236
+ const maxLinesValue = "maxLines" in value ? value.maxLines : undefined;
237
+ const maxLines = typeof maxLinesValue === "number" ? maxLinesValue : 500;
238
+ const extensionsValue = "extensions" in value ? value.extensions : undefined;
239
+ const extensions = Array.isArray(extensionsValue) ? extensionsValue.filter((extension) => typeof extension === "string") : [".tsx"];
240
+ return {
241
+ maxLines,
242
+ extensions: extensions.length > 0 ? extensions : [".tsx"]
243
+ };
244
+ }
245
+ var plugin3 = {
246
+ meta: {
247
+ name: "file-quality"
248
+ },
249
+ rules: {
250
+ "max-file-lines": {
251
+ meta: {
252
+ defaultOptions: [
253
+ {
254
+ maxLines: 500,
255
+ extensions: [".tsx"]
256
+ }
257
+ ],
258
+ schema: [
259
+ {
260
+ type: "object",
261
+ default: {},
262
+ properties: {
263
+ maxLines: { type: "number", default: 500 },
264
+ extensions: {
265
+ type: "array",
266
+ items: { type: "string" },
267
+ default: [".tsx"]
268
+ }
269
+ },
270
+ additionalProperties: false
271
+ }
272
+ ]
273
+ },
274
+ create(context) {
275
+ return {
276
+ Program() {
277
+ const options = parseOptions(context.options[0]);
278
+ const { maxLines, extensions } = options;
279
+ if (!extensions.some((ext) => context.filename.endsWith(ext))) {
280
+ return;
281
+ }
282
+ const lineCount = context.sourceCode.lines.length;
283
+ if (lineCount <= maxLines) {
284
+ return;
285
+ }
286
+ context.report({
287
+ message: `File has ${lineCount} lines (max ${maxLines}). Consider splitting into smaller modules.`,
288
+ node: context.sourceCode.ast
289
+ });
290
+ }
291
+ };
292
+ }
293
+ },
294
+ "no-barrel-files": {
295
+ meta: {
296
+ schema: []
297
+ },
298
+ create(context) {
299
+ return {
300
+ Program(node) {
301
+ const filename = context.filename;
302
+ if (!filename.endsWith("/index.ts")) {
303
+ return;
304
+ }
305
+ if (/packages\/[^/]+\/src\//.test(filename)) {
306
+ return;
307
+ }
308
+ const body = node.body;
309
+ if (body.length === 0) {
310
+ return;
311
+ }
312
+ const isBarrel = body.every((stmt) => stmt.type === "ExportNamedDeclaration" && stmt.source !== null || stmt.type === "ExportAllDeclaration" || stmt.type === "ImportDeclaration");
313
+ if (isBarrel) {
314
+ context.report({
315
+ message: "Barrel files (index.ts with only re-exports) are not allowed. Import directly from source files.",
316
+ node
317
+ });
318
+ }
319
+ }
320
+ };
321
+ }
322
+ },
323
+ "kebab-case-filename": {
324
+ meta: {
325
+ schema: []
326
+ },
327
+ create(context) {
328
+ return {
329
+ Program(node) {
330
+ const filename = context.filename;
331
+ if (filename.includes("node_modules") || filename.endsWith(".d.ts")) {
332
+ return;
333
+ }
334
+ const parts = filename.split("/");
335
+ const basename = parts.at(-1);
336
+ if (!basename) {
337
+ return;
338
+ }
339
+ if (basename.includes("$") || basename.includes("[") || basename.includes("]")) {
340
+ return;
341
+ }
342
+ const nameWithoutExt = basename.replace(/\.(test|e2e|server|client|config)\.(ts|tsx|js|jsx|mjs|cjs)$/, "").replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
343
+ if (nameWithoutExt === "") {
344
+ return;
345
+ }
346
+ if (nameWithoutExt === "routeTree.gen" || nameWithoutExt === "Dockerfile" || nameWithoutExt.startsWith("__") || nameWithoutExt.endsWith(".gen") || nameWithoutExt === "vite-env") {
347
+ return;
348
+ }
349
+ const kebabRegex = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
350
+ if (!kebabRegex.test(nameWithoutExt)) {
351
+ context.report({
352
+ message: `Filename "${basename}" must use kebab-case (e.g., "my-component.tsx").`,
353
+ node
354
+ });
355
+ }
356
+ }
357
+ };
358
+ }
359
+ }
360
+ }
361
+ };
362
+ var max_file_lines_default = plugin3;
363
+
364
+ // src/index.ts
365
+ var oxlintTsConfigPath = "@blogic-cz/oxc-config/oxlint/ts";
366
+ var oxlintTsReactConfigPath = "@blogic-cz/oxc-config/oxlint/ts-react";
367
+ var oxfmtBaseConfigPath = "@blogic-cz/oxc-config/oxfmt/base";
368
+ var oxlintPlugins = {
369
+ effectTs: effect_ts_default,
370
+ enforcePropsTypeName: enforce_props_type_name_default,
371
+ maxFileLines: max_file_lines_default
372
+ };
373
+ export {
374
+ oxlintTsReactConfigPath,
375
+ oxlintTsConfigPath,
376
+ oxlintPlugins,
377
+ oxfmtBaseConfigPath,
378
+ max_file_lines_default as maxFileLines,
379
+ enforce_props_type_name_default as enforcePropsTypeName,
380
+ effect_ts_default as effectTs
381
+ };
@@ -0,0 +1,18 @@
1
+ declare const plugin: {
2
+ meta: {
3
+ name: string;
4
+ };
5
+ rules: {
6
+ "no-direct-tag-check": {
7
+ meta: {
8
+ schema: any[];
9
+ };
10
+ create(context: any): {
11
+ BinaryExpression(node: any): void;
12
+ SwitchStatement(node: any): void;
13
+ };
14
+ };
15
+ };
16
+ };
17
+ export default plugin;
18
+ //# sourceMappingURL=effect-ts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-ts.d.ts","sourceRoot":"","sources":["../../src/plugins/effect-ts.ts"],"names":[],"mappings":"AA+BA,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;CAuEX,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,4 +1,4 @@
1
- // plugins/effect-ts.ts
1
+ // src/plugins/effect-ts.ts
2
2
  var EFFECT_TAGS = {
3
3
  Some: "Use `Option.isSome(value)` or `Option.match(value, { onSome, onNone })`.",
4
4
  None: "Use `Option.isNone(value)` or `Option.match(value, { onSome, onNone })`.",
@@ -0,0 +1,65 @@
1
+ declare const plugin: {
2
+ meta: {
3
+ name: string;
4
+ };
5
+ rules: {
6
+ "enforce-props-type-name": {
7
+ meta: {
8
+ schema: any[];
9
+ };
10
+ create(context: any): {
11
+ TSTypeAliasDeclaration(node: any): void;
12
+ };
13
+ };
14
+ "no-dynamic-type-import": {
15
+ meta: {
16
+ schema: any[];
17
+ };
18
+ create(context: any): {
19
+ TSImportType(node: any): void;
20
+ };
21
+ };
22
+ "no-server-logger-in-client": {
23
+ meta: {
24
+ schema: any[];
25
+ };
26
+ create(context: any): {
27
+ ImportDeclaration(node: any): void;
28
+ };
29
+ };
30
+ "no-console-in-server": {
31
+ meta: {
32
+ schema: any[];
33
+ };
34
+ create(context: any): {
35
+ MemberExpression(node: any): void;
36
+ };
37
+ };
38
+ "no-default-parameter-values": {
39
+ meta: {
40
+ schema: any[];
41
+ };
42
+ create(context: any): {
43
+ AssignmentPattern(node: any): void;
44
+ };
45
+ };
46
+ "no-process-env": {
47
+ meta: {
48
+ schema: any[];
49
+ };
50
+ create(context: any): {
51
+ MemberExpression(node: any): void;
52
+ };
53
+ };
54
+ "no-undefined-parameter-union": {
55
+ meta: {
56
+ schema: any[];
57
+ };
58
+ create(context: any): {
59
+ TSUndefinedKeyword(node: any): void;
60
+ };
61
+ };
62
+ };
63
+ };
64
+ export default plugin;
65
+ //# sourceMappingURL=enforce-props-type-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce-props-type-name.d.ts","sourceRoot":"","sources":["../../src/plugins/enforce-props-type-name.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqOX,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,4 +1,4 @@
1
- // plugins/enforce-props-type-name.ts
1
+ // src/plugins/enforce-props-type-name.ts
2
2
  var isFunctionNode = (type) => type === "FunctionDeclaration" || type === "FunctionExpression" || type === "ArrowFunctionExpression" || type === "TSDeclareFunction";
3
3
  var plugin = {
4
4
  meta: {
@@ -100,6 +100,34 @@ var plugin = {
100
100
  };
101
101
  }
102
102
  },
103
+ "no-process-env": {
104
+ meta: {
105
+ schema: []
106
+ },
107
+ create(context) {
108
+ return {
109
+ MemberExpression(node) {
110
+ if (node.object.type !== "Identifier" || node.object.name !== "process") {
111
+ return;
112
+ }
113
+ const isEnvAccess = !node.computed && node.property.type === "Identifier" && node.property.name === "env" || node.computed && node.property.type === "StringLiteral" && node.property.value === "env";
114
+ if (!isEnvAccess) {
115
+ return;
116
+ }
117
+ const filename = context.filename;
118
+ const parts = filename.split("/");
119
+ const basename = parts.at(-1) ?? "";
120
+ if (basename === "env.ts" || basename.startsWith("env.") || basename === "public-env.ts" || parts.includes("env")) {
121
+ return;
122
+ }
123
+ context.report({
124
+ message: 'Do not access `process.env` directly. Import the typed `env` object from the env module instead (e.g., `import { env } from "@/env/env.server"`).',
125
+ node
126
+ });
127
+ }
128
+ };
129
+ }
130
+ },
103
131
  "no-undefined-parameter-union": {
104
132
  meta: {
105
133
  schema: []
@@ -0,0 +1,54 @@
1
+ declare const plugin: {
2
+ meta: {
3
+ name: string;
4
+ };
5
+ rules: {
6
+ "max-file-lines": {
7
+ meta: {
8
+ defaultOptions: {
9
+ maxLines: number;
10
+ extensions: string[];
11
+ }[];
12
+ schema: {
13
+ type: string;
14
+ default: {};
15
+ properties: {
16
+ maxLines: {
17
+ type: string;
18
+ default: number;
19
+ };
20
+ extensions: {
21
+ type: string;
22
+ items: {
23
+ type: string;
24
+ };
25
+ default: string[];
26
+ };
27
+ };
28
+ additionalProperties: boolean;
29
+ }[];
30
+ };
31
+ create(context: any): {
32
+ Program(): void;
33
+ };
34
+ };
35
+ "no-barrel-files": {
36
+ meta: {
37
+ schema: any[];
38
+ };
39
+ create(context: any): {
40
+ Program(node: any): void;
41
+ };
42
+ };
43
+ "kebab-case-filename": {
44
+ meta: {
45
+ schema: any[];
46
+ };
47
+ create(context: any): {
48
+ Program(node: any): void;
49
+ };
50
+ };
51
+ };
52
+ };
53
+ export default plugin;
54
+ //# sourceMappingURL=max-file-lines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"max-file-lines.d.ts","sourceRoot":"","sources":["../../src/plugins/max-file-lines.ts"],"names":[],"mappings":"AA6BA,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoKX,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,10 +1,12 @@
1
- // plugins/max-file-lines.ts
1
+ // src/plugins/max-file-lines.ts
2
2
  function parseOptions(value) {
3
3
  if (typeof value !== "object" || value === null) {
4
4
  return { maxLines: 500, extensions: [".tsx"] };
5
5
  }
6
- const maxLines = typeof value.maxLines === "number" ? value.maxLines : 500;
7
- const extensions = Array.isArray(value.extensions) ? value.extensions.filter((extension) => typeof extension === "string") : [".tsx"];
6
+ const maxLinesValue = "maxLines" in value ? value.maxLines : undefined;
7
+ const maxLines = typeof maxLinesValue === "number" ? maxLinesValue : 500;
8
+ const extensionsValue = "extensions" in value ? value.extensions : undefined;
9
+ const extensions = Array.isArray(extensionsValue) ? extensionsValue.filter((extension) => typeof extension === "string") : [".tsx"];
8
10
  return {
9
11
  maxLines,
10
12
  extensions: extensions.length > 0 ? extensions : [".tsx"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/oxc-config",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "Shared oxlint and oxfmt configs for blogic projects — layered TypeScript, React, and formatter presets",
5
5
  "keywords": [
6
6
  "oxlint",
@@ -20,19 +20,69 @@
20
20
  "type": "git",
21
21
  "url": "https://github.com/blogic-cz/oxc-config.git"
22
22
  },
23
+ "type": "module",
24
+ "main": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "./plugins/effect-ts": {
33
+ "types": "./dist/plugins/effect-ts.d.ts",
34
+ "import": "./dist/plugins/effect-ts.js",
35
+ "default": "./dist/plugins/effect-ts.js"
36
+ },
37
+ "./plugins/effect-ts.js": {
38
+ "types": "./dist/plugins/effect-ts.d.ts",
39
+ "import": "./dist/plugins/effect-ts.js",
40
+ "default": "./dist/plugins/effect-ts.js"
41
+ },
42
+ "./plugins/enforce-props-type-name": {
43
+ "types": "./dist/plugins/enforce-props-type-name.d.ts",
44
+ "import": "./dist/plugins/enforce-props-type-name.js",
45
+ "default": "./dist/plugins/enforce-props-type-name.js"
46
+ },
47
+ "./plugins/enforce-props-type-name.js": {
48
+ "types": "./dist/plugins/enforce-props-type-name.d.ts",
49
+ "import": "./dist/plugins/enforce-props-type-name.js",
50
+ "default": "./dist/plugins/enforce-props-type-name.js"
51
+ },
52
+ "./plugins/max-file-lines": {
53
+ "types": "./dist/plugins/max-file-lines.d.ts",
54
+ "import": "./dist/plugins/max-file-lines.js",
55
+ "default": "./dist/plugins/max-file-lines.js"
56
+ },
57
+ "./plugins/max-file-lines.js": {
58
+ "types": "./dist/plugins/max-file-lines.d.ts",
59
+ "import": "./dist/plugins/max-file-lines.js",
60
+ "default": "./dist/plugins/max-file-lines.js"
61
+ },
62
+ "./oxlint/ts": "./.oxlintrc.ts.json",
63
+ "./oxlint/ts-react": "./.oxlintrc.ts-react.json",
64
+ "./oxfmt/base": "./.oxfmtrc.base.jsonc",
65
+ "./package.json": "./package.json"
66
+ },
23
67
  "files": [
68
+ "dist",
69
+ "src",
24
70
  ".oxlintrc.ts.json",
25
71
  ".oxlintrc.ts-react.json",
26
72
  ".oxfmtrc.base.jsonc",
27
- "plugins/*.js",
28
- "plugins/*.ts",
29
73
  "README.md",
30
74
  "LICENSE"
31
75
  ],
32
76
  "scripts": {
33
- "build": "bun build plugins/effect-ts.ts --outdir plugins --outfile effect-ts.js && bun build plugins/enforce-props-type-name.ts --outdir plugins --outfile enforce-props-type-name.js && bun build plugins/max-file-lines.ts --outdir plugins --outfile max-file-lines.js",
77
+ "clean": "rm -rf dist",
78
+ "build:js": "bun run clean && bun build src/index.ts --outdir dist && bun build src/plugins/effect-ts.ts --outdir dist/plugins --outfile effect-ts.js && bun build src/plugins/enforce-props-type-name.ts --outdir dist/plugins --outfile enforce-props-type-name.js && bun build src/plugins/max-file-lines.ts --outdir dist/plugins --outfile max-file-lines.js",
79
+ "build:types": "tsc -p tsconfig.build.json",
80
+ "build": "bun run build:js && bun run build:types",
34
81
  "prepublishOnly": "bun run build"
35
82
  },
83
+ "devDependencies": {
84
+ "typescript": "5.9.3"
85
+ },
36
86
  "publishConfig": {
37
87
  "access": "public"
38
88
  }
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ import effectTs from "./plugins/effect-ts";
2
+ import enforcePropsTypeName from "./plugins/enforce-props-type-name";
3
+ import maxFileLines from "./plugins/max-file-lines";
4
+
5
+ export {
6
+ effectTs,
7
+ enforcePropsTypeName,
8
+ maxFileLines,
9
+ };
10
+
11
+ export const oxlintTsConfigPath =
12
+ "@blogic-cz/oxc-config/oxlint/ts";
13
+ export const oxlintTsReactConfigPath =
14
+ "@blogic-cz/oxc-config/oxlint/ts-react";
15
+ export const oxfmtBaseConfigPath =
16
+ "@blogic-cz/oxc-config/oxfmt/base";
17
+
18
+ export const oxlintPlugins = {
19
+ effectTs,
20
+ enforcePropsTypeName,
21
+ maxFileLines,
22
+ };
@@ -1,5 +1,3 @@
1
- import type { Plugin } from "#oxlint/plugins";
2
-
3
1
  const EFFECT_TAGS: Record<string, string> = {
4
2
  Some: 'Use `Option.isSome(value)` or `Option.match(value, { onSome, onNone })`.',
5
3
  None: 'Use `Option.isNone(value)` or `Option.match(value, { onSome, onNone })`.',
@@ -31,7 +29,7 @@ const isEffectTagLiteral = (node: {
31
29
  return undefined;
32
30
  };
33
31
 
34
- const plugin: Plugin = {
32
+ const plugin = {
35
33
  meta: {
36
34
  name: "effect-ts",
37
35
  },
@@ -1,12 +1,10 @@
1
- import type { Plugin } from "#oxlint/plugins";
2
-
3
1
  const isFunctionNode = (type: string): boolean =>
4
2
  type === "FunctionDeclaration" ||
5
3
  type === "FunctionExpression" ||
6
4
  type === "ArrowFunctionExpression" ||
7
5
  type === "TSDeclareFunction";
8
6
 
9
- const plugin: Plugin = {
7
+ const plugin = {
10
8
  meta: {
11
9
  name: "naming",
12
10
  },
@@ -138,6 +136,54 @@ const plugin: Plugin = {
138
136
  };
139
137
  },
140
138
  },
139
+ "no-process-env": {
140
+ meta: {
141
+ schema: [],
142
+ },
143
+ create(context) {
144
+ return {
145
+ MemberExpression(node) {
146
+ if (
147
+ node.object.type !== "Identifier" ||
148
+ node.object.name !== "process"
149
+ ) {
150
+ return;
151
+ }
152
+
153
+ const isEnvAccess =
154
+ (!node.computed &&
155
+ node.property.type === "Identifier" &&
156
+ node.property.name === "env") ||
157
+ (node.computed &&
158
+ node.property.type === "StringLiteral" &&
159
+ node.property.value === "env");
160
+
161
+ if (!isEnvAccess) {
162
+ return;
163
+ }
164
+
165
+ const filename = context.filename;
166
+ const parts = filename.split("/");
167
+ const basename = parts.at(-1) ?? "";
168
+
169
+ if (
170
+ basename === "env.ts" ||
171
+ basename.startsWith("env.") ||
172
+ basename === "public-env.ts" ||
173
+ parts.includes("env")
174
+ ) {
175
+ return;
176
+ }
177
+
178
+ context.report({
179
+ message:
180
+ 'Do not access `process.env` directly. Import the typed `env` object from the env module instead (e.g., `import { env } from "@/env/env.server"`).',
181
+ node,
182
+ });
183
+ },
184
+ };
185
+ },
186
+ },
141
187
  "no-undefined-parameter-union": {
142
188
  meta: {
143
189
  schema: [],
@@ -1,5 +1,3 @@
1
- import type { Plugin } from "#oxlint/plugins";
2
-
3
1
  function parseOptions(value: unknown): {
4
2
  maxLines: number;
5
3
  extensions: string[];
@@ -8,12 +6,16 @@ function parseOptions(value: unknown): {
8
6
  return { maxLines: 500, extensions: [".tsx"] };
9
7
  }
10
8
 
9
+ const maxLinesValue =
10
+ "maxLines" in value ? value.maxLines : undefined;
11
11
  const maxLines =
12
- typeof value.maxLines === "number"
13
- ? value.maxLines
12
+ typeof maxLinesValue === "number"
13
+ ? maxLinesValue
14
14
  : 500;
15
- const extensions = Array.isArray(value.extensions)
16
- ? value.extensions.filter(
15
+ const extensionsValue =
16
+ "extensions" in value ? value.extensions : undefined;
17
+ const extensions = Array.isArray(extensionsValue)
18
+ ? extensionsValue.filter(
17
19
  (extension) => typeof extension === "string"
18
20
  )
19
21
  : [".tsx"];
@@ -25,7 +27,7 @@ function parseOptions(value: unknown): {
25
27
  };
26
28
  }
27
29
 
28
- const plugin: Plugin = {
30
+ const plugin = {
29
31
  meta: {
30
32
  name: "file-quality",
31
33
  },
@@ -93,12 +95,10 @@ const plugin: Plugin = {
93
95
  Program(node) {
94
96
  const filename = context.filename;
95
97
 
96
- // Only check index.ts files (not .tsx — route files are OK)
97
98
  if (!filename.endsWith("/index.ts")) {
98
99
  return;
99
100
  }
100
101
 
101
- // Exception: any index.ts inside packages/ (root and sub-path entry points)
102
102
  if (/packages\/[^/]+\/src\//.test(filename)) {
103
103
  return;
104
104
  }
@@ -136,7 +136,6 @@ const plugin: Plugin = {
136
136
  Program(node) {
137
137
  const filename = context.filename;
138
138
 
139
- // Skip node_modules and .d.ts files
140
139
  if (
141
140
  filename.includes("node_modules") ||
142
141
  filename.endsWith(".d.ts")
@@ -150,7 +149,6 @@ const plugin: Plugin = {
150
149
  return;
151
150
  }
152
151
 
153
- // Skip TanStack Router dynamic segments ($param, $, [param])
154
152
  if (
155
153
  basename.includes("$") ||
156
154
  basename.includes("[") ||
@@ -159,7 +157,6 @@ const plugin: Plugin = {
159
157
  return;
160
158
  }
161
159
 
162
- // Strip compound extensions first, then simple
163
160
  const nameWithoutExt = basename
164
161
  .replace(
165
162
  /\.(test|e2e|server|client|config)\.(ts|tsx|js|jsx|mjs|cjs)$/,
@@ -167,12 +164,10 @@ const plugin: Plugin = {
167
164
  )
168
165
  .replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
169
166
 
170
- // Skip empty names (e.g., just an extension)
171
167
  if (nameWithoutExt === "") {
172
168
  return;
173
169
  }
174
170
 
175
- // Skip known special files
176
171
  if (
177
172
  nameWithoutExt === "routeTree.gen" ||
178
173
  nameWithoutExt === "Dockerfile" ||