@adbayb/stack 2.40.0-next-4c238fc → 2.40.0-next-c8ed12d

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.
@@ -1,6 +1,7 @@
1
+ import type { OxfmtConfig } from "oxfmt";
1
2
  import { defineConfig } from "oxfmt";
2
3
 
3
- export default defineConfig({
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;
@@ -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 RELAXED_LIKE_FILES = [
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
- ...RELAXED_LIKE_FILES,
55
- `*.config(s)?.{${JAVASCRIPT_LIKE_EXTENSIONS_AS_STRING}}`,
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,7 +59,6 @@ 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
63
  "eslint",
66
64
  "import",
@@ -75,6 +73,78 @@ export default defineConfig({
75
73
  "unicorn",
76
74
  ],
77
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",
78
148
  "array-callback-return": ["error", { allowImplicit: true }],
79
149
  "arrow-body-style": ["error", "always"],
80
150
  "id-length": "off",
@@ -93,8 +163,15 @@ export default defineConfig({
93
163
  "max-lines": "off",
94
164
  "max-lines-per-function": "off",
95
165
  "max-statements": "off",
166
+ "nextjs/google-font-display": "error",
167
+ "nextjs/google-font-preconnect": "error",
168
+ "nextjs/no-sync-scripts": "error",
169
+ "nextjs/no-unwanted-polyfillio": "error",
96
170
  "no-async-await": "off",
171
+ "no-console": "off",
97
172
  "no-continue": "off",
173
+ "no-duplicate-imports": ["error", { allowSeparateTypeImports: true }],
174
+ "no-inline-comments": "off",
98
175
  "no-magic-numbers": "off",
99
176
  "no-optional-chaining": "off",
100
177
  "no-rest-spread-properties": "off",
@@ -105,6 +182,8 @@ export default defineConfig({
105
182
  "no-void": "off",
106
183
  "no-warning-comments": "off",
107
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
108
187
  "prefer-named-capture-group": "off",
109
188
  "prefer-readonly-parameter-types": "off",
110
189
  "react/jsx-max-depth": "off",
@@ -119,3 +198,14 @@ export default defineConfig({
119
198
  "unicorn/import-style": "off",
120
199
  },
121
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 function getRequest(url, responseType) {
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
- if (responseType === "text") return response.text();
103
- return response.json();
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) && !/^\d/u.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.`, {
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 = /\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/u.exec(version);
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(/\n|\r\n/u).filter((cleanOutput) => {
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-4c238fc";
444
+ var version = "2.40.0-next-c8ed12d";
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 } = (inputUrl.startsWith("git") ? /^git@.*:(?<repoOwner>.*)\/(?<repoName>.*)\.git$/u : /^https?:\/\/.*\/(?<repoOwner>.*)\/(?<repoName>.*)\.git$/u).exec(inputUrl)?.groups ?? {};
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");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adbayb/stack",
3
- "version": "2.40.0-next-4c238fc",
3
+ "version": "2.40.0-next-c8ed12d",
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",