Skip to content

Commit

Permalink
Fix CLI flags for init command (#516)
Browse files Browse the repository at this point in the history
* Adjust init flags docs and default values

* Add --install-deps and --no-install-deps flags to init

* Fix parsing process.argv for flags

* Changeset

* Update Oclif manifest

* Feedback update
  • Loading branch information
frandiox authored Feb 16, 2023
1 parent b5e9061 commit 061d1e1
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .changeset/eight-lemons-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@shopify/cli-hydrogen': patch
'@shopify/create-hydrogen': patch
---

Fix CLI flags for init command, and add `--install-deps`.
2 changes: 1 addition & 1 deletion packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"4.0.5","commands":{"hydrogen:build":{"id":"hydrogen:build","description":"Builds a Hydrogen storefront for production.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"sourcemap":{"name":"sourcemap","type":"boolean","description":"Generate sourcemaps for the build.","allowNo":false},"disable-route-warning":{"name":"disable-route-warning","type":"boolean","description":"Disable warning about missing standard routes.","allowNo":false}},"args":[]},"hydrogen:check":{"id":"hydrogen:check","description":"Returns diagnostic information about a Hydrogen storefront.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[{"name":"resource","description":"The resource to check. Currently only 'routes' is supported.","required":true,"options":["routes"]}]},"hydrogen:dev":{"id":"hydrogen:dev","description":"Runs Hydrogen storefront in an Oxygen worker for development.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"port":{"name":"port","type":"option","description":"Port to run the server on.","multiple":false,"default":3000},"disable-virtual-routes":{"name":"disable-virtual-routes","type":"boolean","description":"Disable rendering fallback routes when a route file doesn't exist","allowNo":false}},"args":[]},"hydrogen:init":{"id":"hydrogen:init","description":"Creates a new Hydrogen storefront.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"language":{"name":"language","type":"option","description":"Sets the template language to use. One of `js` or `ts`.","multiple":false,"default":"js"},"template":{"name":"template","type":"option","description":"Sets the template to use. One of `demo-store` or `hello-world`.","multiple":false}},"args":[]},"hydrogen:preview":{"id":"hydrogen:preview","description":"Runs a Hydrogen storefront in an Oxygen worker for production.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"port":{"name":"port","type":"option","description":"Port to run the server on.","multiple":false,"default":3000}},"args":[]},"hydrogen:generate:route":{"id":"hydrogen:generate:route","description":"Generates a standard Shopify route.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"adapter":{"name":"adapter","type":"option","description":"Remix adapter used in the route. The default is `@shopify/remix-oxygen`.","multiple":false},"typescript":{"name":"typescript","type":"boolean","description":"Generate TypeScript files","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[{"name":"route","description":"The route to generate. One of home,page,cart,products,collections,policies,robots,sitemap,account,all.","required":true,"options":["home","page","cart","products","collections","policies","robots","sitemap","account","all"]}]},"hydrogen:generate:routes":{"id":"hydrogen:generate:routes","description":"Generates all supported standard shopify routes.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"adapter":{"name":"adapter","type":"option","description":"Remix adapter used in the route. The default is `@shopify/remix-oxygen`.","multiple":false},"typescript":{"name":"typescript","type":"boolean","description":"Generate TypeScript files","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[]}}}
{"version":"4.0.5","commands":{"hydrogen:build":{"id":"hydrogen:build","description":"Builds a Hydrogen storefront for production.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"sourcemap":{"name":"sourcemap","type":"boolean","description":"Generate sourcemaps for the build.","allowNo":false},"disable-route-warning":{"name":"disable-route-warning","type":"boolean","description":"Disable warning about missing standard routes.","allowNo":false}},"args":[]},"hydrogen:check":{"id":"hydrogen:check","description":"Returns diagnostic information about a Hydrogen storefront.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[{"name":"resource","description":"The resource to check. Currently only 'routes' is supported.","required":true,"options":["routes"]}]},"hydrogen:dev":{"id":"hydrogen:dev","description":"Runs Hydrogen storefront in an Oxygen worker for development.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"port":{"name":"port","type":"option","description":"Port to run the server on.","multiple":false,"default":3000},"disable-virtual-routes":{"name":"disable-virtual-routes","type":"boolean","description":"Disable rendering fallback routes when a route file doesn't exist","allowNo":false}},"args":[]},"hydrogen:init":{"id":"hydrogen:init","description":"Creates a new Hydrogen storefront.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the new Hydrogen storefront.","multiple":false},"language":{"name":"language","type":"option","description":"Sets the template language to use. One of `js` or `ts`.","multiple":false},"template":{"name":"template","type":"option","description":"Sets the template to use. One of `demo-store` or `hello-world`.","multiple":false},"install-deps":{"name":"install-deps","type":"boolean","description":"Auto install dependencies using the active package manager","allowNo":true}},"args":[]},"hydrogen:preview":{"id":"hydrogen:preview","description":"Runs a Hydrogen storefront in an Oxygen worker for production.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"port":{"name":"port","type":"option","description":"Port to run the server on.","multiple":false,"default":3000}},"args":[]},"hydrogen:generate:route":{"id":"hydrogen:generate:route","description":"Generates a standard Shopify route.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"adapter":{"name":"adapter","type":"option","description":"Remix adapter used in the route. The default is `@shopify/remix-oxygen`.","multiple":false},"typescript":{"name":"typescript","type":"boolean","description":"Generate TypeScript files","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[{"name":"route","description":"The route to generate. One of home,page,cart,products,collections,policies,robots,sitemap,account,all.","required":true,"options":["home","page","cart","products","collections","policies","robots","sitemap","account","all"]}]},"hydrogen:generate:routes":{"id":"hydrogen:generate:routes","description":"Generates all supported standard shopify routes.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"adapter":{"name":"adapter","type":"option","description":"Remix adapter used in the route. The default is `@shopify/remix-oxygen`.","multiple":false},"typescript":{"name":"typescript","type":"boolean","description":"Generate TypeScript files","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[]}}}
67 changes: 35 additions & 32 deletions packages/cli/src/commands/hydrogen/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,38 @@ import {
import {renderFatalError} from '@shopify/cli-kit/node/ui';
import Flags from '@oclif/core/lib/flags.js';
import {output, path} from '@shopify/cli-kit';
import {commonFlags} from '../../utils/flags.js';
import {commonFlags, parseProcessFlags} from '../../utils/flags.js';
import {transpileProject} from '../../utils/transpile-ts.js';
import {getLatestTemplates} from '../../utils/template-downloader.js';
import {readdir} from 'fs/promises';

const STARTER_TEMPLATES = ['hello-world', 'demo-store'];
const FLAG_MAP = {f: 'force'} as Record<string, string>;

export default class Init extends Command {
static description = 'Creates a new Hydrogen storefront.';
static flags = {
path: commonFlags.path,
force: commonFlags.force,
path: Flags.string({
description: 'The path to the directory of the new Hydrogen storefront.',
env: 'SHOPIFY_HYDROGEN_FLAG_PATH',
}),
language: Flags.string({
description: 'Sets the template language to use. One of `js` or `ts`.',
choices: ['js', 'ts'],
default: 'js',
env: 'SHOPIFY_HYDROGEN_FLAG_LANGUAGE',
}),
template: Flags.string({
description:
'Sets the template to use. One of `demo-store` or `hello-world`.',
choices: STARTER_TEMPLATES,
env: 'SHOPIFY_HYDROGEN_FLAG_TEMPLATE',
}),
'install-deps': Flags.boolean({
description: 'Auto install dependencies using the active package manager',
env: 'SHOPIFY_HYDROGEN_INSTALL_DEPS',
allowNo: true,
}),
};

async run(): Promise<void> {
Expand All @@ -46,7 +57,8 @@ export async function runInit(
language?: string;
token?: string;
force?: boolean;
} = getProcessFlags(),
installDeps?: boolean;
} = parseProcessFlags(process.argv, FLAG_MAP),
) {
supressNodeExperimentalWarnings();

Expand All @@ -67,7 +79,7 @@ export async function runInit(
type: 'select',
name: 'template',
message: 'Choose a template',
choices: ['hello-world', 'demo-store'].map((value) => ({
choices: STARTER_TEMPLATES.map((value) => ({
name: value.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()),
value,
})),
Expand Down Expand Up @@ -157,20 +169,24 @@ export async function runInit(
let packageManager = await packageManagerUsedForCreating();

if (packageManager !== 'unknown') {
const {installDeps} = await ui.prompt([
{
type: 'select',
name: 'installDeps',
message: `Install dependencies with ${packageManager}?`,
choices: [
{name: 'Yes', value: 'true'},
{name: 'No', value: 'false'},
],
default: 'true',
},
]);

if (installDeps === 'true') {
const installDeps =
options.installDeps ??
(
await ui.prompt([
{
type: 'select',
name: 'installDeps',
message: `Install dependencies with ${packageManager}?`,
choices: [
{name: 'Yes', value: 'true'},
{name: 'No', value: 'false'},
],
default: 'true',
},
])
).installDeps === 'true';

if (installDeps) {
await installNodeModules({
directory: projectDir,
packageManager,
Expand Down Expand Up @@ -215,19 +231,6 @@ async function projectExists(projectDir: string) {
);
}

// When calling runInit from @shopify/create-hydrogen,
// parse the flags from the process arguments:
function getProcessFlags() {
const flagMap = {f: 'force'} as Record<string, string>;
const [, , ...flags] = process.argv;

return flags.reduce((acc, flag) => {
const [key, value = true] = flag.replace(/^\-+/, '').split('=');
const mappedKey = flagMap[key!] || key!;
return {...acc, [mappedKey]: value};
}, {});
}

function supressNodeExperimentalWarnings() {
const warningListener = process.listeners('warning')[0]!;
if (warningListener) {
Expand Down
29 changes: 28 additions & 1 deletion packages/cli/src/utils/flags.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {describe, it, expect} from 'vitest';
import {flagsToCamelObject} from './flags.js';
import {flagsToCamelObject, parseProcessFlags} from './flags.js';

describe('CLI flag utils', () => {
it('turns kebab-case flags to camelCase', async () => {
Expand All @@ -15,4 +15,31 @@ describe('CLI flag utils', () => {
flag: 'value',
});
});

it('parses flags from process.argv', async () => {
expect(
parseProcessFlags(
'node ./bin --force --install-deps --template hello-world --path test --language ts'.split(
' ',
),
),
).toMatchObject({
force: true,
installDeps: true,
template: 'hello-world',
path: 'test',
language: 'ts',
});

expect(
parseProcessFlags(
'node ./bin -f --no-install-deps --language js'.split(' '),
{f: 'force'},
),
).toMatchObject({
force: true,
installDeps: false,
language: 'js',
});
});
});
35 changes: 35 additions & 0 deletions packages/cli/src/utils/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,38 @@ export function flagsToCamelObject(obj: Record<string, any>) {
return acc;
}, {} as any);
}

/**
* Parse process arguments into an object for use in the cli as flags.
* This is used when starting the init command from create-hydrogen without Oclif.
* @example
* input: `node ./bin --force --no-install-deps --language js`
* output: { force: true, installDeps: false, language: 'js' }
*/
export function parseProcessFlags(
processArgv: string[],
flagMap: Record<string, string> = {},
) {
const [, , ...args] = processArgv;

const options = {} as Record<string, string | boolean>;

for (let i = 0; i < args.length; i++) {
const arg = args[i];
const nextArg = args[i + 1];

if (arg?.startsWith('-')) {
let key = arg.replace(/^\-{1,2}/, '');
let value = !nextArg || nextArg.startsWith('-') ? true : nextArg;

if (value === true && key.startsWith('no-')) {
value = false;
key = key.replace('no-', '');
}

options[flagMap[key] || key] = value;
}
}

return flagsToCamelObject(options);
}

0 comments on commit 061d1e1

Please sign in to comment.