@adbayb/stack 2.40.0-next-d2a93ae → 2.40.0-next-9f3b748
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 +4 -4
- package/configs/oxfmt/index.ts +11 -1
- package/configs/oxlint/index.ts +107 -22
- package/dist/index.js +13 -13
- package/package.json +4 -1
- package/templates/multi-projects/oxfmt.config.ts +1 -0
- package/templates/multi-projects/oxlint.config.ts +1 -0
- package/templates/single-project/oxfmt.config.ts +1 -0
- package/templates/single-project/oxlint.config.ts +1 -0
- package/templates/multi-projects/eslint.config.js.tmpl +0 -1
- package/templates/single-project/eslint.config.js.tmpl +0 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- Support for several application templates (for now, only a default template is available, but others can be added later)
|
|
14
14
|
- Standardized file structure including optional folder creation such as `examples`, `packages`, ...
|
|
15
15
|
- Enable all project lifecycle steps with commands from the setup, check, fix, build, and test to the versioning and release
|
|
16
|
-
- Built-in configuration preset setup (including Node/NPM runtime, Git ignore/hooks, [TypeScript](https://github.com/adbayb/stack/blob/main/stack/configs/typescript/), [
|
|
16
|
+
- Built-in configuration preset setup (including Node/NPM runtime, Git ignore/hooks, [TypeScript](https://github.com/adbayb/stack/blob/main/stack/configs/typescript/), [Oxlint](https://github.com/adbayb/stack/blob/main/stack/configs/oxlint/), [Oxfmt](https://github.com/adbayb/stack/blob/main/stack/configs/oxfmt/), EditorConfig, Renovate, Changesets, and VSCode)
|
|
17
17
|
- A welcoming environment for users and contributors with the continuous integration setup, and the creation of `README.md`, `CONTRIBUTING.md`, and GitHub template files (including issue and pull request ones), ...
|
|
18
18
|
- An optimized development environment with command caching (no build needed if nothing changes)
|
|
19
19
|
|
|
@@ -63,13 +63,13 @@ stack release
|
|
|
63
63
|
|
|
64
64
|
### Available configurations
|
|
65
65
|
|
|
66
|
-
- [@adbayb/stack/
|
|
67
|
-
- [@adbayb/stack/
|
|
66
|
+
- [@adbayb/stack/oxlint](https://github.com/adbayb/stack/blob/main/stack/configs/oxlint/) to align logical and stylistic rules across projects.
|
|
67
|
+
- [@adbayb/stack/oxfmt](https://github.com/adbayb/stack/blob/main/stack/configs/oxfmt/) to align code formatting conventions across projects.
|
|
68
68
|
- [@adbayb/stack/typescript](https://github.com/adbayb/stack/blob/main/stack/configs/typescript/) to align type checking and processing across projects.
|
|
69
69
|
|
|
70
70
|
### Used technologies
|
|
71
71
|
|
|
72
|
-
- **Static code analysis**:
|
|
72
|
+
- **Static code analysis**: Oxlint, Oxfmt
|
|
73
73
|
- **Node package manager**: PNPM
|
|
74
74
|
- **Task orchestration**: Turborepo
|
|
75
75
|
- **Release**: Changesets
|
package/configs/oxfmt/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { OxfmtConfig } from "oxfmt";
|
|
1
2
|
import { defineConfig } from "oxfmt";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
const config = defineConfig({
|
|
4
5
|
arrowParens: "always",
|
|
5
6
|
bracketSameLine: false,
|
|
6
7
|
bracketSpacing: true,
|
|
@@ -27,3 +28,12 @@ export default defineConfig({
|
|
|
27
28
|
useTabs: true,
|
|
28
29
|
vueIndentScriptAndStyle: false,
|
|
29
30
|
});
|
|
31
|
+
|
|
32
|
+
export const createConfig = (input: Required<Pick<OxfmtConfig, "overrides">>): OxfmtConfig => {
|
|
33
|
+
return {
|
|
34
|
+
...config,
|
|
35
|
+
...input,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default config;
|
package/configs/oxlint/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import e18ePlugin from "@e18e/eslint-plugin";
|
|
2
|
+
import { configs as perfectionistPlugin } from "eslint-plugin-perfectionist";
|
|
3
|
+
import type { OxlintConfig } from "oxlint";
|
|
1
4
|
import { defineConfig } from "oxlint";
|
|
2
5
|
|
|
3
6
|
const JAVASCRIPT_EXTENSIONS = ["js", "jsx", "cjs", "mjs", "mjsx"];
|
|
@@ -10,24 +13,7 @@ const TEST_LIKE_FILES = [
|
|
|
10
13
|
`**/*.{test,test-d}.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
11
14
|
];
|
|
12
15
|
|
|
13
|
-
const
|
|
14
|
-
...TEST_LIKE_FILES,
|
|
15
|
-
"**/.config/**",
|
|
16
|
-
"**/.configs/**",
|
|
17
|
-
"**/config/**",
|
|
18
|
-
"**/configs/**",
|
|
19
|
-
"**/examples/**",
|
|
20
|
-
"**/scripts/**",
|
|
21
|
-
"**/tools/**",
|
|
22
|
-
`**/config.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
23
|
-
`**/configs.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
24
|
-
`**/*.config.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
25
|
-
`**/*.configs.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
26
|
-
`**/stories.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
27
|
-
`**/*.stories.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
export default defineConfig({
|
|
16
|
+
const config = defineConfig({
|
|
31
17
|
categories: {
|
|
32
18
|
correctness: "error",
|
|
33
19
|
nursery: "error",
|
|
@@ -37,6 +23,7 @@ export default defineConfig({
|
|
|
37
23
|
style: "error",
|
|
38
24
|
suspicious: "error",
|
|
39
25
|
},
|
|
26
|
+
jsPlugins: ["@e18e/eslint-plugin", "@stylistic/eslint-plugin", "eslint-plugin-perfectionist"],
|
|
40
27
|
options: {
|
|
41
28
|
denyWarnings: true,
|
|
42
29
|
reportUnusedDisableDirectives: "error",
|
|
@@ -51,8 +38,20 @@ export default defineConfig({
|
|
|
51
38
|
},
|
|
52
39
|
{
|
|
53
40
|
files: [
|
|
54
|
-
...
|
|
55
|
-
|
|
41
|
+
...TEST_LIKE_FILES,
|
|
42
|
+
"**/.config/**",
|
|
43
|
+
"**/.configs/**",
|
|
44
|
+
"**/config/**",
|
|
45
|
+
"**/configs/**",
|
|
46
|
+
"**/examples/**",
|
|
47
|
+
"**/scripts/**",
|
|
48
|
+
"**/tools/**",
|
|
49
|
+
`**/config.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
50
|
+
`**/configs.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
51
|
+
`**/*.config.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
52
|
+
`**/*.configs.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
53
|
+
`**/stories.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
54
|
+
`**/*.stories.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
|
|
56
55
|
],
|
|
57
56
|
rules: {
|
|
58
57
|
"import/no-anonymous-default-export": "off",
|
|
@@ -60,9 +59,7 @@ export default defineConfig({
|
|
|
60
59
|
},
|
|
61
60
|
},
|
|
62
61
|
],
|
|
63
|
-
// TODO: add jsPlugins for perfectionist and padding-line-between-statements + add React rules (such as set-state-in-effect) from React X https://github.com/oxc-project/oxc/issues/1022 + potentially https://github.com/es-tooling/eslint-plugin-depend/blob/main/docs/rules/ban-dependencies.md
|
|
64
62
|
plugins: [
|
|
65
|
-
// TODO: check perfectionist sorting here
|
|
66
63
|
"eslint",
|
|
67
64
|
"import",
|
|
68
65
|
"jsdoc",
|
|
@@ -76,6 +73,78 @@ export default defineConfig({
|
|
|
76
73
|
"unicorn",
|
|
77
74
|
],
|
|
78
75
|
rules: {
|
|
76
|
+
...e18ePlugin.configs.recommended.rules,
|
|
77
|
+
...perfectionistPlugin["recommended-natural"].rules,
|
|
78
|
+
"@stylistic/jsx-pascal-case": "error",
|
|
79
|
+
"@stylistic/jsx-self-closing-comp": "error",
|
|
80
|
+
"@stylistic/lines-between-class-members": "error",
|
|
81
|
+
"@stylistic/multiline-comment-style": "error",
|
|
82
|
+
"@stylistic/padding-line-between-statements": [
|
|
83
|
+
"error",
|
|
84
|
+
// Default: separate everything.
|
|
85
|
+
{
|
|
86
|
+
blankLine: "always",
|
|
87
|
+
next: "*",
|
|
88
|
+
prev: "*",
|
|
89
|
+
},
|
|
90
|
+
// Keep single-line variable declarations together
|
|
91
|
+
{
|
|
92
|
+
blankLine: "never",
|
|
93
|
+
next: ["singleline-const", "singleline-let", "singleline-var"],
|
|
94
|
+
prev: ["singleline-const", "singleline-let", "singleline-var"],
|
|
95
|
+
},
|
|
96
|
+
// Keep single-line exports together
|
|
97
|
+
{
|
|
98
|
+
blankLine: "never",
|
|
99
|
+
next: ["singleline-export"],
|
|
100
|
+
prev: ["singleline-export"],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
blankLine: "never",
|
|
104
|
+
next: ["cjs-export"],
|
|
105
|
+
prev: ["cjs-export"],
|
|
106
|
+
},
|
|
107
|
+
// Keep statements together
|
|
108
|
+
{
|
|
109
|
+
blankLine: "never",
|
|
110
|
+
next: ["singleline-expression"],
|
|
111
|
+
prev: ["singleline-expression"],
|
|
112
|
+
},
|
|
113
|
+
// Keep imports together.
|
|
114
|
+
{
|
|
115
|
+
blankLine: "never",
|
|
116
|
+
next: ["import"],
|
|
117
|
+
prev: ["import"],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
blankLine: "never",
|
|
121
|
+
next: ["cjs-import"],
|
|
122
|
+
prev: ["cjs-import"],
|
|
123
|
+
},
|
|
124
|
+
// Keep directives together
|
|
125
|
+
{
|
|
126
|
+
blankLine: "never",
|
|
127
|
+
next: "directive",
|
|
128
|
+
prev: "directive",
|
|
129
|
+
},
|
|
130
|
+
// Keep switch labels together
|
|
131
|
+
{
|
|
132
|
+
blankLine: "never",
|
|
133
|
+
next: ["case", "default"],
|
|
134
|
+
prev: ["case", "default"],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
blankLine: "never",
|
|
138
|
+
next: "*",
|
|
139
|
+
prev: ["case", "default"],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
"@stylistic/quotes": [
|
|
143
|
+
"error",
|
|
144
|
+
"double",
|
|
145
|
+
{ allowTemplateLiterals: "avoidEscape", avoidEscape: true },
|
|
146
|
+
],
|
|
147
|
+
"@stylistic/spaced-comment": "error",
|
|
79
148
|
"array-callback-return": ["error", { allowImplicit: true }],
|
|
80
149
|
"arrow-body-style": ["error", "always"],
|
|
81
150
|
"id-length": "off",
|
|
@@ -99,7 +168,10 @@ export default defineConfig({
|
|
|
99
168
|
"nextjs/no-sync-scripts": "error",
|
|
100
169
|
"nextjs/no-unwanted-polyfillio": "error",
|
|
101
170
|
"no-async-await": "off",
|
|
171
|
+
"no-console": "off",
|
|
102
172
|
"no-continue": "off",
|
|
173
|
+
"no-duplicate-imports": ["error", { allowSeparateTypeImports: true }],
|
|
174
|
+
"no-inline-comments": "off",
|
|
103
175
|
"no-magic-numbers": "off",
|
|
104
176
|
"no-optional-chaining": "off",
|
|
105
177
|
"no-rest-spread-properties": "off",
|
|
@@ -110,6 +182,8 @@ export default defineConfig({
|
|
|
110
182
|
"no-void": "off",
|
|
111
183
|
"no-warning-comments": "off",
|
|
112
184
|
"node/no-sync": "off",
|
|
185
|
+
"perfectionist/sort-imports": "off", // Already supported by Oxfmt
|
|
186
|
+
"perfectionist/sort-named-imports": "off", // Already supported by eslint/sort-imports
|
|
113
187
|
"prefer-named-capture-group": "off",
|
|
114
188
|
"prefer-readonly-parameter-types": "off",
|
|
115
189
|
"react/jsx-max-depth": "off",
|
|
@@ -124,3 +198,14 @@ export default defineConfig({
|
|
|
124
198
|
"unicorn/import-style": "off",
|
|
125
199
|
},
|
|
126
200
|
});
|
|
201
|
+
|
|
202
|
+
export const createConfig = (
|
|
203
|
+
input: Required<Pick<OxlintConfig, "ignorePatterns">>,
|
|
204
|
+
): OxlintConfig => {
|
|
205
|
+
return {
|
|
206
|
+
...config,
|
|
207
|
+
...input,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export default config;
|
package/dist/index.js
CHANGED
|
@@ -93,16 +93,14 @@ const setPackageManager = async () => {
|
|
|
93
93
|
*/
|
|
94
94
|
await helpers.exec("pnx corepack enable");
|
|
95
95
|
};
|
|
96
|
-
async
|
|
96
|
+
const request = { async get(url, responseType) {
|
|
97
97
|
const response = await fetch(url);
|
|
98
98
|
if (!response.ok) throw createError("fetch", `Failed to fetch resources from ${url} (${JSON.stringify({
|
|
99
99
|
status: response.status,
|
|
100
100
|
statusText: response.statusText
|
|
101
101
|
})})`);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
const request = { get: getRequest };
|
|
102
|
+
return responseType === "text" ? response.text() : response.json();
|
|
103
|
+
} };
|
|
106
104
|
const oxlint = (options) => {
|
|
107
105
|
return async (files = []) => {
|
|
108
106
|
const args = [
|
|
@@ -110,7 +108,7 @@ const oxlint = (options) => {
|
|
|
110
108
|
"--disable-nested-config",
|
|
111
109
|
"--no-error-on-unmatched-pattern"
|
|
112
110
|
];
|
|
113
|
-
if (options.isFixMode) args.push("--fix");
|
|
111
|
+
if (options.isFixMode) args.push("--fix-dangerously");
|
|
114
112
|
try {
|
|
115
113
|
return await helpers.exec(`oxlint ${args.join(" ")}`);
|
|
116
114
|
} catch (error) {
|
|
@@ -221,13 +219,14 @@ const checkDependency = async () => {
|
|
|
221
219
|
checkDependencyVersionRange(pkg);
|
|
222
220
|
}
|
|
223
221
|
};
|
|
222
|
+
const STARTING_WITH_DIGIT_REGEXP = /^\d/u;
|
|
224
223
|
const checkDependencyVersionRange = ({ dependencies, devDependencies, name, peerDependencies }) => {
|
|
225
224
|
for (const [dependencyName, version] of Object.entries(devDependencies)) {
|
|
226
225
|
assertVersion(version, {
|
|
227
226
|
consumedBy: name,
|
|
228
227
|
name: dependencyName
|
|
229
228
|
});
|
|
230
|
-
if (version !== "workspace:*" && !isExcluded(version) &&
|
|
229
|
+
if (version !== "workspace:*" && !isExcluded(version) && !STARTING_WITH_DIGIT_REGEXP.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.`, {
|
|
231
230
|
consumedBy: name,
|
|
232
231
|
name: dependencyName
|
|
233
232
|
});
|
|
@@ -290,8 +289,9 @@ const assertVersion = (version, { consumedBy, name }) => {
|
|
|
290
289
|
});
|
|
291
290
|
});
|
|
292
291
|
};
|
|
292
|
+
const PRERELEASE_VERSION_REGEXP = /\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/u;
|
|
293
293
|
const isExcluded = (version) => {
|
|
294
|
-
const isPreReleaseVersion =
|
|
294
|
+
const isPreReleaseVersion = PRERELEASE_VERSION_REGEXP.exec(version);
|
|
295
295
|
return version.startsWith("npm:") || isPreReleaseVersion;
|
|
296
296
|
};
|
|
297
297
|
const hasCaret = (version) => {
|
|
@@ -427,8 +427,9 @@ const cleanFiles = async (files) => {
|
|
|
427
427
|
const isDirectoryExistAndNotEmpty = (path) => {
|
|
428
428
|
return existsSync(path) && readdirSync(path).length > 0;
|
|
429
429
|
};
|
|
430
|
+
const LINEBREAK_REGEXP = /\n|\r\n/u;
|
|
430
431
|
const retrieveIgnoredFiles = async () => {
|
|
431
|
-
return (await helpers.exec("git clean -fdXn")).split(
|
|
432
|
+
return (await helpers.exec("git clean -fdXn")).split(LINEBREAK_REGEXP).filter((cleanOutput) => {
|
|
432
433
|
return PRESERVE_FILES.every((excludedFile) => {
|
|
433
434
|
return !cleanOutput.includes(excludedFile);
|
|
434
435
|
});
|
|
@@ -440,10 +441,11 @@ const PRESERVE_FILES = ["node_modules"];
|
|
|
440
441
|
|
|
441
442
|
//#endregion
|
|
442
443
|
//#region package.json
|
|
443
|
-
var version = "2.40.0-next-
|
|
444
|
+
var version = "2.40.0-next-9f3b748";
|
|
444
445
|
|
|
445
446
|
//#endregion
|
|
446
447
|
//#region src/commands/create.ts
|
|
448
|
+
const REPOSITORY_REGEXP = /^(?:git@.*:|https?:\/\/.*\/)(?<repoOwner>[^/]+)\/(?<repoName>[^/]+)\.git$/u;
|
|
447
449
|
const createCreateCommand = (program) => {
|
|
448
450
|
program.command({
|
|
449
451
|
description: "Scaffold a new project",
|
|
@@ -481,7 +483,7 @@ const createCreateCommand = (program) => {
|
|
|
481
483
|
}).task({
|
|
482
484
|
async handler({ inputDescription, inputName, inputTemplate, inputUrl }) {
|
|
483
485
|
if (!inputName) throw createError("stack create", "The project name is not optional. Make sure to provide a valid value (non-empty string).");
|
|
484
|
-
const { repoName, repoOwner } =
|
|
486
|
+
const { repoName, repoOwner } = REPOSITORY_REGEXP.exec(inputUrl)?.groups ?? {};
|
|
485
487
|
if (!repoOwner || !repoName) throw createError("git", "The owner and repository name can not be extracted. Please make sure to follow either `/^git@.*:(?<repoOwner>.*)/(?<repoName>.*).git$/` or `/^https?://.*/(?<repoOwner>.*)/(?<repoName>.*).git$/` pattern.");
|
|
486
488
|
const nodeVersion = await request.get("https://resolve-node.vercel.app/lts", "text");
|
|
487
489
|
const { version: pnpmVersion } = await request.get("https://registry.npmjs.org/pnpm/latest", "json");
|
|
@@ -607,8 +609,6 @@ const createTemplateEngine = async (workingPath, { projectName, templateModel, t
|
|
|
607
609
|
});
|
|
608
610
|
const gitignoreFile = join(workingPath, ".gitignore.tmpl");
|
|
609
611
|
if (existsSync(gitignoreFile)) await rename(gitignoreFile, join(workingPath, ".gitignore"));
|
|
610
|
-
const eslintConfigFile = join(workingPath, "eslint.config.js.tmpl");
|
|
611
|
-
if (existsSync(eslintConfigFile)) await rename(eslintConfigFile, join(workingPath, "eslint.config.js"));
|
|
612
612
|
const templateEntries = await getTemplateEntries(workingPath);
|
|
613
613
|
process.chdir(workingPath);
|
|
614
614
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adbayb/stack",
|
|
3
|
-
"version": "2.40.0-next-
|
|
3
|
+
"version": "2.40.0-next-9f3b748",
|
|
4
4
|
"description": "My opinionated JavaScript-based toolchain",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"development",
|
|
@@ -47,6 +47,9 @@
|
|
|
47
47
|
"@changesets/cli": "^2.31.0",
|
|
48
48
|
"@commitlint/cli": "^21.2.1",
|
|
49
49
|
"@commitlint/config-conventional": "^21.2.0",
|
|
50
|
+
"@e18e/eslint-plugin": "^0.5.1",
|
|
51
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
52
|
+
"eslint-plugin-perfectionist": "^5.10.0",
|
|
50
53
|
"oxfmt": "^0.58.0",
|
|
51
54
|
"oxlint": "^1.73.0",
|
|
52
55
|
"oxlint-tsgolint": "^0.24.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@adbayb/stack/oxfmt";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@adbayb/stack/oxlint";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@adbayb/stack/oxfmt";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@adbayb/stack/oxlint";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "@adbayb/stack/eslint";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "@adbayb/stack/eslint";
|