@hublo/sentinel 1.1.7 → 1.2.0-alpha.1

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.
@@ -0,0 +1,39 @@
1
+ // src/roles/build/presets/requirements.json
2
+ var requirements_default = {
3
+ svelte: {
4
+ why: "sentinel ships Vite 8, and @sveltejs/vite-plugin-svelte caps at Vite 6 until its major 7",
5
+ requires: {
6
+ "@sveltejs/vite-plugin-svelte": "7.0.0",
7
+ svelte: "5.46.4"
8
+ }
9
+ }
10
+ };
11
+
12
+ // src/roles/build/presets/shared.json
13
+ var shared_default = {
14
+ target: "chrome89",
15
+ allowedHosts: [".hubpreprod.com", ".playground-hublo.com"],
16
+ publicAssets: [
17
+ {
18
+ dir: "public/branding",
19
+ baseURL: "/branding",
20
+ maxAge: 31536e3
21
+ }
22
+ ]
23
+ };
24
+
25
+ // src/roles/build/preset-data.ts
26
+ var REACT_APP_DEFAULTS = shared_default;
27
+ var REQUIREMENTS = requirements_default;
28
+
29
+ // src/shared/deep-merge.ts
30
+ function isPlainObject(value) {
31
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32
+ }
33
+
34
+ export {
35
+ REACT_APP_DEFAULTS,
36
+ REQUIREMENTS,
37
+ isPlainObject
38
+ };
39
+ //# sourceMappingURL=chunk-XVDOQ3G3.js.map
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@ declare function registerAdapters(): void;
19
19
  declare const VERBS: readonly ["run", "inspect", "init", "migrate", "report", "status"];
20
20
  type Verb = (typeof VERBS)[number];
21
21
  /** Targets: the kind of check. The CLI `--<target>` flags map 1:1 to these. */
22
- declare const TARGETS: readonly ["lint", "format", "typescript", "build", "test", "static-analysis", "runtime-analysis", "arch"];
22
+ declare const TARGETS: readonly ["lint", "format", "typescript", "build", "dev", "test", "static-analysis", "runtime-analysis", "arch"];
23
23
  type Target = (typeof TARGETS)[number];
24
24
  /** Presets: the stack preset a project resolves to (strict by default). */
25
25
  declare const PRESET_NAMES: readonly ["react", "nest", "svelte", "node", "tools"];
package/dist/index.js CHANGED
@@ -7,7 +7,8 @@ import {
7
7
  registerAdapters,
8
8
  resolve,
9
9
  setDefaultRunner
10
- } from "./chunk-W73ISYMG.js";
10
+ } from "./chunk-FLJ2QNFF.js";
11
+ import "./chunk-XVDOQ3G3.js";
11
12
  export {
12
13
  BaseAdapter,
13
14
  all,
@@ -0,0 +1,112 @@
1
+ import { PluginOption, UserConfig } from 'vite';
2
+ export { defineConfig, loadEnv } from 'vite';
3
+
4
+ /** The convention values, in one place so `--inspect` can report what an app departs from. */
5
+ declare const REACT_APP_DEFAULTS: {
6
+ target: string;
7
+ allowedHosts: string[];
8
+ publicAssets: {
9
+ dir: string;
10
+ baseURL: string;
11
+ maxAge: number;
12
+ }[];
13
+ };
14
+
15
+ /** Where the router's routes live and where it writes its generated tree. */
16
+ interface RouterPaths {
17
+ routesDirectory: string;
18
+ generatedRouteTree: string;
19
+ /** Extra lines appended to the generated tree. Two of the three apps use it. */
20
+ routeTreeFileFooter?: string[];
21
+ }
22
+ /**
23
+ * The TanStack factories, supplied by the app.
24
+ *
25
+ * Typed loosely on purpose: sentinel decides WHEN to call them and with what, and has no
26
+ * business asserting their return shape. Narrowing this would only couple sentinel to a
27
+ * version of a package it does not own.
28
+ */
29
+ interface TanstackFactories {
30
+ start: (options: {
31
+ router: RouterPaths;
32
+ }) => PluginOption[];
33
+ router: (options: RouterPaths & {
34
+ target: string;
35
+ autoCodeSplitting: boolean;
36
+ }) => PluginOption;
37
+ }
38
+ /** Everything an app declares. */
39
+ interface ReactAppOptions {
40
+ /** The app's own directory. Always `__dirname`. */
41
+ root: string;
42
+ /** Vite's `mode`, from the config callback. */
43
+ mode: string;
44
+ /** Served under, e.g. `/console/`. */
45
+ base: string;
46
+ /** Dev server port. */
47
+ port: number;
48
+ /** HMR port. Explicit, NOT `port + 1`: career goes DOWN to 9998 where the others go up. */
49
+ hmrPort: number;
50
+ router: RouterPaths;
51
+ tanstack: TanstackFactories;
52
+ /**
53
+ * Sourcemaps: a boolean, however the app decided it.
54
+ *
55
+ * The three apps disagree on how — two use a mode allowlist, one reads an env var. That is
56
+ * an inconsistency in the repo rather than a requirement, and encoding both would make
57
+ * sentinel the owner of a decision it should not hold.
58
+ */
59
+ sourcemap?: boolean;
60
+ /** Passed through verbatim, never read. See the note above. */
61
+ alias?: unknown[];
62
+ /** The app's own plugins (branding, babel). Appended AFTER the base set. */
63
+ plugins?: PluginOption[];
64
+ /** Conventions the app may override. */
65
+ target?: string;
66
+ allowedHosts?: string[];
67
+ /**
68
+ * Anything else this app needs, merged over the preset LAST.
69
+ *
70
+ * The preset is a starting point, not a cage. Two things an app must be able to do and
71
+ * could not before: override a NESTED attribute without losing its siblings, and add an
72
+ * attribute the preset knows nothing about.
73
+ *
74
+ * Objects merge, everything else replaces — see `merge-config` for why arrays replace
75
+ * rather than concatenate. Whatever lands here is reported by `--inspect --build`, so an
76
+ * override is a visible, countable decision instead of something found by reading a config.
77
+ *
78
+ * Real cases today: console needs `ssr`, `optimizeDeps` and `preview`; career needs
79
+ * `css.preprocessorOptions` for Sass; all three need their own `nitro.publicAssets`.
80
+ */
81
+ overrides?: Record<string, unknown>;
82
+ }
83
+
84
+ /**
85
+ * The environment, loaded the way all three apps already load it.
86
+ *
87
+ * `production` is normalised to `prd` because that is this repository's deployment tag: it is
88
+ * the `.env.prd` the build reads and the SSM path segment the values come from. Not a
89
+ * preference, a fact about the repo — which is exactly why it belongs in the preset rather
90
+ * than being retyped in every config.
91
+ */
92
+ declare function appEnv(root: string, mode: string): Record<string, string>;
93
+ /** True when this run is a test rather than a real build. */
94
+ declare function isTestMode(mode: string, env?: NodeJS.ProcessEnv): boolean;
95
+ /**
96
+ * The plugin names in the order they run, which is what `--inspect` reports.
97
+ *
98
+ * `extras` is how many plugins the app appends of its own; they are named positionally
99
+ * because sentinel has no way to name a function someone else passed in.
100
+ */
101
+ declare function pluginPlan(isTest: boolean, extras?: number): string[];
102
+ /**
103
+ * The Vite config for a React app, minus the plugins.
104
+ *
105
+ * Pure and separately testable: everything here is data in, data out, so the composition
106
+ * rules can be asserted without a bundler anywhere near the test.
107
+ */
108
+ declare function reactAppConfig(options: ReactAppOptions): Omit<UserConfig, 'plugins'>;
109
+ /** The config an app spreads into `defineConfig`. */
110
+ declare function reactApp(options: ReactAppOptions): UserConfig;
111
+
112
+ export { REACT_APP_DEFAULTS, type ReactAppOptions, type RouterPaths, type TanstackFactories, appEnv, isTestMode, pluginPlan, reactApp, reactAppConfig };
@@ -0,0 +1,126 @@
1
+ import {
2
+ REACT_APP_DEFAULTS,
3
+ isPlainObject
4
+ } from "../../chunk-XVDOQ3G3.js";
5
+
6
+ // src/roles/build/react-app.ts
7
+ import tailwindcss from "@tailwindcss/vite";
8
+ import react from "@vitejs/plugin-react";
9
+ import { nitro } from "nitro/vite";
10
+ import { loadEnv } from "vite";
11
+ import svgr from "vite-plugin-svgr";
12
+
13
+ // src/roles/build/merge-config.ts
14
+ function mergeConfig(base, overrides) {
15
+ if (!overrides) return base;
16
+ const out = { ...base };
17
+ for (const [key, value] of Object.entries(overrides)) {
18
+ if (value === void 0) {
19
+ delete out[key];
20
+ continue;
21
+ }
22
+ const current = out[key];
23
+ out[key] = isPlainObject(current) && isPlainObject(value) ? mergeConfig(current, value) : value;
24
+ }
25
+ return out;
26
+ }
27
+
28
+ // src/roles/build/react-app.ts
29
+ import { defineConfig, loadEnv as loadEnv2 } from "vite";
30
+ function appEnv(root, mode) {
31
+ return loadEnv(mode === "production" ? "prd" : mode, root, "");
32
+ }
33
+ function isTestMode(mode, env = process.env) {
34
+ return mode === "test" || env.VITEST === "true";
35
+ }
36
+ function pluginEntries(options, isTest) {
37
+ const { tanstack, router } = options;
38
+ return [
39
+ { name: "tailwindcss", build: () => tailwindcss() },
40
+ ...isTest ? [
41
+ {
42
+ name: "tanstackRouter",
43
+ build: () => tanstack.router({ ...router, target: "react", autoCodeSplitting: true })
44
+ }
45
+ ] : [
46
+ { name: "tanstackStart", build: () => tanstack.start({ router }) },
47
+ { name: "nitro", build: () => nitro() }
48
+ ],
49
+ { name: "react", build: () => react() },
50
+ { name: "svgr", build: () => svgr() }
51
+ ];
52
+ }
53
+ function pluginPlan(isTest, extras = 0) {
54
+ const entries = pluginEntries(
55
+ { tanstack: { start: () => [], router: () => null }, router: STUB_ROUTER },
56
+ isTest
57
+ );
58
+ return [
59
+ ...entries.map((entry) => entry.name),
60
+ ...Array.from({ length: extras }, (_, at) => `app:${at}`)
61
+ ];
62
+ }
63
+ var STUB_ROUTER = { routesDirectory: "", generatedRouteTree: "" };
64
+ function basePlugins(options, isTest) {
65
+ return [
66
+ ...pluginEntries(options, isTest).flatMap((entry) => entry.build()),
67
+ ...options.plugins ?? []
68
+ ];
69
+ }
70
+ function reactAppConfig(options) {
71
+ const preset = {
72
+ base: options.base,
73
+ root: options.root,
74
+ define: {
75
+ // Development only. In a build this would leak a browser global into SSR output.
76
+ ...options.mode === "development" ? { global: "window" } : {}
77
+ },
78
+ server: {
79
+ port: options.port,
80
+ allowedHosts: options.allowedHosts ?? [...REACT_APP_DEFAULTS.allowedHosts],
81
+ hmr: { host: "localhost", protocol: "ws", port: options.hmrPort }
82
+ },
83
+ build: {
84
+ target: options.target ?? REACT_APP_DEFAULTS.target,
85
+ sourcemap: options.sourcemap ?? false
86
+ },
87
+ /*
88
+ * Nitro's two settings are sentinel's, and both were measured across the three apps rather
89
+ * than assumed.
90
+ *
91
+ * `baseURL` is always the app's `base`, without exception: `/console/`, `/` and `/admin/`.
92
+ * It is derived, so an app cannot get them out of step.
93
+ *
94
+ * `publicAssets` is byte-identical in all three — the branding directory, its URL and a
95
+ * one-year max-age. That is a convention about where branding lives, not data about an
96
+ * app, and it was sitting in three `overrides` blocks saying the same thing.
97
+ *
98
+ * An app that needs MORE from nitro overrides the key: career adds `externals` to keep the
99
+ * SSR sanitisation chain out of the server bundle. `mergeConfig` merges objects, so adding
100
+ * one key does not take these two away.
101
+ */
102
+ nitro: {
103
+ baseURL: options.base,
104
+ publicAssets: [...REACT_APP_DEFAULTS.publicAssets]
105
+ },
106
+ ...options.alias ? { resolve: { alias: options.alias } } : {}
107
+ };
108
+ return mergeConfig(preset, options.overrides);
109
+ }
110
+ function reactApp(options) {
111
+ return {
112
+ ...reactAppConfig(options),
113
+ plugins: basePlugins(options, isTestMode(options.mode))
114
+ };
115
+ }
116
+ export {
117
+ REACT_APP_DEFAULTS,
118
+ appEnv,
119
+ defineConfig,
120
+ isTestMode,
121
+ loadEnv2 as loadEnv,
122
+ pluginPlan,
123
+ reactApp,
124
+ reactAppConfig
125
+ };
126
+ //# sourceMappingURL=react-app.js.map
package/lint/nest.json ADDED
@@ -0,0 +1,358 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": [
4
+ "typescript",
5
+ "jest",
6
+ "import",
7
+ "unicorn",
8
+ "oxc",
9
+ "node"
10
+ ],
11
+ "categories": {
12
+ "correctness": "off",
13
+ "suspicious": "off",
14
+ "pedantic": "off",
15
+ "perf": "off",
16
+ "style": "off",
17
+ "restriction": "off",
18
+ "nursery": "off"
19
+ },
20
+ "rules": {
21
+ "no-var": "error",
22
+ "prefer-const": [
23
+ "error",
24
+ {
25
+ "destructuring": "any",
26
+ "ignoreReadBeforeAssign": false
27
+ }
28
+ ],
29
+ "prefer-rest-params": "error",
30
+ "prefer-spread": "error",
31
+ "typescript/ban-ts-comment": "error",
32
+ "typescript/no-array-constructor": "error",
33
+ "typescript/no-duplicate-enum-values": "error",
34
+ "typescript/no-empty-object-type": "error",
35
+ "typescript/no-extra-non-null-assertion": "error",
36
+ "typescript/no-misused-new": "error",
37
+ "typescript/no-namespace": "error",
38
+ "typescript/no-non-null-asserted-optional-chain": "error",
39
+ "typescript/no-this-alias": "error",
40
+ "typescript/no-unnecessary-type-constraint": "error",
41
+ "typescript/no-unsafe-declaration-merging": "error",
42
+ "typescript/no-unsafe-function-type": "error",
43
+ "typescript/no-unused-expressions": [
44
+ "error",
45
+ {
46
+ "allowShortCircuit": false,
47
+ "allowTaggedTemplates": false,
48
+ "allowTernary": false
49
+ }
50
+ ],
51
+ "typescript/no-wrapper-object-types": "error",
52
+ "typescript/prefer-as-const": "error",
53
+ "typescript/prefer-namespace-keyword": "error",
54
+ "typescript/triple-slash-reference": "error",
55
+ "array-callback-return": [
56
+ "error",
57
+ {
58
+ "allowImplicit": false,
59
+ "checkForEach": false,
60
+ "allowVoid": false
61
+ }
62
+ ],
63
+ "complexity": [
64
+ "error",
65
+ 30
66
+ ],
67
+ "curly": [
68
+ "error",
69
+ "all"
70
+ ],
71
+ "dot-notation": [
72
+ "error",
73
+ {
74
+ "allowKeywords": true,
75
+ "allowPattern": ""
76
+ }
77
+ ],
78
+ "eqeqeq": "warn",
79
+ "for-direction": "error",
80
+ "func-style": [
81
+ "warn",
82
+ "expression",
83
+ {
84
+ "allowArrowFunctions": false,
85
+ "allowTypeAnnotation": false,
86
+ "overrides": {}
87
+ }
88
+ ],
89
+ "import/no-duplicates": [
90
+ "error",
91
+ {
92
+ "considerQueryString": true,
93
+ "prefer-inline": true
94
+ }
95
+ ],
96
+ "jest/expect-expect": "warn",
97
+ "jest/no-alias-methods": "error",
98
+ "jest/no-commented-out-tests": "warn",
99
+ "jest/no-deprecated-functions": "error",
100
+ "jest/no-disabled-tests": "warn",
101
+ "jest/no-done-callback": "error",
102
+ "jest/no-export": "error",
103
+ "jest/no-focused-tests": "error",
104
+ "jest/no-identical-title": "error",
105
+ "jest/no-interpolation-in-snapshots": "error",
106
+ "jest/no-jasmine-globals": "error",
107
+ "jest/no-mocks-import": "error",
108
+ "jest/no-standalone-expect": "error",
109
+ "jest/no-test-prefixes": "error",
110
+ "jest/prefer-to-be": "error",
111
+ "jest/prefer-to-contain": "error",
112
+ "jest/prefer-to-have-length": "error",
113
+ "jest/valid-describe-callback": "error",
114
+ "jest/valid-expect": "warn",
115
+ "jest/valid-expect-in-promise": "error",
116
+ "jest/valid-title": "error",
117
+ "no-async-promise-executor": "error",
118
+ "no-case-declarations": "error",
119
+ "no-compare-neg-zero": "error",
120
+ "no-cond-assign": [
121
+ "error",
122
+ "except-parens"
123
+ ],
124
+ "no-console": [
125
+ "warn",
126
+ {
127
+ "allow": [
128
+ "warn",
129
+ "error"
130
+ ]
131
+ }
132
+ ],
133
+ "no-constant-binary-expression": "error",
134
+ "no-constant-condition": [
135
+ "error",
136
+ {
137
+ "checkLoops": "allExceptWhileTrue"
138
+ }
139
+ ],
140
+ "no-control-regex": "off",
141
+ "no-debugger": "error",
142
+ "no-delete-var": "error",
143
+ "no-dupe-else-if": "error",
144
+ "no-dupe-keys": "error",
145
+ "no-duplicate-case": "error",
146
+ "no-else-return": [
147
+ "error",
148
+ {
149
+ "allowElseIf": true
150
+ }
151
+ ],
152
+ "no-empty": [
153
+ "error",
154
+ {
155
+ "allowEmptyCatch": false
156
+ }
157
+ ],
158
+ "no-empty-character-class": "error",
159
+ "no-empty-pattern": [
160
+ "error",
161
+ {
162
+ "allowObjectPatternsAsParameters": false
163
+ }
164
+ ],
165
+ "no-empty-static-block": "error",
166
+ "no-ex-assign": "error",
167
+ "no-extra-boolean-cast": [
168
+ "error",
169
+ {}
170
+ ],
171
+ "no-fallthrough": [
172
+ "error",
173
+ {
174
+ "allowEmptyCase": false,
175
+ "reportUnusedFallthroughComment": false
176
+ }
177
+ ],
178
+ "no-global-assign": [
179
+ "error",
180
+ {
181
+ "exceptions": []
182
+ }
183
+ ],
184
+ "no-implicit-coercion": [
185
+ "error",
186
+ {
187
+ "allow": [],
188
+ "boolean": true,
189
+ "disallowTemplateShorthand": false,
190
+ "number": true,
191
+ "string": true
192
+ }
193
+ ],
194
+ "no-invalid-regexp": [
195
+ "error",
196
+ {}
197
+ ],
198
+ "no-irregular-whitespace": [
199
+ "error",
200
+ {
201
+ "skipComments": false,
202
+ "skipJSXText": false,
203
+ "skipRegExps": false,
204
+ "skipStrings": true,
205
+ "skipTemplates": false
206
+ }
207
+ ],
208
+ "no-lonely-if": "error",
209
+ "no-loss-of-precision": "error",
210
+ "no-misleading-character-class": "error",
211
+ "no-nonoctal-decimal-escape": "error",
212
+ "no-prototype-builtins": "error",
213
+ "no-regex-spaces": "error",
214
+ "no-restricted-imports": [
215
+ "error",
216
+ {
217
+ "paths": [
218
+ {
219
+ "name": "@front/theme",
220
+ "importNames": [
221
+ "hubloTheme",
222
+ "legacyHubloTheme",
223
+ "customCommonColors",
224
+ "customPalette",
225
+ "breakpointsOptions",
226
+ "breakpointsValues",
227
+ "spacingConstants"
228
+ ],
229
+ "message": "Use the MUI theme context or the public providers exposed by @front/theme."
230
+ }
231
+ ],
232
+ "patterns": [
233
+ {
234
+ "group": [
235
+ "@front/theme/*",
236
+ "!@front/theme/testing"
237
+ ],
238
+ "message": "Deep imports from @front/theme are forbidden outside the theme lib."
239
+ }
240
+ ]
241
+ }
242
+ ],
243
+ "no-self-assign": [
244
+ "error",
245
+ {
246
+ "props": true
247
+ }
248
+ ],
249
+ "no-shadow-restricted-names": [
250
+ "error",
251
+ {
252
+ "reportGlobalThis": false
253
+ }
254
+ ],
255
+ "no-sparse-arrays": "error",
256
+ "no-unneeded-ternary": [
257
+ "error",
258
+ {
259
+ "defaultAssignment": true
260
+ }
261
+ ],
262
+ "no-unsafe-finally": "error",
263
+ "no-unsafe-optional-chaining": [
264
+ "warn",
265
+ {
266
+ "disallowArithmeticOperators": false
267
+ }
268
+ ],
269
+ "no-unused-labels": "error",
270
+ "no-unused-private-class-members": "error",
271
+ "no-useless-backreference": "error",
272
+ "no-useless-catch": "error",
273
+ "no-useless-escape": [
274
+ "error",
275
+ {
276
+ "allowRegexCharacters": []
277
+ }
278
+ ],
279
+ "no-useless-return": "error",
280
+ "nx/enforce-module-boundaries": [
281
+ "error",
282
+ {
283
+ "enforceBuildableLibDependency": true,
284
+ "allow": [],
285
+ "depConstraints": [
286
+ {
287
+ "sourceTag": "*",
288
+ "onlyDependOnLibsWithTags": [
289
+ "*"
290
+ ]
291
+ }
292
+ ]
293
+ }
294
+ ],
295
+ "prefer-arrow-callback": [
296
+ "error",
297
+ {
298
+ "allowNamedFunctions": false,
299
+ "allowUnboundThis": true
300
+ }
301
+ ],
302
+ "require-yield": "error",
303
+ "typescript/await-thenable": "error",
304
+ "typescript/no-confusing-non-null-assertion": "error",
305
+ "typescript/no-empty-function": [
306
+ "warn",
307
+ {
308
+ "allow": []
309
+ }
310
+ ],
311
+ "typescript/no-explicit-any": "error",
312
+ "typescript/no-floating-promises": [
313
+ "error",
314
+ {
315
+ "ignoreVoid": true
316
+ }
317
+ ],
318
+ "typescript/no-import-type-side-effects": "error",
319
+ "typescript/no-inferrable-types": "error",
320
+ "typescript/no-non-null-assertion": "error",
321
+ "typescript/no-unused-vars": [
322
+ "warn",
323
+ {
324
+ "argsIgnorePattern": "^_",
325
+ "caughtErrors": "none"
326
+ }
327
+ ],
328
+ "typescript/return-await": [
329
+ "warn",
330
+ "in-try-catch"
331
+ ],
332
+ "typescript/switch-exhaustiveness-check": [
333
+ "error",
334
+ {
335
+ "considerDefaultExhaustiveForUnions": true
336
+ }
337
+ ],
338
+ "use-isnan": [
339
+ "error",
340
+ {
341
+ "enforceForIndexOf": false,
342
+ "enforceForSwitchCase": true
343
+ }
344
+ ],
345
+ "valid-typeof": [
346
+ "error",
347
+ {
348
+ "requireStringLiterals": false
349
+ }
350
+ ]
351
+ },
352
+ "jsPlugins": [
353
+ {
354
+ "name": "nx",
355
+ "specifier": "@nx/eslint-plugin"
356
+ }
357
+ ]
358
+ }