@yasainet/eslint 0.0.3 → 0.0.4

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/README.md CHANGED
@@ -8,34 +8,42 @@ Shared ESLint configuration for feature-based architecture.
8
8
  npm install -D @yasainet/eslint eslint
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Entry Points
12
+
13
+ | Entry | Feature Root | Description |
14
+ | ----------------------- | ------------------------------ | ----------------------------------------------------------------------------------- |
15
+ | `@yasainet/eslint/next` | `src/features/` | Common rules + Next.js-specific rules (hooks, components, directives, lib-boundary) |
16
+ | `@yasainet/eslint/node` | `scripts/features/` | Common rules only |
17
+ | `@yasainet/eslint/deno` | `supabase/functions/features/` | Common rules only |
12
18
 
13
- ### Next.js
19
+ ## Usage
14
20
 
15
21
  ```js
16
22
  // eslint.config.mjs
23
+ import { eslintConfig as nextEslintConfig } from "@yasainet/eslint/next";
24
+ import { eslintConfig as nodeEslintConfig } from "@yasainet/eslint/node";
25
+ import { eslintConfig as denoEslintConfig } from "@yasainet/eslint/deno";
17
26
  import { defineConfig, globalIgnores } from "eslint/config";
18
27
  import nextVitals from "eslint-config-next/core-web-vitals";
19
28
  import nextTs from "eslint-config-next/typescript";
20
- import { eslintConfig } from "@yasainet/eslint/next";
21
29
 
22
30
  export default defineConfig([
23
31
  ...nextVitals,
24
32
  ...nextTs,
25
- globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
26
- ...eslintConfig,
33
+ // Override default ignores of eslint-config-next.
34
+ globalIgnores([
35
+ // Default ignores of eslint-config-next:
36
+ ".next/**",
37
+ "out/**",
38
+ "build/**",
39
+ "next-env.d.ts",
40
+ ]),
41
+ ...nextEslintConfig,
42
+ ...nodeEslintConfig,
43
+ ...denoEslintConfig,
27
44
  ]);
28
45
  ```
29
46
 
30
- ### Node.js
31
-
32
- ```js
33
- // eslint.config.mjs
34
- import { eslintConfig } from "@yasainet/eslint/node";
35
-
36
- export default eslintConfig;
37
- ```
38
-
39
47
  ## Release
40
48
 
41
49
  1. Update `version` in `package.json`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yasainet/eslint",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "ESLint",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,6 +11,10 @@
11
11
  "./node": {
12
12
  "types": "./src/node/index.d.mts",
13
13
  "default": "./src/node/index.mjs"
14
+ },
15
+ "./deno": {
16
+ "types": "./src/deno/index.d.mts",
17
+ "default": "./src/deno/index.mjs"
14
18
  }
15
19
  },
16
20
  "files": [
@@ -20,12 +20,6 @@ function findProjectRoot() {
20
20
 
21
21
  const PROJECT_ROOT = findProjectRoot();
22
22
 
23
- export const FEATURE_ROOTS = [
24
- "src/features",
25
- "scripts/features",
26
- "supabase/functions/features",
27
- ];
28
-
29
23
  const EXCLUDE_LIST = ["proxy.ts", "types"];
30
24
 
31
25
  function generatePrefixLibMapping() {
@@ -68,5 +62,6 @@ function generatePrefixLibMapping() {
68
62
 
69
63
  export const PREFIX_LIB_MAPPING = generatePrefixLibMapping();
70
64
 
71
- export const featuresGlob = (subpath) =>
72
- FEATURE_ROOTS.map((root) => `${root}/${subpath}`);
65
+ export const featuresGlob = (featureRoot, subpath) => [
66
+ `${featureRoot}/${subpath}`,
67
+ ];
@@ -112,22 +112,26 @@ function makeConfig(name, files, ...patternArrays) {
112
112
  };
113
113
  }
114
114
 
115
- function generateImportConfigs() {
116
- const configs = [];
117
-
118
- configs.push({
115
+ /** @description Next.js-only: restrict @/lib imports to repositories */
116
+ export const libBoundaryConfigs = [
117
+ {
119
118
  name: "imports/lib-boundary",
120
119
  files: ["src/**/*.{ts,tsx}"],
121
120
  ignores: ["src/lib/**", "src/proxy.ts", "src/app/sitemap.ts"],
122
121
  rules: {
123
122
  "no-restricted-imports": ["error", { patterns: LIB_BOUNDARY_PATTERNS }],
124
123
  },
125
- });
124
+ },
125
+ ];
126
+
127
+ /** @description Scope import restriction rules to the given feature root */
128
+ export function createImportsConfigs(featureRoot) {
129
+ const configs = [];
126
130
 
127
131
  configs.push(
128
132
  makeConfig(
129
133
  "repositories",
130
- ["**/repositories/*.ts"],
134
+ [`${featureRoot}/**/repositories/*.ts`],
131
135
  LAYER_PATTERNS.repositories,
132
136
  LATERAL_PATTERNS.repositories,
133
137
  ),
@@ -139,7 +143,7 @@ function generateImportConfigs() {
139
143
  configs.push(
140
144
  makeConfig(
141
145
  `repositories/${prefix}`,
142
- [`**/repositories/${prefix}.repo.ts`],
146
+ [`${featureRoot}/**/repositories/${prefix}.repo.ts`],
143
147
  LAYER_PATTERNS.repositories,
144
148
  LATERAL_PATTERNS.repositories,
145
149
  patterns,
@@ -150,7 +154,7 @@ function generateImportConfigs() {
150
154
  configs.push(
151
155
  makeConfig(
152
156
  "services",
153
- ["**/services/*.ts"],
157
+ [`${featureRoot}/**/services/*.ts`],
154
158
  LAYER_PATTERNS.services,
155
159
  LATERAL_PATTERNS.services,
156
160
  LIB_BOUNDARY_PATTERNS,
@@ -160,7 +164,7 @@ function generateImportConfigs() {
160
164
  configs.push(
161
165
  makeConfig(
162
166
  "actions",
163
- ["**/actions/*.ts"],
167
+ [`${featureRoot}/**/actions/*.ts`],
164
168
  LAYER_PATTERNS.actions,
165
169
  LATERAL_PATTERNS.actions,
166
170
  LIB_BOUNDARY_PATTERNS,
@@ -171,7 +175,7 @@ function generateImportConfigs() {
171
175
  configs.push(
172
176
  makeConfig(
173
177
  `actions/${prefix}`,
174
- [`**/actions/${prefix}.action.ts`],
178
+ [`${featureRoot}/**/actions/${prefix}.action.ts`],
175
179
  LAYER_PATTERNS.actions,
176
180
  LATERAL_PATTERNS.actions,
177
181
  CARDINALITY_PATTERNS[prefix],
@@ -182,5 +186,3 @@ function generateImportConfigs() {
182
186
 
183
187
  return configs.filter(Boolean);
184
188
  }
185
-
186
- export const importsConfigs = generateImportConfigs();
@@ -1,13 +1,16 @@
1
- import { importsConfigs } from "./imports.mjs";
2
- import { jsdocConfigs } from "./jsdoc.mjs";
3
- import { layersConfigs } from "./layers.mjs";
4
- import { namingConfigs } from "./naming.mjs";
1
+ import { createImportsConfigs } from "./imports.mjs";
2
+ import { createJsdocConfigs } from "./jsdoc.mjs";
3
+ import { createLayersConfigs } from "./layers.mjs";
4
+ import { createNamingConfigs } from "./naming.mjs";
5
5
  import { rulesConfigs } from "./rules.mjs";
6
6
 
7
- export const commonConfigs = [
8
- ...rulesConfigs,
9
- ...namingConfigs,
10
- ...layersConfigs,
11
- ...importsConfigs,
12
- ...jsdocConfigs,
13
- ];
7
+ /** @description Build common configs scoped to the given feature root */
8
+ export function createCommonConfigs(featureRoot) {
9
+ return [
10
+ ...rulesConfigs,
11
+ ...createNamingConfigs(featureRoot),
12
+ ...createLayersConfigs(featureRoot),
13
+ ...createImportsConfigs(featureRoot),
14
+ ...createJsdocConfigs(featureRoot),
15
+ ];
16
+ }
@@ -2,38 +2,41 @@ import jsdocPlugin from "eslint-plugin-jsdoc";
2
2
 
3
3
  import { featuresGlob } from "./constants.mjs";
4
4
 
5
- export const jsdocConfigs = [
6
- {
7
- name: "jsdoc",
8
- files: [
9
- ...featuresGlob("**/repositories/*.ts"),
10
- ...featuresGlob("**/services*/*.ts"),
11
- ...featuresGlob("**/utils*/*.ts"),
12
- ],
13
- plugins: {
14
- jsdoc: jsdocPlugin,
15
- },
16
- rules: {
17
- "jsdoc/require-jsdoc": [
18
- "warn",
19
- {
20
- publicOnly: true,
21
- require: {
22
- FunctionDeclaration: true,
23
- ArrowFunctionExpression: true,
24
- FunctionExpression: true,
25
- },
26
- checkGetters: false,
27
- checkSetters: false,
28
- checkConstructors: false,
29
- },
30
- ],
31
- "jsdoc/require-description": [
32
- "warn",
33
- {
34
- contexts: ["any"],
35
- },
5
+ /** @description Scope JSDoc rules to the given feature root */
6
+ export function createJsdocConfigs(featureRoot) {
7
+ return [
8
+ {
9
+ name: "jsdoc",
10
+ files: [
11
+ ...featuresGlob(featureRoot, "**/repositories/*.ts"),
12
+ ...featuresGlob(featureRoot, "**/services*/*.ts"),
13
+ ...featuresGlob(featureRoot, "**/utils*/*.ts"),
36
14
  ],
15
+ plugins: {
16
+ jsdoc: jsdocPlugin,
17
+ },
18
+ rules: {
19
+ "jsdoc/require-jsdoc": [
20
+ "warn",
21
+ {
22
+ publicOnly: true,
23
+ require: {
24
+ FunctionDeclaration: true,
25
+ ArrowFunctionExpression: true,
26
+ FunctionExpression: true,
27
+ },
28
+ checkGetters: false,
29
+ checkSetters: false,
30
+ checkConstructors: false,
31
+ },
32
+ ],
33
+ "jsdoc/require-description": [
34
+ "warn",
35
+ {
36
+ contexts: ["any"],
37
+ },
38
+ ],
39
+ },
37
40
  },
38
- },
39
- ];
41
+ ];
42
+ }
@@ -1,35 +1,38 @@
1
- export const layersConfigs = [
2
- {
3
- name: "layers/repositories",
4
- files: ["**/repositories/*.ts"],
5
- rules: {
6
- "no-restricted-syntax": [
7
- "error",
8
- {
9
- selector: "TryStatement",
10
- message:
11
- "try-catch is not allowed in repositories. Error handling belongs in actions.",
12
- },
13
- {
14
- selector: "IfStatement",
15
- message:
16
- "if statements are not allowed in repositories. Conditional logic belongs in services.",
17
- },
18
- ],
1
+ /** @description Scope layer rules to the given feature root */
2
+ export function createLayersConfigs(featureRoot) {
3
+ return [
4
+ {
5
+ name: "layers/repositories",
6
+ files: [`${featureRoot}/**/repositories/*.ts`],
7
+ rules: {
8
+ "no-restricted-syntax": [
9
+ "error",
10
+ {
11
+ selector: "TryStatement",
12
+ message:
13
+ "try-catch is not allowed in repositories. Error handling belongs in actions.",
14
+ },
15
+ {
16
+ selector: "IfStatement",
17
+ message:
18
+ "if statements are not allowed in repositories. Conditional logic belongs in services.",
19
+ },
20
+ ],
21
+ },
19
22
  },
20
- },
21
- {
22
- name: "layers/services",
23
- files: ["**/services/*.ts"],
24
- rules: {
25
- "no-restricted-syntax": [
26
- "error",
27
- {
28
- selector: "TryStatement",
29
- message:
30
- "try-catch is not allowed in services. Error handling belongs in actions.",
31
- },
32
- ],
23
+ {
24
+ name: "layers/services",
25
+ files: [`${featureRoot}/**/services/*.ts`],
26
+ rules: {
27
+ "no-restricted-syntax": [
28
+ "error",
29
+ {
30
+ selector: "TryStatement",
31
+ message:
32
+ "try-catch is not allowed in services. Error handling belongs in actions.",
33
+ },
34
+ ],
35
+ },
33
36
  },
34
- },
35
- ];
37
+ ];
38
+ }
@@ -4,147 +4,152 @@ import { actionHandleServiceRule } from "./local-plugins/action-handle-service.m
4
4
 
5
5
  const prefixPattern = `@(${Object.keys(PREFIX_LIB_MAPPING).join("|")})`;
6
6
 
7
- export const namingConfigs = [
8
- {
9
- name: "naming/services",
10
- files: featuresGlob("**/services/*.ts"),
11
- plugins: { "check-file": checkFile },
12
- rules: {
13
- "check-file/filename-naming-convention": [
14
- "error",
15
- { "**/*.ts": `${prefixPattern}.service` },
16
- ],
7
+ /** @description Scope naming rules to the given feature root */
8
+ export function createNamingConfigs(featureRoot) {
9
+ return [
10
+ {
11
+ name: "naming/services",
12
+ files: featuresGlob(featureRoot, "**/services/*.ts"),
13
+ plugins: { "check-file": checkFile },
14
+ rules: {
15
+ "check-file/filename-naming-convention": [
16
+ "error",
17
+ { "**/*.ts": `${prefixPattern}.service` },
18
+ ],
19
+ },
17
20
  },
18
- },
19
- {
20
- name: "naming/repositories",
21
- files: featuresGlob("**/repositories/*.ts"),
22
- plugins: { "check-file": checkFile },
23
- rules: {
24
- "check-file/filename-naming-convention": [
25
- "error",
26
- { "**/*.ts": `${prefixPattern}.repo` },
27
- ],
21
+ {
22
+ name: "naming/repositories",
23
+ files: featuresGlob(featureRoot, "**/repositories/*.ts"),
24
+ plugins: { "check-file": checkFile },
25
+ rules: {
26
+ "check-file/filename-naming-convention": [
27
+ "error",
28
+ { "**/*.ts": `${prefixPattern}.repo` },
29
+ ],
30
+ },
28
31
  },
29
- },
30
- {
31
- name: "naming/types",
32
- files: featuresGlob("*/types/*.type.ts"),
33
- ignores: featuresGlob("shared/types/*.ts"),
34
- plugins: { "check-file": checkFile },
35
- rules: {
36
- "check-file/filename-naming-convention": [
37
- "error",
38
- { "**/*/types/*.ts": "<1>" },
39
- { ignoreMiddleExtensions: true },
40
- ],
32
+ {
33
+ name: "naming/types",
34
+ files: featuresGlob(featureRoot, "*/types/*.type.ts"),
35
+ ignores: featuresGlob(featureRoot, "shared/types/*.ts"),
36
+ plugins: { "check-file": checkFile },
37
+ rules: {
38
+ "check-file/filename-naming-convention": [
39
+ "error",
40
+ { "**/*/types/*.ts": "<1>" },
41
+ { ignoreMiddleExtensions: true },
42
+ ],
43
+ },
41
44
  },
42
- },
43
- {
44
- name: "naming/types-shared",
45
- files: featuresGlob("shared/types/*.ts"),
46
- plugins: { "check-file": checkFile },
47
- rules: {
48
- "check-file/filename-naming-convention": [
49
- "error",
50
- { "**/*.ts": "+([a-z0-9_-]).type" },
51
- ],
45
+ {
46
+ name: "naming/types-shared",
47
+ files: featuresGlob(featureRoot, "shared/types/*.ts"),
48
+ plugins: { "check-file": checkFile },
49
+ rules: {
50
+ "check-file/filename-naming-convention": [
51
+ "error",
52
+ { "**/*.ts": "+([a-z0-9_-]).type" },
53
+ ],
54
+ },
52
55
  },
53
- },
54
- {
55
- name: "naming/schemas",
56
- files: featuresGlob("**/schemas/*.ts"),
57
- plugins: { "check-file": checkFile },
58
- rules: {
59
- "check-file/filename-naming-convention": [
60
- "error",
61
- { "**/*.ts": "+([a-z0-9-]).schema" },
62
- ],
56
+ {
57
+ name: "naming/schemas",
58
+ files: featuresGlob(featureRoot, "**/schemas/*.ts"),
59
+ plugins: { "check-file": checkFile },
60
+ rules: {
61
+ "check-file/filename-naming-convention": [
62
+ "error",
63
+ { "**/*.ts": "+([a-z0-9-]).schema" },
64
+ ],
65
+ },
63
66
  },
64
- },
65
- {
66
- name: "naming/utils",
67
- files: featuresGlob("*/utils/*.util.ts"),
68
- ignores: featuresGlob("shared/utils/*.ts"),
69
- plugins: { "check-file": checkFile },
70
- rules: {
71
- "check-file/filename-naming-convention": [
72
- "error",
73
- { "**/*/utils/*.ts": "<1>" },
74
- { ignoreMiddleExtensions: true },
75
- ],
67
+ {
68
+ name: "naming/utils",
69
+ files: featuresGlob(featureRoot, "*/utils/*.util.ts"),
70
+ ignores: featuresGlob(featureRoot, "shared/utils/*.ts"),
71
+ plugins: { "check-file": checkFile },
72
+ rules: {
73
+ "check-file/filename-naming-convention": [
74
+ "error",
75
+ { "**/*/utils/*.ts": "<1>" },
76
+ { ignoreMiddleExtensions: true },
77
+ ],
78
+ },
76
79
  },
77
- },
78
- {
79
- name: "naming/utils-shared",
80
- files: featuresGlob("shared/utils/*.ts"),
81
- plugins: { "check-file": checkFile },
82
- rules: {
83
- "check-file/filename-naming-convention": [
84
- "error",
85
- { "**/*.ts": "+([a-z0-9_-]).util" },
86
- ],
80
+ {
81
+ name: "naming/utils-shared",
82
+ files: featuresGlob(featureRoot, "shared/utils/*.ts"),
83
+ plugins: { "check-file": checkFile },
84
+ rules: {
85
+ "check-file/filename-naming-convention": [
86
+ "error",
87
+ { "**/*.ts": "+([a-z0-9_-]).util" },
88
+ ],
89
+ },
87
90
  },
88
- },
89
- {
90
- name: "naming/constants",
91
- files: featuresGlob("**/constants/*.ts"),
92
- plugins: { "check-file": checkFile },
93
- rules: {
94
- "check-file/filename-naming-convention": [
95
- "error",
96
- { "**/*.ts": "+([a-z0-9-]).constant" },
97
- ],
91
+ {
92
+ name: "naming/constants",
93
+ files: featuresGlob(featureRoot, "**/constants/*.ts"),
94
+ plugins: { "check-file": checkFile },
95
+ rules: {
96
+ "check-file/filename-naming-convention": [
97
+ "error",
98
+ { "**/*.ts": "+([a-z0-9-]).constant" },
99
+ ],
100
+ },
98
101
  },
99
- },
100
- {
101
- name: "naming/actions",
102
- files: featuresGlob("**/actions/*.ts"),
103
- plugins: { "check-file": checkFile },
104
- rules: {
105
- "check-file/filename-naming-convention": [
106
- "error",
107
- { "**/*.ts": `${prefixPattern}.action` },
108
- ],
102
+ {
103
+ name: "naming/actions",
104
+ files: featuresGlob(featureRoot, "**/actions/*.ts"),
105
+ plugins: { "check-file": checkFile },
106
+ rules: {
107
+ "check-file/filename-naming-convention": [
108
+ "error",
109
+ { "**/*.ts": `${prefixPattern}.action` },
110
+ ],
111
+ },
109
112
  },
110
- },
111
- {
112
- name: "naming/actions-export",
113
- files: featuresGlob("**/actions/*.ts"),
114
- rules: {
115
- "no-restricted-syntax": [
116
- "error",
117
- {
118
- selector:
119
- "ExportNamedDeclaration > FunctionDeclaration[id.name!=/^handle[A-Z]/]",
120
- message:
121
- "Exported functions in actions must start with 'handle' (e.g., handleGetComics).",
122
- },
123
- ],
124
- },
125
- },
126
- {
127
- name: "naming/actions-handle-service",
128
- files: featuresGlob("**/actions/*.ts"),
129
- plugins: {
130
- local: { rules: { "action-handle-service": actionHandleServiceRule } },
113
+ {
114
+ name: "naming/actions-export",
115
+ files: featuresGlob(featureRoot, "**/actions/*.ts"),
116
+ rules: {
117
+ "no-restricted-syntax": [
118
+ "error",
119
+ {
120
+ selector:
121
+ "ExportNamedDeclaration > FunctionDeclaration[id.name!=/^handle[A-Z]/]",
122
+ message:
123
+ "Exported functions in actions must start with 'handle' (e.g., handleGetComics).",
124
+ },
125
+ ],
126
+ },
131
127
  },
132
- rules: {
133
- "local/action-handle-service": "error",
134
- },
135
- },
136
- {
137
- name: "naming/features-ts-only",
138
- files: featuresGlob("**/*.tsx"),
139
- rules: {
140
- "no-restricted-syntax": [
141
- "error",
142
- {
143
- selector: "Program",
144
- message:
145
- "features/ must only contain .ts files. Components belong in src/components/.",
128
+ {
129
+ name: "naming/actions-handle-service",
130
+ files: featuresGlob(featureRoot, "**/actions/*.ts"),
131
+ plugins: {
132
+ local: {
133
+ rules: { "action-handle-service": actionHandleServiceRule },
146
134
  },
147
- ],
135
+ },
136
+ rules: {
137
+ "local/action-handle-service": "error",
138
+ },
139
+ },
140
+ {
141
+ name: "naming/features-ts-only",
142
+ files: featuresGlob(featureRoot, "**/*.tsx"),
143
+ rules: {
144
+ "no-restricted-syntax": [
145
+ "error",
146
+ {
147
+ selector: "Program",
148
+ message:
149
+ "features/ must only contain .ts files. Components belong in src/components/.",
150
+ },
151
+ ],
152
+ },
148
153
  },
149
- },
150
- ];
154
+ ];
155
+ }
@@ -0,0 +1,3 @@
1
+ import type { Linter } from "eslint";
2
+
3
+ export declare const eslintConfig: Linter.Config[];
@@ -0,0 +1,5 @@
1
+ import { createCommonConfigs } from "../common/index.mjs";
2
+
3
+ export const eslintConfig = [
4
+ ...createCommonConfigs("supabase/functions/features"),
5
+ ];
@@ -1,10 +1,12 @@
1
- import { commonConfigs } from "../common/index.mjs";
1
+ import { createCommonConfigs } from "../common/index.mjs";
2
+ import { libBoundaryConfigs } from "../common/imports.mjs";
2
3
 
3
4
  import { directivesConfigs } from "./directives.mjs";
4
5
  import { namingConfigs } from "./naming.mjs";
5
6
 
6
7
  export const eslintConfig = [
7
- ...commonConfigs,
8
+ ...createCommonConfigs("src/features"),
9
+ ...libBoundaryConfigs,
8
10
  ...namingConfigs,
9
11
  ...directivesConfigs,
10
12
  ];
@@ -1,10 +1,9 @@
1
- import { featuresGlob } from "../common/constants.mjs";
2
1
  import { checkFile } from "../common/plugins.mjs";
3
2
 
4
3
  export const namingConfigs = [
5
4
  {
6
5
  name: "naming/hooks",
7
- files: featuresGlob("**/hooks/*.ts"),
6
+ files: ["src/features/**/hooks/*.ts"],
8
7
  plugins: { "check-file": checkFile },
9
8
  rules: {
10
9
  "check-file/filename-naming-convention": [
@@ -16,7 +15,7 @@ export const namingConfigs = [
16
15
  },
17
16
  {
18
17
  name: "naming/hooks-export",
19
- files: featuresGlob("**/hooks/*.ts"),
18
+ files: ["src/features/**/hooks/*.ts"],
20
19
  rules: {
21
20
  "no-restricted-syntax": [
22
21
  "error",
@@ -1,3 +1,3 @@
1
- import { commonConfigs } from "../common/index.mjs";
1
+ import { createCommonConfigs } from "../common/index.mjs";
2
2
 
3
- export const eslintConfig = [...commonConfigs];
3
+ export const eslintConfig = [...createCommonConfigs("scripts/features")];