Skip to content

Commit 1f40636

Browse files
committed
chore(core): Update generated entry modules to remove duplicated options parsing
1 parent 8ca409c commit 1f40636

37 files changed

Lines changed: 3804 additions & 1324 deletions

devenv.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
"devenv": {
44
"locked": {
55
"dir": "src/modules",
6-
"lastModified": 1775673122,
7-
"narHash": "sha256-zVHWCQ0kCkaNIubCVv5fZyrPYfI4EGEPLRFRARwGSl8=",
6+
"lastModified": 1775742783,
7+
"narHash": "sha256-2u8i+4s9hx6KEptCtCN2vn2nrFBXPBlTcI4wfVfLAio=",
88
"owner": "cachix",
99
"repo": "devenv",
10-
"rev": "010a22c855d22b127c6fb007868b2f3fc09bc2c8",
10+
"rev": "52fa484bd1a2ccba8c8025d41e41fdace52634e6",
1111
"type": "github"
1212
},
1313
"original": {
@@ -55,11 +55,11 @@
5555
},
5656
"nixpkgs-unstable": {
5757
"locked": {
58-
"lastModified": 1775639890,
59-
"narHash": "sha256-9O9gNidrdzcb7vgKGtff7QiLtr0IsVaCi0pAXm8anhQ=",
58+
"lastModified": 1775701739,
59+
"narHash": "sha256-2FWWY1rr/+pGUJK1npcVcsWNEblzmKs6VxD3VEvwJSs=",
6060
"owner": "nixos",
6161
"repo": "nixpkgs",
62-
"rev": "456e8a9468b9d46bd8c9524425026c00745bc4d2",
62+
"rev": "0f7663154ff2fec150f9dbf5f81ec2785dc1e0db",
6363
"type": "github"
6464
},
6565
"original": {

packages/core/src/components/command-validation-logic.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,7 @@ export function CommandValidationLogic(props: CommandValidationLogicProps) {
115115
option.choices.length > 0
116116
}>
117117
<Show
118-
when={
119-
(option.kind === CommandParameterKinds.string ||
120-
option.kind === CommandParameterKinds.number) &&
121-
option.variadic
122-
}
118+
when={!option.variadic}
123119
fallback={
124120
<ElseIfClause
125121
condition={code`!options${

packages/core/src/components/exec-builtin.tsx

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,18 @@ export function ExecBuiltin(props: ExecBuiltinProps) {
6262
"node:stream": [{ name: "Stream", default: true, type: true }]
6363
})}
6464
builtinImports={defu(rest.builtinImports ?? {}, {
65-
env: ["isWindows"]
65+
env: ["isWindows", "env", { name: "Env", type: true }]
6666
})}>
6767
<FunctionDeclaration
6868
name="resolveCommandEnv"
6969
parameters={[
7070
{
7171
name: "params",
72-
type: "{ argv: string[]; env?: NodeJS.ProcessEnv; baseEnv?: NodeJS.ProcessEnv; }"
72+
type: "{ argv: string[]; env?: NodeJS.ProcessEnv; }"
7373
}
7474
]}
7575
returnType="NodeJS.ProcessEnv">
76-
{code`const baseEnv = params.baseEnv ?? process.env;
77-
const argv = params.argv;
76+
{code`const argv = params.argv;
7877
const shouldSuppressNpmFund = (() => {
7978
const cmd = basename(argv[0] ?? "");
8079
if (cmd === "npm" || cmd === "npm.cmd" || cmd === "npm.exe") {
@@ -88,17 +87,19 @@ const shouldSuppressNpmFund = (() => {
8887
return false;
8988
})();
9089
91-
const mergedEnv = params.env ? { ...baseEnv, ...params.env } : { ...baseEnv };
92-
const resolvedEnv = Object.fromEntries(
93-
Object.entries(mergedEnv)
94-
.filter(([, value]) => value !== undefined)
95-
.map(([key, value]) => [key, String(value)])
90+
const result = Object.fromEntries(
91+
Object.entries({
92+
...env,
93+
...(params.env ?? {})
94+
})
95+
.filter(([, value]) => value !== undefined)
96+
.map(([key, value]) => [key, String(value)])
9697
);
9798
if (shouldSuppressNpmFund) {
98-
resolvedEnv.NPM_CONFIG_FUND ??= "false";
99-
resolvedEnv.npm_config_fund ??= "false";
99+
result.NPM_CONFIG_FUND ??= "false";
100+
result.npm_config_fund ??= "false";
100101
}
101-
return resolvedEnv;`}
102+
return result; `}
102103
</FunctionDeclaration>
103104
<Spacing />
104105
<FunctionDeclaration
@@ -283,7 +284,7 @@ return false;`}
283284
defaultValue="300000"
284285
/>
285286
</TSDoc>
286-
<InterfaceMember name="timeoutMs" type="number" />
287+
<InterfaceMember name="timeoutMs" optional type="number" />
287288
<Spacing />
288289
<TSDoc heading="The current working directory of the child process." />
289290
<InterfaceMember name="cwd" optional type="string" />
@@ -347,43 +348,40 @@ return false;`}
347348
typeof optionsOrTimeoutMs === "number"
348349
? { timeoutMs: optionsOrTimeoutMs }
349350
: optionsOrTimeoutMs;
350-
const { timeoutMs, cwd, input, env, noOutputTimeoutMs } = options;
351-
const hasInput = input !== undefined;
352-
const resolvedEnv = resolveCommandEnv({ argv, env });
353-
const stdio = resolveCommandStdio({ hasInput, preferInherit: true });
351+
const { timeoutMs = 300000, cwd, input, noOutputTimeoutMs } = options;
354352
355-
const finalArgv =
353+
const resolvedArgv =
356354
isWindows
357355
? (resolveNpmArgvForWindows(argv) ?? argv)
358356
: argv;
359357
const resolvedCommand =
360-
finalArgv !== argv
361-
? (finalArgv[0] ?? "")
358+
resolvedArgv !== argv
359+
? (resolvedArgv[0] ?? "")
362360
: resolveCommand(argv[0] ?? "");
363361
const useCmdWrapper = isWindowsBatchCommand(resolvedCommand);
364362
365363
const child = _spawn(
366364
useCmdWrapper
367-
? (process.env.ComSpec ?? "cmd.exe")
365+
? (env.COMSPEC ?? "cmd.exe")
368366
: resolvedCommand,
369367
useCmdWrapper
370368
? [
371369
"/d",
372370
"/s",
373371
"/c",
374-
buildCmdExeCommandLine(resolvedCommand, finalArgv.slice(1))
372+
buildCmdExeCommandLine(resolvedCommand, resolvedArgv.slice(1))
375373
]
376-
: finalArgv.slice(1), {
377-
stdio,
374+
: resolvedArgv.slice(1), {
375+
stdio: resolveCommandStdio({ hasInput: input !== undefined, preferInherit: true }),
378376
cwd,
379-
env: resolvedEnv,
377+
env: resolveCommandEnv({ argv, env: options.env }),
380378
windowsHide: true,
381379
windowsVerbatimArguments: useCmdWrapper
382380
? true
383381
: options.windowsVerbatimArguments,
384382
...(shouldSpawnWithShell({
385383
resolvedCommand: useCmdWrapper
386-
? (process.env.ComSpec ?? "cmd.exe")
384+
? (env.COMSPEC ?? "cmd.exe")
387385
: resolvedCommand,
388386
platform: process.platform
389387
})
@@ -453,7 +451,7 @@ return new Promise((resolve, reject) => {
453451
}, timeoutMs >= 0 ? timeoutMs : Number.POSITIVE_INFINITY);
454452
armNoOutputTimer();
455453
456-
if (hasInput && child.stdin) {
454+
if (input !== undefined && child.stdin) {
457455
child.stdin.write(input ?? "");
458456
child.stdin.end();
459457
}
@@ -506,7 +504,7 @@ return new Promise((resolve, reject) => {
506504
explicitCode: childExitState?.code ?? code,
507505
childExitCode: child.exitCode,
508506
resolvedSignal,
509-
usesWindowsExitCodeShim: isWindows && (useCmdWrapper || finalArgv !== argv),
507+
usesWindowsExitCodeShim: isWindows && (useCmdWrapper || resolvedArgv !== argv),
510508
timedOut,
511509
noOutputTimedOut,
512510
killIssuedByTimeout
@@ -626,23 +624,16 @@ return new Promise((resolve, reject) => {
626624
name="execSync"
627625
parameters={[
628626
{ name: "argv", type: "string[]" },
629-
{ name: "options", type: "SpawnOptions", optional: true }
627+
{ name: "options", type: "SpawnOptions", default: "{}" }
630628
]}
631629
returnType="string">
632630
{code`return execFileSync(argv.length > 0 ? argv[0] : "", argv.slice(1), {
633631
encoding: "utf8",
634632
stdio: ["ignore", "pipe", "ignore"],
635-
timeout: options?.timeoutMs ?? 300000,
636-
env: options?.env ? resolveCommandEnv({ argv, env: options.env }) : process.env,
637-
cwd: options?.cwd,
638-
windowsHide: true,
639-
windowsVerbatimArguments: options?.windowsVerbatimArguments,
640-
...(shouldSpawnWithShell({
641-
resolvedCommand: argv[0] ?? "",
642-
platform: process.platform
643-
})
644-
? { shell: true }
645-
: {})
633+
timeout: options.timeoutMs ?? 300000,
634+
env: resolveCommandEnv({ argv, env: options.env }),
635+
cwd: options.cwd || process.cwd(),
636+
windowsHide: true
646637
}).trim(); `}
647638
</FunctionDeclaration>
648639
<Spacing />

packages/core/src/components/options-parser-logic.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { camelCase } from "@stryke/string-format/camel-case";
3030
import { constantCase } from "@stryke/string-format/constant-case";
3131
import { pascalCase } from "@stryke/string-format/pascal-case";
3232
import { isSetString } from "@stryke/type-checks/is-set-string";
33+
import type { PartialKeys } from "@stryke/types/base";
3334
import { computedOptions } from "../contexts/options";
3435
import {
3536
getDynamicPathSegmentName,
@@ -672,12 +673,10 @@ export function OptionsInterfaceDeclaration(props: { command: CommandTree }) {
672673
);
673674
}
674675

675-
export interface OptionsParserLogicProps {
676-
/**
677-
* The command to generate the options parser logic for.
678-
*/
679-
command: CommandTree;
680-
676+
export interface OptionsParserLogicProps extends PartialKeys<
677+
Pick<CommandTree, "segments" | "options" | "name">,
678+
"segments" | "name"
679+
> {
681680
/**
682681
* The environment variable prefix to use for options that have an associated environment variable. This prefix will be used in the generated code to access the environment variables (e.g., `env.${appSpecificEnvPrefix}_OPTION_NAME`).
683682
*/
@@ -695,11 +694,17 @@ export interface OptionsParserLogicProps {
695694
* The command options parser logic.
696695
*/
697696
export function OptionsParserLogic(props: OptionsParserLogicProps) {
698-
const { command, appSpecificEnvPrefix, isCaseSensitive = false } = props;
697+
const {
698+
segments = [],
699+
options: _options,
700+
name: _name,
701+
appSpecificEnvPrefix,
702+
isCaseSensitive = false
703+
} = props;
699704

700705
const options = computed(() =>
701706
computedOptions(
702-
Object.entries(command.options).map(([name, option]) => ({
707+
Object.entries(_options).map(([name, option]) => ({
703708
...option,
704709
name
705710
}))
@@ -712,7 +717,9 @@ export function OptionsParserLogic(props: OptionsParserLogicProps) {
712717
const
713718
name="options"
714719
initializer={code` {
715-
${Object.entries(options.value)
720+
${
721+
segments.length > 0 ? "...useGlobalOptions(), " : ""
722+
}${Object.entries(options.value)
716723
.filter(
717724
([, option]) =>
718725
option.env ||
@@ -769,11 +776,11 @@ export function OptionsParserLogic(props: OptionsParserLogicProps) {
769776
return "";
770777
})
771778
.join("")}
772-
} as unknown as ${pascalCase(command.name)}Options;`}
779+
} as unknown as ${segments.length > 0 ? pascalCase(_name) : "Global"}Options;`}
773780
/>
774781
<Spacing />
775782
{code`for (let i = 0; i < args.slice(${
776-
command.segments.filter(segment => isDynamicPathSegment(segment)).length
783+
segments.filter(segment => isDynamicPathSegment(segment)).length
777784
}).length; i++) { `}
778785
<hbr />
779786
<VarDeclaration
@@ -791,7 +798,6 @@ export function OptionsParserLogic(props: OptionsParserLogicProps) {
791798
: args[i]; `}
792799
/>
793800
<hbr />
794-
795801
<For each={Object.entries(options.value)} hardline>
796802
{([name, option], i) => (
797803
<Show
@@ -831,7 +837,6 @@ export function OptionsParserLogic(props: OptionsParserLogicProps) {
831837
</Show>
832838
)}
833839
</For>
834-
835840
<hbr />
836841
{code` } `}
837842
<hbr />
@@ -872,7 +877,9 @@ export function CommandParserLogic(props: CommandParserLogicProps) {
872877
/>
873878
<Spacing />
874879
<OptionsParserLogic
875-
command={command}
880+
name={command.name}
881+
options={command.options}
882+
segments={command.segments}
876883
appSpecificEnvPrefix={appSpecificEnvPrefix}
877884
isCaseSensitive={isCaseSensitive}
878885
/>

packages/core/src/components/state-builtin.tsx

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { computedOptions } from "../contexts/options";
4141
import { getAppBin } from "../plugin-utils";
4242
import { getAppTitle } from "../plugin-utils/context-helpers";
4343
import type { Context } from "../types";
44-
import { OptionsMember } from "./options-parser-logic";
44+
import { OptionsMember, OptionsParserLogic } from "./options-parser-logic";
4545

4646
export function GlobalTypeDefinitions() {
4747
const context = usePowerlines<Context>();
@@ -158,6 +158,10 @@ export function ArgsUtilities() {
158158
export function ContextUtilities() {
159159
const context = usePowerlines<Context>();
160160

161+
const options = computed(() =>
162+
Object.fromEntries(context.options.map(option => [option.name, option]))
163+
);
164+
161165
return (
162166
<>
163167
<Spacing />
@@ -174,9 +178,6 @@ export function ContextUtilities() {
174178
heading={`Get the ${getAppTitle(
175179
context
176180
)} application context for the current application.`}>
177-
<TSDocParam name="options">
178-
{`The options to use when getting the context. This can include parameters for how to handle missing contexts, such as whether to throw an error or return undefined. By default, if the context is not available, this function will return undefined.`}
179-
</TSDocParam>
180181
<TSDocReturns>
181182
{`The ${getAppTitle(
182183
context
@@ -199,6 +200,23 @@ export function ContextUtilities() {
199200
{code`return useGlobal()?.inputArgs ?? getInputArgs();`}
200201
</FunctionDeclaration>
201202
<Spacing />
203+
<TSDoc
204+
heading={`A utility hook function to get the command-line global options from the ${getAppTitle(
205+
context
206+
)} application context.`}>
207+
<TSDocReturns>
208+
{
209+
"An object containing the global options from the application context."
210+
}
211+
</TSDocReturns>
212+
</TSDoc>
213+
<FunctionDeclaration
214+
export
215+
name="useGlobalOptions"
216+
returnType="GlobalOptions">
217+
{code`return useGlobal()?.options ?? {};`}
218+
</FunctionDeclaration>
219+
<Spacing />
202220
<TSDoc
203221
heading={`A utility hook function to get the state of the ${getAppTitle(
204222
context
@@ -395,8 +413,20 @@ return result;`}
395413
name="withGlobal"
396414
parameters={[{ name: "handler", type: "() => any" }]}
397415
returnType="Promise<void>">
416+
<VarDeclaration
417+
const
418+
name="args"
419+
initializer={code`getInputArgs(); `}
420+
/>
421+
<Spacing />
422+
<OptionsParserLogic
423+
options={options.value}
424+
appSpecificEnvPrefix={context.config.appSpecificEnvPrefix}
425+
isCaseSensitive={context.config.isCaseSensitive}
426+
/>
427+
<Spacing />
398428
{code`
399-
return unstable_globalStore.run({ options: {} as unknown as GlobalOptions, inputArgs: getInputArgs(), state: { executionId: randomUUID(), status: "initializing", isError: false, meta: new Map() } as GlobalContextState }, handler);`}
429+
return unstable_globalStore.run({ options, inputArgs: args, state: { executionId: randomUUID(), status: "initializing", isError: false, meta: new Map() } as GlobalContextState }, handler);`}
400430
</FunctionDeclaration>
401431
<Spacing />
402432
<TSDoc
@@ -465,7 +495,7 @@ export function StateBuiltin(props: StateBuiltinProps) {
465495
"node:crypto": ["randomUUID"]
466496
})}
467497
builtinImports={defu(rest.builtinImports ?? {}, {
468-
env: ["isCI"]
498+
env: ["isCI", "env"]
469499
})}>
470500
<GlobalTypeDefinitions />
471501
<Spacing />

0 commit comments

Comments
 (0)