@adbayb/stack 0.0.0-next-711fb0a → 0.0.0-next-fd6185e

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 CHANGED
@@ -3,6 +3,6 @@
3
3
  import { join } from "node:path";
4
4
  import { createRequire } from "node:module";
5
5
 
6
- const pkg = createRequire(import.meta.url)("../package.json");
6
+ const package_ = createRequire(import.meta.url)("../package.json");
7
7
 
8
- import(join("..", pkg.exports["."].default));
8
+ import(join("..", package_.exports["."].default));
@@ -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,13 +66,16 @@ export const config = [
66
66
  "no-regex-spaces": "error",
67
67
  "no-restricted-syntax": [
68
68
  "error",
69
- // https://medium.com/@hbarcelos/why-i-banned-null-from-my-js-code-and-why-you-should-too-13df90323cfa
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
  ],
@@ -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",
@@ -196,7 +196,6 @@ export const config = [
196
196
  "sonarjs/sonar-no-misleading-character-class": "error",
197
197
  "sonarjs/sonar-no-regex-spaces": "error",
198
198
  // "sonarjs/sonar-no-unused-class-component-methods": "error", // Disabled since it involves `eslint-plugin-react` activation
199
- "sonarjs/sonar-prefer-regexp-exec": "error",
200
199
  "sonarjs/sql-queries": "error",
201
200
  "sonarjs/stable-tests": "error",
202
201
  "sonarjs/stateful-regex": "error",
@@ -0,0 +1,116 @@
1
+ import unicornPlugin from "eslint-plugin-unicorn";
2
+
3
+ import { JAVASCRIPT_LIKE_EXTENSIONS } from "../constants.js";
4
+
5
+ export const config = [
6
+ {
7
+ files: JAVASCRIPT_LIKE_EXTENSIONS,
8
+ plugins: {
9
+ unicorn: unicornPlugin,
10
+ },
11
+ rules: {
12
+ "unicorn/better-regex": "error",
13
+ "unicorn/catch-error-name": [
14
+ "error",
15
+ {
16
+ name: "error",
17
+ },
18
+ ],
19
+ "unicorn/consistent-empty-array-spread": "error",
20
+ "unicorn/consistent-existence-index-check": "error",
21
+ "unicorn/consistent-function-scoping": "error",
22
+ "unicorn/custom-error-definition": "error",
23
+ "unicorn/error-message": "error",
24
+ "unicorn/escape-case": "error",
25
+ "unicorn/expiring-todo-comments": "error",
26
+ "unicorn/explicit-length-check": "error",
27
+ "unicorn/filename-case": [
28
+ "error",
29
+ {
30
+ cases: {
31
+ camelCase: true,
32
+ pascalCase: true,
33
+ },
34
+ },
35
+ ],
36
+ "unicorn/new-for-builtins": "error",
37
+ "unicorn/no-abusive-eslint-disable": "error",
38
+ "unicorn/no-array-callback-reference": "error",
39
+ "unicorn/no-array-push-push": "error",
40
+ "unicorn/no-await-in-promise-methods": "error",
41
+ "unicorn/no-console-spaces": "error",
42
+ "unicorn/no-document-cookie": "error",
43
+ "unicorn/no-empty-file": "error",
44
+ "unicorn/no-for-loop": "error",
45
+ "unicorn/no-instanceof-array": "error",
46
+ "unicorn/no-invalid-fetch-options": "error",
47
+ "unicorn/no-invalid-remove-event-listener": "error",
48
+ "unicorn/no-length-as-slice-end": "error",
49
+ "unicorn/no-new-buffer": "error",
50
+ "unicorn/no-object-as-default-parameter": "error",
51
+ "unicorn/no-process-exit": "error",
52
+ "unicorn/no-single-promise-in-promise-methods": "error",
53
+ "unicorn/no-static-only-class": "error",
54
+ "unicorn/no-typeof-undefined": "error",
55
+ "unicorn/no-unnecessary-polyfills": "error",
56
+ "unicorn/no-unreadable-array-destructuring": "error",
57
+ "unicorn/no-unreadable-iife": "error",
58
+ "unicorn/no-unused-properties": "error",
59
+ "unicorn/no-useless-fallback-in-spread": "error",
60
+ "unicorn/no-useless-length-check": "error",
61
+ "unicorn/no-useless-promise-resolve-reject": "error",
62
+ "unicorn/no-useless-spread": "error",
63
+ "unicorn/no-zero-fractions": "error",
64
+ "unicorn/number-literal-case": "error",
65
+ "unicorn/prefer-add-event-listener": "error",
66
+ "unicorn/prefer-array-find": "error",
67
+ "unicorn/prefer-array-flat": "error",
68
+ "unicorn/prefer-array-flat-map": "error",
69
+ "unicorn/prefer-array-index-of": "error",
70
+ "unicorn/prefer-array-some": "error",
71
+ "unicorn/prefer-blob-reading-methods": "error",
72
+ "unicorn/prefer-code-point": "error",
73
+ "unicorn/prefer-date-now": "error",
74
+ "unicorn/prefer-dom-node-append": "error",
75
+ "unicorn/prefer-dom-node-remove": "error",
76
+ "unicorn/prefer-dom-node-text-content": "error",
77
+ "unicorn/prefer-event-target": "error",
78
+ "unicorn/prefer-export-from": "error",
79
+ "unicorn/prefer-global-this": "error",
80
+ "unicorn/prefer-includes": "error",
81
+ "unicorn/prefer-keyboard-event-key": "error",
82
+ "unicorn/prefer-logical-operator-over-ternary": "error",
83
+ "unicorn/prefer-math-min-max": "error",
84
+ "unicorn/prefer-math-trunc": "error",
85
+ "unicorn/prefer-modern-dom-apis": "error",
86
+ "unicorn/prefer-modern-math-apis": "error",
87
+ "unicorn/prefer-module": "error",
88
+ "unicorn/prefer-native-coercion-functions": "error",
89
+ "unicorn/prefer-negative-index": "error",
90
+ "unicorn/prefer-number-properties": "error",
91
+ "unicorn/prefer-object-from-entries": "error",
92
+ "unicorn/prefer-optional-catch-binding": "error",
93
+ "unicorn/prefer-prototype-methods": "error",
94
+ "unicorn/prefer-query-selector": "error",
95
+ "unicorn/prefer-reflect-apply": "error",
96
+ "unicorn/prefer-regexp-test": "error",
97
+ "unicorn/prefer-set-has": "error",
98
+ "unicorn/prefer-set-size": "error",
99
+ "unicorn/prefer-spread": "error",
100
+ "unicorn/prefer-string-raw": "error",
101
+ "unicorn/prefer-string-replace-all": "error",
102
+ "unicorn/prefer-string-slice": "error",
103
+ "unicorn/prefer-string-starts-ends-with": "error",
104
+ "unicorn/prefer-string-trim-start-end": "error",
105
+ "unicorn/prefer-structured-clone": "error",
106
+ "unicorn/prefer-switch": "error",
107
+ "unicorn/prefer-ternary": "error",
108
+ "unicorn/prefer-top-level-await": "error",
109
+ "unicorn/prefer-type-error": "error",
110
+ "unicorn/prevent-abbreviations": "error",
111
+ "unicorn/relative-url-style": ["error", "always"],
112
+ "unicorn/require-array-join-separator": "error",
113
+ "unicorn/require-number-to-fixed-digits-argument": "error",
114
+ },
115
+ },
116
+ ];
package/dist/index.js CHANGED
@@ -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 setPkgManager = async ()=>{
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((ext)=>file.endsWith(ext));
120
+ return ESLINT_EXTENSIONS.some((extension)=>file.endsWith(extension));
121
121
  });
122
- if (eslintFiles.length === 0) return Promise.resolve();
122
+ if (eslintFiles.length === 0) return;
123
123
  }
124
- const args = [
124
+ const arguments_ = [
125
125
  ...eslintFiles
126
126
  ];
127
- args.push("--cache");
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
- args.push("--fix");
129
+ arguments_.push("--fix");
133
130
  }
134
131
  try {
135
- return await helpers.exec(`eslint ${args.join(" ")}`);
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
- return await helpers.exec(`turbo run ${command}`, options);
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 = "0.0.0-next-711fb0a";
172
-
173
- const VERSION = version;
170
+ var version = "0.0.0-next-fd6185e";
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 Promise.resolve();
286
+ if (prettierFiles.length === 0) return;
290
287
  }
291
- const args = [
288
+ const arguments_ = [
292
289
  ...prettierFiles
293
290
  ];
294
291
  if (existsSync(resolveFromProjectDirectory(".gitignore"))) {
295
- args.push("--ignore-path .gitignore");
292
+ arguments_.push("--ignore-path .gitignore");
296
293
  }
297
- args.push("--write");
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 ${args.join(" ")}`);
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${VERSION}, your bot assistant`,
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 setPkgManager();
418
+ await setPackageManager();
424
419
  }
425
420
  }).task({
426
421
  label: label$2("Install dependencies"),
427
422
  async handler ({ data }) {
428
- const localDevDependencies = [
423
+ const localDevelopmentDependencies = [
429
424
  "quickbundle",
430
425
  "vitest"
431
426
  ];
432
- const globalDevDependencies = [
427
+ const globalDevelopmentDependencies = [
433
428
  "@adbayb/stack"
434
429
  ];
435
430
  try {
436
- await helpers.exec(`pnpm add ${globalDevDependencies.join(" ")} --save-dev --ignore-workspace-root-check`);
437
- await helpers.exec(`pnpm add ${localDevDependencies.join(" ")} --save-dev --filter ${data.projectName}`);
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.replace(templateExpressionRegExp, (_, key)=>dataModel[key] || "");
474
+ return content.replaceAll(templateExpressionRegExp, (_, key)=>dataModel[key] || "");
480
475
  };
481
476
  /** Copy the template before mutations. */ cpSync(templateRootPath, projectRootPath, {
482
477
  force: true,
@@ -489,10 +484,10 @@ const label$2 = (message)=>`${message} 🔨`;
489
484
  writeFileSync(projectFilePath, content, "utf-8");
490
485
  });
491
486
  /** Template folder mutations. */ new fdir().withBasePath().onlyDirs().filter((path)=>{
492
- return Boolean(templateExpressionRegExp.exec(path));
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.replace(templateExpressionRegExp, (_, dataModelKey)=>{
490
+ const newPath = templateFolderPath.replaceAll(templateExpressionRegExp, (_, dataModelKey)=>{
496
491
  return dataModel[dataModelKey];
497
492
  });
498
493
  renameSync(templateFolderPath, newPath);
@@ -541,7 +536,8 @@ const isDirectoryExistAndNotEmpty = (path)=>{
541
536
  return existsSync(path) && readdirSync(path).length > 0;
542
537
  };
543
538
  const retrieveIgnoredFiles = async ()=>{
544
- const rawFiles = await helpers.exec(`git clean -fdXn | grep -v '${PRESERVE_FILES.join("\\|")}' | cut -c 14-`);
539
+ const excludedFiles = PRESERVE_FILES.join(String.raw`\|`);
540
+ const rawFiles = await helpers.exec(`git clean -fdXn | grep -v '${excludedFiles}' | cut -c 14-`);
545
541
  return rawFiles.split(/\n/).filter(Boolean);
546
542
  };
547
543
  const PRESERVE_FILES = [
@@ -559,25 +555,25 @@ const checkTypes = async ()=>{
559
555
  const checkPackages = async ()=>{
560
556
  const stdout = await helpers.exec("pnpm recursive ls --json");
561
557
  const checkPackagesVersionMismatch = createPackagesVersionMismatchChecker();
562
- const packages = JSON.parse(stdout).map((pkg)=>{
563
- const pkgPath = join(pkg.path, "package.json");
564
- assert(pkg.name, ()=>createPackageError(`\`${pkgPath}\` must have a name field.`));
565
- const pkgContent = require(pkgPath);
566
- const peerDependencies = pkgContent.peerDependencies ?? {};
567
- const devDependencies = pkgContent.devDependencies ?? {};
568
- const dependencies = pkgContent.dependencies ?? {};
558
+ const packages = JSON.parse(stdout).map((package_)=>{
559
+ const packagePath = join(package_.path, "package.json");
560
+ assert(package_.name, ()=>createPackageError(`\`${packagePath}\` must have a name field.`));
561
+ const packageContent = require(packagePath);
562
+ const peerDependencies = packageContent.peerDependencies ?? {};
563
+ const devDependencies = packageContent.devDependencies ?? {};
564
+ const dependencies = packageContent.dependencies ?? {};
569
565
  return {
570
- name: pkg.name,
566
+ name: package_.name,
571
567
  dependencies,
572
568
  devDependencies,
573
569
  peerDependencies
574
570
  };
575
571
  });
576
- for (const pkg of packages){
572
+ for (const package_ of packages){
577
573
  // 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(pkg);
574
+ checkPackagesVersionMismatch(package_);
579
575
  // Check version range accordingly to our dependency guidelines (ie. dev dependencies must be pinned and dependencies must have caret)
580
- checkPackagesVersionRange(pkg);
576
+ checkPackagesVersionRange(package_);
581
577
  }
582
578
  };
583
579
  const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDependencies })=>{
@@ -587,13 +583,11 @@ const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDe
587
583
  name: dependencyName,
588
584
  consumedBy: name
589
585
  });
590
- if (version !== "workspace:*" && !/^\d/.exec(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.`, {
586
+ 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
587
  name: dependencyName,
592
588
  consumedBy: name
593
589
  });
594
590
  }
595
- const isPreReleaseVersion = (version)=>/\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/.exec(version);
596
- const hasNoCaret = (version)=>!isPreReleaseVersion(version) && !/^\^/.exec(version);
597
591
  for (const dependencyName of Object.keys(dependencies)){
598
592
  const version = dependencies[dependencyName];
599
593
  assertVersion(version, {
@@ -623,12 +617,12 @@ const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDe
623
617
  };
624
618
  const createPackagesVersionMismatchChecker = ()=>{
625
619
  const monorepoDependencies = new Map();
626
- const monorepoDevDependencies = new Map();
627
- const lint = (pkg, type)=>{
628
- const pkgName = pkg.name;
629
- const isDev = type === "development";
630
- const store = isDev ? monorepoDevDependencies : monorepoDependencies;
631
- const dependencies = isDev ? pkg.devDependencies : pkg.dependencies;
620
+ const monorepoDevelopmentDependencies = new Map();
621
+ const lint = (package_, type)=>{
622
+ const packageName = package_.name;
623
+ const isDevelopment = type === "development";
624
+ const store = isDevelopment ? monorepoDevelopmentDependencies : monorepoDependencies;
625
+ const dependencies = isDevelopment ? package_.devDependencies : package_.dependencies;
632
626
  for (const dependencyName of Object.keys(dependencies)){
633
627
  const depVersion = dependencies[dependencyName];
634
628
  if (!depVersion) continue;
@@ -641,14 +635,14 @@ const createPackagesVersionMismatchChecker = ()=>{
641
635
  if (!isSameMonorepoVersion) {
642
636
  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
637
  name: dependencyName,
644
- consumedBy: pkgName
638
+ consumedBy: packageName
645
639
  });
646
640
  }
647
641
  }
648
642
  };
649
- return (pkg)=>{
650
- lint(pkg, "development");
651
- lint(pkg, "production");
643
+ return (package_)=>{
644
+ lint(package_, "development");
645
+ lint(package_, "production");
652
646
  };
653
647
  };
654
648
  const createPackageError = (message, context)=>{
@@ -660,6 +654,8 @@ function assertVersion(version, { name, consumedBy }) {
660
654
  consumedBy
661
655
  }));
662
656
  }
657
+ const isPreReleaseVersion = (version)=>/\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/.exec(version);
658
+ const hasNoCaret = (version)=>!isPreReleaseVersion(version) && !version.startsWith("^");
663
659
 
664
660
  const checkLinter = eslint({
665
661
  isFixMode: false
@@ -759,7 +755,7 @@ const createProgram = (...commandFactories)=>{
759
755
  type: "error"
760
756
  });
761
757
  },
762
- version: VERSION
758
+ version: version
763
759
  });
764
760
  for (const commandBuilder of commandFactories){
765
761
  commandBuilder(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adbayb/stack",
3
- "version": "0.0.0-next-711fb0a",
3
+ "version": "0.0.0-next-fd6185e",
4
4
  "description": "My opinionated JavaScript-based toolchain",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -67,6 +67,7 @@
67
67
  "eslint-plugin-react": "^7.37.2",
68
68
  "eslint-plugin-sonarjs": "^2.0.4",
69
69
  "eslint-plugin-sort-keys-custom-order": "^2.2.0",
70
+ "eslint-plugin-unicorn": "^56.0.0",
70
71
  "eslint": "^9.14.0",
71
72
  "fdir": "^6.4.2",
72
73
  "globals": "^15.12.0",