@howells/lint 0.2.4 → 0.2.6

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.
@@ -4,131 +4,123 @@ import { join } from "node:path";
4
4
  const workspaceDirs = ["apps", "packages", "services", "workers", "examples"];
5
5
  const requiredNodeVersion = "22.18.0";
6
6
 
7
- function readRootPackageJson() {
8
- const packageJsonPath = join(process.cwd(), "package.json");
9
-
10
- if (!existsSync(packageJsonPath)) {
11
- return {
12
- errors: ["root package.json is missing"],
13
- packageJson: undefined,
14
- };
15
- }
16
-
17
- try {
18
- return {
19
- errors: [],
20
- packageJson: JSON.parse(readFileSync(packageJsonPath, "utf8")),
21
- };
22
- } catch (error) {
23
- return {
24
- errors: [`root package.json could not be parsed: ${error.message}`],
25
- packageJson: undefined,
26
- };
27
- }
28
- }
29
-
30
- function hasWorkspaceChildren(directory) {
31
- const directoryPath = join(process.cwd(), directory);
32
-
33
- if (!existsSync(directoryPath)) {
34
- return false;
35
- }
36
-
37
- return readdirSync(directoryPath, { withFileTypes: true }).some((entry) => {
38
- return (
39
- entry.isDirectory() &&
40
- existsSync(join(directoryPath, entry.name, "package.json"))
41
- );
42
- });
43
- }
44
-
45
- function hasLikelyWorkspaceLayout(packageJson) {
46
- return (
47
- Boolean(packageJson?.workspaces) || workspaceDirs.some(hasWorkspaceChildren)
48
- );
49
- }
50
-
51
- function isAtLeastRequiredNodeVersion(major, minor, patch = 0) {
52
- if (major > 22) {
53
- return true;
54
- }
55
-
56
- if (major < 22) {
57
- return false;
58
- }
59
-
60
- if (minor > 18) {
61
- return true;
62
- }
63
-
64
- if (minor < 18) {
65
- return false;
66
- }
67
-
68
- return patch >= 0;
69
- }
70
-
71
- function isNode2218Engine(range) {
72
- if (typeof range !== "string") {
73
- return false;
74
- }
75
-
76
- const normalizedRange = range.replaceAll(/\s+/g, "");
77
- const match = normalizedRange.match(
78
- /^(?:>=|\^|~)?(?<major>\d+)(?:\.(?<minor>\d+))?(?:\.(?<patch>\d+))?$/,
79
- );
80
-
81
- if (!match?.groups?.major || !match.groups.minor) {
82
- return false;
83
- }
84
-
85
- return isAtLeastRequiredNodeVersion(
86
- Number(match.groups.major),
87
- Number(match.groups.minor),
88
- Number(match.groups.patch ?? 0),
89
- );
90
- }
91
-
92
- function readNodeVersionFile() {
93
- const nodeVersionPath = join(process.cwd(), ".node-version");
94
-
95
- if (!existsSync(nodeVersionPath)) {
96
- return undefined;
97
- }
98
-
99
- return readFileSync(nodeVersionPath, "utf8").trim();
100
- }
101
-
102
- export function runWorkspacePreflight() {
103
- const { errors, packageJson } = readRootPackageJson();
104
-
105
- if (!packageJson) {
106
- return errors;
107
- }
108
-
109
- if (typeof packageJson.packageManager !== "string") {
110
- errors.push("root package.json must declare packageManager");
111
- } else if (!packageJson.packageManager.startsWith("pnpm@")) {
112
- errors.push("root package.json packageManager must use pnpm");
113
- }
114
-
115
- if (!isNode2218Engine(packageJson.engines?.node)) {
116
- errors.push(
117
- `root package.json engines.node must require Node ${requiredNodeVersion}+`,
118
- );
119
- }
120
-
121
- const nodeVersion = readNodeVersionFile();
122
- if (nodeVersion !== requiredNodeVersion) {
123
- errors.push(`root .node-version must be ${requiredNodeVersion}`);
124
- }
125
-
126
- if (
127
- hasLikelyWorkspaceLayout(packageJson) &&
128
- !existsSync(join(process.cwd(), "pnpm-workspace.yaml"))
129
- ) {
130
- errors.push("pnpm-workspace.yaml is required for workspace projects");
131
- }
132
-
133
- return errors;
134
- }
7
+ const readRootPackageJson = () => {
8
+ const packageJsonPath = join(process.cwd(), "package.json");
9
+
10
+ if (!existsSync(packageJsonPath)) {
11
+ return {
12
+ errors: ["root package.json is missing"],
13
+ packageJson: null,
14
+ };
15
+ }
16
+
17
+ try {
18
+ return {
19
+ errors: [],
20
+ packageJson: JSON.parse(readFileSync(packageJsonPath, "utf-8")),
21
+ };
22
+ } catch (error) {
23
+ return {
24
+ errors: [`root package.json could not be parsed: ${error.message}`],
25
+ packageJson: null,
26
+ };
27
+ }
28
+ };
29
+
30
+ const hasWorkspaceChildren = (directory) => {
31
+ const directoryPath = join(process.cwd(), directory);
32
+
33
+ if (!existsSync(directoryPath)) {
34
+ return false;
35
+ }
36
+
37
+ return readdirSync(directoryPath, { withFileTypes: true }).some(
38
+ (entry) => entry.isDirectory() && existsSync(join(directoryPath, entry.name, "package.json")),
39
+ );
40
+ };
41
+
42
+ const hasLikelyWorkspaceLayout = (packageJson) =>
43
+ Boolean(packageJson?.workspaces) || workspaceDirs.some(hasWorkspaceChildren);
44
+
45
+ const isAtLeastRequiredNodeVersion = (major, minor, patch = 0) => {
46
+ if (major > 22) {
47
+ return true;
48
+ }
49
+
50
+ if (major < 22) {
51
+ return false;
52
+ }
53
+
54
+ if (minor > 18) {
55
+ return true;
56
+ }
57
+
58
+ if (minor < 18) {
59
+ return false;
60
+ }
61
+
62
+ return patch >= 0;
63
+ };
64
+
65
+ const isNode2218Engine = (range) => {
66
+ if (typeof range !== "string") {
67
+ return false;
68
+ }
69
+
70
+ const normalizedRange = range.replaceAll(/\s+/gu, "");
71
+ const match = normalizedRange.match(
72
+ /^(?:>=|\^|~)?(?<major>\d+)(?:\.(?<minor>\d+))?(?:\.(?<patch>\d+))?$/u,
73
+ );
74
+
75
+ if (!match?.groups?.major || !match.groups.minor) {
76
+ return false;
77
+ }
78
+
79
+ return isAtLeastRequiredNodeVersion(
80
+ Number(match.groups.major),
81
+ Number(match.groups.minor),
82
+ Number(match.groups.patch ?? 0),
83
+ );
84
+ };
85
+
86
+ const readNodeVersionFile = () => {
87
+ const nodeVersionPath = join(process.cwd(), ".node-version");
88
+
89
+ if (!existsSync(nodeVersionPath)) {
90
+ return null;
91
+ }
92
+
93
+ return readFileSync(nodeVersionPath, "utf-8").trim();
94
+ };
95
+
96
+ export const runWorkspacePreflight = () => {
97
+ const { errors, packageJson } = readRootPackageJson();
98
+
99
+ if (!packageJson) {
100
+ return errors;
101
+ }
102
+
103
+ if (typeof packageJson.packageManager !== "string") {
104
+ errors.push("root package.json must declare packageManager");
105
+ } else if (!packageJson.packageManager.startsWith("pnpm@")) {
106
+ errors.push("root package.json packageManager must use pnpm");
107
+ }
108
+
109
+ if (!isNode2218Engine(packageJson.engines?.node)) {
110
+ errors.push(`root package.json engines.node must require Node ${requiredNodeVersion}+`);
111
+ }
112
+
113
+ const nodeVersion = readNodeVersionFile();
114
+ if (nodeVersion !== requiredNodeVersion) {
115
+ errors.push(`root .node-version must be ${requiredNodeVersion}`);
116
+ }
117
+
118
+ if (
119
+ hasLikelyWorkspaceLayout(packageJson) &&
120
+ !existsSync(join(process.cwd(), "pnpm-workspace.yaml"))
121
+ ) {
122
+ errors.push("pnpm-workspace.yaml is required for workspace projects");
123
+ }
124
+
125
+ return errors;
126
+ };
package/biome/core.json CHANGED
@@ -1,42 +1,42 @@
1
1
  {
2
- "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
3
- "extends": ["ultracite/biome/core"],
4
- "formatter": {
5
- "indentStyle": "space",
6
- "indentWidth": 2
7
- },
8
- "files": {
9
- "ignoreUnknown": true,
10
- "includes": [
11
- "**",
12
- "!**/node_modules/**",
13
- "!**/.next/**",
14
- "!**/.turbo/**",
15
- "!**/.vercel/**",
16
- "!**/dist/**",
17
- "!**/build/**",
18
- "!**/coverage/**",
19
- "!**/out/**",
20
- "!**/storybook-static/**",
21
- "!**/playwright-report/**",
22
- "!**/test-results/**",
23
- "!**/.source/**",
24
- "!**/.cache/**",
25
- "!**/.expo/**",
26
- "!**/.output/**",
27
- "!**/.wrangler/**",
28
- "!**/.svelte-kit/**",
29
- "!**/.nuxt/**",
30
- "!**/.vite/**",
31
- "!**/.vinxi/**",
32
- "!**/dev-dist/**",
33
- "!**/tmp/**",
34
- "!**/temp/**"
35
- ]
36
- },
37
- "vcs": {
38
- "enabled": true,
39
- "clientKind": "git",
40
- "useIgnoreFile": true
41
- }
2
+ "$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
3
+ "extends": ["ultracite/biome/core"],
4
+ "formatter": {
5
+ "indentStyle": "space",
6
+ "indentWidth": 2
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": true,
10
+ "includes": [
11
+ "**",
12
+ "!**/node_modules/**",
13
+ "!**/.next/**",
14
+ "!**/.turbo/**",
15
+ "!**/.vercel/**",
16
+ "!**/dist/**",
17
+ "!**/build/**",
18
+ "!**/coverage/**",
19
+ "!**/out/**",
20
+ "!**/storybook-static/**",
21
+ "!**/playwright-report/**",
22
+ "!**/test-results/**",
23
+ "!**/.source/**",
24
+ "!**/.cache/**",
25
+ "!**/.expo/**",
26
+ "!**/.output/**",
27
+ "!**/.wrangler/**",
28
+ "!**/.svelte-kit/**",
29
+ "!**/.nuxt/**",
30
+ "!**/.vite/**",
31
+ "!**/.vinxi/**",
32
+ "!**/dev-dist/**",
33
+ "!**/tmp/**",
34
+ "!**/temp/**"
35
+ ]
36
+ },
37
+ "vcs": {
38
+ "enabled": true,
39
+ "clientKind": "git",
40
+ "useIgnoreFile": true
41
+ }
42
42
  }
package/biome/next.json CHANGED
@@ -1,38 +1,38 @@
1
1
  {
2
- "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
3
- "extends": ["./react.json", "ultracite/biome/next"],
4
- "files": {
5
- "ignoreUnknown": true,
6
- "includes": [
7
- "**",
8
- "!**/node_modules/**",
9
- "!**/.next/**",
10
- "!**/.turbo/**",
11
- "!**/.vercel/**",
12
- "!**/dist/**",
13
- "!**/build/**",
14
- "!**/coverage/**",
15
- "!**/out/**",
16
- "!**/storybook-static/**",
17
- "!**/playwright-report/**",
18
- "!**/test-results/**",
19
- "!**/.source/**",
20
- "!**/.cache/**",
21
- "!**/.expo/**",
22
- "!**/.output/**",
23
- "!**/.wrangler/**",
24
- "!**/.svelte-kit/**",
25
- "!**/.nuxt/**",
26
- "!**/.vite/**",
27
- "!**/.vinxi/**",
28
- "!**/dev-dist/**",
29
- "!**/tmp/**",
30
- "!**/temp/**"
31
- ]
32
- },
33
- "css": {
34
- "parser": {
35
- "tailwindDirectives": true
36
- }
37
- }
2
+ "$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
3
+ "extends": ["./react.json", "ultracite/biome/next"],
4
+ "files": {
5
+ "ignoreUnknown": true,
6
+ "includes": [
7
+ "**",
8
+ "!**/node_modules/**",
9
+ "!**/.next/**",
10
+ "!**/.turbo/**",
11
+ "!**/.vercel/**",
12
+ "!**/dist/**",
13
+ "!**/build/**",
14
+ "!**/coverage/**",
15
+ "!**/out/**",
16
+ "!**/storybook-static/**",
17
+ "!**/playwright-report/**",
18
+ "!**/test-results/**",
19
+ "!**/.source/**",
20
+ "!**/.cache/**",
21
+ "!**/.expo/**",
22
+ "!**/.output/**",
23
+ "!**/.wrangler/**",
24
+ "!**/.svelte-kit/**",
25
+ "!**/.nuxt/**",
26
+ "!**/.vite/**",
27
+ "!**/.vinxi/**",
28
+ "!**/dev-dist/**",
29
+ "!**/tmp/**",
30
+ "!**/temp/**"
31
+ ]
32
+ },
33
+ "css": {
34
+ "parser": {
35
+ "tailwindDirectives": true
36
+ }
37
+ }
38
38
  }
package/biome/react.json CHANGED
@@ -1,38 +1,38 @@
1
1
  {
2
- "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
3
- "extends": ["./core.json", "ultracite/biome/react"],
4
- "files": {
5
- "ignoreUnknown": true,
6
- "includes": [
7
- "**",
8
- "!**/node_modules/**",
9
- "!**/.next/**",
10
- "!**/.turbo/**",
11
- "!**/.vercel/**",
12
- "!**/dist/**",
13
- "!**/build/**",
14
- "!**/coverage/**",
15
- "!**/out/**",
16
- "!**/storybook-static/**",
17
- "!**/playwright-report/**",
18
- "!**/test-results/**",
19
- "!**/.source/**",
20
- "!**/.cache/**",
21
- "!**/.expo/**",
22
- "!**/.output/**",
23
- "!**/.wrangler/**",
24
- "!**/.svelte-kit/**",
25
- "!**/.nuxt/**",
26
- "!**/.vite/**",
27
- "!**/.vinxi/**",
28
- "!**/dev-dist/**",
29
- "!**/tmp/**",
30
- "!**/temp/**"
31
- ]
32
- },
33
- "css": {
34
- "parser": {
35
- "tailwindDirectives": true
36
- }
37
- }
2
+ "$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
3
+ "extends": ["./core.json", "ultracite/biome/react"],
4
+ "files": {
5
+ "ignoreUnknown": true,
6
+ "includes": [
7
+ "**",
8
+ "!**/node_modules/**",
9
+ "!**/.next/**",
10
+ "!**/.turbo/**",
11
+ "!**/.vercel/**",
12
+ "!**/dist/**",
13
+ "!**/build/**",
14
+ "!**/coverage/**",
15
+ "!**/out/**",
16
+ "!**/storybook-static/**",
17
+ "!**/playwright-report/**",
18
+ "!**/test-results/**",
19
+ "!**/.source/**",
20
+ "!**/.cache/**",
21
+ "!**/.expo/**",
22
+ "!**/.output/**",
23
+ "!**/.wrangler/**",
24
+ "!**/.svelte-kit/**",
25
+ "!**/.nuxt/**",
26
+ "!**/.vite/**",
27
+ "!**/.vinxi/**",
28
+ "!**/dev-dist/**",
29
+ "!**/tmp/**",
30
+ "!**/temp/**"
31
+ ]
32
+ },
33
+ "css": {
34
+ "parser": {
35
+ "tailwindDirectives": true
36
+ }
37
+ }
38
38
  }
@@ -1,57 +1,55 @@
1
1
  import { defineConfig } from "oxlint";
2
2
 
3
- export const boundaryJsPlugins = [
4
- { name: "boundaries", specifier: "eslint-plugin-boundaries" },
5
- ];
3
+ export const boundaryJsPlugins = [{ name: "boundaries", specifier: "eslint-plugin-boundaries" }];
6
4
 
7
5
  export const boundarySettings = {
8
- "import/resolver": {
9
- node: {
10
- extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"],
11
- },
12
- typescript: {
13
- alwaysTryTypes: true,
14
- noWarnOnMultipleProjects: true,
15
- project: ["tsconfig.json", "apps/*/tsconfig.json", "packages/*/tsconfig.json"],
16
- },
17
- },
18
- "boundaries/elements": [
19
- { type: "app", pattern: "apps/*", capture: ["name"] },
20
- { type: "package", pattern: "packages/*", capture: ["name"] },
21
- ],
6
+ "boundaries/elements": [
7
+ { capture: ["name"], pattern: "apps/*", type: "app" },
8
+ { capture: ["name"], pattern: "packages/*", type: "package" },
9
+ ],
10
+ "import/resolver": {
11
+ node: {
12
+ extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"],
13
+ },
14
+ typescript: {
15
+ alwaysTryTypes: true,
16
+ noWarnOnMultipleProjects: true,
17
+ project: ["tsconfig.json", "apps/*/tsconfig.json", "packages/*/tsconfig.json"],
18
+ },
19
+ },
22
20
  };
23
21
 
24
22
  export const boundaryRules = {
25
- "boundaries/dependencies": [
26
- "error",
27
- {
28
- default: "allow",
29
- checkUnknownLocals: true,
30
- rules: [
31
- {
32
- from: { type: "package" },
33
- disallow: [{ to: { type: "app" } }],
34
- message: "Packages must not import from apps.",
35
- },
36
- {
37
- from: { type: "app" },
38
- disallow: [
39
- {
40
- to: {
41
- type: "app",
42
- captured: { name: "!{{ from.captured.name }}" },
43
- },
44
- },
45
- ],
46
- message: "Apps must not import from other apps.",
47
- },
48
- ],
49
- },
50
- ],
23
+ "boundaries/dependencies": [
24
+ "error",
25
+ {
26
+ checkUnknownLocals: true,
27
+ default: "allow",
28
+ rules: [
29
+ {
30
+ disallow: [{ to: { type: "app" } }],
31
+ from: { type: "package" },
32
+ message: "Packages must not import from apps.",
33
+ },
34
+ {
35
+ disallow: [
36
+ {
37
+ to: {
38
+ captured: { name: "!{{ from.captured.name }}" },
39
+ type: "app",
40
+ },
41
+ },
42
+ ],
43
+ from: { type: "app" },
44
+ message: "Apps must not import from other apps.",
45
+ },
46
+ ],
47
+ },
48
+ ],
51
49
  };
52
50
 
53
51
  export default defineConfig({
54
- jsPlugins: boundaryJsPlugins,
55
- settings: boundarySettings,
56
- rules: boundaryRules,
52
+ jsPlugins: boundaryJsPlugins,
53
+ rules: boundaryRules,
54
+ settings: boundarySettings,
57
55
  });