@adbayb/stack 2.4.0 → 2.5.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/bin/index.js +2 -2
- package/configs/eslint/index.js +2 -0
- package/configs/eslint/presets/eslint.js +7 -3
- package/configs/eslint/presets/import.js +0 -1
- package/configs/eslint/presets/overridable.js +0 -5
- package/configs/eslint/presets/react.js +19 -3
- package/configs/eslint/presets/sonar.js +1 -4
- package/configs/eslint/presets/stylistic.js +8 -0
- package/configs/eslint/presets/typescript.js +9 -1
- package/configs/eslint/presets/unicorn.js +138 -0
- package/dist/index.js +65 -66
- package/package.json +9 -6
package/bin/index.js
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const package_ = createRequire(import.meta.url)("../package.json");
|
|
7
7
|
|
|
8
|
-
import(join("..",
|
|
8
|
+
import(join("..", package_.exports["."].default));
|
package/configs/eslint/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { config as unicornConfig } from "./presets/unicorn.js";
|
|
1
2
|
import { config as uncategorizedConfig } from "./presets/uncategorized.js";
|
|
2
3
|
import { config as typescriptConfig } from "./presets/typescript.js";
|
|
3
4
|
import { config as testConfig } from "./presets/test.js";
|
|
@@ -24,6 +25,7 @@ export default createConfig(
|
|
|
24
25
|
...sonarConfig,
|
|
25
26
|
...stylisticConfig,
|
|
26
27
|
...testConfig,
|
|
28
|
+
...unicornConfig,
|
|
27
29
|
...uncategorizedConfig,
|
|
28
30
|
...overridableConfig,
|
|
29
31
|
);
|
|
@@ -66,19 +66,23 @@ export const config = [
|
|
|
66
66
|
"no-regex-spaces": "error",
|
|
67
67
|
"no-restricted-syntax": [
|
|
68
68
|
"error",
|
|
69
|
-
|
|
69
|
+
/*
|
|
70
|
+
* https://medium.com/@hbarcelos/why-i-banned-null-from-my-js-code-and-why-you-should-too-13df90323cfa
|
|
71
|
+
* https://www.youtube.com/watch?v=PSGEjv3Tqo0&t=561s
|
|
72
|
+
*/
|
|
70
73
|
{
|
|
71
|
-
message: "Use undefined instead of null",
|
|
74
|
+
message: "Use `undefined` instead of `null`.",
|
|
72
75
|
selector: "VariableDeclarator > Literal[raw='null']",
|
|
73
76
|
},
|
|
74
77
|
{
|
|
75
|
-
message: "Use undefined instead of null",
|
|
78
|
+
message: "Use `undefined` instead of `null`.",
|
|
76
79
|
selector: "AssignmentExpression > Literal[raw='null']",
|
|
77
80
|
},
|
|
78
81
|
],
|
|
79
82
|
"no-self-assign": "error",
|
|
80
83
|
"no-shadow-restricted-names": "error",
|
|
81
84
|
"no-sparse-arrays": "error",
|
|
85
|
+
"no-template-curly-in-string": "error",
|
|
82
86
|
"no-unexpected-multiline": "error",
|
|
83
87
|
"no-unsafe-finally": "error",
|
|
84
88
|
"no-unsafe-optional-chaining": "error",
|
|
@@ -17,7 +17,6 @@ export const config = [
|
|
|
17
17
|
"import-x/no-absolute-path": "error",
|
|
18
18
|
"import-x/no-amd": "error",
|
|
19
19
|
"import-x/no-anonymous-default-export": "error",
|
|
20
|
-
"import-x/no-commonjs": "error",
|
|
21
20
|
"import-x/no-cycle": "error",
|
|
22
21
|
"import-x/no-default-export": "error",
|
|
23
22
|
"import-x/no-deprecated": "error",
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import tseslint from "typescript-eslint";
|
|
2
1
|
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
|
|
3
2
|
|
|
4
3
|
export const config = [
|
|
5
|
-
{
|
|
6
|
-
files: ["**/*.{js,jsx,cjs,mjs}"],
|
|
7
|
-
...tseslint.configs.disableTypeChecked,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
// Relaxed rules for example-like folder, and [config-, story-, and test]-like files
|
|
11
6
|
files: [
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import reactWebApiPlugin from "eslint-plugin-react-web-api";
|
|
2
|
+
import reactHooksExtraPlugin from "eslint-plugin-react-hooks-extra";
|
|
1
3
|
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
|
2
4
|
// eslint-disable-next-line depend/ban-dependencies
|
|
3
5
|
import reactPlugin from "eslint-plugin-react";
|
|
@@ -8,16 +10,29 @@ export const config = [
|
|
|
8
10
|
plugins: {
|
|
9
11
|
"react": reactPlugin,
|
|
10
12
|
"react-hooks": reactHooksPlugin,
|
|
13
|
+
"react-hooks-extra": reactHooksExtraPlugin,
|
|
14
|
+
"react-web-api": reactWebApiPlugin,
|
|
11
15
|
},
|
|
12
16
|
rules: {
|
|
17
|
+
"react-hooks-extra/no-direct-set-state-in-use-effect": "error",
|
|
18
|
+
"react-hooks-extra/no-direct-set-state-in-use-layout-effect": "error",
|
|
19
|
+
"react-hooks-extra/no-redundant-custom-hook": "error",
|
|
20
|
+
"react-hooks-extra/no-unnecessary-use-callback": "error",
|
|
21
|
+
"react-hooks-extra/no-unnecessary-use-memo": "error",
|
|
22
|
+
"react-hooks-extra/prefer-use-state-lazy-initialization": "error",
|
|
13
23
|
"react-hooks/exhaustive-deps": "warn",
|
|
14
24
|
"react-hooks/rules-of-hooks": "error",
|
|
25
|
+
"react-web-api/no-leaked-event-listener": "error",
|
|
26
|
+
"react-web-api/no-leaked-interval": "error",
|
|
27
|
+
"react-web-api/no-leaked-resize-observer": "error",
|
|
28
|
+
"react-web-api/no-leaked-timeout": "error",
|
|
15
29
|
"react/boolean-prop-naming": [
|
|
16
30
|
"error",
|
|
17
31
|
{ rule: "^(is|has)[A-Z]([A-Za-z0-9]?)+" },
|
|
18
32
|
],
|
|
19
33
|
"react/button-has-type": "error",
|
|
20
34
|
"react/checked-requires-onchange-or-readonly": "error",
|
|
35
|
+
"react/destructuring-assignment": ["error", "always"],
|
|
21
36
|
"react/display-name": "error",
|
|
22
37
|
"react/forbid-component-props": "error",
|
|
23
38
|
"react/forward-ref-uses-ref": "error",
|
|
@@ -28,14 +43,13 @@ export const config = [
|
|
|
28
43
|
"react/jsx-handler-names": "error",
|
|
29
44
|
"react/jsx-key": "error",
|
|
30
45
|
"react/jsx-no-bind": "error",
|
|
46
|
+
"react/jsx-no-comment-textnodes": "error",
|
|
31
47
|
"react/jsx-no-constructed-context-values": "error",
|
|
32
48
|
"react/jsx-no-leaked-render": "error",
|
|
33
49
|
"react/jsx-no-script-url": "error",
|
|
34
50
|
"react/jsx-no-target-blank": "error",
|
|
35
51
|
"react/jsx-no-useless-fragment": "error",
|
|
36
|
-
"react/jsx-pascal-case": "error",
|
|
37
52
|
"react/jsx-props-no-spread-multi": "error",
|
|
38
|
-
"react/jsx-sort-props": "error",
|
|
39
53
|
"react/jsx-uses-react": "error",
|
|
40
54
|
"react/jsx-uses-vars": "error",
|
|
41
55
|
"react/no-access-state-in-setstate": "error",
|
|
@@ -56,6 +70,7 @@ export const config = [
|
|
|
56
70
|
"react/no-this-in-sfc": "error",
|
|
57
71
|
"react/no-typos": "error",
|
|
58
72
|
"react/no-unescaped-entities": "error",
|
|
73
|
+
"react/no-unknown-property": "error",
|
|
59
74
|
"react/no-unsafe": "error",
|
|
60
75
|
"react/no-unstable-nested-components": "error",
|
|
61
76
|
"react/no-unused-class-component-methods": "error",
|
|
@@ -63,7 +78,8 @@ export const config = [
|
|
|
63
78
|
"react/no-will-update-set-state": "error",
|
|
64
79
|
"react/prefer-read-only-props": "error",
|
|
65
80
|
"react/prefer-stateless-function": "error",
|
|
66
|
-
"react/
|
|
81
|
+
"react/sort-comp": "error",
|
|
82
|
+
"react/state-in-constructor": ["error", "never"],
|
|
67
83
|
"react/style-prop-object": "error",
|
|
68
84
|
"react/void-dom-elements-no-children": "error",
|
|
69
85
|
},
|
|
@@ -151,7 +151,6 @@ export const config = [
|
|
|
151
151
|
"sonarjs/no-unenclosed-multiline-block": "error",
|
|
152
152
|
"sonarjs/no-uniq-key": "error",
|
|
153
153
|
"sonarjs/no-unsafe-unzip": "error",
|
|
154
|
-
"sonarjs/no-unstable-nested-components": "error",
|
|
155
154
|
"sonarjs/no-unthrown-error": "error",
|
|
156
155
|
"sonarjs/no-unused-collection": "error",
|
|
157
156
|
"sonarjs/no-useless-call": "error",
|
|
@@ -193,11 +192,9 @@ export const config = [
|
|
|
193
192
|
"sonarjs/sonar-no-empty-character-class": "error",
|
|
194
193
|
"sonarjs/sonar-no-fallthrough": "error",
|
|
195
194
|
"sonarjs/sonar-no-invalid-regexp": "error",
|
|
196
|
-
"sonarjs/sonar-no-magic-numbers": "error",
|
|
197
195
|
"sonarjs/sonar-no-misleading-character-class": "error",
|
|
198
196
|
"sonarjs/sonar-no-regex-spaces": "error",
|
|
199
|
-
"sonarjs/sonar-no-unused-class-component-methods": "error",
|
|
200
|
-
"sonarjs/sonar-prefer-regexp-exec": "error",
|
|
197
|
+
// "sonarjs/sonar-no-unused-class-component-methods": "error", // Disabled since it involves `eslint-plugin-react` activation
|
|
201
198
|
"sonarjs/sql-queries": "error",
|
|
202
199
|
"sonarjs/stable-tests": "error",
|
|
203
200
|
"sonarjs/stateful-regex": "error",
|
|
@@ -9,6 +9,9 @@ export const config = [
|
|
|
9
9
|
"@stylistic": stylistic,
|
|
10
10
|
},
|
|
11
11
|
rules: {
|
|
12
|
+
"@stylistic/jsx-pascal-case": "error",
|
|
13
|
+
"@stylistic/jsx-self-closing-comp": "error",
|
|
14
|
+
"@stylistic/jsx-sort-props": "error",
|
|
12
15
|
"@stylistic/lines-between-class-members": ["error", "always"],
|
|
13
16
|
"@stylistic/multiline-comment-style": ["error", "starred-block"],
|
|
14
17
|
"@stylistic/padding-line-between-statements": [
|
|
@@ -59,6 +62,11 @@ export const config = [
|
|
|
59
62
|
prev: ["cjs-export", "export"],
|
|
60
63
|
},
|
|
61
64
|
],
|
|
65
|
+
"@stylistic/quotes": [
|
|
66
|
+
"error",
|
|
67
|
+
"double",
|
|
68
|
+
{ allowTemplateLiterals: false, avoidEscape: true },
|
|
69
|
+
],
|
|
62
70
|
"@stylistic/spaced-comment": [
|
|
63
71
|
"error",
|
|
64
72
|
"always",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import tseslint from "typescript-eslint";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CWD,
|
|
5
|
+
JAVASCRIPT_EXTENSIONS,
|
|
6
|
+
JAVASCRIPT_LIKE_EXTENSIONS,
|
|
7
|
+
} from "../constants.js";
|
|
4
8
|
|
|
5
9
|
export const config = [
|
|
6
10
|
{
|
|
@@ -169,4 +173,8 @@ export const config = [
|
|
|
169
173
|
"@typescript-eslint/use-unknown-in-catch-callback-variable": "error",
|
|
170
174
|
},
|
|
171
175
|
},
|
|
176
|
+
{
|
|
177
|
+
files: JAVASCRIPT_EXTENSIONS,
|
|
178
|
+
...tseslint.configs.disableTypeChecked,
|
|
179
|
+
},
|
|
172
180
|
];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* eslint-disable unicorn/string-content */
|
|
2
|
+
import unicornPlugin from "eslint-plugin-unicorn";
|
|
3
|
+
|
|
4
|
+
import { JAVASCRIPT_LIKE_EXTENSIONS } from "../constants.js";
|
|
5
|
+
|
|
6
|
+
export const config = [
|
|
7
|
+
{
|
|
8
|
+
files: JAVASCRIPT_LIKE_EXTENSIONS,
|
|
9
|
+
plugins: {
|
|
10
|
+
unicorn: unicornPlugin,
|
|
11
|
+
},
|
|
12
|
+
rules: {
|
|
13
|
+
"unicorn/better-regex": "error",
|
|
14
|
+
"unicorn/catch-error-name": [
|
|
15
|
+
"error",
|
|
16
|
+
{
|
|
17
|
+
name: "error",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
"unicorn/consistent-empty-array-spread": "error",
|
|
21
|
+
"unicorn/consistent-existence-index-check": "error",
|
|
22
|
+
"unicorn/consistent-function-scoping": "error",
|
|
23
|
+
"unicorn/custom-error-definition": "error",
|
|
24
|
+
"unicorn/error-message": "error",
|
|
25
|
+
"unicorn/escape-case": "error",
|
|
26
|
+
"unicorn/expiring-todo-comments": "error",
|
|
27
|
+
"unicorn/explicit-length-check": "error",
|
|
28
|
+
"unicorn/filename-case": [
|
|
29
|
+
"error",
|
|
30
|
+
{
|
|
31
|
+
cases: {
|
|
32
|
+
camelCase: true,
|
|
33
|
+
pascalCase: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
"unicorn/new-for-builtins": "error",
|
|
38
|
+
"unicorn/no-abusive-eslint-disable": "error",
|
|
39
|
+
"unicorn/no-array-callback-reference": "error",
|
|
40
|
+
"unicorn/no-array-push-push": "error",
|
|
41
|
+
"unicorn/no-await-in-promise-methods": "error",
|
|
42
|
+
"unicorn/no-console-spaces": "error",
|
|
43
|
+
"unicorn/no-document-cookie": "error",
|
|
44
|
+
"unicorn/no-empty-file": "error",
|
|
45
|
+
"unicorn/no-for-loop": "error",
|
|
46
|
+
"unicorn/no-instanceof-array": "error",
|
|
47
|
+
"unicorn/no-invalid-fetch-options": "error",
|
|
48
|
+
"unicorn/no-invalid-remove-event-listener": "error",
|
|
49
|
+
"unicorn/no-length-as-slice-end": "error",
|
|
50
|
+
"unicorn/no-new-buffer": "error",
|
|
51
|
+
"unicorn/no-object-as-default-parameter": "error",
|
|
52
|
+
"unicorn/no-process-exit": "error",
|
|
53
|
+
"unicorn/no-single-promise-in-promise-methods": "error",
|
|
54
|
+
"unicorn/no-static-only-class": "error",
|
|
55
|
+
"unicorn/no-typeof-undefined": "error",
|
|
56
|
+
"unicorn/no-unnecessary-polyfills": "error",
|
|
57
|
+
"unicorn/no-unreadable-array-destructuring": "error",
|
|
58
|
+
"unicorn/no-unreadable-iife": "error",
|
|
59
|
+
"unicorn/no-unused-properties": "error",
|
|
60
|
+
"unicorn/no-useless-fallback-in-spread": "error",
|
|
61
|
+
"unicorn/no-useless-length-check": "error",
|
|
62
|
+
"unicorn/no-useless-promise-resolve-reject": "error",
|
|
63
|
+
"unicorn/no-useless-spread": "error",
|
|
64
|
+
"unicorn/no-zero-fractions": "error",
|
|
65
|
+
"unicorn/number-literal-case": "error",
|
|
66
|
+
"unicorn/prefer-add-event-listener": "error",
|
|
67
|
+
"unicorn/prefer-array-find": "error",
|
|
68
|
+
"unicorn/prefer-array-flat": "error",
|
|
69
|
+
"unicorn/prefer-array-flat-map": "error",
|
|
70
|
+
"unicorn/prefer-array-index-of": "error",
|
|
71
|
+
"unicorn/prefer-array-some": "error",
|
|
72
|
+
"unicorn/prefer-blob-reading-methods": "error",
|
|
73
|
+
"unicorn/prefer-code-point": "error",
|
|
74
|
+
"unicorn/prefer-date-now": "error",
|
|
75
|
+
"unicorn/prefer-dom-node-append": "error",
|
|
76
|
+
"unicorn/prefer-dom-node-remove": "error",
|
|
77
|
+
"unicorn/prefer-dom-node-text-content": "error",
|
|
78
|
+
"unicorn/prefer-event-target": "error",
|
|
79
|
+
"unicorn/prefer-export-from": "error",
|
|
80
|
+
"unicorn/prefer-global-this": "error",
|
|
81
|
+
"unicorn/prefer-includes": "error",
|
|
82
|
+
"unicorn/prefer-keyboard-event-key": "error",
|
|
83
|
+
"unicorn/prefer-logical-operator-over-ternary": "error",
|
|
84
|
+
"unicorn/prefer-math-min-max": "error",
|
|
85
|
+
"unicorn/prefer-math-trunc": "error",
|
|
86
|
+
"unicorn/prefer-modern-dom-apis": "error",
|
|
87
|
+
"unicorn/prefer-modern-math-apis": "error",
|
|
88
|
+
"unicorn/prefer-module": "error",
|
|
89
|
+
"unicorn/prefer-native-coercion-functions": "error",
|
|
90
|
+
"unicorn/prefer-negative-index": "error",
|
|
91
|
+
"unicorn/prefer-number-properties": "error",
|
|
92
|
+
"unicorn/prefer-object-from-entries": "error",
|
|
93
|
+
"unicorn/prefer-optional-catch-binding": "error",
|
|
94
|
+
"unicorn/prefer-prototype-methods": "error",
|
|
95
|
+
"unicorn/prefer-query-selector": "error",
|
|
96
|
+
"unicorn/prefer-reflect-apply": "error",
|
|
97
|
+
"unicorn/prefer-regexp-test": "error",
|
|
98
|
+
"unicorn/prefer-set-has": "error",
|
|
99
|
+
"unicorn/prefer-set-size": "error",
|
|
100
|
+
"unicorn/prefer-spread": "error",
|
|
101
|
+
"unicorn/prefer-string-raw": "error",
|
|
102
|
+
"unicorn/prefer-string-replace-all": "error",
|
|
103
|
+
"unicorn/prefer-string-slice": "error",
|
|
104
|
+
"unicorn/prefer-string-starts-ends-with": "error",
|
|
105
|
+
"unicorn/prefer-string-trim-start-end": "error",
|
|
106
|
+
"unicorn/prefer-structured-clone": "error",
|
|
107
|
+
"unicorn/prefer-switch": "error",
|
|
108
|
+
"unicorn/prefer-ternary": "error",
|
|
109
|
+
"unicorn/prefer-top-level-await": "error",
|
|
110
|
+
"unicorn/prefer-type-error": "error",
|
|
111
|
+
"unicorn/prevent-abbreviations": [
|
|
112
|
+
"error",
|
|
113
|
+
{
|
|
114
|
+
allowList: {
|
|
115
|
+
props: true,
|
|
116
|
+
Props: true,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
"unicorn/relative-url-style": ["error", "always"],
|
|
121
|
+
"unicorn/require-array-join-separator": "error",
|
|
122
|
+
"unicorn/require-number-to-fixed-digits-argument": "error",
|
|
123
|
+
"unicorn/string-content": [
|
|
124
|
+
"error",
|
|
125
|
+
{
|
|
126
|
+
patterns: {
|
|
127
|
+
"->": "→",
|
|
128
|
+
"\\.\\.\\.": "…",
|
|
129
|
+
"^http:\\/\\/": String.raw`^https:\/\/`,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
"unicorn/switch-case-braces": "error",
|
|
134
|
+
"unicorn/text-encoding-identifier-case": "error",
|
|
135
|
+
"unicorn/throw-new-error": "error",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
];
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { helpers, termost } from 'termost';
|
|
|
2
2
|
import process from 'node:process';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
|
-
import { writeFile, chmod, mkdir, symlink } from 'node:fs/promises';
|
|
5
|
+
import { writeFile, chmod, mkdir, symlink, rm } from 'node:fs/promises';
|
|
6
6
|
import { existsSync, cpSync, readFileSync, renameSync, writeFileSync, readdirSync } from 'node:fs';
|
|
7
7
|
import { fdir } from 'fdir';
|
|
8
8
|
|
|
@@ -96,7 +96,7 @@ const getStackCommand = (command, isNodeRuntime = true)=>{
|
|
|
96
96
|
const hasDependency = (packageName)=>{
|
|
97
97
|
return Boolean(require.resolve(packageName));
|
|
98
98
|
};
|
|
99
|
-
const
|
|
99
|
+
const setPackageManager = async ()=>{
|
|
100
100
|
return helpers.exec("corepack enable");
|
|
101
101
|
};
|
|
102
102
|
const request = {
|
|
@@ -117,31 +117,30 @@ const eslint = (options)=>async (files = [])=>{
|
|
|
117
117
|
eslintFiles.push(".");
|
|
118
118
|
} else {
|
|
119
119
|
eslintFiles = files.filter((file)=>{
|
|
120
|
-
return ESLINT_EXTENSIONS.some((
|
|
120
|
+
return ESLINT_EXTENSIONS.some((extension)=>file.endsWith(extension));
|
|
121
121
|
});
|
|
122
|
-
if (eslintFiles.length === 0) return
|
|
122
|
+
if (eslintFiles.length === 0) return;
|
|
123
123
|
}
|
|
124
|
-
const
|
|
124
|
+
const arguments_ = [
|
|
125
125
|
...eslintFiles
|
|
126
126
|
];
|
|
127
|
-
|
|
128
|
-
args.push(`--cache-location ${resolveFromProjectDirectory("node_modules/.cache/.eslintcache")}`);
|
|
129
|
-
// @note: prevent errors when no matched file is found
|
|
130
|
-
args.push("--no-error-on-unmatched-pattern");
|
|
127
|
+
arguments_.push("--cache", `--cache-location ${resolveFromProjectDirectory("node_modules/.cache/.eslintcache")}`, "--no-error-on-unmatched-pattern");
|
|
131
128
|
if (options.isFixMode) {
|
|
132
|
-
|
|
129
|
+
arguments_.push("--fix");
|
|
133
130
|
}
|
|
134
131
|
try {
|
|
135
|
-
return await helpers.exec(`eslint ${
|
|
132
|
+
return await helpers.exec(`eslint ${arguments_.join(" ")}`);
|
|
136
133
|
} catch (error) {
|
|
137
134
|
throw createError("eslint", error);
|
|
138
135
|
}
|
|
139
136
|
};
|
|
140
|
-
const turbo = async (command, options = {
|
|
141
|
-
hasLiveOutput: true
|
|
142
|
-
})=>{
|
|
137
|
+
const turbo = async (command, options = {})=>{
|
|
143
138
|
try {
|
|
144
|
-
|
|
139
|
+
const { hasLiveOutput = true, ...restOptions } = options;
|
|
140
|
+
return await helpers.exec(`turbo run ${command}`, {
|
|
141
|
+
...restOptions,
|
|
142
|
+
hasLiveOutput
|
|
143
|
+
});
|
|
145
144
|
} catch (error) {
|
|
146
145
|
throw createError("turbo", error);
|
|
147
146
|
}
|
|
@@ -168,9 +167,7 @@ const ESLINT_EXTENSIONS = [
|
|
|
168
167
|
"mdx"
|
|
169
168
|
];
|
|
170
169
|
|
|
171
|
-
var version = "2.
|
|
172
|
-
|
|
173
|
-
const VERSION = version;
|
|
170
|
+
var version = "2.5.0";
|
|
174
171
|
|
|
175
172
|
const createWatchCommand = (program)=>{
|
|
176
173
|
program.command({
|
|
@@ -286,19 +283,17 @@ const fixFormatting = async (files)=>{
|
|
|
286
283
|
return !PRETTIER_IGNORE_FILES.some((filename)=>file.endsWith(filename)) && // The root `README.md` file is ignored to prevent error due to its symbolic link nature when specified explicitly as a file
|
|
287
284
|
file !== "README.md";
|
|
288
285
|
});
|
|
289
|
-
if (prettierFiles.length === 0) return
|
|
286
|
+
if (prettierFiles.length === 0) return;
|
|
290
287
|
}
|
|
291
|
-
const
|
|
288
|
+
const arguments_ = [
|
|
292
289
|
...prettierFiles
|
|
293
290
|
];
|
|
294
291
|
if (existsSync(resolveFromProjectDirectory(".gitignore"))) {
|
|
295
|
-
|
|
292
|
+
arguments_.push("--ignore-path .gitignore");
|
|
296
293
|
}
|
|
297
|
-
|
|
298
|
-
args.push("--ignore-unknown");
|
|
299
|
-
args.push("--no-error-on-unmatched-pattern");
|
|
294
|
+
arguments_.push("--write", "--ignore-unknown", "--no-error-on-unmatched-pattern");
|
|
300
295
|
try {
|
|
301
|
-
return await helpers.exec(`prettier ${
|
|
296
|
+
return await helpers.exec(`prettier ${arguments_.join(" ")}`);
|
|
302
297
|
} catch (error) {
|
|
303
298
|
throw createError("prettier", error);
|
|
304
299
|
}
|
|
@@ -336,7 +331,7 @@ const createCreateCommand = (program)=>{
|
|
|
336
331
|
}).task({
|
|
337
332
|
handler () {
|
|
338
333
|
botMessage({
|
|
339
|
-
title: `I'm Stack v${
|
|
334
|
+
title: `I'm Stack v${version}, your bot assistant`,
|
|
340
335
|
description: "I can guarantee you a project creation in under 1 minute 🚀",
|
|
341
336
|
type: "information"
|
|
342
337
|
});
|
|
@@ -420,21 +415,21 @@ const createCreateCommand = (program)=>{
|
|
|
420
415
|
}).task({
|
|
421
416
|
label: label$2("Set up the package manager"),
|
|
422
417
|
async handler () {
|
|
423
|
-
await
|
|
418
|
+
await setPackageManager();
|
|
424
419
|
}
|
|
425
420
|
}).task({
|
|
426
421
|
label: label$2("Install dependencies"),
|
|
427
422
|
async handler ({ data }) {
|
|
428
|
-
const
|
|
423
|
+
const localDevelopmentDependencies = [
|
|
429
424
|
"quickbundle",
|
|
430
425
|
"vitest"
|
|
431
426
|
];
|
|
432
|
-
const
|
|
427
|
+
const globalDevelopmentDependencies = [
|
|
433
428
|
"@adbayb/stack"
|
|
434
429
|
];
|
|
435
430
|
try {
|
|
436
|
-
await helpers.exec(`pnpm add ${
|
|
437
|
-
await helpers.exec(`pnpm add ${
|
|
431
|
+
await helpers.exec(`pnpm add ${globalDevelopmentDependencies.join(" ")} --save-dev --ignore-workspace-root-check`);
|
|
432
|
+
await helpers.exec(`pnpm add ${localDevelopmentDependencies.join(" ")} --save-dev --filter ${data.projectName}`);
|
|
438
433
|
await helpers.exec("pnpm install");
|
|
439
434
|
} catch (error) {
|
|
440
435
|
throw createError("pnpm", error);
|
|
@@ -476,7 +471,7 @@ const label$2 = (message)=>`${message} 🔨`;
|
|
|
476
471
|
const projectRootPath = resolveFromProjectDirectory("./");
|
|
477
472
|
const templateExpressionRegExp = /{{(.*?)}}/g;
|
|
478
473
|
const evaluate = (content)=>{
|
|
479
|
-
return content.
|
|
474
|
+
return content.replaceAll(templateExpressionRegExp, (_, key)=>dataModel[key] || "");
|
|
480
475
|
};
|
|
481
476
|
/** Copy the template before mutations. */ cpSync(templateRootPath, projectRootPath, {
|
|
482
477
|
force: true,
|
|
@@ -484,15 +479,15 @@ const label$2 = (message)=>`${message} 🔨`;
|
|
|
484
479
|
});
|
|
485
480
|
/** Template file mutations. */ new fdir().withBasePath().glob(`**/*${templateExtension}`).crawl(projectRootPath).sync().forEach((templateFilePath)=>{
|
|
486
481
|
const projectFilePath = templateFilePath.slice(0, templateFilePath.lastIndexOf(templateExtension));
|
|
487
|
-
const content = evaluate(readFileSync(templateFilePath, "
|
|
482
|
+
const content = evaluate(readFileSync(templateFilePath, "utf8"));
|
|
488
483
|
renameSync(templateFilePath, projectFilePath);
|
|
489
|
-
writeFileSync(projectFilePath, content, "
|
|
484
|
+
writeFileSync(projectFilePath, content, "utf8");
|
|
490
485
|
});
|
|
491
486
|
/** Template folder mutations. */ new fdir().withBasePath().onlyDirs().filter((path)=>{
|
|
492
|
-
return
|
|
487
|
+
return templateExpressionRegExp.test(path);
|
|
493
488
|
}).crawl(projectRootPath).sync()// Re-order from longest to lowest path length to apply first renaming operations on deepest file structure
|
|
494
489
|
.sort((a, b)=>b.length - a.length).forEach((templateFolderPath)=>{
|
|
495
|
-
const newPath = templateFolderPath.
|
|
490
|
+
const newPath = templateFolderPath.replaceAll(templateExpressionRegExp, (_, dataModelKey)=>{
|
|
496
491
|
return dataModel[dataModelKey];
|
|
497
492
|
});
|
|
498
493
|
renameSync(templateFolderPath, newPath);
|
|
@@ -524,7 +519,8 @@ const createCleanCommand = (program)=>{
|
|
|
524
519
|
}
|
|
525
520
|
}).task({
|
|
526
521
|
handler ({ files }) {
|
|
527
|
-
helpers.message(
|
|
522
|
+
helpers.message(files.join("\n "), {
|
|
523
|
+
label: "Removed assets",
|
|
528
524
|
type: "information"
|
|
529
525
|
});
|
|
530
526
|
},
|
|
@@ -535,14 +531,17 @@ const createCleanCommand = (program)=>{
|
|
|
535
531
|
};
|
|
536
532
|
const label$1 = (message)=>`${message} 🧹`;
|
|
537
533
|
const cleanFiles = async (files)=>{
|
|
538
|
-
return
|
|
534
|
+
return Promise.all(files.map(async (file)=>rm(file, {
|
|
535
|
+
force: true,
|
|
536
|
+
recursive: true
|
|
537
|
+
})));
|
|
539
538
|
};
|
|
540
539
|
const isDirectoryExistAndNotEmpty = (path)=>{
|
|
541
540
|
return existsSync(path) && readdirSync(path).length > 0;
|
|
542
541
|
};
|
|
543
542
|
const retrieveIgnoredFiles = async ()=>{
|
|
544
|
-
const rawFiles = await helpers.exec(
|
|
545
|
-
return rawFiles.split(/\n/).filter(
|
|
543
|
+
const rawFiles = await helpers.exec("git clean -fdXn");
|
|
544
|
+
return rawFiles.split(/\n|\r\n/).filter((cleanOutput)=>!PRESERVE_FILES.some((excludedFile)=>cleanOutput.includes(excludedFile))).map((cleanOutput)=>cleanOutput.split(" ").at(-1));
|
|
546
545
|
};
|
|
547
546
|
const PRESERVE_FILES = [
|
|
548
547
|
"node_modules"
|
|
@@ -559,25 +558,25 @@ const checkTypes = async ()=>{
|
|
|
559
558
|
const checkPackages = async ()=>{
|
|
560
559
|
const stdout = await helpers.exec("pnpm recursive ls --json");
|
|
561
560
|
const checkPackagesVersionMismatch = createPackagesVersionMismatchChecker();
|
|
562
|
-
const packages = JSON.parse(stdout).map((
|
|
563
|
-
const
|
|
564
|
-
assert(
|
|
565
|
-
const
|
|
566
|
-
const peerDependencies =
|
|
567
|
-
const devDependencies =
|
|
568
|
-
const dependencies =
|
|
561
|
+
const packages = JSON.parse(stdout).map((package_)=>{
|
|
562
|
+
const packagePath = join(package_.path, "package.json");
|
|
563
|
+
assert(package_.name, ()=>createPackageError(`\`${packagePath}\` must have a name field.`));
|
|
564
|
+
const packageContent = require(packagePath);
|
|
565
|
+
const peerDependencies = packageContent.peerDependencies ?? {};
|
|
566
|
+
const devDependencies = packageContent.devDependencies ?? {};
|
|
567
|
+
const dependencies = packageContent.dependencies ?? {};
|
|
569
568
|
return {
|
|
570
|
-
name:
|
|
569
|
+
name: package_.name,
|
|
571
570
|
dependencies,
|
|
572
571
|
devDependencies,
|
|
573
572
|
peerDependencies
|
|
574
573
|
};
|
|
575
574
|
});
|
|
576
|
-
for (const
|
|
575
|
+
for (const package_ of packages){
|
|
577
576
|
// Check version mismatches to guarantee a single copy for a given package in the monorepo (use case: prevent singleton-like issues with React contexts)
|
|
578
|
-
checkPackagesVersionMismatch(
|
|
577
|
+
checkPackagesVersionMismatch(package_);
|
|
579
578
|
// Check version range accordingly to our dependency guidelines (ie. dev dependencies must be pinned and dependencies must have caret)
|
|
580
|
-
checkPackagesVersionRange(
|
|
579
|
+
checkPackagesVersionRange(package_);
|
|
581
580
|
}
|
|
582
581
|
};
|
|
583
582
|
const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDependencies })=>{
|
|
@@ -587,13 +586,11 @@ const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDe
|
|
|
587
586
|
name: dependencyName,
|
|
588
587
|
consumedBy: name
|
|
589
588
|
});
|
|
590
|
-
if (version !== "workspace:*" && !/^\d/.
|
|
589
|
+
if (version !== "workspace:*" && !/^\d/.test(version)) throw createPackageError(`As a dev dependency, \`${dependencyName}\` version must be fixed (or set as "workspace:*" for local packages) to reduce accidental breaking change risks due to an implicit semver upgrade.`, {
|
|
591
590
|
name: dependencyName,
|
|
592
591
|
consumedBy: name
|
|
593
592
|
});
|
|
594
593
|
}
|
|
595
|
-
const isPreReleaseVersion = (version)=>/\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/.exec(version);
|
|
596
|
-
const hasNoCaret = (version)=>!isPreReleaseVersion(version) && !/^\^/.exec(version);
|
|
597
594
|
for (const dependencyName of Object.keys(dependencies)){
|
|
598
595
|
const version = dependencies[dependencyName];
|
|
599
596
|
assertVersion(version, {
|
|
@@ -623,12 +620,12 @@ const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDe
|
|
|
623
620
|
};
|
|
624
621
|
const createPackagesVersionMismatchChecker = ()=>{
|
|
625
622
|
const monorepoDependencies = new Map();
|
|
626
|
-
const
|
|
627
|
-
const lint = (
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
const store =
|
|
631
|
-
const dependencies =
|
|
623
|
+
const monorepoDevelopmentDependencies = new Map();
|
|
624
|
+
const lint = (package_, type)=>{
|
|
625
|
+
const packageName = package_.name;
|
|
626
|
+
const isDevelopment = type === "development";
|
|
627
|
+
const store = isDevelopment ? monorepoDevelopmentDependencies : monorepoDependencies;
|
|
628
|
+
const dependencies = isDevelopment ? package_.devDependencies : package_.dependencies;
|
|
632
629
|
for (const dependencyName of Object.keys(dependencies)){
|
|
633
630
|
const depVersion = dependencies[dependencyName];
|
|
634
631
|
if (!depVersion) continue;
|
|
@@ -639,16 +636,16 @@ const createPackagesVersionMismatchChecker = ()=>{
|
|
|
639
636
|
}
|
|
640
637
|
const isSameMonorepoVersion = depVersion === storedVersion;
|
|
641
638
|
if (!isSameMonorepoVersion) {
|
|
642
|
-
throw createPackageError(`Mismatched versions: received version \`${depVersion}\` while others use \`${storedVersion}\`. To prevent issues with singleton-like code (React contexts,
|
|
639
|
+
throw createPackageError(`Mismatched versions: received version \`${depVersion}\` while others use \`${storedVersion}\`. To prevent issues with singleton-like code (React contexts, …), please make sure to update all packages to use the same \`${dependencyName}\` version (either \`${storedVersion}\` or \`${depVersion}\`.`, {
|
|
643
640
|
name: dependencyName,
|
|
644
|
-
consumedBy:
|
|
641
|
+
consumedBy: packageName
|
|
645
642
|
});
|
|
646
643
|
}
|
|
647
644
|
}
|
|
648
645
|
};
|
|
649
|
-
return (
|
|
650
|
-
lint(
|
|
651
|
-
lint(
|
|
646
|
+
return (package_)=>{
|
|
647
|
+
lint(package_, "development");
|
|
648
|
+
lint(package_, "production");
|
|
652
649
|
};
|
|
653
650
|
};
|
|
654
651
|
const createPackageError = (message, context)=>{
|
|
@@ -660,6 +657,8 @@ function assertVersion(version, { name, consumedBy }) {
|
|
|
660
657
|
consumedBy
|
|
661
658
|
}));
|
|
662
659
|
}
|
|
660
|
+
const isPreReleaseVersion = (version)=>/\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/.exec(version);
|
|
661
|
+
const hasNoCaret = (version)=>!isPreReleaseVersion(version) && !version.startsWith("^");
|
|
663
662
|
|
|
664
663
|
const checkLinter = eslint({
|
|
665
664
|
isFixMode: false
|
|
@@ -759,7 +758,7 @@ const createProgram = (...commandFactories)=>{
|
|
|
759
758
|
type: "error"
|
|
760
759
|
});
|
|
761
760
|
},
|
|
762
|
-
version:
|
|
761
|
+
version: version
|
|
763
762
|
});
|
|
764
763
|
for (const commandBuilder of commandFactories){
|
|
765
764
|
commandBuilder(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adbayb/stack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "My opinionated JavaScript-based toolchain",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"@changesets/cli": "^2.27.9",
|
|
51
51
|
"@commitlint/cli": "^19.5.0",
|
|
52
52
|
"@commitlint/config-conventional": "^19.5.0",
|
|
53
|
-
"@eslint/compat": "^1.2.
|
|
54
|
-
"@eslint/eslintrc": "^3.
|
|
53
|
+
"@eslint/compat": "^1.2.3",
|
|
54
|
+
"@eslint/eslintrc": "^3.2.0",
|
|
55
55
|
"@stylistic/eslint-plugin": "^2.10.1",
|
|
56
|
-
"@vitest/eslint-plugin": "^1.1.
|
|
56
|
+
"@vitest/eslint-plugin": "^1.1.10",
|
|
57
57
|
"eslint-config-prettier": "^9.1.0",
|
|
58
58
|
"eslint-import-resolver-typescript": "^3.6.3",
|
|
59
59
|
"eslint-plugin-depend": "^0.12.0",
|
|
@@ -61,18 +61,21 @@
|
|
|
61
61
|
"eslint-plugin-jest-formatting": "^3.1.0",
|
|
62
62
|
"eslint-plugin-jsdoc": "^50.5.0",
|
|
63
63
|
"eslint-plugin-mdx": "^3.1.5",
|
|
64
|
-
"eslint-plugin-n": "^17.13.
|
|
64
|
+
"eslint-plugin-n": "^17.13.2",
|
|
65
65
|
"eslint-plugin-prettier": "^5.2.1",
|
|
66
|
+
"eslint-plugin-react-hooks-extra": "^1.16.1",
|
|
66
67
|
"eslint-plugin-react-hooks": "^5.0.0",
|
|
68
|
+
"eslint-plugin-react-web-api": "^1.16.1",
|
|
67
69
|
"eslint-plugin-react": "^7.37.2",
|
|
68
70
|
"eslint-plugin-sonarjs": "^2.0.4",
|
|
69
71
|
"eslint-plugin-sort-keys-custom-order": "^2.2.0",
|
|
72
|
+
"eslint-plugin-unicorn": "^56.0.0",
|
|
70
73
|
"eslint": "^9.14.0",
|
|
71
74
|
"fdir": "^6.4.2",
|
|
72
75
|
"globals": "^15.12.0",
|
|
73
76
|
"prettier": "^3.3.3",
|
|
74
77
|
"termost": "^1.2.0",
|
|
75
|
-
"turbo": "^2.
|
|
78
|
+
"turbo": "^2.3.0",
|
|
76
79
|
"typescript-eslint": "^8.14.0",
|
|
77
80
|
"typescript": "^5.6.3"
|
|
78
81
|
},
|