@uniflowed/config 0.0.0-alpha.14 → 0.0.0-alpha.16

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/index.js CHANGED
@@ -5,8 +5,13 @@
5
5
  export type {
6
6
  CapabilityJsHost,
7
7
  CoverageThresholds,
8
+ DeployAdapter,
9
+ PackageManagerPreference,
8
10
  Permissions,
11
+ PluginEntry,
9
12
  RuleLevel,
13
+ RuntimeEngine,
14
+ SizeBudget,
10
15
  TaskDefinition,
11
16
  UniflowedConfig,
12
17
  } from "./internal/schema.js";
@@ -2,6 +2,30 @@
2
2
  //
3
3
  // Owns the Flow shape of `uf.config.js`; `index.js` keeps the public package
4
4
  // entry point thin.
5
+ //
6
+ // # Every key uf reads, and nothing else
7
+ //
8
+ // The guide says a config file "is type-checked as Flow code", which is the
9
+ // whole reason to write one in Flow. That makes an undeclared key worse than a
10
+ // missing feature: `lint.files`, `lint.ignore` and `app.router.enabled` were
11
+ // documented in the configuration reference, accepted by the runtime, and
12
+ // absent from this type — so a project following the reference had to choose
13
+ // between the documented key and a config that checks
14
+ // (ubugeeei-prod/uf#481). They were not the only three.
15
+ //
16
+ // So the key *names* below are no longer a person's job to keep in step with
17
+ // the loader. `crates/uf_config`'s `the_flow_schema_declares_every_key_uf_reads`
18
+ // parses this file with uf's own Flow parser and compares the paths it declares
19
+ // against the paths `uf_config::UniflowedConfig` serializes — in both
20
+ // directions, because a key declared here and read by nothing is an option that
21
+ // silently does nothing, which is the same defect wearing the other face.
22
+ //
23
+ // The value *types* are still this file's own judgement, and deliberately so.
24
+ // A test over names cannot say whether `"biome" | "prettier" | "none"` is the
25
+ // right set, and several keys below are narrower than what the loader will
26
+ // parse — `orm.module` is `"@uniflowed/orm"` because there is one
27
+ // implementation, where the loader takes any string. Where this package means
28
+ // to be more opinionated than the parser, that is what these say.
5
29
 
6
30
  export type RuleLevel = "off" | "warn" | "error" | 0 | 1 | 2 | boolean;
7
31
 
@@ -25,6 +49,68 @@ export type TaskDefinition =
25
49
 
26
50
  export type CapabilityJsHost = "node" | "deno" | "bun";
27
51
 
52
+ /**
53
+ * The package manager uf drives, overriding what it infers from the project.
54
+ *
55
+ * `"auto"` reads the project itself: an explicit `"packageManager"` field, then
56
+ * a lockfile, then the nearest workspace root, then uf's own resolver.
57
+ */
58
+ // The eight below are the *names a project may pin*, not commands this module
59
+ // runs: `@uniflowed/config` declares a type and executes nothing at all.
60
+ // `uniflowed/no-npm-script-invocation` is a line scanner, and a string whose
61
+ // entire contents is `pnpm` reads exactly like the `spawn("pnpm", […])` the
62
+ // rule exists to catch — `crates/uf_lint/src/scan/search.rs` says so in its own
63
+ // documentation and calls what is left "rare and suppressible". This is that
64
+ // residue, and there is no spelling of these values that is not one of them.
65
+ // uf-lint-disable uniflowed/no-npm-script-invocation
66
+ export type PackageManagerPreference =
67
+ | "auto"
68
+ | "uf"
69
+ | "npm"
70
+ | "pnpm"
71
+ | "yarn"
72
+ | "yarn-classic"
73
+ | "yarn-berry"
74
+ | "bun";
75
+ // uf-lint-enable uniflowed/no-npm-script-invocation
76
+
77
+ export type RuntimeEngine = "uf" | "node" | "deno" | "bun" | "edge" | "serverless" | "container";
78
+
79
+ export type DeployAdapter =
80
+ | "node"
81
+ | "bun"
82
+ | "deno"
83
+ | "edge"
84
+ | "serverless"
85
+ | "static"
86
+ | "container";
87
+
88
+ /**
89
+ * One entry of `plugins: [...]`.
90
+ *
91
+ * A bare name takes the default band and applies to every pipeline; the long
92
+ * form says otherwise. Declaration order decides within a band, so the
93
+ * resolved pipeline is a function of this file alone.
94
+ */
95
+ export type PluginEntry =
96
+ | string
97
+ | {
98
+ readonly name: string,
99
+ readonly order?: "pre" | "normal" | "post",
100
+ readonly apply?: "build" | "serve" | "always",
101
+ };
102
+
103
+ /**
104
+ * A ceiling `uf build` fails over, and what it is measured on.
105
+ *
106
+ * `max` accepts a byte count or a size a person would write — `"180kb"` —
107
+ * because a budget is written by hand and read back by a report.
108
+ */
109
+ export type SizeBudget = {
110
+ readonly max: number | string,
111
+ readonly metric?: "raw" | "gzip" | "brotli",
112
+ };
113
+
28
114
  /**
29
115
  * What the project's own code may reach.
30
116
  *
@@ -70,7 +156,52 @@ export type CoverageThresholds = {
70
156
  };
71
157
 
72
158
  export type UniflowedConfig = {
159
+ /**
160
+ * The runtime accessibility audit: `expect(el).toHaveNoAxeViolations()` in a
161
+ * test, and the page `uf dev` is serving.
162
+ *
163
+ * One block for both on purpose. A rule a project has decided cannot be
164
+ * judged here — `color-contrast` against a DOM with no layout is the
165
+ * standing example — has to be the same rule in CI and in the loop somebody
166
+ * is working in, or the audit that finds a violation while the component is
167
+ * being written disagrees with the one that blocks the pull request.
168
+ *
169
+ * Inert without axe-core, which uf does not install: add it and both halves
170
+ * start working.
171
+ *
172
+ * Not the same thing as `lint.rules`' `a11y/*`, which read JSX that was
173
+ * never rendered.
174
+ */
175
+ readonly accessibility?: {
176
+ readonly devAudit?: boolean,
177
+ readonly axe?: {
178
+ // Run only rules carrying one of these axe tags; every rule when absent.
179
+ readonly tags?: $ReadOnlyArray<string>,
180
+ readonly disabledRules?: $ReadOnlyArray<string>,
181
+ readonly minImpact?: "minor" | "moderate" | "serious" | "critical",
182
+ },
183
+ },
73
184
  readonly app?: {
185
+ // Whether a component with no directive is rendered on the server or
186
+ // shipped to the browser.
187
+ readonly componentDefault?: "server" | "client",
188
+ readonly framework?: "uniflowed" | "react" | "react-native",
189
+ readonly react?: {
190
+ // React 19's Strict Mode, on by default in `uf dev`: it double-invokes
191
+ // render and effects so an impurity shows up in development rather
192
+ // than in production. Off has to be a choice a project makes.
193
+ readonly strictMode?: boolean,
194
+ readonly version?: string,
195
+ readonly asyncReact?: boolean,
196
+ readonly suspense?: boolean,
197
+ readonly useHook?: boolean,
198
+ },
199
+ // Whether the project builds React Server Components at all, and whether
200
+ // a `"use server"` export is wired to an endpoint.
201
+ readonly rsc?: boolean,
202
+ readonly serverActions?: boolean,
203
+ // The runtimes the build must satisfy.
204
+ readonly targets?: $ReadOnlyArray<"web" | "react-native" | "server" | "hermes">,
74
205
  readonly orm?: {
75
206
  readonly enabled?: boolean,
76
207
  readonly module?: "@uniflowed/orm",
@@ -79,11 +210,18 @@ export type UniflowedConfig = {
79
210
  readonly preparedByDefault?: true,
80
211
  },
81
212
  readonly builtins?: {
213
+ readonly data?: "uniflowed-query",
214
+ readonly effect?: "uniflowed-effect",
82
215
  readonly fetch?: {
83
216
  readonly module?: "@uniflowed/fetch",
84
217
  readonly overrideGlobalFetch?: false,
85
218
  },
86
219
  readonly cell?: boolean,
220
+ readonly frameworkLints?: boolean,
221
+ readonly nativeTestRunner?: boolean,
222
+ readonly reactTestingLibrary?: boolean,
223
+ readonly relay?: boolean,
224
+ readonly style?: "style-x",
87
225
  readonly reactCompiler?: {
88
226
  readonly enabled?: boolean,
89
227
  readonly implementation?: "official-rust",
@@ -106,6 +244,19 @@ export type UniflowedConfig = {
106
244
  readonly extensions?: $ReadOnlyArray<".mdx">,
107
245
  readonly jsxImportSource?: "@uniflowed/jsx-runtime",
108
246
  readonly pipelinePlugin?: "built-in",
247
+ // Colours are computed during the build and written into the HTML,
248
+ // so nothing ships to the browser to do it. Both themes are emitted
249
+ // together as CSS variables, because a build cannot know which the
250
+ // reader prefers.
251
+ readonly highlight?: {
252
+ readonly enabled?: boolean,
253
+ readonly themes?: {
254
+ readonly light?: string,
255
+ readonly dark?: string,
256
+ },
257
+ // Grammars beyond the ones a uf project uses by default.
258
+ readonly langs?: $ReadOnlyArray<string>,
259
+ },
109
260
  },
110
261
  readonly cache?: "opt-in",
111
262
  },
@@ -124,6 +275,27 @@ export type UniflowedConfig = {
124
275
  // faces `uf_assets::font::LOCAL_FACES` knows the metrics of, because
125
276
  // the scaling is a ratio against real numbers rather than a guess.
126
277
  readonly fallback?: string,
278
+ // How an imported family is cut down. `"none"` is the default and the
279
+ // argued position: subsetting is lossy, and a build cannot see the
280
+ // text a server will render or a user will type. `"ranges"` splits the
281
+ // font's own coverage into script buckets with exact `unicode-range`
282
+ // values and loses nothing.
283
+ readonly subset?: "none" | "ranges",
284
+ // Whether `Font` preloads the primary face. Exactly one file is ever
285
+ // preloaded, however many buckets a split produced.
286
+ readonly preload?: boolean,
287
+ },
288
+ readonly icons?: {
289
+ readonly enabled?: boolean,
290
+ // Where `uf:icon/<name>` looks for `<name>.svg`, from the project
291
+ // root. A directory rather than a claim on `.svg`, which stays Vite's.
292
+ readonly dir?: string,
293
+ },
294
+ readonly og?: {
295
+ readonly enabled?: boolean,
296
+ // The typeface every `*.og.json` is drawn with unless it names its
297
+ // own. uf embeds none, so a project that draws cards points at one.
298
+ readonly font?: string | null,
127
299
  },
128
300
  readonly motion?: {
129
301
  readonly module?: "@uniflowed/motion",
@@ -158,10 +330,8 @@ export type UniflowedConfig = {
158
330
  },
159
331
  },
160
332
  readonly runtime?: {
161
- readonly default?: "node" | "deno" | "bun" | "uf",
162
- readonly compatibility?: $ReadOnlyArray<
163
- "node" | "bun" | "deno" | "edge" | "serverless" | "container",
164
- >,
333
+ readonly default?: RuntimeEngine,
334
+ readonly compatibility?: $ReadOnlyArray<RuntimeEngine>,
165
335
  readonly capabilityJsHost?: {
166
336
  readonly default?: CapabilityJsHost,
167
337
  readonly hosts?: $ReadOnlyArray<CapabilityJsHost>,
@@ -169,15 +339,24 @@ export type UniflowedConfig = {
169
339
  },
170
340
  readonly deploy?: {
171
341
  readonly enabled?: boolean,
172
- readonly adapters?: $ReadOnlyArray<
173
- "node" | "bun" | "deno" | "edge" | "serverless" | "static" | "container",
174
- >,
342
+ // The target `uf build` writes an artefact for when none is named on
343
+ // the command line; `--adapter` beats it.
344
+ readonly adapter?: DeployAdapter,
345
+ readonly adapters?: $ReadOnlyArray<DeployAdapter>,
175
346
  },
176
347
  },
177
348
  readonly router?: {
349
+ // `false` says this project is not a uf application, and is the only way
350
+ // to say it: a library has no routes to scan for.
351
+ readonly enabled?: boolean,
178
352
  readonly entry?: string,
179
353
  readonly root?: string,
180
354
  readonly manifest?: string,
355
+ readonly convention?: "file-system",
356
+ // Turning the file-system router off is what makes a project a
357
+ // **library** rather than an application, and `uf build` reads it: see
358
+ // `build.lib` below and docs/app/reference/config.
359
+ readonly enabled?: boolean,
181
360
  },
182
361
  readonly rendering?: {
183
362
  readonly modes?: $ReadOnlyArray<"ppr" | "ssr" | "ssg" | "isr">,
@@ -190,12 +369,48 @@ export type UniflowedConfig = {
190
369
  },
191
370
  },
192
371
  readonly build?: {
372
+ // Size ceilings that fail the build. Unset by default: failing a build
373
+ // nobody asked uf to police is worse than reporting the size.
374
+ readonly budgets?: {
375
+ readonly total?: SizeBudget,
376
+ readonly initialJs?: SizeBudget,
377
+ readonly perRoute?: SizeBudget,
378
+ readonly perAsset?: SizeBudget,
379
+ },
193
380
  readonly entries?: $ReadOnlyArray<string>,
381
+ // Commands to run around the build, in the same shape as `tasks`.
382
+ readonly hooks?: { readonly [string]: TaskDefinition },
194
383
  readonly outDir?: string,
195
384
  // Prerender every route and leave no server bundle behind. Read together
196
385
  // with `app.rendering.modes`; see docs/app/reference/config.
197
386
  readonly staticBuild?: boolean,
198
387
  readonly sourcemap?: boolean,
388
+ /**
389
+ * What a **library** build writes, for a project whose
390
+ * `app.router.enabled` is false.
391
+ *
392
+ * Never the switch. `app.router.enabled` decides which of the two builds
393
+ * `uf build` runs, and declaring this key in a project whose router is on
394
+ * is refused where the config is read rather than resolved by precedence.
395
+ * Every field has a default that builds what `uf new --lib` scaffolds, so
396
+ * a library ordinarily writes none of this.
397
+ *
398
+ * `"umd"` and `"iife"` are deliberately absent from `formats`: each needs
399
+ * a global name per entry, and what a Flow library's global should be is
400
+ * not a decision uf has made.
401
+ */
402
+ readonly lib?: {
403
+ // The modules to build, relative to the project root. Each names its own
404
+ // output by its path — `internal/parse.js` is written to
405
+ // `dist/internal/parse.js` — so two entries cannot collide.
406
+ readonly entries?: $ReadOnlyArray<string>,
407
+ readonly formats?: $ReadOnlyArray<"es" | "cjs">,
408
+ // Package names to leave as imports *beyond* the ones the manifest
409
+ // declares. A library build already externalises `dependencies`,
410
+ // `peerDependencies`, `optionalDependencies` and the host's built-in
411
+ // modules; this is for what a manifest cannot say.
412
+ readonly external?: $ReadOnlyArray<string>,
413
+ },
199
414
  },
200
415
  // Which builder uf drives. Vite is the default, not a dependency: any module
201
416
  // satisfying the contract in docs/architecture.md can be named here.
@@ -206,6 +421,17 @@ export type UniflowedConfig = {
206
421
  readonly host?: string,
207
422
  readonly port?: number,
208
423
  readonly strictPort?: boolean,
424
+ // Which files the dev server may serve. `deny` is evaluated on the
425
+ // canonical path and beats `allow`; see docs/security.md.
426
+ readonly fs?: {
427
+ readonly allow?: $ReadOnlyArray<string>,
428
+ readonly deny?: $ReadOnlyArray<string>,
429
+ },
430
+ // `--host` refuses to bind a routable address while this is empty: a dev
431
+ // server reachable from the network with no host allow-list is a file
432
+ // server for your source tree.
433
+ readonly allowedHosts?: $ReadOnlyArray<string>,
434
+ readonly allowedOrigins?: $ReadOnlyArray<string>,
209
435
  },
210
436
  readonly docs?: {
211
437
  readonly enabled?: boolean,
@@ -215,13 +441,19 @@ export type UniflowedConfig = {
215
441
  readonly staticBuild?: boolean,
216
442
  readonly deploy?: "void",
217
443
  },
218
- readonly lint?: {
219
- readonly engine?: "rust",
220
- readonly flow?: {
221
- readonly builtins?: "mixed",
222
- readonly parser?: "official-flow-rust",
223
- },
224
- readonly rules?: { readonly [string]: RuleLevel },
444
+ /**
445
+ * The `.env` cascade, the mode it is read for, and the pinned toolchain.
446
+ *
447
+ * `active` empty means the command decides — `development` for `uf dev`,
448
+ * `production` for a build, `test` for `uf test`. `files` empty selects the
449
+ * conventional cascade rather than no files at all.
450
+ */
451
+ readonly env?: {
452
+ readonly active?: string,
453
+ readonly files?: $ReadOnlyArray<string>,
454
+ // Runtimes and package managers by exact version — `{ node: "24.14.0" }`.
455
+ // Exact, because a range is not an environment.
456
+ readonly toolchain?: { readonly [string]: string },
225
457
  },
226
458
  readonly fmt?: {
227
459
  readonly indentWidth?: number,
@@ -247,6 +479,43 @@ export type UniflowedConfig = {
247
479
  readonly quotes?: "single" | "double",
248
480
  readonly semicolons?: boolean,
249
481
  },
482
+ /**
483
+ * Paths no command walks into.
484
+ *
485
+ * Top level because every command that walks the project reads it: `uf fmt`,
486
+ * `uf lint`, `uf check`, `uf test` and `uf doc`. A bare name — `dist` — names
487
+ * a kind of directory and matches at any depth; a path — `src/generated` —
488
+ * names one place. `.uf` and `.git` are uf's and git's and are not a
489
+ * project's to opt back into.
490
+ *
491
+ * Absent takes uf's own list, `["node_modules", "dist", "target"]`. An empty
492
+ * list is a different instruction: it is a project that has looked at that
493
+ * list and wants none of it.
494
+ */
495
+ readonly ignore?: $ReadOnlyArray<string>,
496
+ readonly lint?: {
497
+ readonly engine?: "rust",
498
+ // Globs to lint.
499
+ readonly files?: $ReadOnlyArray<string>,
500
+ /**
501
+ * The old spelling of the top-level `ignore`.
502
+ *
503
+ * **Deprecated**, and read only when `ignore` is absent. It was never
504
+ * `uf lint`'s alone: `uf fmt`, `uf check`, `uf test` and `uf doc` walk the
505
+ * project through the same code and have always obeyed it, so the key was
506
+ * named after one of the five commands that read it. It keeps working for
507
+ * as long as alpha lasts, and every one of those commands says so once.
508
+ * See ubugeeei-prod/uf#575.
509
+ */
510
+ readonly ignore?: $ReadOnlyArray<string>,
511
+ readonly flow?: {
512
+ readonly builtins?: "mixed",
513
+ readonly parser?: "official-flow-rust",
514
+ },
515
+ // Changes to uf's rule table, not the whole of it: a rule you did not
516
+ // mention keeps the level uf ships. Say `"off"` to switch one off.
517
+ readonly rules?: { readonly [string]: RuleLevel },
518
+ },
250
519
  readonly package?: {
251
520
  readonly generator?: "napi-rs",
252
521
  readonly targets?: $ReadOnlyArray<
@@ -255,12 +524,17 @@ export type UniflowedConfig = {
255
524
  readonly typescriptDeclarationsToFlow?: true,
256
525
  },
257
526
  readonly permissions?: Permissions,
527
+ // Plugins the project adds, appended to uf's own and resolved in the order
528
+ // they are written. A name that names a file is code to run, so `uf_plugin`
529
+ // refuses any that reaches outside the project root.
530
+ readonly plugins?: $ReadOnlyArray<PluginEntry>,
258
531
  readonly pm?: {
259
532
  readonly module?: "@uniflowed/pm",
260
533
  readonly resolver?: "uf-native",
261
534
  readonly lockfile?: "uf.lock",
262
535
  readonly storeDir?: string,
263
536
  readonly allowLifecycleScripts?: false,
537
+ readonly packageManager?: PackageManagerPreference,
264
538
  /**
265
539
  * The registry uf *reads* from: packuments, provenance attestations, and
266
540
  * the versions `uf update` reports against.
@@ -420,10 +694,10 @@ export type UniflowedConfig = {
420
694
  readonly test?: {
421
695
  readonly module?: "@uniflowed/test",
422
696
  readonly runner?: {
423
- readonly runtime?: "capability-js-host" | "uf-self-hosted",
697
+ readonly runtime?: "vite-task" | "capability-js-host" | "uf-self-hosted",
424
698
  readonly jsHosts?: $ReadOnlyArray<CapabilityJsHost>,
425
- readonly scheduler?: "native-work-stealing",
426
- readonly performanceTarget?: "faster-than-bun",
699
+ readonly scheduler?: "vite-task-cache" | "native-work-stealing",
700
+ readonly performanceTarget?: "vite-task" | "faster-than-bun",
427
701
  readonly officialFlowParser?: true,
428
702
  },
429
703
  readonly reactTestingLibraryNative?: true,
@@ -446,6 +720,15 @@ export type UniflowedConfig = {
446
720
  },
447
721
  },
448
722
  readonly tasks?: { readonly [string]: TaskDefinition },
723
+ /**
724
+ * Vite's own configuration, merged over the one uf generates.
725
+ *
726
+ * uf reads none of it, which is the point: an option Vite adds tomorrow
727
+ * works in a uf project tomorrow rather than after a uf release that names
728
+ * it. Deliberately unshaped for the same reason — a Flow type over Vite's
729
+ * options would be the re-declaration this key exists to avoid.
730
+ */
731
+ readonly vite?: { readonly [string]: mixed },
449
732
  readonly vrt?: {
450
733
  readonly enabled?: boolean,
451
734
  readonly module?: "@uniflowed/vrt",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniflowed/config",
3
- "version": "0.0.0-alpha.14",
3
+ "version": "0.0.0-alpha.16",
4
4
  "description": "Flow declarations for @uniflowed/config, part of the Unified Toolchain for Flow.",
5
5
  "type": "module",
6
6
  "license": "MIT",