@devdogsuga/devtools 0.1.26 → 0.1.27
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/README.md +72 -11
- package/bin/devtools.mjs +4 -4
- package/dist/cli-CZKkNHK5.js +6619 -0
- package/dist/failure-log-BBPGejQv.js +256 -0
- package/dist/launch.js +267 -281
- package/dist/local-env-D8HC2JK_.js +351 -0
- package/dist/local-env-DrBCXqD8.js +2 -0
- package/dist/menu-CQBsQRgH.js +1684 -0
- package/dist/peer-redirect-hooks.js +15 -0
- package/dist/peers-RBplwovA.js +229 -0
- package/dist/root-C2OmZDwD.js +94 -0
- package/dist/stack-Bx-55oi_.js +2 -0
- package/dist/stack-BynCgRVS.js +181 -0
- package/dist/supabase-project-0knM0mqd.js +426 -0
- package/dist/telemetry-C4foD8MD.js +249 -0
- package/dist/telemetry.js +2 -148
- package/package.json +10 -29
- package/bin/devtools-ci-bare.mjs +0 -48
- package/bin/devtools-ci.mjs +0 -31
- package/dist/args.d.ts +0 -26
- package/dist/args.js +0 -78
- package/dist/bws/bw.d.ts +0 -18
- package/dist/bws/bw.js +0 -70
- package/dist/bws/client.d.ts +0 -43
- package/dist/bws/client.js +0 -363
- package/dist/bws/environments.d.ts +0 -96
- package/dist/bws/environments.js +0 -111
- package/dist/bws/pace.d.ts +0 -25
- package/dist/bws/pace.js +0 -43
- package/dist/bws/retry.d.ts +0 -12
- package/dist/bws/retry.js +0 -39
- package/dist/bws/store.d.ts +0 -1
- package/dist/bws/store.js +0 -32
- package/dist/bws/token.d.ts +0 -31
- package/dist/bws/token.js +0 -88
- package/dist/bws/vault.d.ts +0 -29
- package/dist/bws/vault.js +0 -200
- package/dist/catalog.d.ts +0 -57
- package/dist/catalog.js +0 -57
- package/dist/cf/build.d.ts +0 -13
- package/dist/cf/build.js +0 -65
- package/dist/cf/commands.d.ts +0 -1
- package/dist/cf/commands.js +0 -172
- package/dist/cf/local-env.d.ts +0 -58
- package/dist/cf/local-env.js +0 -177
- package/dist/ci.d.ts +0 -1
- package/dist/ci.js +0 -328
- package/dist/cli.d.ts +0 -11
- package/dist/cli.js +0 -1094
- package/dist/commands.d.ts +0 -261
- package/dist/commands.js +0 -1318
- package/dist/completions.d.ts +0 -4
- package/dist/completions.js +0 -110
- package/dist/cron/commands.d.ts +0 -47
- package/dist/cron/commands.js +0 -372
- package/dist/cron/discovery.d.ts +0 -55
- package/dist/cron/discovery.js +0 -85
- package/dist/cron/schema.d.ts +0 -45
- package/dist/cron/schema.js +0 -35
- package/dist/db/config-push.d.ts +0 -8
- package/dist/db/config-push.js +0 -11
- package/dist/db/connection.d.ts +0 -79
- package/dist/db/connection.js +0 -114
- package/dist/db/exec.d.ts +0 -1
- package/dist/db/exec.js +0 -11
- package/dist/db/generate-types.d.ts +0 -1
- package/dist/db/generate-types.js +0 -4
- package/dist/db/generated-env.d.ts +0 -69
- package/dist/db/generated-env.js +0 -113
- package/dist/db/introspect.d.ts +0 -1
- package/dist/db/introspect.js +0 -122
- package/dist/db/local-env.d.ts +0 -65
- package/dist/db/local-env.js +0 -23
- package/dist/db/new-migration.d.ts +0 -8
- package/dist/db/new-migration.js +0 -69
- package/dist/db/pooler.d.ts +0 -23
- package/dist/db/pooler.js +0 -55
- package/dist/db/run.d.ts +0 -68
- package/dist/db/run.js +0 -156
- package/dist/db/seed-buckets.d.ts +0 -2
- package/dist/db/seed-buckets.js +0 -4
- package/dist/db/seed-production.d.ts +0 -8
- package/dist/db/seed-production.js +0 -79
- package/dist/db/seed-roles.d.ts +0 -7
- package/dist/db/seed-roles.js +0 -33
- package/dist/db/session-refresh.d.ts +0 -43
- package/dist/db/session-refresh.js +0 -40
- package/dist/deploy/migrations.d.ts +0 -20
- package/dist/deploy/migrations.js +0 -68
- package/dist/deploy/orphans.d.ts +0 -20
- package/dist/deploy/orphans.js +0 -177
- package/dist/deploy/preflight.d.ts +0 -53
- package/dist/deploy/preflight.js +0 -189
- package/dist/deploy/report.d.ts +0 -26
- package/dist/deploy/report.js +0 -70
- package/dist/deploy/require-planner.d.ts +0 -2
- package/dist/deploy/require-planner.js +0 -60
- package/dist/deploy/require-token.d.ts +0 -15
- package/dist/deploy/require-token.js +0 -78
- package/dist/deploy/secrets-file.d.ts +0 -17
- package/dist/deploy/secrets-file.js +0 -166
- package/dist/deploy/write-env.d.ts +0 -40
- package/dist/deploy/write-env.js +0 -395
- package/dist/docs/index-pages.d.ts +0 -38
- package/dist/docs/index-pages.js +0 -186
- package/dist/emails/commands.d.ts +0 -15
- package/dist/emails/commands.js +0 -130
- package/dist/emails/email-types.d.ts +0 -13
- package/dist/emails/email-types.js +0 -1
- package/dist/emails/fixtures.d.ts +0 -20
- package/dist/emails/fixtures.js +0 -20
- package/dist/env/audit.d.ts +0 -206
- package/dist/env/audit.js +0 -436
- package/dist/env/cloudflare.d.ts +0 -21
- package/dist/env/cloudflare.js +0 -61
- package/dist/env/commands.d.ts +0 -99
- package/dist/env/commands.js +0 -613
- package/dist/env/discovery.d.ts +0 -15
- package/dist/env/discovery.js +0 -239
- package/dist/env/document.d.ts +0 -119
- package/dist/env/document.js +0 -323
- package/dist/env/example.d.ts +0 -68
- package/dist/env/example.js +0 -605
- package/dist/env/selection.d.ts +0 -105
- package/dist/env/selection.js +0 -228
- package/dist/env-entry.d.ts +0 -49
- package/dist/env-entry.js +0 -204
- package/dist/environment-doctor.d.ts +0 -45
- package/dist/environment-doctor.js +0 -429
- package/dist/environment.d.ts +0 -92
- package/dist/environment.js +0 -185
- package/dist/fingerprint.d.ts +0 -10
- package/dist/fingerprint.js +0 -18
- package/dist/gen/campus-map.d.ts +0 -1
- package/dist/gen/campus-map.js +0 -473
- package/dist/gen/commands.d.ts +0 -1
- package/dist/gen/commands.js +0 -25
- package/dist/gen/hypno.d.ts +0 -1
- package/dist/gen/hypno.js +0 -134
- package/dist/gh/client.d.ts +0 -80
- package/dist/gh/client.js +0 -259
- package/dist/gh/environments.d.ts +0 -83
- package/dist/gh/environments.js +0 -178
- package/dist/gh/rulesets/actors.d.ts +0 -14
- package/dist/gh/rulesets/actors.js +0 -69
- package/dist/gh/rulesets/api.d.ts +0 -15
- package/dist/gh/rulesets/api.js +0 -104
- package/dist/gh/rulesets/commands.d.ts +0 -3
- package/dist/gh/rulesets/commands.js +0 -203
- package/dist/gh/rulesets/desired.d.ts +0 -112
- package/dist/gh/rulesets/desired.js +0 -271
- package/dist/gh/rulesets/diff.d.ts +0 -65
- package/dist/gh/rulesets/diff.js +0 -170
- package/dist/gh/rulesets/types.d.ts +0 -76
- package/dist/gh/rulesets/types.js +0 -13
- package/dist/gh/settings/api.d.ts +0 -40
- package/dist/gh/settings/api.js +0 -180
- package/dist/gh/settings/commands.d.ts +0 -1
- package/dist/gh/settings/commands.js +0 -254
- package/dist/gh/settings/desired.d.ts +0 -53
- package/dist/gh/settings/desired.js +0 -43
- package/dist/gh/settings/diff.d.ts +0 -57
- package/dist/gh/settings/diff.js +0 -161
- package/dist/gh/settings/types.d.ts +0 -92
- package/dist/gh/settings/types.js +0 -11
- package/dist/gh/settings/workflows.d.ts +0 -41
- package/dist/gh/settings/workflows.js +0 -128
- package/dist/grantRoot.d.ts +0 -42
- package/dist/grantRoot.js +0 -89
- package/dist/help.d.ts +0 -18
- package/dist/help.js +0 -169
- package/dist/images/commands.d.ts +0 -32
- package/dist/images/commands.js +0 -231
- package/dist/images/events.d.ts +0 -47
- package/dist/images/events.js +0 -116
- package/dist/images/graphics.d.ts +0 -70
- package/dist/images/graphics.js +0 -234
- package/dist/images/open-graph-types.d.ts +0 -75
- package/dist/images/open-graph-types.js +0 -1
- package/dist/images/prompts.d.ts +0 -34
- package/dist/images/prompts.js +0 -100
- package/dist/images/render.d.ts +0 -21
- package/dist/images/render.js +0 -61
- package/dist/images/select.d.ts +0 -64
- package/dist/images/select.js +0 -96
- package/dist/instance.d.ts +0 -69
- package/dist/instance.js +0 -102
- package/dist/invocation.d.ts +0 -49
- package/dist/invocation.js +0 -78
- package/dist/launch-ci.d.ts +0 -1
- package/dist/launch-ci.js +0 -90
- package/dist/launch.d.ts +0 -18
- package/dist/menu.d.ts +0 -43
- package/dist/menu.js +0 -300
- package/dist/moderation.d.ts +0 -70
- package/dist/moderation.js +0 -128
- package/dist/oauth/browser.d.ts +0 -1
- package/dist/oauth/browser.js +0 -13
- package/dist/oauth/config.d.ts +0 -24
- package/dist/oauth/config.js +0 -24
- package/dist/oauth/connect-url.d.ts +0 -17
- package/dist/oauth/connect-url.js +0 -23
- package/dist/oauth/db.d.ts +0 -37
- package/dist/oauth/db.js +0 -108
- package/dist/oauth/device.d.ts +0 -93
- package/dist/oauth/device.js +0 -198
- package/dist/oauth/discovery.d.ts +0 -28
- package/dist/oauth/discovery.js +0 -54
- package/dist/oauth/env-file.d.ts +0 -6
- package/dist/oauth/env-file.js +0 -35
- package/dist/oauth/exchange.d.ts +0 -43
- package/dist/oauth/exchange.js +0 -93
- package/dist/oauth/label.d.ts +0 -1
- package/dist/oauth/label.js +0 -25
- package/dist/oauth/loopback.d.ts +0 -52
- package/dist/oauth/loopback.js +0 -168
- package/dist/oauth/pkce.d.ts +0 -6
- package/dist/oauth/pkce.js +0 -34
- package/dist/oauth/target.d.ts +0 -54
- package/dist/oauth/target.js +0 -102
- package/dist/oauth/transport.d.ts +0 -41
- package/dist/oauth/transport.js +0 -32
- package/dist/oauth/wizard.d.ts +0 -40
- package/dist/oauth/wizard.js +0 -578
- package/dist/persona.d.ts +0 -18
- package/dist/persona.js +0 -221
- package/dist/pick.d.ts +0 -6
- package/dist/pick.js +0 -64
- package/dist/pipes.d.ts +0 -1
- package/dist/pipes.js +0 -25
- package/dist/planner/checks.d.ts +0 -26
- package/dist/planner/checks.js +0 -104
- package/dist/planner/commands.d.ts +0 -10
- package/dist/planner/commands.js +0 -367
- package/dist/planner/db.d.ts +0 -15
- package/dist/planner/db.js +0 -33
- package/dist/planner/role.d.ts +0 -33
- package/dist/planner/role.js +0 -68
- package/dist/repo/peer-redirect-hooks.d.ts +0 -16
- package/dist/repo/peer-redirect-hooks.js +0 -10
- package/dist/repo/peer-redirect.d.ts +0 -4
- package/dist/repo/peer-redirect.js +0 -26
- package/dist/repo/peers.d.ts +0 -39
- package/dist/repo/peers.js +0 -139
- package/dist/repo/resolve.d.ts +0 -41
- package/dist/repo/resolve.js +0 -152
- package/dist/repo/root.d.ts +0 -23
- package/dist/repo/root.js +0 -91
- package/dist/repo/source.d.ts +0 -14
- package/dist/repo/source.js +0 -54
- package/dist/repo/supabase-project.d.ts +0 -60
- package/dist/repo/supabase-project.js +0 -124
- package/dist/repo/tsx-loader.d.ts +0 -6
- package/dist/repo/tsx-loader.js +0 -41
- package/dist/run/pick.d.ts +0 -90
- package/dist/run/pick.js +0 -664
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -280
- package/dist/stack.d.ts +0 -48
- package/dist/stack.js +0 -403
- package/dist/telemetry.d.ts +0 -59
- package/dist/tier.d.ts +0 -40
- package/dist/tier.js +0 -96
- package/dist/ui.d.ts +0 -40
- package/dist/ui.js +0 -71
- package/dist/version.d.ts +0 -7
- package/dist/version.js +0 -17
- package/dist/workers.d.ts +0 -5
- package/dist/workers.js +0 -33
- package/dist/workflows/commands.d.ts +0 -54
- package/dist/workflows/commands.js +0 -763
package/dist/help.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `--help`, one level at a time.
|
|
3
|
-
*
|
|
4
|
-
* ## Why this is small
|
|
5
|
-
*
|
|
6
|
-
* The help this replaced was ~200 lines and printed the whole tree: every
|
|
7
|
-
* subcommand of every group, the Bitwarden target table, the four places an
|
|
8
|
-
* access token is looked for, the grants `migration_planner` holds, and which
|
|
9
|
-
* deploy steps must avoid the `with-env` wrapper. A contributor running
|
|
10
|
-
* `pnpm devtools --help` to find out how to start a database read all of it.
|
|
11
|
-
*
|
|
12
|
-
* Two rules now:
|
|
13
|
-
*
|
|
14
|
-
* 1. **A level prints its own children and stops.** `--help` lists the
|
|
15
|
-
* top-level commands; `env --help` lists env's subcommands; `env pull
|
|
16
|
-
* --help` lists that command's options. Depth is reached by asking for
|
|
17
|
-
* it.
|
|
18
|
-
* 2. **No more than the caller needs to choose.** Operator internals are
|
|
19
|
-
* `docs/`'s job: which project a target maps to, how a credential is
|
|
20
|
-
* resolved, what a deploy job's environment holds. Help says what a
|
|
21
|
-
* command does and what it takes.
|
|
22
|
-
*
|
|
23
|
-
* Both fall out of rendering `commands.ts` rather than hand-writing prose, so
|
|
24
|
-
* neither can rot back into a wall of text one paragraph at a time.
|
|
25
|
-
*/
|
|
26
|
-
import { findCommand, GROUPS, SCOPES, } from "./commands.js";
|
|
27
|
-
const INDENT = " ";
|
|
28
|
-
/** `--target <t>`, or `--prune` for a boolean. */
|
|
29
|
-
function optionLabel(option) {
|
|
30
|
-
return option.value ? `${option.flag} ${option.value}` : option.flag;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Two columns, with the gutter sized to the widest label in THIS block.
|
|
34
|
-
*
|
|
35
|
-
* Per-block rather than one width for the whole file: `deploy`'s labels are
|
|
36
|
-
* long and `setup`'s are not, and a shared width would indent the short list
|
|
37
|
-
* halfway across the terminal to accommodate a group the reader is not
|
|
38
|
-
* looking at.
|
|
39
|
-
*/
|
|
40
|
-
function columns(rows) {
|
|
41
|
-
const width = Math.max(...rows.map(([label]) => label.length));
|
|
42
|
-
return rows.map(([label, text]) => `${INDENT}${label.padEnd(width)} ${text}`);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* A list of commands, split into scope blocks when any of them declares one.
|
|
46
|
-
*
|
|
47
|
-
* Used at two levels: a GROUP's own commands (no group has scoped commands
|
|
48
|
-
* today — `db` folded the old "Supabase" group's scopes into itself), and a
|
|
49
|
-
* command's own SUBCOMMANDS (`db`'s: `start`/`connect`/`stop`/`restart` on
|
|
50
|
-
* this machine, `migration` in the repo, `status`/`migrate`/`reset`/… on an
|
|
51
|
-
* endpoint, `planner`/`signing-key` naming their own connection). Either way,
|
|
52
|
-
* a flat list leaves the reader to work out which of `restart` and `reset` is
|
|
53
|
-
* which, the distinction that costs people an afternoon. A list with no
|
|
54
|
-
* scopes renders as it always did: one block, one indent, no headings.
|
|
55
|
-
*
|
|
56
|
-
* The blocks follow declaration order rather than `SCOPES` order, so the tree
|
|
57
|
-
* stays the one place that decides how the list reads. The gutter is sized
|
|
58
|
-
* across the whole list, not per block, so every block lines up with the
|
|
59
|
-
* others rather than each finding its own column.
|
|
60
|
-
*/
|
|
61
|
-
function scopedBody(commands) {
|
|
62
|
-
if (!commands.some((command) => command.scope)) {
|
|
63
|
-
return columns(childRows(commands));
|
|
64
|
-
}
|
|
65
|
-
const width = Math.max(...commands.map((command) => command.name.length));
|
|
66
|
-
const lines = [];
|
|
67
|
-
let open;
|
|
68
|
-
for (const command of commands) {
|
|
69
|
-
if (command.scope && command.scope !== open) {
|
|
70
|
-
open = command.scope;
|
|
71
|
-
lines.push(`${INDENT}${SCOPES[open].help}:`);
|
|
72
|
-
}
|
|
73
|
-
lines.push(`${INDENT.repeat(2)}${command.name.padEnd(width)} ${command.summary}`);
|
|
74
|
-
}
|
|
75
|
-
return lines;
|
|
76
|
-
}
|
|
77
|
-
function optionRows(options) {
|
|
78
|
-
return options.map((option) => [optionLabel(option), option.summary]);
|
|
79
|
-
}
|
|
80
|
-
function childRows(children) {
|
|
81
|
-
return children.map((child) => [child.name, child.summary]);
|
|
82
|
-
}
|
|
83
|
-
// ── The three levels ─────────────────────────────────────────────────────────
|
|
84
|
-
/** `pnpm devtools --help`: the groups, and nothing below them. */
|
|
85
|
-
function renderRoot() {
|
|
86
|
-
const lines = [
|
|
87
|
-
"pnpm devtools [command] [options]",
|
|
88
|
-
"",
|
|
89
|
-
"Run with no command to choose from a menu.",
|
|
90
|
-
"",
|
|
91
|
-
"Common tasks:",
|
|
92
|
-
...columns([
|
|
93
|
-
["setup", "Prepare a new checkout"],
|
|
94
|
-
["run dev", "Start development servers"],
|
|
95
|
-
["db start", "Start Supabase on this machine"],
|
|
96
|
-
["db reset", "Rebuild the local database"],
|
|
97
|
-
["cron run", "Choose and run a scheduled job"],
|
|
98
|
-
]),
|
|
99
|
-
];
|
|
100
|
-
for (const group of GROUPS) {
|
|
101
|
-
lines.push("", `${group.title}:`, ...scopedBody(group.commands));
|
|
102
|
-
}
|
|
103
|
-
lines.push("", "Options:", ...columns([
|
|
104
|
-
["--help, -h", "Show this message"],
|
|
105
|
-
[
|
|
106
|
-
"--tier <t>",
|
|
107
|
-
"Deploy tier for this whole invocation (development, staging, production)",
|
|
108
|
-
],
|
|
109
|
-
]), "", "`pnpm devtools <command> --help` shows what that command takes.");
|
|
110
|
-
return lines.join("\n");
|
|
111
|
-
}
|
|
112
|
-
/** `pnpm devtools <path…> --help`: one command's own children and options. */
|
|
113
|
-
function renderCommand(path, node) {
|
|
114
|
-
const children = node.subcommands ?? [];
|
|
115
|
-
const options = node.options ?? [];
|
|
116
|
-
const trail = path.join(" ");
|
|
117
|
-
const usage = [
|
|
118
|
-
"pnpm devtools",
|
|
119
|
-
trail,
|
|
120
|
-
children.length > 0 ? "<subcommand>" : "",
|
|
121
|
-
options.length > 0 ? "[options]" : "",
|
|
122
|
-
]
|
|
123
|
-
.filter(Boolean)
|
|
124
|
-
.join(" ");
|
|
125
|
-
const lines = [usage, "", node.summary];
|
|
126
|
-
if (children.length > 0) {
|
|
127
|
-
lines.push("", "Subcommands:", ...scopedBody(children));
|
|
128
|
-
}
|
|
129
|
-
if (options.length > 0) {
|
|
130
|
-
lines.push("", "Options:", ...columns(optionRows(options)));
|
|
131
|
-
}
|
|
132
|
-
if (children.length > 0) {
|
|
133
|
-
lines.push("", `\`pnpm devtools ${trail} <subcommand> --help\` shows what that one takes.`);
|
|
134
|
-
}
|
|
135
|
-
return lines.join("\n");
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Renders help for a path, falling back to the root.
|
|
139
|
-
*
|
|
140
|
-
* An unknown path renders the root rather than an error: this is only reached
|
|
141
|
-
* when `--help` was asked for, and answering a mistyped command with the list
|
|
142
|
-
* it was mistyped from is more use than a refusal.
|
|
143
|
-
*/
|
|
144
|
-
export function renderHelp(path = []) {
|
|
145
|
-
if (path.length === 0)
|
|
146
|
-
return renderRoot();
|
|
147
|
-
const node = findCommand(path);
|
|
148
|
-
if (!node)
|
|
149
|
-
return renderRoot();
|
|
150
|
-
return renderCommand(path, node);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* The command names in `argv` that precede any flag.
|
|
154
|
-
*
|
|
155
|
-
* `env pull --help` asks about `["env", "pull"]`; `--help env` asks about
|
|
156
|
-
* nothing, because a flag's value is not a command path. Kept separate from
|
|
157
|
-
* `positionals()`: that one strips a known set of value-taking flags to find a
|
|
158
|
-
* subcommand, and here anything after the first flag is not part of the path at
|
|
159
|
-
* all.
|
|
160
|
-
*/
|
|
161
|
-
export function helpPath(argv) {
|
|
162
|
-
const path = [];
|
|
163
|
-
for (const arg of argv) {
|
|
164
|
-
if (arg.startsWith("-"))
|
|
165
|
-
break;
|
|
166
|
-
path.push(arg);
|
|
167
|
-
}
|
|
168
|
-
return path;
|
|
169
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { Format } from "./open-graph-types.js";
|
|
2
|
-
import { type EventReader } from "./events.js";
|
|
3
|
-
import { type Selection } from "./select.js";
|
|
4
|
-
export interface ImagesOptions {
|
|
5
|
-
/** Graphic patterns. Empty means ask. */
|
|
6
|
-
patterns: string[];
|
|
7
|
-
/** Format names. Empty with `allFormats` false means ask. */
|
|
8
|
-
formats: string[];
|
|
9
|
-
allFormats: boolean;
|
|
10
|
-
/** An explicit directory; everything lands in it, flat. */
|
|
11
|
-
out?: string;
|
|
12
|
-
/** Use each graphic's own default directory. */
|
|
13
|
-
defaultOut: boolean;
|
|
14
|
-
/** List what would be written, and write nothing. */
|
|
15
|
-
noOutput: boolean;
|
|
16
|
-
}
|
|
17
|
-
export declare function parseImagesArgs(argv: readonly string[]): ImagesOptions;
|
|
18
|
-
/** `~/images` when the shell did not expand it, which quoting prevents. */
|
|
19
|
-
export declare function expandHome(path: string): string;
|
|
20
|
-
/** Whether anything asked for could be an event, so whether to reach for a database. */
|
|
21
|
-
export declare function wantsEvents(patterns: readonly string[]): boolean;
|
|
22
|
-
/** Whether a pattern names events specifically, rather than sweeping them up. */
|
|
23
|
-
export declare function namesEvents(patterns: readonly string[]): boolean;
|
|
24
|
-
export interface ImagesDeps {
|
|
25
|
-
/** Overridden in tests so nothing reads the peer package or the filesystem. */
|
|
26
|
-
reader?: () => EventReader;
|
|
27
|
-
}
|
|
28
|
-
/** Where one selection is written. */
|
|
29
|
-
export declare function destinationOf({ graphic, format }: Selection, out: string | undefined): string;
|
|
30
|
-
export declare function runImages(argv: string[], deps?: ImagesDeps): Promise<void>;
|
|
31
|
-
/** Re-exported so `render.ts`'s options type is the format's own shape. */
|
|
32
|
-
export type { Format };
|
package/dist/images/commands.js
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `pnpm devtools images [graphic…] [--format …] [--out …]`
|
|
3
|
-
*
|
|
4
|
-
* Renders the club's pictures from the templates in `@devdogsuga/open-graph`. Two axes,
|
|
5
|
-
* asked for separately: WHICH picture (`event/2026-09-08`, `page/events`,
|
|
6
|
-
* `app/dogdays`) and at WHAT SIZE (`gdgc-square`, `og`, `icon-512`). That split
|
|
7
|
-
* is the point of the command — an event poster for the GDG on Campus platform
|
|
8
|
-
* is exactly the pairing "this meeting" x "that platform's banner", and it
|
|
9
|
-
* could not be asked for while the two were one hard-coded list.
|
|
10
|
-
*
|
|
11
|
-
* Anything the command line leaves out, it asks for; anything it names, it does
|
|
12
|
-
* not. So an officer can type `pnpm devtools images` and be walked through it,
|
|
13
|
-
* and a script can pass every flag and never see a prompt.
|
|
14
|
-
*
|
|
15
|
-
* Event graphics used to be the one group backed by a database rather than by
|
|
16
|
-
* files in this repo — and the one group that could therefore be unavailable
|
|
17
|
-
* without one. They no longer are: `configEvents()` (`events.ts`) reads
|
|
18
|
-
* `@devdogsuga/events`'s committed config directly, the same source a
|
|
19
|
-
* reconcile job used to copy into the database first, so this command needs
|
|
20
|
-
* no running stack at all any more. See {@link loadEvents} for what remains
|
|
21
|
-
* of the "unavailable" branch — a repo `@devdogsuga/events` cannot resolve
|
|
22
|
-
* in, rather than a database that is not up.
|
|
23
|
-
*/
|
|
24
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
25
|
-
import { homedir } from "node:os";
|
|
26
|
-
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
27
|
-
import { log, note } from "@clack/prompts";
|
|
28
|
-
import { loadOpenGraph } from "../repo/source.js";
|
|
29
|
-
import { findRepoRoot } from "../repo/root.js";
|
|
30
|
-
import { positionals } from "../args.js";
|
|
31
|
-
import { errorMessage, explain, explainError, UsageError } from "../ui.js";
|
|
32
|
-
import { configEvents } from "./events.js";
|
|
33
|
-
import { assertUniqueStems, eventGraphics, staticGraphics, } from "./graphics.js";
|
|
34
|
-
import { pickFormats, pickGraphics, pickOutput } from "./prompts.js";
|
|
35
|
-
import { render } from "./render.js";
|
|
36
|
-
import { commaList, formatsFor, matchGraphics, normalizeArgv, pair, } from "./select.js";
|
|
37
|
-
/**
|
|
38
|
-
* The workspace root. Every default directory is written relative to it, and
|
|
39
|
-
* resolving those against `process.cwd()` would put the club's icons wherever
|
|
40
|
-
* the contributor happened to be standing.
|
|
41
|
-
*/
|
|
42
|
-
function flagValue(argv, flag) {
|
|
43
|
-
const index = argv.indexOf(flag);
|
|
44
|
-
if (index === -1)
|
|
45
|
-
return undefined;
|
|
46
|
-
const value = argv[index + 1];
|
|
47
|
-
return value && !value.startsWith("--") ? value : undefined;
|
|
48
|
-
}
|
|
49
|
-
export function parseImagesArgs(argv) {
|
|
50
|
-
const normalized = normalizeArgv(argv);
|
|
51
|
-
return {
|
|
52
|
-
patterns: positionals(normalized),
|
|
53
|
-
formats: commaList(flagValue(normalized, "--format")),
|
|
54
|
-
allFormats: normalized.includes("--all-formats"),
|
|
55
|
-
out: flagValue(normalized, "--out"),
|
|
56
|
-
defaultOut: normalized.includes("--default-out"),
|
|
57
|
-
noOutput: normalized.includes("--dry-run") || normalized.includes("--no-output"),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/** `~/images` when the shell did not expand it, which quoting prevents. */
|
|
61
|
-
export function expandHome(path) {
|
|
62
|
-
return path === "~" || path.startsWith("~/")
|
|
63
|
-
? resolve(homedir(), path.slice(2))
|
|
64
|
-
: path;
|
|
65
|
-
}
|
|
66
|
-
/** Whether anything asked for could be an event, so whether to reach for a database. */
|
|
67
|
-
export function wantsEvents(patterns) {
|
|
68
|
-
// No pattern means the picker, and the picker should list meetings.
|
|
69
|
-
if (patterns.length === 0)
|
|
70
|
-
return true;
|
|
71
|
-
return patterns.some((pattern) => pattern === "*" ||
|
|
72
|
-
pattern === "*/*" ||
|
|
73
|
-
pattern === "event" ||
|
|
74
|
-
pattern.startsWith("event/"));
|
|
75
|
-
}
|
|
76
|
-
/** Whether a pattern names events specifically, rather than sweeping them up. */
|
|
77
|
-
export function namesEvents(patterns) {
|
|
78
|
-
return patterns.some((pattern) => pattern === "event" || pattern.startsWith("event/"));
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Meetings, or a clear account of why there are none.
|
|
82
|
-
*
|
|
83
|
-
* `@devdogsuga/events` is an optional peer (see `repo/peers.ts`'s header), so
|
|
84
|
-
* loading it can still fail — outside a DevDogsUGA checkout, or one where
|
|
85
|
-
* nothing depends on it. That is the one way this command still needs an
|
|
86
|
-
* account of what went wrong, and two failures are treated differently,
|
|
87
|
-
* matching what each one means:
|
|
88
|
-
*
|
|
89
|
-
* - **Unavailable, under a wildcard.** `images *` means "everything", and
|
|
90
|
-
* everything else is renderable. It renders, and reports the events it
|
|
91
|
-
* could not.
|
|
92
|
-
* - **Unavailable, named.** `images 'event/*'` asked for exactly the thing
|
|
93
|
-
* that is missing. Nothing to degrade to, so it fails.
|
|
94
|
-
*/
|
|
95
|
-
async function loadEvents(patterns, deps) {
|
|
96
|
-
const required = namesEvents(patterns);
|
|
97
|
-
const reader = (deps.reader ?? configEvents)();
|
|
98
|
-
let meetings;
|
|
99
|
-
try {
|
|
100
|
-
meetings = await reader.meetings();
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
if (required)
|
|
104
|
-
throw err;
|
|
105
|
-
return { graphics: [], skipped: errorMessage(err) };
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
graphics: eventGraphics(await loadOpenGraph(), meetings),
|
|
109
|
-
skipped: null,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
/** Where one selection is written. */
|
|
113
|
-
export function destinationOf({ graphic, format }, out) {
|
|
114
|
-
if (out) {
|
|
115
|
-
// Flat, and always `<stem>-<format>.png`: the pinned filenames a graphic
|
|
116
|
-
// uses in its own directory (`icon.png`, `opengraph-image.png`) are only
|
|
117
|
-
// unambiguous BECAUSE the directories differ. Side by side they collide.
|
|
118
|
-
return resolve(expandHome(out), `${graphic.stem}-${format.name}.png`);
|
|
119
|
-
}
|
|
120
|
-
const { dir, file } = graphic.destination(format);
|
|
121
|
-
return resolve(findRepoRoot(), dir, file);
|
|
122
|
-
}
|
|
123
|
-
/** Repo-relative where possible, so the log reads as paths in this project. */
|
|
124
|
-
function display(file) {
|
|
125
|
-
const rel = relative(findRepoRoot(), file);
|
|
126
|
-
return rel.startsWith("..") || isAbsolute(rel) ? file : rel;
|
|
127
|
-
}
|
|
128
|
-
export async function runImages(argv, deps = {}) {
|
|
129
|
-
const options = parseImagesArgs(argv);
|
|
130
|
-
if (options.out && options.defaultOut) {
|
|
131
|
-
explain("Two answers to one question.", "--out names a directory and --default-out says to use each graphic's own.", [
|
|
132
|
-
"pnpm devtools images 'event/*' --out ~/images",
|
|
133
|
-
"pnpm devtools images '*' --default-out",
|
|
134
|
-
]);
|
|
135
|
-
process.exitCode = 1;
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
await images(options, deps);
|
|
140
|
-
}
|
|
141
|
-
catch (err) {
|
|
142
|
-
explainError("Could not render that.", err, [
|
|
143
|
-
"pnpm devtools images # pick from a list",
|
|
144
|
-
"pnpm devtools images 'event/*' --all-formats --out ~/images",
|
|
145
|
-
"pnpm devtools images '*' --default-out",
|
|
146
|
-
]);
|
|
147
|
-
process.exitCode = 1;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
async function images(options, deps) {
|
|
151
|
-
// ── Which pictures ────────────────────────────────────────────────────────
|
|
152
|
-
const registry = await staticGraphics();
|
|
153
|
-
let skipped = null;
|
|
154
|
-
if (wantsEvents(options.patterns)) {
|
|
155
|
-
const events = await loadEvents(options.patterns, deps);
|
|
156
|
-
registry.push(...events.graphics);
|
|
157
|
-
skipped = events.skipped;
|
|
158
|
-
}
|
|
159
|
-
assertUniqueStems(registry);
|
|
160
|
-
const graphics = options.patterns.length === 0
|
|
161
|
-
? await pickGraphics(registry)
|
|
162
|
-
: resolvePatterns(options.patterns, registry);
|
|
163
|
-
if (graphics.length === 0) {
|
|
164
|
-
throw new UsageError("Nothing to render.");
|
|
165
|
-
}
|
|
166
|
-
// ── At what sizes ─────────────────────────────────────────────────────────
|
|
167
|
-
const { FORMATS } = await loadOpenGraph();
|
|
168
|
-
const requested = options.allFormats
|
|
169
|
-
? (await formatsFor(graphics)).map((format) => format.name)
|
|
170
|
-
: options.formats.length > 0
|
|
171
|
-
? options.formats
|
|
172
|
-
: await pickFormats(graphics);
|
|
173
|
-
const unknown = requested.filter((name) => !FORMATS[name]);
|
|
174
|
-
if (unknown.length > 0) {
|
|
175
|
-
throw new UsageError(`No format called ${unknown.join(", ")}. Try one of ${Object.keys(FORMATS).join(", ")}.`);
|
|
176
|
-
}
|
|
177
|
-
const { selections, unsupported } = await pair(graphics, requested);
|
|
178
|
-
// Naming one graphic and one format it cannot do is a mistake worth saying
|
|
179
|
-
// out loud; sweeping up combinations that do not exist under a wildcard is
|
|
180
|
-
// not, and reporting every one of those would bury the output.
|
|
181
|
-
if (selections.length === 0 && unsupported.length > 0) {
|
|
182
|
-
throw new UsageError(unsupported
|
|
183
|
-
.map((miss) => `${miss.graphic} has no ${miss.format} rendition`)
|
|
184
|
-
.join("; "));
|
|
185
|
-
}
|
|
186
|
-
if (selections.length === 0)
|
|
187
|
-
throw new UsageError("Nothing to render.");
|
|
188
|
-
// ── Where ─────────────────────────────────────────────────────────────────
|
|
189
|
-
// An empty `--out` counts as absent, exactly as the old truthiness test did.
|
|
190
|
-
const explicitOut = options.out === "" ? undefined : options.out;
|
|
191
|
-
const out = options.defaultOut
|
|
192
|
-
? undefined
|
|
193
|
-
: (explicitOut ??
|
|
194
|
-
(options.noOutput ? undefined : await pickOutput(graphics)));
|
|
195
|
-
if (skipped)
|
|
196
|
-
log.warn(`event graphics skipped — ${skipped}`);
|
|
197
|
-
if (options.noOutput) {
|
|
198
|
-
note(selections
|
|
199
|
-
.map((selection) => `${selection.graphic.name} ${selection.format.name}\n ${display(destinationOf(selection, out))}\n ${selection.format.why}`)
|
|
200
|
-
.join("\n"), `${selections.length} image${selections.length === 1 ? "" : "s"}`);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// ── Render ────────────────────────────────────────────────────────────────
|
|
204
|
-
let bytes = 0;
|
|
205
|
-
for (const selection of selections) {
|
|
206
|
-
const file = destinationOf(selection, out);
|
|
207
|
-
const { png, width, height } = await render(selection.graphic.render(selection.format), selection.format);
|
|
208
|
-
await mkdir(dirname(file), { recursive: true });
|
|
209
|
-
await writeFile(file, png);
|
|
210
|
-
bytes += png.length;
|
|
211
|
-
log.success(`${display(file)} ${width}x${height} (${Math.round(png.length / 1024)} KB)`);
|
|
212
|
-
}
|
|
213
|
-
log.info(`${selections.length} image${selections.length === 1 ? "" : "s"}, ${Math.round(bytes / 1024)} KB total.`);
|
|
214
|
-
}
|
|
215
|
-
function resolvePatterns(patterns, registry) {
|
|
216
|
-
const { matched, unmatched } = matchGraphics(patterns, registry);
|
|
217
|
-
if (unmatched.length === 0)
|
|
218
|
-
return matched;
|
|
219
|
-
// An event pattern that matched nothing is almost never a typo — it is a
|
|
220
|
-
// database with no meetings in it. Saying "try brand/*, page/*" sends the
|
|
221
|
-
// reader hunting for a spelling mistake that is not there.
|
|
222
|
-
const askedForEvents = unmatched.some((pattern) => pattern === "event" || pattern.startsWith("event/"));
|
|
223
|
-
const haveEvents = registry.some((graphic) => graphic.group === "event");
|
|
224
|
-
if (askedForEvents && !haveEvents) {
|
|
225
|
-
throw new UsageError("This database has no meetings, so there are no event images to render. " +
|
|
226
|
-
"Reconcile it from `@devdogsuga/events`, or point at a database that has been.");
|
|
227
|
-
}
|
|
228
|
-
const groups = [...new Set(registry.map((graphic) => `${graphic.group}/*`))];
|
|
229
|
-
throw new UsageError(`Nothing called ${unmatched.join(", ")}. Names are group/name — try ` +
|
|
230
|
-
`${groups.join(", ")}, or * for all of them.`);
|
|
231
|
-
}
|
package/dist/images/events.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { Meeting } from "@devdogsuga/events";
|
|
2
|
-
import type { EventGraphicSource } from "./graphics.js";
|
|
3
|
-
/**
|
|
4
|
-
* Meetings, for the one group of graphics that cannot be drawn from files
|
|
5
|
-
* alone.
|
|
6
|
-
*
|
|
7
|
-
* Every other graphic is a function of committed files. An event poster used
|
|
8
|
-
* to be a function of meetings RECONCILED into the database from
|
|
9
|
-
* `@devdogsuga/events` — which meant `devtools images event/*` needed a
|
|
10
|
-
* running local stack just to draw a picture that comes entirely out of this
|
|
11
|
-
* repo. It no longer does: `@devdogsuga/events`'s `getClubConfig()` is the
|
|
12
|
-
* authored source those database rows were always reconciled FROM (see
|
|
13
|
-
* `apps/platform/src/server/config/reconcile.ts` in the target repo), so
|
|
14
|
-
* reading it directly here skips the round trip through Postgres rather than
|
|
15
|
-
* duplicating it. {@link EventReader} is the seam the command talks to, so
|
|
16
|
-
* tests never touch the filesystem or the network.
|
|
17
|
-
*/
|
|
18
|
-
/** The seam the command talks to, so tests never open a socket. */
|
|
19
|
-
export interface EventReader {
|
|
20
|
-
meetings(): Promise<EventGraphicSource[]>;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Builds the reader from `@devdogsuga/events`'s committed data file.
|
|
24
|
-
*
|
|
25
|
-
* Replaces `supabaseEvents`, which read the `meetings`/`workshops` tables a
|
|
26
|
-
* reconcile job had already copied this same config into. The config's shape
|
|
27
|
-
* (`schema.ts`) is deliberately a near-mirror of those tables' columns for
|
|
28
|
-
* exactly this reason — see that file's header — so the mapping below is
|
|
29
|
-
* closer to a rename than a translation: `Meeting.id` is the row's `slug`,
|
|
30
|
-
* `Meeting.title` is what the DB called `nameOverride`, and a workshop's
|
|
31
|
-
* `project` (free text in the config) stands in for the DB's
|
|
32
|
-
* `projects.displayName` join.
|
|
33
|
-
*/
|
|
34
|
-
export declare function configEvents(): EventReader;
|
|
35
|
-
/**
|
|
36
|
-
* The meeting's name, in descending order of how much somebody meant it.
|
|
37
|
-
*
|
|
38
|
-
* Mirrors `apps/platform/src/lib/meetingTitle.ts`: an authored name, then the
|
|
39
|
-
* kind, then the workshops it teaches, then the date. Two named workshops read
|
|
40
|
-
* as a heading; three do not, and the date carries it while the agenda below
|
|
41
|
-
* shows the detail.
|
|
42
|
-
*
|
|
43
|
-
* Duplicated rather than imported because it lives in the Next app, which this
|
|
44
|
-
* package cannot depend on. `events.test.ts` pins the chain so a change to one
|
|
45
|
-
* shows up as a failure rather than as a differently-titled poster.
|
|
46
|
-
*/
|
|
47
|
-
export declare function meetingTitle(nameOverride: string | null, kind: Meeting["kind"], startsAt: Date, agenda: readonly string[]): string;
|
package/dist/images/events.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { loadBrandEvent, loadEvents } from "../repo/peers.js";
|
|
2
|
-
/**
|
|
3
|
-
* Builds the reader from `@devdogsuga/events`'s committed data file.
|
|
4
|
-
*
|
|
5
|
-
* Replaces `supabaseEvents`, which read the `meetings`/`workshops` tables a
|
|
6
|
-
* reconcile job had already copied this same config into. The config's shape
|
|
7
|
-
* (`schema.ts`) is deliberately a near-mirror of those tables' columns for
|
|
8
|
-
* exactly this reason — see that file's header — so the mapping below is
|
|
9
|
-
* closer to a rename than a translation: `Meeting.id` is the row's `slug`,
|
|
10
|
-
* `Meeting.title` is what the DB called `nameOverride`, and a workshop's
|
|
11
|
-
* `project` (free text in the config) stands in for the DB's
|
|
12
|
-
* `projects.displayName` join.
|
|
13
|
-
*/
|
|
14
|
-
export function configEvents() {
|
|
15
|
-
return {
|
|
16
|
-
async meetings() {
|
|
17
|
-
const { getClubConfig } = await loadEvents();
|
|
18
|
-
const { EVENT_SEGMENT_VISUALS, meetingCardDetail, meetingLocation } = await loadBrandEvent();
|
|
19
|
-
// Newest first, matching the DB reader's `order("startsAt", {
|
|
20
|
-
// ascending: false })` — the picker should offer the most recent
|
|
21
|
-
// meeting first, not whatever order the config file happens to list
|
|
22
|
-
// them in.
|
|
23
|
-
const meetings = [...getClubConfig().meetings].sort((a, b) => Date.parse(b.startsAt) - Date.parse(a.startsAt));
|
|
24
|
-
return meetings.map((row) => {
|
|
25
|
-
const meeting = {
|
|
26
|
-
slug: row.id,
|
|
27
|
-
startsAt: new Date(row.startsAt),
|
|
28
|
-
endsAt: new Date(row.endsAt),
|
|
29
|
-
building: row.building,
|
|
30
|
-
location: row.location,
|
|
31
|
-
kind: row.kind,
|
|
32
|
-
cancelledAt: row.cancelledAt ? new Date(row.cancelledAt) : null,
|
|
33
|
-
cancellationReason: row.cancellationReason,
|
|
34
|
-
};
|
|
35
|
-
// No competitions here either, for the same reason `supabaseEvents`
|
|
36
|
-
// dropped them: a competition is a mirrored GitHub issue now, not
|
|
37
|
-
// config a meeting kicks off. Every agenda item is plain "workshop".
|
|
38
|
-
const items = row.agenda.map((workshop) => ({
|
|
39
|
-
label: workshopLabel(workshop),
|
|
40
|
-
segment: "workshop",
|
|
41
|
-
}));
|
|
42
|
-
const detail = meetingCardDetail({
|
|
43
|
-
meeting,
|
|
44
|
-
title: meetingTitle(row.title, row.kind, meeting.startsAt, items.map((item) => item.label)),
|
|
45
|
-
agenda: items.map((item) => `${EVENT_SEGMENT_VISUALS[item.segment].label}: ${item.label}`),
|
|
46
|
-
location: meetingLocation(row.building, row.location),
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
slug: row.id,
|
|
50
|
-
hint: describeMeeting(meeting),
|
|
51
|
-
detail,
|
|
52
|
-
items: items.map((item) => {
|
|
53
|
-
const visual = EVENT_SEGMENT_VISUALS[item.segment];
|
|
54
|
-
return {
|
|
55
|
-
stem: slugPart(item.label),
|
|
56
|
-
detail: {
|
|
57
|
-
...detail,
|
|
58
|
-
title: item.label,
|
|
59
|
-
agenda: undefined,
|
|
60
|
-
badge: { label: visual.label, accent: visual.accent },
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}),
|
|
64
|
-
};
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
/** A readable path component for a workshop card. */
|
|
70
|
-
function slugPart(label) {
|
|
71
|
-
return (label
|
|
72
|
-
.toLowerCase()
|
|
73
|
-
.normalize("NFKD")
|
|
74
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
75
|
-
.replace(/^-|-$/g, "") || "agenda-item");
|
|
76
|
-
}
|
|
77
|
-
/** Unlike the old DB `WorkshopRow`, `Workshop.title` is non-nullable in the
|
|
78
|
-
* config schema (`workshopSchema`), so there is no `projects.displayName`
|
|
79
|
-
* fallback left to reach for. */
|
|
80
|
-
function workshopLabel(row) {
|
|
81
|
-
return row.title;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* The meeting's name, in descending order of how much somebody meant it.
|
|
85
|
-
*
|
|
86
|
-
* Mirrors `apps/platform/src/lib/meetingTitle.ts`: an authored name, then the
|
|
87
|
-
* kind, then the workshops it teaches, then the date. Two named workshops read
|
|
88
|
-
* as a heading; three do not, and the date carries it while the agenda below
|
|
89
|
-
* shows the detail.
|
|
90
|
-
*
|
|
91
|
-
* Duplicated rather than imported because it lives in the Next app, which this
|
|
92
|
-
* package cannot depend on. `events.test.ts` pins the chain so a change to one
|
|
93
|
-
* shows up as a failure rather than as a differently-titled poster.
|
|
94
|
-
*/
|
|
95
|
-
export function meetingTitle(nameOverride, kind, startsAt, agenda) {
|
|
96
|
-
if (nameOverride !== null)
|
|
97
|
-
return nameOverride;
|
|
98
|
-
if (kind !== null)
|
|
99
|
-
return kind;
|
|
100
|
-
if (agenda.length === 1)
|
|
101
|
-
return `Workshop: ${agenda[0]}`;
|
|
102
|
-
if (agenda.length === 2)
|
|
103
|
-
return `Workshop: ${agenda[0]} & ${agenda[1]}`;
|
|
104
|
-
return startsAt.toLocaleDateString("en-US", {
|
|
105
|
-
timeZone: "America/New_York",
|
|
106
|
-
dateStyle: "long",
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
/** One line for the picker: when it is, and whether it is still on. */
|
|
110
|
-
function describeMeeting(meeting) {
|
|
111
|
-
const when = meeting.startsAt.toLocaleDateString("en-US", {
|
|
112
|
-
timeZone: "America/New_York",
|
|
113
|
-
dateStyle: "medium",
|
|
114
|
-
});
|
|
115
|
-
return meeting.cancelledAt === null ? when : `${when}, cancelled`;
|
|
116
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { EventDetail, Format, OpenGraphModule } from "./open-graph-types.js";
|
|
2
|
-
import type { ReactElement } from "react";
|
|
3
|
-
/**
|
|
4
|
-
* What the club draws pictures OF, as opposed to what size it draws them at.
|
|
5
|
-
*
|
|
6
|
-
* Those are two axes and this file owns one of them; `@devdogsuga/open-graph`'s
|
|
7
|
-
* `formats.ts` owns the other. Keeping them apart is the point: an event poster
|
|
8
|
-
* for the GDG on Campus platform is the pairing "this meeting" x "that
|
|
9
|
-
* platform's banner", and while they were one flat list of hard-coded
|
|
10
|
-
* (content, size) targets, no such pairing could be asked for — only the ones
|
|
11
|
-
* somebody had already written down existed.
|
|
12
|
-
*
|
|
13
|
-
* The matrix is SPARSE, and each graphic declares its own column of it. Every
|
|
14
|
-
* graphic supports the two GDG renditions, because anything the club makes may
|
|
15
|
-
* end up on that platform. Beyond that: `savvycal` is one cover on one
|
|
16
|
-
* scheduling page and belongs to the club lockup alone, the email densities
|
|
17
|
-
* belong to the lockup too, and the icon sizes belong to the apps.
|
|
18
|
-
*/
|
|
19
|
-
/** Where a rendered file goes, relative to the repo root. */
|
|
20
|
-
export interface Destination {
|
|
21
|
-
dir: string;
|
|
22
|
-
file: string;
|
|
23
|
-
}
|
|
24
|
-
export interface Graphic {
|
|
25
|
-
/** `group/name`, which is what the CLI matches and globs against. */
|
|
26
|
-
name: string;
|
|
27
|
-
group: string;
|
|
28
|
-
/** The leaf. Unique across every graphic — `assertUniqueStems` enforces it. */
|
|
29
|
-
stem: string;
|
|
30
|
-
/** Format names this can be rendered at. */
|
|
31
|
-
formats: string[];
|
|
32
|
-
/** One line: what this picture is. */
|
|
33
|
-
why: string;
|
|
34
|
-
render: (format: Format) => ReactElement;
|
|
35
|
-
/** Where `--default-out` puts it. */
|
|
36
|
-
destination: (format: Format) => Destination;
|
|
37
|
-
}
|
|
38
|
-
/** A meeting, already loaded and formatted, ready to be drawn. */
|
|
39
|
-
export interface EventGraphicSource {
|
|
40
|
-
slug: string;
|
|
41
|
-
detail: EventDetail;
|
|
42
|
-
/** For the picker's hint: when it is, and whether it is on. */
|
|
43
|
-
hint: string;
|
|
44
|
-
/** Separate cards generated only when the night contains multiple items. */
|
|
45
|
-
items?: Array<{
|
|
46
|
-
detail: EventDetail;
|
|
47
|
-
stem: string;
|
|
48
|
-
}>;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* One graphic per meeting.
|
|
52
|
-
*
|
|
53
|
-
* These are the reason the two axes were split at all: "this meeting" x "the
|
|
54
|
-
* GDG platform's banner" is a pairing nobody could have enumerated in advance.
|
|
55
|
-
*
|
|
56
|
-
* Their default directory is the one that is NOT in the repo. Every other
|
|
57
|
-
* graphic is a committed asset the club re-renders; an event poster is an
|
|
58
|
-
* export headed somewhere else, made once for one night. Writing those into
|
|
59
|
-
* `public/brand` would fill a tracked directory with a growing pile of
|
|
60
|
-
* one-offs, so they go to {@link EVENT_SCRATCH_DIR}, which `.gitignore` covers.
|
|
61
|
-
*/
|
|
62
|
-
export declare const EVENT_SCRATCH_DIR = ".images/events";
|
|
63
|
-
export declare function eventGraphics(og: OpenGraphModule, events: EventGraphicSource[]): Graphic[];
|
|
64
|
-
/** Everything that does not need a database. */
|
|
65
|
-
export declare function staticGraphics(): Promise<Graphic[]>;
|
|
66
|
-
/**
|
|
67
|
-
* Every leaf is unique, so a flat `--out` directory cannot silently overwrite
|
|
68
|
-
* one graphic's file with another's.
|
|
69
|
-
*/
|
|
70
|
-
export declare function assertUniqueStems(graphics: Graphic[]): void;
|