@adbayb/stack 2.26.0 → 2.28.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.
@@ -1,6 +1,4 @@
1
- import { cwd } from "node:process";
2
-
3
- export const CWD = cwd();
1
+ export const CWD = process.cwd();
4
2
 
5
3
  export const JAVASCRIPT_EXTENSIONS = ["**/*.{js,jsx,cjs,mjs}"];
6
4
 
@@ -7,6 +7,7 @@ import { config as sonarConfig } from "./presets/sonar.js";
7
7
  import { config as reactConfig } from "./presets/react.js";
8
8
  import { config as overridableConfig } from "./presets/overridable.js";
9
9
  import { config as nodeConfig } from "./presets/node.js";
10
+ import { config as markdownConfig } from "./presets/markdown.js";
10
11
  import { config as jsdocConfig } from "./presets/jsdoc.js";
11
12
  import { config as importConfig } from "./presets/import.js";
12
13
  import { config as eslintConfig } from "./presets/eslint.js";
@@ -20,6 +21,7 @@ export default createConfig(
20
21
  ...typescriptConfig,
21
22
  ...importConfig,
22
23
  ...jsdocConfig,
24
+ ...markdownConfig,
23
25
  ...nodeConfig,
24
26
  ...reactConfig,
25
27
  ...sonarConfig,
@@ -19,7 +19,6 @@ export const config = [
19
19
  "import-x/no-anonymous-default-export": "error",
20
20
  "import-x/no-cycle": "error",
21
21
  "import-x/no-default-export": "error",
22
- "import-x/no-deprecated": "error",
23
22
  "import-x/no-duplicates": "error",
24
23
  "import-x/no-empty-named-blocks": "error",
25
24
  "import-x/no-extraneous-dependencies": "error",
@@ -0,0 +1,3 @@
1
+ import { flat } from "eslint-plugin-mdx";
2
+
3
+ export const config = [flat];
@@ -14,6 +14,7 @@ export const config = [
14
14
  rules: {
15
15
  "n/callback-return": "error",
16
16
  "n/exports-style": ["error", "module.exports"],
17
+ "n/hashbang": "error",
17
18
  "n/no-exports-assign": "error",
18
19
  "n/no-path-concat": "error",
19
20
  "n/no-process-env": [
@@ -29,9 +30,9 @@ export const config = [
29
30
  "error",
30
31
  { allowExperimental: true },
31
32
  ],
32
- "n/prefer-global/buffer": ["error", "never"],
33
+ "n/prefer-global/buffer": ["error", "always"],
33
34
  "n/prefer-global/console": ["error", "always"],
34
- "n/prefer-global/process": ["error", "never"],
35
+ "n/prefer-global/process": ["error", "always"],
35
36
  "n/prefer-global/text-decoder": ["error", "always"],
36
37
  "n/prefer-global/text-encoder": ["error", "always"],
37
38
  "n/prefer-global/url": ["error", "always"],
@@ -22,6 +22,7 @@ export const config = [
22
22
  "react/dom/no-missing-iframe-sandbox": "error",
23
23
  "react/dom/no-namespace": "error",
24
24
  "react/dom/no-render": "error",
25
+ "react/dom/no-render-return-value": "error",
25
26
  "react/dom/no-script-url": "error",
26
27
  "react/dom/no-unsafe-iframe-sandbox": "error",
27
28
  "react/dom/no-unsafe-target-blank": "error",
@@ -60,6 +61,7 @@ export const config = [
60
61
  "react/no-unstable-context-value": "error",
61
62
  "react/no-unstable-default-props": "error",
62
63
  "react/no-use-context": "error",
64
+ "react/no-useless-forward-ref": "error",
63
65
  "react/no-useless-fragment": "error",
64
66
  "react/prefer-shorthand-boolean": "error",
65
67
  "react/prefer-shorthand-fragment": "error",
@@ -1,6 +1,5 @@
1
1
  import sortKeysCustomOrderPlugin from "eslint-plugin-sort-keys-custom-order";
2
2
  import dependPlugin from "eslint-plugin-depend";
3
- import { FlatCompat } from "@eslint/eslintrc";
4
3
 
5
4
  import { JAVASCRIPT_LIKE_EXTENSIONS } from "../constants.js";
6
5
 
@@ -41,11 +40,4 @@ export const config = [
41
40
  ],
42
41
  },
43
42
  },
44
- ...new FlatCompat().extends("plugin:mdx/recommended").map((mdxConfig) => ({
45
- ...mdxConfig,
46
- files: ["**/*.{md,mdx}"],
47
- settings: {
48
- "mdx/code-blocks": true,
49
- },
50
- })),
51
43
  ];
@@ -1,9 +1,9 @@
1
- /* eslint-disable unicorn/string-content */
2
1
  import unicornPlugin from "eslint-plugin-unicorn";
3
2
 
4
3
  import { JAVASCRIPT_LIKE_EXTENSIONS } from "../constants.js";
5
4
 
6
5
  export const config = [
6
+ // TODO: Add new rules https://github.com/sindresorhus/eslint-plugin-unicorn/releases/tag/v60.0.0
7
7
  {
8
8
  files: JAVASCRIPT_LIKE_EXTENSIONS,
9
9
  plugins: {
@@ -37,6 +37,7 @@ export const config = [
37
37
  "unicorn/new-for-builtins": "error",
38
38
  "unicorn/no-abusive-eslint-disable": "error",
39
39
  "unicorn/no-array-callback-reference": "error",
40
+ "unicorn/no-array-reverse": "error",
40
41
  "unicorn/no-await-in-promise-methods": "error",
41
42
  "unicorn/no-console-spaces": "error",
42
43
  "unicorn/no-document-cookie": "error",
@@ -58,6 +59,7 @@ export const config = [
58
59
  "unicorn/no-unreadable-array-destructuring": "error",
59
60
  "unicorn/no-unreadable-iife": "error",
60
61
  "unicorn/no-unused-properties": "error",
62
+ "unicorn/no-useless-error-capture-stack-trace": "error",
61
63
  "unicorn/no-useless-fallback-in-spread": "error",
62
64
  "unicorn/no-useless-length-check": "error",
63
65
  "unicorn/no-useless-promise-resolve-reject": "error",
@@ -71,6 +73,7 @@ export const config = [
71
73
  "unicorn/prefer-array-index-of": "error",
72
74
  "unicorn/prefer-array-some": "error",
73
75
  "unicorn/prefer-blob-reading-methods": "error",
76
+ "unicorn/prefer-class-fields": "error",
74
77
  "unicorn/prefer-code-point": "error",
75
78
  "unicorn/prefer-date-now": "error",
76
79
  "unicorn/prefer-dom-node-append": "error",
@@ -117,25 +120,12 @@ export const config = [
117
120
  "unicorn/prevent-abbreviations": [
118
121
  "error",
119
122
  {
120
- allowList: {
121
- props: true,
122
- Props: true,
123
- },
123
+ ignore: [/^props/i, /^ref/i],
124
124
  },
125
125
  ],
126
126
  "unicorn/relative-url-style": ["error", "always"],
127
127
  "unicorn/require-array-join-separator": "error",
128
128
  "unicorn/require-number-to-fixed-digits-argument": "error",
129
- "unicorn/string-content": [
130
- "error",
131
- {
132
- patterns: {
133
- "->": "→",
134
- "\\.\\.\\.": "…",
135
- "^http:\\/\\/": String.raw`^https:\/\/`,
136
- },
137
- },
138
- ],
139
129
  "unicorn/switch-case-braces": "error",
140
130
  "unicorn/text-encoding-identifier-case": "error",
141
131
  "unicorn/throw-new-error": "error",
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { helpers, termost } from 'termost';
2
- import process from 'node:process';
3
2
  import { resolve, join } from 'node:path';
4
3
  import { createRequire } from 'node:module';
5
4
  import { writeFile, chmod, mkdir, symlink, rm } from 'node:fs/promises';
@@ -196,7 +195,7 @@ const ESLINT_EXTENSIONS = [
196
195
  "mdx"
197
196
  ];
198
197
 
199
- var version = "2.26.0";
198
+ var version = "2.28.0";
200
199
 
201
200
  const createWatchCommand = (program)=>{
202
201
  program.command({
@@ -285,7 +284,7 @@ const createInstallCommand = (program)=>{
285
284
  }).task({
286
285
  label: label$4("Install `git.commit-msg` hook"),
287
286
  async handler () {
288
- await installGitHook("commit-msg", getStackCommand("check --only commit", false));
287
+ await installGitHook("commit-msg", getStackCommand("check --filter commit", false));
289
288
  }
290
289
  }).task({
291
290
  label: label$4("Check the package manager"),
@@ -589,7 +588,7 @@ const PRESERVE_FILES = [
589
588
  "node_modules"
590
589
  ];
591
590
 
592
- const checkTypes = async ()=>{
591
+ const checkType = async ()=>{
593
592
  try {
594
593
  return await helpers.exec("pnpm --parallel exec tsc --noEmit");
595
594
  } catch (error) {
@@ -597,9 +596,9 @@ const checkTypes = async ()=>{
597
596
  }
598
597
  };
599
598
 
600
- const checkPackages = async ()=>{
599
+ const checkDependency = async ()=>{
601
600
  const stdout = await helpers.exec("pnpm recursive ls --json");
602
- const checkPackagesVersionMismatch = createPackagesVersionMismatchChecker();
601
+ const checkDependencyVersionMismatch = createPackagesVersionMismatchChecker();
603
602
  const packages = JSON.parse(stdout).map((package_)=>{
604
603
  const packagePath = join(package_.path, "package.json");
605
604
  assert(package_.name, ()=>createPackageError(`\`${packagePath}\` must have a name field.`));
@@ -616,19 +615,19 @@ const checkPackages = async ()=>{
616
615
  });
617
616
  for (const package_ of packages){
618
617
  // Check version mismatches to guarantee a single copy for a given package in the monorepo (use case: prevent singleton-like issues with React contexts)
619
- checkPackagesVersionMismatch(package_);
618
+ checkDependencyVersionMismatch(package_);
620
619
  // Check version range accordingly to our dependency guidelines (ie. dev dependencies must be pinned and dependencies must have caret)
621
- checkPackagesVersionRange(package_);
620
+ checkDependencyVersionRange(package_);
622
621
  }
623
622
  };
624
- const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDependencies })=>{
623
+ const checkDependencyVersionRange = ({ name, dependencies, devDependencies, peerDependencies })=>{
625
624
  for (const dependencyName of Object.keys(devDependencies)){
626
625
  const version = devDependencies[dependencyName];
627
626
  assertVersion(version, {
628
627
  name: dependencyName,
629
628
  consumedBy: name
630
629
  });
631
- 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.`, {
630
+ if (version !== "workspace:*" && !isExcluded(version) && !/^\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.`, {
632
631
  name: dependencyName,
633
632
  consumedBy: name
634
633
  });
@@ -639,7 +638,7 @@ const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDe
639
638
  name: dependencyName,
640
639
  consumedBy: name
641
640
  });
642
- if (version !== "workspace:^" && hasNoCaret(version)) throw createPackageError(`As a dependency, \`${dependencyName}\` version must be prefixed with a caret (or set as "workspace:^" for local packages) to optimize the size (whether of installation or bundle output) on the consumer side.`, {
641
+ if (version !== "workspace:^" && !hasCaret(version) && !isExcluded(version)) throw createPackageError(`As a dependency, \`${dependencyName}\` version must be prefixed with a caret (or set as "workspace:^" for local packages) to optimize the size (whether of installation or bundle output) on the consumer side.`, {
643
642
  name: dependencyName,
644
643
  consumedBy: name
645
644
  });
@@ -650,7 +649,7 @@ const checkPackagesVersionRange = ({ name, dependencies, devDependencies, peerDe
650
649
  name: dependencyName,
651
650
  consumedBy: name
652
651
  });
653
- if (hasNoCaret(version)) /*
652
+ if (!hasCaret(version) && !isExcluded(version)) /*
654
653
  * Why disallowing workspace protocol as a version resolver?
655
654
  * To reduce the update frequency needs consumer-side and guarantee on our side the minimum compatible version,
656
655
  * the best practice should be to keeping an explicit number version which represents the lowest compatible version from an API perspective.
@@ -699,12 +698,12 @@ function assertVersion(version, { name, consumedBy }) {
699
698
  consumedBy
700
699
  }));
701
700
  }
702
- const isPreReleaseVersion = (version)=>/\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/.exec(version);
703
- const hasNoCaret = (version)=>!isPreReleaseVersion(version) && !version.startsWith("^");
704
-
705
- const checkLinter = eslint({
706
- isFixMode: false
707
- });
701
+ const isExcluded = (version)=>{
702
+ const isPreReleaseVersion = /\d+\.\d+\.\d+-(alpha|beta|experimental|next|rc).*/.exec(version);
703
+ const isNpmProtocol = version.startsWith("npm:");
704
+ return isNpmProtocol || isPreReleaseVersion;
705
+ };
706
+ const hasCaret = (version)=>version.startsWith("^");
708
707
 
709
708
  const checkCommit = async ()=>{
710
709
  try {
@@ -714,26 +713,30 @@ const checkCommit = async ()=>{
714
713
  }
715
714
  };
716
715
 
716
+ const checkCode = eslint({
717
+ isFixMode: false
718
+ });
719
+
717
720
  const ONLY_VALUES = [
718
721
  "commit",
719
- "linter",
720
- "packages",
721
- "types"
722
+ "code",
723
+ "dependency",
724
+ "type"
722
725
  ];
723
726
  const createCheckCommand = (program)=>{
724
727
  program.command({
725
728
  name: "check",
726
729
  description: "Check code health (static analysis)"
727
730
  }).option({
728
- key: "only",
729
- name: "only",
730
- description: `Run only one specified task (accepted value: ${ONLY_VALUES.join(", ")})`,
731
+ key: "filter",
732
+ name: "filter",
733
+ description: `Filter the compliance check to run (accepted value: ${ONLY_VALUES.join(", ")})`,
731
734
  defaultValue: undefined
732
735
  }).task({
733
736
  handler (_, argv) {
734
737
  logCheckableFiles(argv.operands);
735
738
  },
736
- skip: ifOnlyDefinedAndNotEqualTo("linter")
739
+ skip: ifFilterDefinedAndNotEqualTo("code")
737
740
  }).task({
738
741
  label: label("Prepare the project"),
739
742
  async handler () {
@@ -742,46 +745,46 @@ const createCheckCommand = (program)=>{
742
745
  hasLiveOutput: false
743
746
  });
744
747
  },
745
- skip ({ only }) {
746
- return only === "commit"; // No need to build if only commit is checked
748
+ skip ({ filter }) {
749
+ return filter === "commit"; // No need to build if only commit is checked
747
750
  }
748
751
  }).task({
749
- label: label("Check package guidelines"),
752
+ label: label("Check dependency compliance"),
750
753
  async handler () {
751
- await checkPackages();
754
+ await checkDependency();
752
755
  },
753
- skip: ifOnlyDefinedAndNotEqualTo("packages")
756
+ skip: ifFilterDefinedAndNotEqualTo("dependency")
754
757
  }).task({
755
- label: label("Check linter rules"),
758
+ label: label("Check code compliance"),
756
759
  async handler (_, argv) {
757
760
  const filenames = argv.operands;
758
- await checkLinter(filenames);
761
+ await checkCode(filenames);
759
762
  },
760
- skip: ifOnlyDefinedAndNotEqualTo("linter")
763
+ skip: ifFilterDefinedAndNotEqualTo("code")
761
764
  }).task({
762
- label: label("Check types"),
765
+ label: label("Check type compliance"),
763
766
  async handler () {
764
- await checkTypes();
767
+ await checkType();
765
768
  },
766
769
  skip (context, argv) {
767
- return ifOnlyDefinedAndNotEqualTo("types")(context) || !hasDependency("typescript") || /**
770
+ return ifFilterDefinedAndNotEqualTo("type")(context) || !hasDependency("typescript") || /**
768
771
  * For now, skip type-checking if some files are passed down.
769
772
  * @see https://github.com/microsoft/TypeScript/issues/27379
770
773
  */ argv.operands.length > 0;
771
774
  }
772
775
  }).task({
773
- label: label("Check commit"),
776
+ label: label("Check commit compliance"),
774
777
  async handler () {
775
778
  await checkCommit();
776
779
  },
777
- skip ({ only }) {
778
- return only !== "commit";
780
+ skip (context) {
781
+ return context.filter !== "commit";
779
782
  }
780
783
  });
781
784
  };
782
785
  const label = (message)=>`${message} 🧐`;
783
- const ifOnlyDefinedAndNotEqualTo = (only)=>(context)=>{
784
- return context.only !== undefined && context.only !== only;
786
+ const ifFilterDefinedAndNotEqualTo = (filter)=>(context)=>{
787
+ return context.filter !== undefined && context.filter !== filter;
785
788
  };
786
789
 
787
790
  const createBuildCommand = (program)=>{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adbayb/stack",
3
- "version": "2.26.0",
3
+ "version": "2.28.0",
4
4
  "description": "My opinionated JavaScript-based toolchain",
5
5
  "keywords": [
6
6
  "stack",
@@ -40,39 +40,38 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "@changesets/changelog-github": "^0.5.1",
43
- "@changesets/cli": "^2.29.4",
43
+ "@changesets/cli": "^2.29.5",
44
44
  "@commitlint/cli": "^19.8.1",
45
45
  "@commitlint/config-conventional": "^19.8.1",
46
- "@eslint-react/eslint-plugin": "^1.52.2",
47
- "@eslint/compat": "^1.3.0",
48
- "@eslint/eslintrc": "^3.3.1",
49
- "@stylistic/eslint-plugin": "^4.4.1",
50
- "@vitest/eslint-plugin": "^1.2.7",
51
- "eslint": "^9.29.0",
52
- "eslint-config-prettier": "^10.1.5",
53
- "eslint-import-resolver-typescript": "^4.4.3",
46
+ "@eslint-react/eslint-plugin": "^1.52.3",
47
+ "@eslint/compat": "^1.3.1",
48
+ "@stylistic/eslint-plugin": "^5.2.2",
49
+ "@vitest/eslint-plugin": "^1.3.3",
50
+ "eslint": "^9.32.0",
51
+ "eslint-config-prettier": "^10.1.8",
52
+ "eslint-import-resolver-typescript": "^4.4.4",
54
53
  "eslint-plugin-depend": "^1.2.0",
55
- "eslint-plugin-import-x": "^4.15.2",
54
+ "eslint-plugin-import-x": "^4.16.1",
56
55
  "eslint-plugin-jest-formatting": "^3.1.0",
57
- "eslint-plugin-jsdoc": "^51.0.3",
58
- "eslint-plugin-mdx": "^3.5.0",
59
- "eslint-plugin-n": "^17.20.0",
60
- "eslint-plugin-prettier": "^5.5.0",
56
+ "eslint-plugin-jsdoc": "^51.4.1",
57
+ "eslint-plugin-mdx": "^3.6.2",
58
+ "eslint-plugin-n": "^17.21.0",
59
+ "eslint-plugin-prettier": "^5.5.3",
61
60
  "eslint-plugin-react-hooks": "^5.2.0",
62
- "eslint-plugin-sonarjs": "^3.0.3",
61
+ "eslint-plugin-sonarjs": "^3.0.4",
63
62
  "eslint-plugin-sort-keys-custom-order": "^2.2.1",
64
- "eslint-plugin-unicorn": "^59.0.1",
63
+ "eslint-plugin-unicorn": "^60.0.0",
65
64
  "fdir": "^6.4.6",
66
- "globals": "^16.2.0",
67
- "prettier": "^3.5.3",
68
- "prettier-plugin-packagejson": "^2.5.15",
69
- "termost": "^1.4.0",
70
- "turbo": "^2.5.4",
65
+ "globals": "^16.3.0",
66
+ "prettier": "^3.6.2",
67
+ "prettier-plugin-packagejson": "^2.5.19",
68
+ "termost": "^1.8.0",
69
+ "turbo": "^2.5.5",
71
70
  "typescript": "^5.8.3",
72
- "typescript-eslint": "^8.34.1"
71
+ "typescript-eslint": "^8.38.0"
73
72
  },
74
73
  "devDependencies": {
75
- "@types/node": "22.15.32",
74
+ "@types/node": "22.16.5",
76
75
  "quickbundle": "2.13.0"
77
76
  },
78
77
  "publishConfig": {