This repository has been archived by the owner on Jul 8, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add library and cli args to lsc create app
- Loading branch information
Rafael Calpena Rodrigues
committed
Aug 18, 2020
1 parent
6a4c3fd
commit a5debee
Showing
8 changed files
with
194 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { sync as commandExistsSync } from 'command-exists'; | ||
import { join } from 'path'; | ||
import { SpawnSyncStrict } from './spawn-sync-strict'; | ||
|
||
const defaultSpawnOptions = { stdio: 'inherit', encoding: 'utf-8' } as const; | ||
|
||
export function bootstrapUIPackage(answers: any, context) { | ||
let isGitInstalled = commandExistsSync('git'); | ||
|
||
/** Helper to exit process in case one sub-process fails */ | ||
const spawnSyncStrict = SpawnSyncStrict(({ status }, a, b) => { | ||
context.log.error(`Bootstrapping process failed because sub-task "${a} ${b.join(' ')}" did not succeed (status ${status})`); | ||
return 1; | ||
}); | ||
|
||
/* Initialize before npm i because husky requires git for setup */ | ||
if (isGitInstalled) { | ||
context.log.info(`Git is installed, initializing repo.`); | ||
spawnSyncStrict('git', ['init'], defaultSpawnOptions); | ||
} | ||
|
||
/* Some files should be automatically generated. Set them to read-only to avoid user manipulation */ | ||
if (commandExistsSync('chmod')) { | ||
const uiModulesFolder = join(process.cwd(), 'projects', `${answers.appNameSlug}-app`, 'src', 'app', 'ui-modules'); | ||
context.log.info(`Setting files as read-only to avoid overwrite`); | ||
spawnSyncStrict('chmod', ['444', 'menu-items.ts', 'module-routes.ts'], {...defaultSpawnOptions, cwd: uiModulesFolder }); | ||
} | ||
else { | ||
context.log.info(`OS does not contain chmod command.`); | ||
} | ||
|
||
/* Run NPM Install */ | ||
context.log.info(`Installing npm dependencies.`); | ||
spawnSyncStrict('npm', ['i'], defaultSpawnOptions); | ||
|
||
/* Create initial commit */ | ||
if (isGitInstalled) { | ||
context.log.info(`Committing initial work.`); | ||
spawnSyncStrict('git', ['add', '-A']); | ||
spawnSyncStrict('git', ['commit', '-m', 'chore: create lsc ui app'], defaultSpawnOptions); | ||
} | ||
|
||
/* Build library for first usage in development */ | ||
context.log.info(`Building library for development`); | ||
spawnSyncStrict('npm', ['run', 'build:lib'], defaultSpawnOptions); | ||
|
||
context.log.info(`lsc create app done.`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import * as yargs from 'yargs'; | ||
import { flipObject } from './flip-object'; | ||
import { getCLIAnswers } from './get-cli-answers'; | ||
|
||
/** Mapping of CLI argument names to respective prompt names */ | ||
export const argsToPromptNames = { | ||
type: 'projectType', | ||
name: 'appName', | ||
description: 'appDescription', | ||
y: 'moveon' | ||
} as const; | ||
|
||
/** Inverse mapping of argsToPromptNames */ | ||
const promptNamesToArgs = flipObject(argsToPromptNames); | ||
|
||
/** Available CLI arguments for programmatic use. */ | ||
const defaultCLIArgs = yargs.options({ | ||
type: { type: 'string', choices: ['cli', 'api', 'ui'] }, | ||
name: { type: 'string' }, | ||
y: { type: 'boolean' } | ||
}).argv; | ||
|
||
/** Default App Name based on CWD */ | ||
const defaultAppName = process | ||
.cwd() | ||
.split('/') | ||
.pop() | ||
.split('\\') | ||
.pop(); | ||
|
||
/** Prompt options */ | ||
export const defaultPrompts = [ | ||
{ | ||
name: 'projectType', | ||
message: 'Which type of LabShare package do you want to create?', | ||
type: 'list', | ||
default: 'cli', | ||
choices: ['cli', 'api', 'ui'], | ||
}, | ||
{ | ||
name: 'appName', | ||
message: 'What is the name of your project?', | ||
default: defaultAppName, | ||
}, | ||
{ | ||
name: 'appDescription', | ||
message: 'What is the description?', | ||
}, | ||
{ | ||
type: 'confirm', | ||
name: 'moveon', | ||
message: 'Continue?', | ||
}, | ||
] as const; | ||
|
||
export const getPromptNameFor = (key: keyof typeof argsToPromptNames) => { | ||
return argsToPromptNames[key]; | ||
}; | ||
|
||
/** Decides if a prompt should be showed to the user. | ||
* Returns false in case the equivalent cli argument has been provided. | ||
*/ | ||
const shouldPrompt = (cliArgs) => (prompt) => { | ||
const { name } = prompt; | ||
const arg = promptNamesToArgs[name]; | ||
return (typeof arg === 'string') ? !cliArgs[arg] : true; | ||
}; | ||
|
||
/** Read arguments provided in the CLI and return remaining prompts to be used. */ | ||
export const readArguments = (prompts: typeof defaultPrompts) => { | ||
return { | ||
prompts: prompts.filter(shouldPrompt(defaultCLIArgs)), | ||
cliAnswers: getCLIAnswers(defaultCLIArgs) | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** Transform object keys into values and vice-versa */ | ||
export const flipObject = <O extends Record<string, string>>(object: O) : Invert<O> => { | ||
return Object.keys(object).reduce((result, key) => { | ||
result[object[key]] = key; | ||
return result; | ||
}, {} as Invert<O>); | ||
}; | ||
|
||
|
||
/* Adapted from https://stackoverflow.com/questions/56415826/is-it-possible-to-precisely-type-invert-in-typescript */ | ||
type KeyFromValue<V, T extends Record<string, string>> = { | ||
[K in keyof T]: V extends T[K] ? K : never | ||
}[keyof T]; | ||
|
||
type Invert<T extends Record<string, string>> = { | ||
[V in T[keyof T]]: KeyFromValue<V, T> | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { argsToPromptNames, getPromptNameFor } from "./create-utils"; | ||
|
||
/** Return CLI answers mapped to prompt names */ | ||
export const getCLIAnswers = (cliArgs) => { | ||
return Object.keys(cliArgs).filter( | ||
k => k in argsToPromptNames | ||
).reduce((obj, key: keyof typeof argsToPromptNames) => { | ||
obj[getPromptNameFor(key)] = cliArgs[key]; | ||
return obj; | ||
}, {}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function padLeft(dateValue: number) { | ||
return dateValue < 10 ? '0' + dateValue : dateValue.toString(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { spawnSync } from 'child_process'; | ||
type onErrorFn = (spawnObj: ReturnType<typeof spawnSync>, ...args: spawnSyncParameters) => number; | ||
type spawnSyncParameters = Parameters<typeof spawnSync>; | ||
|
||
/** Wraps spawnSync to abort main process in case sub process fails */ | ||
export const SpawnSyncStrict = (onError: onErrorFn = () => 1) => (...args: spawnSyncParameters) => { | ||
let wrappedCall = spawnSync(...args); | ||
if (wrappedCall.status !== 0) { | ||
process.exit(onError(wrappedCall, ...args)); | ||
} | ||
return wrappedCall; | ||
}; |