Skip to content

Commit

Permalink
Feature/add prompt library and fix typescript (#874)
Browse files Browse the repository at this point in the history
* wip: working prompt library with commander to fire off prompt and create to handle output json

* * refactor typescript approach: all typescript packages will contain a tsconfig that extends the base config
* rename module.export files back to .js extensions and remove allowJS from typescript config
* base file no longer compiles to dist and base file will no longer allow for JS to be compile alongside TS files
* particle-cli: fix testing type error and add repo name option

* WIP clean up types and add conditional queries to inquierer

* remove duplicate type ConfigurationOptions and use ConfigOptions in check for custom

* remove babel from project as it is unnecessary alongside typescript

* add Particle to keywords

* add more types and swap single options to booleans

* update readme for devs regarding new workflow with individual packages compiling to typescript within lib.
  • Loading branch information
FrederickEngelhardt authored Jul 1, 2020
1 parent e19f84f commit d6b0d7b
Show file tree
Hide file tree
Showing 16 changed files with 317 additions and 57 deletions.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ node_modules
# Errors
npm-debug.log
php_errors.log
lerna-debug.log

# Typescript
dist
# Typescript outdir
packages/*/lib
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ TBD
1. Clone the repo
1. Run `npm install`, This will install all dev dependencies and run the postinstall script `lerna:install` which runs `lerna bootstrap --nohoist` and installs all package dependencies.
1. If you update subdependencies, simply run `npm install` or `npm run lerna:install` to re-install lerna package dependencies. This is especially helpful when you are pulling in new code (with sub dependency additionas) from another branch.
1. `npm run build:watch` build the project in the dist folder
1. `npm run build:watch` build the project in the `packages/<PACKAGE_NAME>/lib` folder, packages that do not contain a .tsconfig and do not have a `tsc` and `tsc:build` script will be ignored lerna executes packages that contain only these scripts.
1. For all packages, simply execute the target file such as `node packages/particle-cli/lib/bin/particle-cli.js init` to run things, typescript must be compiled for typescript packages to execute
1. `npm run test:watch` to start jest in watch mode (recommended)

### Installing A Dependency

1. Run `npm run build`, build will fire off the `tsc` build script and also copy the package.json and README.md files from the `packages/*` directories directly into the dist folder. Alternatively have the compiler in watch mode `npm run build:watch` and run `npm run postbuild` to copy the files in.
1. Cd into `dist/<REPO_NAME>` and run `npm link`, this will link the **bin** alias as an alias in your terminal. Example the bin is named `@phase2/particle-cli` therefore running `npx @phase2/particle-cli -v` will invoke the binary file `particle-cli`.
1. Cd into `package/<PACKAGE_NAME>` and run `npm link`, this will link the **lib/bin** or `main/index.js` alias as an alias in your terminal. Example the bin is named (or aliased) `@phase2/particle-cli` therefore running `npx @phase2/particle-cli -v` will invoke the binary file `particle-cli`.

#### Example

Expand All @@ -41,7 +42,7 @@ particle-cli -V; // works

### Clean the repo

To remove package-lock.json from all levels of the repo simply run this command. PS is used to prevent grep from exiting as this throws an error with `lerna exec` even with the `--no-bail` flag.
To remove package-lock.json from all levels of the repo simply run this command. `ps` is used to prevent grep from exiting as this throws an error with `lerna exec` even with the `--no-bail` flag.

```bash
ps -ef | (grep -q -s -R ^$1 package-lock.json && rm -rf package-lock.json) | { grep -v grep || true; }; lerna exec -- ps -ef | (grep -q -s -R ^$1 package-lock.json && rm -rf package-lock.json) | { grep -v grep || true; }
Expand Down
20 changes: 0 additions & 20 deletions babel.config.js

This file was deleted.

15 changes: 6 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"private": true,
"keywords:": [
"Particle",
"Storybook",
"Pattern Lab",
"Drupal"
Expand All @@ -27,17 +28,16 @@
"pretty-check": "prettier --check packages/**/*.js",
"test": "jest",
"test:watch": "jest --watch",
"build": "tsc -p tsconfig.json && npm run postbuild",
"build": "lerna run --parallel --scope='@phase2/*' tsc && npm run postbuild",
"build:watch": "lerna run --parallel --scope='@phase2/*' tsc:watch",
"postbuild": "ts-node ./src/scripts/build.ts",
"build:watch": "tsc -p tsconfig.json --watch",
"update:check": "npx ncu && npx lerna exec --concurrency 1 --no-bail -- npx ncu",
"update:start": "npm-upgrade && lerna exec --concurrency 1 -- npm-upgrade; npm run lerna:install"
},
"devDependencies": {
"@babel/core": "^7.10.2",
"@babel/preset-env": "^7.10.2",
"@lerna/link": "^3.21.0",
"@types/jest": "^26.0.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"@types/node": "^14.0.14",
"eslint": "^7.2.0",
"jest": "^26.0.1",
"jest-watch-typeahead": "^0.6.0",
Expand All @@ -48,8 +48,5 @@
"ts-jest": "^26.1.0",
"ts-node": "^8.10.2",
"typescript": "^3.9.5"
},
"files": [
"dist/**/*"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ module.exports = {
rules: {
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
},
};
}
9 changes: 5 additions & 4 deletions packages/particle-cli/bin/particle-cli.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/usr/bin/env node

const program = require('commander')
// const pkg = require('../package'); // can't do since this is not copied over into dist unless its an import
import program from 'commander'

import pkg from '../package.json'
const create = require('../lib/create')
import { generatePromptOptions } from '../src/generatePromptOptions'
import create from '../src/create'

/**
* Initialize Commander program with version.
Expand All @@ -16,7 +17,7 @@ program
.description('Scaffold your project from a set of prompts.')
.action(function () {
// @TODO Implement Create Function.
create()
generatePromptOptions().then(create)
})

// allow commander to parse `process.argv`
Expand Down
14 changes: 10 additions & 4 deletions packages/particle-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"init"
],
"bin": {
"@phase2/particle-cli": "bin/particle-cli.js",
"particle-cli": "bin/particle-cli.js"
"@phase2/particle-cli": "lib/bin/particle-cli.js",
"particle-cli": "lib/bin/particle-cli.js"
},
"homepage": "https://github.com/phase2/particle#readme",
"repository": {
Expand All @@ -27,10 +27,16 @@
},
"license": "GPL-2.0",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"tsc": "tsc",
"tsc:watch": "tsc --watch"
},
"dependencies": {
"commander": "^5.1.0",
"esm": "^3.2.25"
"esm": "^3.2.25",
"inquirer": "^7.2.0"
},
"devDependencies": {
"@types/inquirer": "^6.5.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
*
*/

const create = () => {
console.log('Create Particle Project')
const create = (data: any) => {
console.log('Create Particle Project with the data:', data)
return true
}

Expand Down
197 changes: 197 additions & 0 deletions packages/particle-cli/src/generatePromptOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import {
CustomAnswers,
ConfigOptions,
ConfigurationAnswers,
CSSLibraryOptions,
DesignSystemPatternLibraryOptions,
FrontendFrameworkOptions,
TestingLibraryOptions,
} from './types'
import inquirer from 'inquirer'

const prompt = inquirer.createPromptModule()

const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => (
answer: Record<string, string>[]
) => {
if (answer.length < min || (!max ? false : answer.length > max)) {
return `You must choose a minimum of ${min} option(s)${
max ? ` and a maximum of ${max} option(s)` : ''
}`
}
return true
}

const configurationPrompt = (): Promise<ConfigurationAnswers> =>
prompt([
{
type: 'input',
message: 'choose a project name using kebab case, example: "my-project"',
name: 'projectName',
validate: (name: string) => {
if (!name || name.length < 4) {
return 'Please enter a project name of more than 4 characters length'
}
if (name.indexOf(' ') > 0) {
return 'Please enter a project name with no spaces'
}
return true
},
},
{
type: 'input',
message: 'choose a design system name',
name: 'designSystemName',
default: 'default',
validate: (name: string) => {
if (!name || name.length < 4) {
return 'Please enter a repo name of more than 4 characters length'
}
return true
},
},
{
type: 'list',
message: 'Choose a configuration',
name: 'config',
choices: [
{
name:
'modern react (storybook, tailwind, react, typescript, jest | cypress, svgs)',
value: 'modern-react',
},
{ name: 'drupal only (Pattern Lab, Tailwind, Svgs)', value: 'drupal' },
{ name: 'custom', value: 'custom' },
],
},
])

const customPromptOptions = (): Promise<CustomAnswers> => {
return prompt([
{
type: 'checkbox',
message: 'choose a Component/Pattern Library or a Design System',
name: 'designSystem',
choices: [
new inquirer.Separator('-- Design System choose(1 or both)--'),
{
name: 'Storybook',
value: DesignSystemPatternLibraryOptions.STORYBOOK,
checked: true,
},
{
name: 'Pattern Lab',
value: DesignSystemPatternLibraryOptions.PATTERN_LAB,
},
],
validate: minMaxOptionsValidate({ min: 1 }),
},
{
type: 'checkbox',
message: 'What frontend framework are you using with Storybook?',
name: 'frontendFramework',
choices: [
{
name: 'React',
checked: true,
value: FrontendFrameworkOptions.REACT,
},
{
name: 'Webcomponents',
value: FrontendFrameworkOptions.WEBCOMPONENTS,
},
],
// PR up for docs on inquirer to annotate second param answers https://github.com/SBoudrias/Inquirer.js/pull/936
filter: (value: FrontendFrameworkOptions[], answers: CustomAnswers) => {
if (
answers.designSystem.includes(
DesignSystemPatternLibraryOptions.PATTERN_LAB
)
) {
return [FrontendFrameworkOptions.TWIG, ...value]
}
return value
// throw new Error(answers.designSystem)
// input will { answers, values } as we are modifying the return value in the choices section
},
when: (answers: CustomAnswers) => {
// Checks to see if we enabled typescript previously then asks the prompt
if (
new Set(answers.designSystem).has(
DesignSystemPatternLibraryOptions.STORYBOOK
)
) {
return true
}

// Mutates answers object adds twig to selected choice (does not prompt user)
answers.frontendFramework = [FrontendFrameworkOptions.TWIG]

return false
},
},
{
type: 'list',
message: 'Choose a CSS library',
name: 'cssLibrary',
choices: [
{ name: 'Tailwind', checked: true, value: CSSLibraryOptions.TAILWIND },
{ name: 'Sass', value: CSSLibraryOptions.SASS },
{
name: 'Bootstrap',
disabled: true,
value: CSSLibraryOptions.BOOTSTRAP,
},
],
},
{
type: 'confirm',
message: 'Do you want to use typescript?',
name: 'hasTypescript',
},
{
type: 'confirm',
message: 'Do you want ESModule support for typescript?',
name: 'typescriptEsm',
when: (answer: CustomAnswers) => {
// Checks to see if we enabled typescript previously then asks the prompt
if (answer.hasTypescript) {
return true
}
return false
},
},
{
type: 'confirm',
name: 'Are you using SVGs?',
},
{
type: 'checkbox',
message: 'What testing libraries do you want to use?',
name: 'testingLibraries',
choices: [
{ name: 'Jest', value: TestingLibraryOptions.JEST },
{ name: 'Cypress', value: TestingLibraryOptions.CYPRESS },
{ name: 'Selenium', value: TestingLibraryOptions.SELENIUM },
{
name: 'Loki (Storybook only VRT)',
value: TestingLibraryOptions.LOKI,
},
],
validate: minMaxOptionsValidate({ min: 1 }),
},
])
}

/**
* Returns a promise with a json schema
* The JSON schema will be used for the create method to generate files based off of the options selected in the prompt
*/
export const generatePromptOptions = async () => {
const results = await configurationPrompt()
if (results.config === ConfigOptions.CUSTOM) {
return customPromptOptions()
}

return Promise.resolve(results)
}
Loading

0 comments on commit d6b0d7b

Please sign in to comment.