@ciridae/configs 0.2.0 → 0.4.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.
- package/CHANGELOG.md +12 -0
- package/README.md +17 -0
- package/eslint.js +44 -49
- package/package.json +25 -6
- package/vite.d.ts +8 -0
- package/vite.js +63 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ciridae/configs
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `@ciridae/configs/vite` with `versionManifest`, which writes `version.json` for the update prompt, and `sentrySourceMaps`, which uploads production source maps to Sentry when its credentials are set.
|
|
8
|
+
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- Keep one ESLint 10 preset with regexp correctness checks and remove copied code-shape, compiler-advisory, and Tailwind lint policy.
|
|
14
|
+
|
|
3
15
|
## 0.2.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -23,3 +23,20 @@ export { default } from "@ciridae/configs/prettier" with { type: "json" }
|
|
|
23
23
|
"extends": "@ciridae/configs/tsconfig/frontend-app.json"
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
|
+
|
|
27
|
+
## Vite
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { sentrySourceMaps, versionManifest } from "@ciridae/configs/vite"
|
|
31
|
+
|
|
32
|
+
export default defineConfig(({ mode }) => ({
|
|
33
|
+
plugins: [
|
|
34
|
+
versionManifest({ buildSha: process.env.VITE_BUILD_SHA ?? "development" }),
|
|
35
|
+
sentrySourceMaps({ mode }),
|
|
36
|
+
],
|
|
37
|
+
}))
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`versionManifest` writes `version.json` beside the bundle so an open tab can
|
|
41
|
+
tell a newer build is deployed. `sentrySourceMaps` uploads production source
|
|
42
|
+
maps when `SENTRY_ORG`, `SENTRY_PROJECT`, and `SENTRY_AUTH_TOKEN` are set.
|
package/eslint.js
CHANGED
|
@@ -1,26 +1,49 @@
|
|
|
1
|
-
import { fixupPluginRules } from "@eslint/compat";
|
|
2
1
|
import eslintReact from "@eslint-react/eslint-plugin";
|
|
3
2
|
import js from "@eslint/js";
|
|
4
3
|
import { createNodeResolver, importX } from "eslint-plugin-import-x";
|
|
5
4
|
import globals from "globals";
|
|
6
5
|
import reactHooks from "eslint-plugin-react-hooks";
|
|
7
6
|
import reactRefresh from "eslint-plugin-react-refresh";
|
|
8
|
-
import
|
|
7
|
+
import regexpPlugin from "eslint-plugin-regexp";
|
|
9
8
|
import tseslint from "typescript-eslint";
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
const reactCompilerAdvisoryRules = {
|
|
11
|
+
"react-hooks/config": "off",
|
|
12
|
+
"react-hooks/gating": "off",
|
|
13
|
+
"react-hooks/globals": "off",
|
|
14
|
+
"react-hooks/immutability": "off",
|
|
15
|
+
"react-hooks/incompatible-library": "off",
|
|
16
|
+
"react-hooks/preserve-manual-memoization": "off",
|
|
17
|
+
"react-hooks/purity": "off",
|
|
18
|
+
"react-hooks/refs": "off",
|
|
19
|
+
"react-hooks/set-state-in-effect": "off",
|
|
20
|
+
"react-hooks/static-components": "off",
|
|
21
|
+
"react-hooks/unsupported-syntax": "off",
|
|
22
|
+
"react-hooks/use-memo": "off",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const recommendedRegexpRules = Object.fromEntries(
|
|
26
|
+
Object.entries(regexpPlugin.configs["flat/recommended"].rules)
|
|
27
|
+
.filter(
|
|
28
|
+
([ruleName, setting]) =>
|
|
29
|
+
ruleName.startsWith("regexp/") && setting !== "off" && setting !== 0,
|
|
30
|
+
)
|
|
31
|
+
.map(([ruleName, setting]) => [
|
|
32
|
+
ruleName,
|
|
33
|
+
Array.isArray(setting) ? ["error", ...setting.slice(1)] : "error",
|
|
34
|
+
]),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const regexpRules = {
|
|
38
|
+
...recommendedRegexpRules,
|
|
39
|
+
"no-empty-character-class": "off",
|
|
40
|
+
"no-invalid-regexp": "off",
|
|
41
|
+
"no-misleading-character-class": "off",
|
|
42
|
+
"no-useless-backreference": "off",
|
|
43
|
+
"prefer-regex-literals": "error",
|
|
44
|
+
"regexp/no-super-linear-move": "error",
|
|
45
|
+
"regexp/prefer-escape-replacement-dollar-char": "error",
|
|
46
|
+
};
|
|
24
47
|
|
|
25
48
|
export function reactAppConfig({ ignores = ["dist"], rules = {} } = {}) {
|
|
26
49
|
return tseslint.config(
|
|
@@ -35,9 +58,12 @@ export function reactAppConfig({ ignores = ["dist"], rules = {} } = {}) {
|
|
|
35
58
|
plugins: {
|
|
36
59
|
"react-hooks": reactHooks,
|
|
37
60
|
"react-refresh": reactRefresh,
|
|
61
|
+
regexp: regexpPlugin,
|
|
38
62
|
},
|
|
39
63
|
rules: {
|
|
40
64
|
...reactHooks.configs.recommended.rules,
|
|
65
|
+
...reactCompilerAdvisoryRules,
|
|
66
|
+
...regexpRules,
|
|
41
67
|
"react-refresh/only-export-components": [
|
|
42
68
|
"warn",
|
|
43
69
|
{ allowConstantExport: true },
|
|
@@ -57,9 +83,6 @@ function createStrictReactAppConfig(
|
|
|
57
83
|
{
|
|
58
84
|
ignores = ["dist"],
|
|
59
85
|
tsconfigRootDir,
|
|
60
|
-
maxLines = 300,
|
|
61
|
-
maxLinesPerFunction = 80,
|
|
62
|
-
tailwindCssFiles,
|
|
63
86
|
rules = {},
|
|
64
87
|
settings = {},
|
|
65
88
|
testFiles = ["**/*.test.{ts,tsx}", "**/test/**/*.{ts,tsx}"],
|
|
@@ -71,7 +94,7 @@ function createStrictReactAppConfig(
|
|
|
71
94
|
"import-x": importX,
|
|
72
95
|
"react-hooks": reactHooks,
|
|
73
96
|
"react-refresh": reactRefresh,
|
|
74
|
-
|
|
97
|
+
regexp: regexpPlugin,
|
|
75
98
|
};
|
|
76
99
|
|
|
77
100
|
return tseslint.config(
|
|
@@ -93,6 +116,8 @@ function createStrictReactAppConfig(
|
|
|
93
116
|
plugins,
|
|
94
117
|
rules: {
|
|
95
118
|
...plugins["react-hooks"].configs.recommended.rules,
|
|
119
|
+
...reactCompilerAdvisoryRules,
|
|
120
|
+
...regexpRules,
|
|
96
121
|
"@typescript-eslint/no-explicit-any": "error",
|
|
97
122
|
"@typescript-eslint/no-non-null-assertion": "error",
|
|
98
123
|
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
|
@@ -103,25 +128,7 @@ function createStrictReactAppConfig(
|
|
|
103
128
|
"@typescript-eslint/no-unsafe-member-access": "error",
|
|
104
129
|
"@typescript-eslint/no-unsafe-return": "error",
|
|
105
130
|
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
|
106
|
-
complexity: ["error", 12],
|
|
107
|
-
"import-x/no-cycle": "error",
|
|
108
131
|
"import-x/no-self-import": "error",
|
|
109
|
-
"max-depth": ["error", 3],
|
|
110
|
-
"max-lines": [
|
|
111
|
-
"error",
|
|
112
|
-
{ max: maxLines, skipBlankLines: true, skipComments: true },
|
|
113
|
-
],
|
|
114
|
-
"max-lines-per-function": [
|
|
115
|
-
"error",
|
|
116
|
-
{
|
|
117
|
-
max: maxLinesPerFunction,
|
|
118
|
-
skipBlankLines: true,
|
|
119
|
-
skipComments: true,
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
"max-nested-callbacks": ["error", 3],
|
|
123
|
-
"max-params": ["error", 4],
|
|
124
|
-
"no-restricted-syntax": ["error", ...typeAssertionBan],
|
|
125
132
|
"@eslint-react/no-nested-component-definitions": "error",
|
|
126
133
|
"@eslint-react/no-unstable-context-value": "error",
|
|
127
134
|
"react-refresh/only-export-components": [
|
|
@@ -138,14 +145,6 @@ function createStrictReactAppConfig(
|
|
|
138
145
|
"import-x/resolver-next": [
|
|
139
146
|
createNodeResolver({ extensions: [".js", ".jsx", ".ts", ".tsx"] }),
|
|
140
147
|
],
|
|
141
|
-
...(tailwindCssFiles
|
|
142
|
-
? {
|
|
143
|
-
tailwindcss: {
|
|
144
|
-
config: {},
|
|
145
|
-
cssFiles: tailwindCssFiles,
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
: {}),
|
|
149
148
|
...settings,
|
|
150
149
|
},
|
|
151
150
|
},
|
|
@@ -159,10 +158,6 @@ function createStrictReactAppConfig(
|
|
|
159
158
|
"@typescript-eslint/no-unsafe-return": "off",
|
|
160
159
|
"@typescript-eslint/no-explicit-any": "off",
|
|
161
160
|
"@typescript-eslint/no-non-null-assertion": "off",
|
|
162
|
-
"no-restricted-syntax": "off",
|
|
163
|
-
"max-lines-per-function": "off",
|
|
164
|
-
"max-lines": "off",
|
|
165
|
-
"max-nested-callbacks": "off",
|
|
166
161
|
},
|
|
167
162
|
},
|
|
168
163
|
...overrides,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciridae/configs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Shared ESLint, Prettier, and
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Shared ESLint, Prettier, TypeScript, and Vite configurations for Ciridae projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.18"
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"CHANGELOG.md",
|
|
17
17
|
"eslint.js",
|
|
18
18
|
"prettier.json",
|
|
19
|
+
"vite.js",
|
|
20
|
+
"vite.d.ts",
|
|
19
21
|
"tsconfig"
|
|
20
22
|
],
|
|
21
23
|
"exports": {
|
|
@@ -23,23 +25,40 @@
|
|
|
23
25
|
"./eslint": "./eslint.js",
|
|
24
26
|
"./prettier": "./prettier.json",
|
|
25
27
|
"./tsconfig/frontend-app.json": "./tsconfig/frontend-app.json",
|
|
26
|
-
"./tsconfig/frontend-node.json": "./tsconfig/frontend-node.json"
|
|
28
|
+
"./tsconfig/frontend-node.json": "./tsconfig/frontend-node.json",
|
|
29
|
+
"./vite": {
|
|
30
|
+
"types": "./vite.d.ts",
|
|
31
|
+
"default": "./vite.js"
|
|
32
|
+
}
|
|
27
33
|
},
|
|
28
34
|
"publishConfig": {
|
|
29
35
|
"access": "public"
|
|
30
36
|
},
|
|
31
37
|
"dependencies": {
|
|
32
|
-
"@eslint/compat": "^2.1.0",
|
|
33
38
|
"@eslint-react/eslint-plugin": "^5.14.6",
|
|
34
39
|
"@eslint/js": "^10.0.1",
|
|
35
40
|
"eslint-plugin-import-x": "^4.17.1",
|
|
36
41
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
37
42
|
"eslint-plugin-react-refresh": "^0.5.3",
|
|
38
|
-
"eslint-plugin-
|
|
43
|
+
"eslint-plugin-regexp": "^3.1.1",
|
|
39
44
|
"globals": "^17.7.0",
|
|
40
45
|
"typescript-eslint": "^8.63.0"
|
|
41
46
|
},
|
|
42
47
|
"peerDependencies": {
|
|
43
|
-
"eslint": "^10.7.0"
|
|
48
|
+
"eslint": "^10.7.0",
|
|
49
|
+
"vite": ">=8",
|
|
50
|
+
"@sentry/vite-plugin": ">=5"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"vite": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"@sentry/vite-plugin": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@sentry/vite-plugin": "^5.2.1",
|
|
62
|
+
"vite": "catalog:frontend8"
|
|
44
63
|
}
|
|
45
64
|
}
|
package/vite.d.ts
ADDED
package/vite.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes `version.json` beside the bundle so an open tab can tell the deployment moved on
|
|
3
|
+
* without it. In development it serves the same file, and a `deployedBuildSha` other than
|
|
4
|
+
* `buildSha` exercises the update prompt.
|
|
5
|
+
*
|
|
6
|
+
* @param {{ buildSha: string, deployedBuildSha?: string }} options
|
|
7
|
+
* @returns {import("vite").Plugin[]}
|
|
8
|
+
*/
|
|
9
|
+
export function versionManifest({ buildSha, deployedBuildSha = buildSha }) {
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
name: "ciridae-version-manifest",
|
|
13
|
+
apply: "build",
|
|
14
|
+
generateBundle() {
|
|
15
|
+
this.emitFile({
|
|
16
|
+
type: "asset",
|
|
17
|
+
fileName: "version.json",
|
|
18
|
+
source: `${JSON.stringify({ buildSha })}\n`,
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "ciridae-dev-version-manifest",
|
|
24
|
+
apply: "serve",
|
|
25
|
+
configureServer(server) {
|
|
26
|
+
server.middlewares.use("/version.json", (_request, response) => {
|
|
27
|
+
response.setHeader("Content-Type", "application/json");
|
|
28
|
+
response.end(`${JSON.stringify({ buildSha: deployedBuildSha })}\n`);
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Uploads production source maps to Sentry, then deletes them from the output, when the build
|
|
37
|
+
* has `SENTRY_ORG`, `SENTRY_PROJECT`, and `SENTRY_AUTH_TOKEN`. Otherwise it adds nothing.
|
|
38
|
+
*
|
|
39
|
+
* @param {{ mode: string }} options
|
|
40
|
+
* @returns {import("vite").PluginOption}
|
|
41
|
+
*/
|
|
42
|
+
export function sentrySourceMaps({ mode }) {
|
|
43
|
+
const {
|
|
44
|
+
SENTRY_ORG: org,
|
|
45
|
+
SENTRY_PROJECT: project,
|
|
46
|
+
SENTRY_AUTH_TOKEN: authToken,
|
|
47
|
+
} = process.env;
|
|
48
|
+
if (mode !== "production" || !org || !project || !authToken) return [];
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
name: "ciridae-sentry-source-maps",
|
|
52
|
+
config: () => ({ build: { sourcemap: "hidden" } }),
|
|
53
|
+
},
|
|
54
|
+
import("@sentry/vite-plugin").then(({ sentryVitePlugin }) =>
|
|
55
|
+
sentryVitePlugin({
|
|
56
|
+
org,
|
|
57
|
+
project,
|
|
58
|
+
authToken,
|
|
59
|
+
sourcemaps: { filesToDeleteAfterUpload: ["dist/**/*.js.map"] },
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
62
|
+
];
|
|
63
|
+
}
|