From 78916359592f329c305094eb07b10d9a282f8b78 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Thu, 22 Oct 2020 09:43:40 -0700 Subject: [PATCH 01/18] chore: add drupal root prompt. --- .../src/generators/app/generatePromptOptions.ts | 9 +++++++++ .../src/generators/app/index.ts | 10 ++++++++++ packages/particle-types/types.ts | 1 + 3 files changed, 20 insertions(+) diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index 4c06371886..bc9618220e 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -100,6 +100,15 @@ export const configurationPrompt = [ }, ] +export const drupalRootOptions = [ + { + type:'input', + message: 'Where should your Drupal root exist?', + default: `./source/drupal/`, + name: 'drupalRootPath', + } +] + export const customPromptOptions = [ { type: 'checkbox', diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index b7dbb303fa..c443906ed5 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -12,6 +12,7 @@ import { import { configurationPrompt, customPromptOptions, + drupalRootOptions, options as configOptions, } from './generatePromptOptions' @@ -100,6 +101,15 @@ module.exports = class extends Generator { ...results, options: customOptions, } + } else if (results.config === ConfigOptions.DRUPAL) { + const root = await this.prompt(drupalRootOptions) + + this.configuration = { + ...results, + ...root, + options: configOptions[results.config] + } + // ADD DRUPAL LOCATION PROMPT HERE } else { this.configuration = { ...results, diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index cc10351dd1..663c5f9932 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -25,6 +25,7 @@ export interface Naming { projectName: string componentLibraryName: string componentLibraryPath: string + drupalRootPath: string } export enum TestingLibraryOptions { From 8b6a911dede65635c089882b159fc6250c3cf22b Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Thu, 22 Oct 2020 11:36:54 -0700 Subject: [PATCH 02/18] chore: update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e936be4f9..ac8731088a 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ To remove package-lock.json from all levels of the repo simply run this command. 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; } ``` -To remove all typescript lib files run `npm dev:clean:lib` +To remove all typescript lib files run `npm run dev:clean:lib` -To remove all node_modules in packages run `npm dev:clean:node_modules` +To remove all node_modules in packages run `npm run dev:clean:node_modules` ### Upgrading dependencies From 653dc5406f78f211ff799122d8a86320174b2604 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Tue, 27 Oct 2020 10:57:40 -0700 Subject: [PATCH 03/18] chore: Update prompts and associated types to reflect planned changes. --- .../generators/app/generatePromptOptions.ts | 248 +++++++----------- packages/particle-types/types.ts | 34 ++- 2 files changed, 116 insertions(+), 166 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index bc9618220e..cb8bd6dade 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -2,12 +2,9 @@ import inquirer from 'inquirer' import { CustomAnswers, - ConfigOptions, - ConfigurationAnswers, - CSSLibraryOptions, - ComponentLibraryOptions, FrontendFrameworkOptions, TestingLibraryOptions, + DesignTheme, } from '@phase2/particle-types' const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( @@ -21,35 +18,28 @@ const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( return true } -export const options: Record = { - [ConfigOptions.MODERN_REACT]: { - cssLibrary: CSSLibraryOptions.TAILWIND, - componentLibraryTypes: [ComponentLibraryOptions.STORYBOOK], - frontendFramework: [FrontendFrameworkOptions.REACT], - hasSVG: true, - hasTypescript: true, - testingLibraries: [TestingLibraryOptions.JEST], - typescriptEsm: false, - }, - [ConfigOptions.DRUPAL]: { - cssLibrary: CSSLibraryOptions.TAILWIND, - componentLibraryTypes: [ComponentLibraryOptions.PATTERN_LAB], - frontendFramework: [FrontendFrameworkOptions.TWIG], - hasSVG: true, - hasTypescript: false, // TODO find out if there is much benefit especially if most things are TWIG centric - testingLibraries: [ - TestingLibraryOptions.CYPRESS, - TestingLibraryOptions.PA11Y, - ], // How much JS are we actually using for Twig centric functions - typescriptEsm: false, +export const designThemePrompt = () => [ + { + type: 'list', + message: 'What frontend framework are you using with Storybook?', + name: 'frontendFramework', + choices: [ + { + name: 'Webcomponents', + checked: true, + value: FrontendFrameworkOptions.WEBCOMPONENTS + }, + { + name: 'React', + value: FrontendFrameworkOptions.REACT + } + ], }, -} - -export const configurationPrompt = [ { type: 'input', - message: 'choose a project name using kebab case, example: "p2-project"', - name: 'projectName', + message: 'choose a design theme name using kebab case. (min 4 chars) Ex: "alpha"', + name: 'themeName', + default: 'default', validate: (name: string) => { if (!name || name.length < 4) { return 'Please enter a project name of more than 4 characters length' @@ -58,138 +48,102 @@ export const configurationPrompt = [ return 'Please enter a two word project name with no spaces' } return true - }, + } }, { type: 'input', message: - 'choose a component library name using kebab case. example "project-default"', - name: 'componentLibraryName', - default: 'project-default', + 'Where does your design theme exist relative to the root of the project', + default: (answers: DesignTheme) => + `./source/design/${answers.themeName}/`, + name: 'themePath' + } +] + +const compileThemes = async (prev: DesignTheme[]) => { + return [...prev, await inquirer.prompt(designThemePrompt())] +} + +const rerun = [{ + type: 'confirm', + name: 'generateTheme', + message: 'Do you want to add another theme?', + default: true +}] + +export const generatorLoop = async() => { + let loop = true; + let themesArray:DesignTheme[] = [] + do { + themesArray = await compileThemes(themesArray) + loop = await inquirer.prompt(rerun).then(answers => answers.generateTheme) + } while (loop) + + return themesArray; +} + +// export const drupalRootOptions = [ +// { +// type: 'input', +// message: 'Where should your Drupal root exist?', +// default: `./source/drupal/`, +// name: 'drupalRootPath', +// when: (answer: CustomAnswers) => { +// if (answer.hasDrupal) { +// inquirer.prompt(drupalRootOptions) +// } +// } +// } +// ] + +export const propOptions = [ + { + type: 'input', + message: 'choose a abbreviation for your/client\'s name. (min 3 chars) Ex: Home Depot "hdp"', + name: 'clientAbbreviation', validate: (name: string) => { - if (!name || name.length < 4) { - return 'Please enter a library name of more than 4 characters length' + if (!name || name.length < 3) { + return 'Please enter a project name of more than 4 characters length' + } + if (name.indexOf(' ') > 0) { + return 'Please enter a two word project name with no spaces' } return true - }, + } }, { type: 'input', - message: - 'Where does your component library exist relative to the root of the project', - default: (answers: ConfigurationAnswers) => - `./source/${answers.componentLibraryName}/`, - name: 'componentLibraryPath', + message: 'choose a name for the overall project using kebab case. (min 4 chars) Ex: "website", or "saphire-dagger"', + 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 two word project name with no spaces' + } + return true + }, }, { - type: 'list', - message: 'Choose a configuration', - name: 'config', - choices: [ - { - name: - 'modern react (storybook, tailwind, react, typescript, jest | cypress, svgs)', - value: ConfigOptions.MODERN_REACT, - }, - { - name: 'drupal only (Pattern Lab, Tailwind, Svgs)', - value: ConfigOptions.DRUPAL, - }, - { name: 'custom', value: 'custom' }, - ], + type: 'confirm', + message: 'will you be using Drupal?', + name: 'hasDrupal', + default: false, }, -] - -export const drupalRootOptions = [ { - type:'input', + type: 'input', message: 'Where should your Drupal root exist?', default: `./source/drupal/`, name: 'drupalRootPath', - } -] - -export const customPromptOptions = [ - { - type: 'checkbox', - message: 'choose a Component Library', - name: 'componentLibraryTypes', - choices: [ - new inquirer.Separator('-- Component Library choose(1 or both)--'), - { - name: 'Storybook', - value: ComponentLibraryOptions.STORYBOOK, - checked: true, - }, - { - name: 'Pattern Lab', - value: ComponentLibraryOptions.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.componentLibraryTypes.includes( - ComponentLibraryOptions.PATTERN_LAB - ) - ) { - return [FrontendFrameworkOptions.TWIG, ...value] - } - return value - // throw new Error(answers.componentLibrary) - // 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.componentLibraryTypes).has( - ComponentLibraryOptions.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, - }, - ], + when: (answer: CustomAnswers) => { + return answer.hasDrupal + } }, { type: 'confirm', message: 'Do you want to use typescript?', - name: 'hasTypescript', + name: 'hasTypescript' }, { type: 'confirm', @@ -201,11 +155,11 @@ export const customPromptOptions = [ return true } return false - }, + } }, { type: 'confirm', - name: 'Are you using SVGs?', + name: 'Are you using SVGs?' }, { type: 'checkbox', @@ -217,10 +171,10 @@ export const customPromptOptions = [ { name: 'Selenium', value: TestingLibraryOptions.SELENIUM }, { name: 'Loki (Storybook only VRT)', - value: TestingLibraryOptions.LOKI, + value: TestingLibraryOptions.LOKI }, - { name: 'Pa11y', value: TestingLibraryOptions.PA11Y }, + { name: 'Pa11y', value: TestingLibraryOptions.PA11Y } ], - validate: minMaxOptionsValidate({ min: 1 }), - }, + validate: minMaxOptionsValidate({ min: 1 }) + } ] diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index 663c5f9932..efad388b65 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -4,28 +4,16 @@ export enum CSSLibraryOptions { BOOTSTRAP = 'bootstrap', } -export enum ComponentLibraryOptions { - STORYBOOK = 'storybook', - PATTERN_LAB = 'pattern_lab', -} - export enum FrontendFrameworkOptions { - TWIG = 'twig', REACT = 'react', WEBCOMPONENTS = 'webcomponents', } -export enum ConfigOptions { - MODERN_REACT = 'modern_react', - DRUPAL = 'drupal', - CUSTOM = 'custom', -} - export interface Naming { - projectName: string - componentLibraryName: string componentLibraryPath: string drupalRootPath: string + projectName: string + themeName: string } export enum TestingLibraryOptions { @@ -36,20 +24,28 @@ export enum TestingLibraryOptions { SELENIUM = 'selenium', } -export interface ConfigurationAnswers extends Naming { - config: ConfigOptions +export interface DesignTheme { + frontendFramework: FrontendFrameworkOptions, + themeName: string, + themePath: string, } export interface CustomAnswers { - cssLibrary: CSSLibraryOptions - componentLibraryTypes: ComponentLibraryOptions[] - frontendFramework: FrontendFrameworkOptions[] + clientAbbreviation: string + hasDrupal: boolean hasSVG: boolean hasTypescript: boolean + projectName: string testingLibraries: TestingLibraryOptions[] + drupalRootPath?: string typescriptEsm?: boolean } +export interface ConfigurationAnswers { + config: CustomAnswers + designThemes: DesignTheme[] +} + export interface Answers extends Naming { options: CustomAnswers } From 20efda2c83ed3b928f225ae37dfbc2643ce2d829 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Tue, 27 Oct 2020 10:59:26 -0700 Subject: [PATCH 04/18] chore: Update base generator logic. --- .../src/generators/app/index.ts | 73 +++++++------------ 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index c443906ed5..a9eca82c85 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -3,22 +3,17 @@ import merge from 'lodash.merge' import fs from 'fs' import { - Answers, ConfigurationAnswers, - ConfigOptions, - FrontendFrameworkOptions, } from '@phase2/particle-types' import { - configurationPrompt, - customPromptOptions, - drupalRootOptions, - options as configOptions, + propOptions, + generatorLoop } from './generatePromptOptions' module.exports = class extends Generator { // configuration will come from the constructor argument - configuration: Answers + configuration: ConfigurationAnswers packageJson: Record cliVersion = '' @@ -78,7 +73,7 @@ module.exports = class extends Generator { * Creeate a particle config file */ - _writeParticleConfig() { + async _writeParticleConfig() { fs.writeFileSync( '.particlerc', JSON.stringify( @@ -90,33 +85,15 @@ module.exports = class extends Generator { } async _promptUser() { - // Initialize storybook - const results: ConfigurationAnswers = await this.prompt(configurationPrompt) + // const configuration: CustomAnswers = await this.prompt(propOptions) - // if custom exit here - if (results.config === ConfigOptions.CUSTOM) { - const customOptions = await this.prompt(customPromptOptions) - - this.configuration = { - ...results, - options: customOptions, - } - } else if (results.config === ConfigOptions.DRUPAL) { - const root = await this.prompt(drupalRootOptions) - - this.configuration = { - ...results, - ...root, - options: configOptions[results.config] - } - // ADD DRUPAL LOCATION PROMPT HERE - } else { - this.configuration = { - ...results, - options: configOptions[results.config], - } + const results: ConfigurationAnswers = { + config: await this.prompt(propOptions), + designThemes: await generatorLoop() } - this.packageJson.name = results.projectName + + this.configuration = results; + this.packageJson.name = results.config.projectName } /** @@ -125,20 +102,20 @@ module.exports = class extends Generator { async initializing() { await this._promptUser() - // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html - if ( - this.configuration.options.frontendFramework.includes( - FrontendFrameworkOptions.REACT - ) - ) { - this.composeWith( - require.resolve('@phase2/generator-particle-storybook'), - { - configuration: this.configuration, - updatePackageJson: this._updatePackageJson, - } - ) - } + // // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html + // if ( + // this.configuration.options.frontendFramework.includes( + // FrontendFrameworkOptions.REACT + // ) + // ) { + // this.composeWith( + // require.resolve('@phase2/generator-particle-storybook'), + // { + // configuration: this.configuration, + // updatePackageJson: this._updatePackageJson, + // } + // ) + // } // Add other subgenerators here } From f036da000862e78197fa4820878e874361ad330b Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Tue, 27 Oct 2020 12:14:51 -0700 Subject: [PATCH 05/18] chore: cleanup unused types. --- packages/particle-types/types.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index efad388b65..9ec381c5ee 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -1,9 +1,3 @@ -export enum CSSLibraryOptions { - TAILWIND = 'tailwind', - SASS = 'sass', - BOOTSTRAP = 'bootstrap', -} - export enum FrontendFrameworkOptions { REACT = 'react', WEBCOMPONENTS = 'webcomponents', @@ -32,6 +26,7 @@ export interface DesignTheme { export interface CustomAnswers { clientAbbreviation: string + designThemes: DesignTheme[] hasDrupal: boolean hasSVG: boolean hasTypescript: boolean @@ -43,7 +38,6 @@ export interface CustomAnswers { export interface ConfigurationAnswers { config: CustomAnswers - designThemes: DesignTheme[] } export interface Answers extends Naming { From 46c8ef8ff1e47c9877a1c1c0dc53c37bf8197e7a Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Tue, 27 Oct 2020 12:16:53 -0700 Subject: [PATCH 06/18] chore: minor R/F and cleanup of unused prompts. --- .../src/generators/app/generatePromptOptions.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index cb8bd6dade..dfa7468449 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -82,20 +82,6 @@ export const generatorLoop = async() => { return themesArray; } -// export const drupalRootOptions = [ -// { -// type: 'input', -// message: 'Where should your Drupal root exist?', -// default: `./source/drupal/`, -// name: 'drupalRootPath', -// when: (answer: CustomAnswers) => { -// if (answer.hasDrupal) { -// inquirer.prompt(drupalRootOptions) -// } -// } -// } -// ] - export const propOptions = [ { type: 'input', From 03287294ac3c56a2a50d6029c70c71aeaaf77b5f Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Tue, 27 Oct 2020 12:17:47 -0700 Subject: [PATCH 07/18] chore: Clean up _propmtUser(). --- .../src/generators/app/index.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index a9eca82c85..3103b7ad45 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -3,7 +3,9 @@ import merge from 'lodash.merge' import fs from 'fs' import { + CustomAnswers, ConfigurationAnswers, + DesignTheme, } from '@phase2/particle-types' import { @@ -85,15 +87,12 @@ module.exports = class extends Generator { } async _promptUser() { - // const configuration: CustomAnswers = await this.prompt(propOptions) + const config: CustomAnswers = await this.prompt(propOptions) + config.designThemes = await generatorLoop() - const results: ConfigurationAnswers = { - config: await this.prompt(propOptions), - designThemes: await generatorLoop() - } + this.configuration.config = config - this.configuration = results; - this.packageJson.name = results.config.projectName + this.packageJson.name = config.projectName } /** From 1d673a57e1e36e2de5faa82a09362dac03be9a1d Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Tue, 27 Oct 2020 12:34:07 -0700 Subject: [PATCH 08/18] chore: cleanup. --- .../generators/app/generatePromptOptions.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index dfa7468449..7934a6e5f2 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -12,7 +12,7 @@ const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( ) => { 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)` : '' + max ? ` and a maximum of ${max} option(s).` : '' }` } return true @@ -37,7 +37,7 @@ export const designThemePrompt = () => [ }, { type: 'input', - message: 'choose a design theme name using kebab case. (min 4 chars) Ex: "alpha"', + message: 'Choose a design theme name using kebab case. (min 4 chars) Ex: "alpha".', name: 'themeName', default: 'default', validate: (name: string) => { @@ -85,35 +85,35 @@ export const generatorLoop = async() => { export const propOptions = [ { type: 'input', - message: 'choose a abbreviation for your/client\'s name. (min 3 chars) Ex: Home Depot "hdp"', + message: 'Choose a abbreviation for your/client\'s name. (min 3 chars)', name: 'clientAbbreviation', validate: (name: string) => { if (!name || name.length < 3) { - return 'Please enter a project name of more than 4 characters length' + return 'Please enter a project name of more than 4 characters length.' } if (name.indexOf(' ') > 0) { - return 'Please enter a two word project name with no spaces' + return 'Please enter a two word project name with no spaces.' } return true } }, { type: 'input', - message: 'choose a name for the overall project using kebab case. (min 4 chars) Ex: "website", or "saphire-dagger"', + message: 'Choose a name for the overall project using kebab case. (min 4 chars) Ex: "website", or "saphire-dagger"', name: 'projectName', validate: (name: string) => { if (!name || name.length < 4) { - return 'Please enter a project name of more than 4 characters length' + return 'Please enter a project name of more than 4 characters length.' } if (name.indexOf(' ') > 0) { - return 'Please enter a two word project name with no spaces' + return 'Please enter a two word project name with no spaces.' } return true }, }, { type: 'confirm', - message: 'will you be using Drupal?', + message: 'Will you be using Drupal?', name: 'hasDrupal', default: false, }, From 3f6ff9bd27faf1e748ac7faeb6786674f8de8809 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Mon, 2 Nov 2020 15:07:35 -0800 Subject: [PATCH 09/18] chore: Update types. --- packages/particle-types/types.ts | 43 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index 9ec381c5ee..42dd91e656 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -3,13 +3,6 @@ export enum FrontendFrameworkOptions { WEBCOMPONENTS = 'webcomponents', } -export interface Naming { - componentLibraryPath: string - drupalRootPath: string - projectName: string - themeName: string -} - export enum TestingLibraryOptions { CYPRESS = 'cypress', JEST = 'jest', @@ -18,28 +11,38 @@ export enum TestingLibraryOptions { SELENIUM = 'selenium', } -export interface DesignTheme { - frontendFramework: FrontendFrameworkOptions, - themeName: string, - themePath: string, +export interface Dist { + dist?: string +} + +export interface Bundle { + name: string + frontendFramework?: FrontendFrameworkOptions, + storybook?: Dist + drupal?: Dist +} + +export interface BundleAnswers { + frontendFramework: FrontendFrameworkOptions + name: string + storybook: string + consuming?: boolean + drupal?: string } export interface CustomAnswers { - clientAbbreviation: string - designThemes: DesignTheme[] - hasDrupal: boolean - hasSVG: boolean + bundles: Bundle[] + designRoot: string hasTypescript: boolean + nameSpace: string projectName: string testingLibraries: TestingLibraryOptions[] - drupalRootPath?: string typescriptEsm?: boolean + hasDrupal?: boolean + drupal?: string + cliVersion?: string } export interface ConfigurationAnswers { config: CustomAnswers } - -export interface Answers extends Naming { - options: CustomAnswers -} From 8d1f2723722a60ddab75023a96b8aa2f090a20f9 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Mon, 2 Nov 2020 15:09:38 -0800 Subject: [PATCH 10/18] chore: Add cleanup func, update _promptUser. --- .../src/generators/app/index.ts | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index 3103b7ad45..a3b73a4931 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -5,12 +5,11 @@ import fs from 'fs' import { CustomAnswers, ConfigurationAnswers, - DesignTheme, } from '@phase2/particle-types' import { propOptions, - generatorLoop + bundleLoop } from './generatePromptOptions' module.exports = class extends Generator { @@ -72,14 +71,25 @@ module.exports = class extends Generator { } /** - * Creeate a particle config file + * Remove unwanted properties generated by CLI prompts + */ + + cleanup = (obj:any, props:string[]) => { + props.map(prop => + !!obj[prop] || obj[prop] === false ? delete obj[prop] : null + ) + return obj + } + + /** + * Create a particle config file */ async _writeParticleConfig() { fs.writeFileSync( '.particlerc', JSON.stringify( - { ...this.configuration, ...{ 'cli-version': this.cliVersion } }, + { ...{ 'cli-version': this.cliVersion }, ...this.configuration }, null, 2 ) @@ -87,10 +97,13 @@ module.exports = class extends Generator { } async _promptUser() { + const cleanOuts = ['hasDrupal'] const config: CustomAnswers = await this.prompt(propOptions) - config.designThemes = await generatorLoop() + const usingDrupal:boolean = config.hasDrupal ? config.hasDrupal : false + + config.bundles = await bundleLoop(config.designRoot, usingDrupal) - this.configuration.config = config + this.configuration.config = this.cleanup(config,cleanOuts) this.packageJson.name = config.projectName } From d05dc69e1fa8b7c693a2056b697ee5ccd45e5fec Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Mon, 2 Nov 2020 15:11:49 -0800 Subject: [PATCH 11/18] fix: Temp comment while working on new approach. --- .../src/generators/app/index.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/generator-particle-storybook/src/generators/app/index.ts b/packages/generator-particle-storybook/src/generators/app/index.ts index 4b4c678c0d..8c9e36821f 100644 --- a/packages/generator-particle-storybook/src/generators/app/index.ts +++ b/packages/generator-particle-storybook/src/generators/app/index.ts @@ -3,7 +3,7 @@ import Generator from 'yeoman-generator' import { white } from 'chalk' import fs from 'fs' -import { Answers, FrontendFrameworkOptions } from '@phase2/particle-types' +import { CustomAnswers, FrontendFrameworkOptions } from '@phase2/particle-types' import { main } from './templates/main' export const storybookAddons: string[] = [ @@ -23,7 +23,7 @@ const storiesRoot: string[] = ['./stories/**/*.story.tsx'] * Currently only supports react */ module.exports = class extends Generator { - configuration: Answers + configuration: CustomAnswers updatePackageJson: (newJson: Record) => void constructor(args: any, opts: any) { super(args, opts) @@ -61,14 +61,14 @@ module.exports = class extends Generator { fs.mkdirSync(`${process.cwd()}/${storybookPath}/`, { recursive: true }) // create the files - fs.writeFileSync( - this.destinationPath(`${storybookPath}/main.js`), - main({ - addons: storybookAddons, - componentLibraryPath: `../../${this.configuration.componentLibraryPath}`, - storiesRoot, - }) - ) + // fs.writeFileSync( + // this.destinationPath(`${storybookPath}/main.js`), + // main({ + // addons: storybookAddons, + // componentLibraryPath: `../../${this.configuration.componentLibraryPath}`, + // storiesRoot, + // }) + // ) fs.writeFileSync( this.destinationPath(`${storybookPath}/preview.js`), preview({ From af55606ec07781dffce8d35e669e6c917752ebee Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Mon, 2 Nov 2020 15:16:01 -0800 Subject: [PATCH 12/18] chore: Separate string validation to own func. R/F and rename various funcs. --- .../generators/app/generatePromptOptions.ts | 197 ++++++++++-------- 1 file changed, 113 insertions(+), 84 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index 7934a6e5f2..b011b68087 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -4,126 +4,159 @@ import { CustomAnswers, FrontendFrameworkOptions, TestingLibraryOptions, - DesignTheme, + Bundle, + BundleAnswers } from '@phase2/particle-types' const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( answer: Record[] ) => { 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).` : '' - }` + const maxText = max? `and a maximum of ${max} options.` : ' option(s).' + return `You must choose a minimum of ${min}${maxText}` } return true } -export const designThemePrompt = () => [ - { - type: 'list', - message: 'What frontend framework are you using with Storybook?', - name: 'frontendFramework', - choices: [ - { - name: 'Webcomponents', - checked: true, - value: FrontendFrameworkOptions.WEBCOMPONENTS - }, - { - name: 'React', - value: FrontendFrameworkOptions.REACT - } - ], - }, - { - type: 'input', - message: 'Choose a design theme name using kebab case. (min 4 chars) Ex: "alpha".', - name: 'themeName', - default: 'default', - validate: (name: string) => { - if (!name || name.length < 4) { - return 'Please enter a project name of more than 4 characters length' +const validateString = (length: number) => ( + answer: string +) => { + if (!answer || answer.length < length) { + return `Please enter a name of at least ${length} characters length` + } + if (answer.indexOf(' ') > 0) { + return 'Please enter a two word name with no spaces' + } + return true +} + +export const bundlePrompt = (designRoot: string, usingDrupal: boolean) => +[ + { + type: 'input', + message: 'Choose a design theme name using kebab case. (min 4 chars) Ex: "alpha".', + name: 'name', + default: 'default', + validate: validateString(4) + }, + { + type: 'list', + message: 'What frontend framework are you using with Storybook?', + name: 'frontendFramework', + choices: [ + { + name: 'Webcomponents', + checked: true, + value: FrontendFrameworkOptions.WEBCOMPONENTS + }, + { + name: 'React', + value: FrontendFrameworkOptions.REACT + } + ] + }, + { + type: 'input', + message: 'Where does this theme\'s storybook compile to?', + name: 'storybook', + default: (answers: BundleAnswers) => + `${designRoot}/${answers.name}/dist/` + }, + { + type: 'confirm', + message: 'Will Drupal use this theme?', + name: 'consuming', + default: true, + when: usingDrupal + }, + { + type: 'input', + message: 'Where should your Drupal theme compile to?', + default: `./themes/particle/dist/`, + name: 'drupal', + when: (answers: BundleAnswers) => { + return answers.consuming } - if (name.indexOf(' ') > 0) { - return 'Please enter a two word project name with no spaces' + } + ] + +const generateBundle = async (prev: Bundle[], root: string, usingDrupal: boolean) => { + const res: BundleAnswers = await inquirer.prompt(bundlePrompt(root, usingDrupal)) + const { frontendFramework, consuming, name, storybook, drupal } = res + if (consuming) { + const curr: Bundle = { + name: name, + frontendFramework: frontendFramework, + storybook: { + dist: storybook + }, + drupal: { + dist: drupal } - return true } - }, - { - type: 'input', - message: - 'Where does your design theme exist relative to the root of the project', - default: (answers: DesignTheme) => - `./source/design/${answers.themeName}/`, - name: 'themePath' + return [...prev, curr] + + } + const curr: Bundle = { + name: name, + frontendFramework: frontendFramework, + storybook: { + dist: storybook + } } -] -const compileThemes = async (prev: DesignTheme[]) => { - return [...prev, await inquirer.prompt(designThemePrompt())] + return [...prev, curr] } -const rerun = [{ - type: 'confirm', - name: 'generateTheme', - message: 'Do you want to add another theme?', - default: true -}] - -export const generatorLoop = async() => { - let loop = true; - let themesArray:DesignTheme[] = [] +export const bundleLoop = async (designRoot: string, hasDrupal: boolean) => { + let loop = true + let bundlesArray: any[] = [{name: 'base'}] + const rerun = [{ + type: 'confirm', + name: 'generateTheme', + message: 'Do you want to add another theme?', + default: true + }] do { - themesArray = await compileThemes(themesArray) - loop = await inquirer.prompt(rerun).then(answers => answers.generateTheme) - } while (loop) + bundlesArray = await generateBundle(bundlesArray, designRoot, hasDrupal) + loop = await inquirer.prompt(rerun).then(answers => answers.generateTheme) + } while (loop) - return themesArray; + return bundlesArray } export const propOptions = [ { type: 'input', message: 'Choose a abbreviation for your/client\'s name. (min 3 chars)', - name: 'clientAbbreviation', - validate: (name: string) => { - if (!name || name.length < 3) { - return 'Please enter a project name of more than 4 characters length.' - } - if (name.indexOf(' ') > 0) { - return 'Please enter a two word project name with no spaces.' - } - return true - } + name: 'nameSpace', + validate: validateString(3) }, { type: 'input', message: 'Choose a name for the overall project using kebab case. (min 4 chars) Ex: "website", or "saphire-dagger"', 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 two word project name with no spaces.' - } - return true - }, + validate: validateString(4) + }, + { + type: 'input', + message: 'Where would you like to place your design system?', + name: 'designRoot', + default: './project/frontend' }, { type: 'confirm', message: 'Will you be using Drupal?', name: 'hasDrupal', - default: false, + default: false }, { type: 'input', message: 'Where should your Drupal root exist?', - default: `./source/drupal/`, - name: 'drupalRootPath', + default: `./drupal/`, + name: 'drupal', when: (answer: CustomAnswers) => { - return answer.hasDrupal + return answer.hasDrupal } }, { @@ -143,10 +176,6 @@ export const propOptions = [ return false } }, - { - type: 'confirm', - name: 'Are you using SVGs?' - }, { type: 'checkbox', message: 'What testing libraries do you want to use?', From b53f297fa6e30296d464504372c22e5d96a1edac Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Wed, 18 Nov 2020 09:01:56 -0800 Subject: [PATCH 13/18] chore: Snapshot save. Pared down multi bundle approach. --- README.md | 1 - .../generators/app/generatePromptOptions.ts | 142 +++++++----------- .../src/generators/app/index.ts | 34 +++-- .../src/generators/app/index.ts | 24 +-- .../src/generators/app/templates/main.ts | 4 + packages/particle-types/types.ts | 14 +- 6 files changed, 105 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index ac8731088a..485f23243c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ TBD 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 for all typescript repos in order of dependency chain. Example: `particle-types` has 0 internal dependencies but other typescript packages depend on it. We would need to run the compiler first on particle-types in order to be able to compile for other typescript packages. 1. Cd into `package/` 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`. 1. Alternatively use `node` to test out a dependency in lib. Example `node packages/particle-cli/lib/bin/particle-cli.js -V` diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index b011b68087..8e0e04a597 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -1,11 +1,11 @@ -import inquirer from 'inquirer' +import inquirer from 'inquirer'; +import chalk from 'chalk'; import { CustomAnswers, FrontendFrameworkOptions, TestingLibraryOptions, Bundle, - BundleAnswers } from '@phase2/particle-types' const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( @@ -18,72 +18,67 @@ const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( return true } -const validateString = (length: number) => ( +const validateString = (length: number, defaultVal?:string ) => ( answer: string ) => { + const defaultText = defaultVal ? `\n Recommended: ${defaultVal}` : '' if (!answer || answer.length < length) { - return `Please enter a name of at least ${length} characters length` + return `Please enter a value of at least ${length} characters length.${defaultText}` } if (answer.indexOf(' ') > 0) { - return 'Please enter a two word name with no spaces' + return 'Please enter a value name with no spaces' } return true } -export const bundlePrompt = (designRoot: string, usingDrupal: boolean) => -[ - { - type: 'input', - message: 'Choose a design theme name using kebab case. (min 4 chars) Ex: "alpha".', - name: 'name', - default: 'default', - validate: validateString(4) - }, - { - type: 'list', - message: 'What frontend framework are you using with Storybook?', - name: 'frontendFramework', - choices: [ - { - name: 'Webcomponents', - checked: true, - value: FrontendFrameworkOptions.WEBCOMPONENTS - }, - { - name: 'React', - value: FrontendFrameworkOptions.REACT - } - ] - }, - { - type: 'input', - message: 'Where does this theme\'s storybook compile to?', - name: 'storybook', - default: (answers: BundleAnswers) => - `${designRoot}/${answers.name}/dist/` - }, - { - type: 'confirm', - message: 'Will Drupal use this theme?', - name: 'consuming', - default: true, - when: usingDrupal - }, - { - type: 'input', - message: 'Where should your Drupal theme compile to?', - default: `./themes/particle/dist/`, - name: 'drupal', - when: (answers: BundleAnswers) => { - return answers.consuming +const nameWork = [ + { + type: 'input', + message:'Enter a component library name using kebab case. (min 4 chars) Ex: "alpha".', + name: 'name', + default: 'default', + validate: validateString(4) + }, + { + type: 'list', + message: 'What frontend framework are you using with Storybook?', + name: 'frontendFramework', + choices: [ + { + name: 'Webcomponents', + checked: true, + value: FrontendFrameworkOptions.WEBCOMPONENTS + }, + { + name: 'React', + value: FrontendFrameworkOptions.REACT } - } - ] + ] + }, +] + +const dists = (designRoot: string, usingDrupal: boolean, name: string) => [ + { + type: 'input', + message: `Where does ${chalk.blue(name)}\'s storybook compile to?`, + name: 'storybook', + default:`${designRoot}/${name}/dist/`, + validate: validateString(5) + }, + { + type: 'input', + message: `Where should Drupal compile ${chalk.blue(name)} to?`, + default: './themes/particle/dist/', + name: 'drupal', + validate: validateString(5), + when: usingDrupal + } +] const generateBundle = async (prev: Bundle[], root: string, usingDrupal: boolean) => { - const res: BundleAnswers = await inquirer.prompt(bundlePrompt(root, usingDrupal)) - const { frontendFramework, consuming, name, storybook, drupal } = res - if (consuming) { + const {name, frontendFramework } = await inquirer.prompt(nameWork) + const { storybook, drupal } = await inquirer.prompt(dists(root, usingDrupal, name)) + if (drupal) { const curr: Bundle = { name: name, frontendFramework: frontendFramework, @@ -130,7 +125,7 @@ export const propOptions = [ type: 'input', message: 'Choose a abbreviation for your/client\'s name. (min 3 chars)', name: 'nameSpace', - validate: validateString(3) + validate: validateString(2) }, { type: 'input', @@ -142,39 +137,24 @@ export const propOptions = [ type: 'input', message: 'Where would you like to place your design system?', name: 'designRoot', - default: './project/frontend' + default: './project/design', + validate: validateString(5, './project/design') }, { type: 'confirm', message: 'Will you be using Drupal?', name: 'hasDrupal', - default: false + default: true }, { type: 'input', message: 'Where should your Drupal root exist?', - default: `./drupal/`, + default: `./project/`, name: 'drupal', when: (answer: CustomAnswers) => { return answer.hasDrupal - } - }, - { - 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 - } + }, + validate: validateString(5,`./project/`) }, { type: 'checkbox', @@ -183,12 +163,6 @@ export const propOptions = [ choices: [ { name: 'Jest', value: TestingLibraryOptions.JEST }, { name: 'Cypress', value: TestingLibraryOptions.CYPRESS }, - { name: 'Selenium', value: TestingLibraryOptions.SELENIUM }, - { - name: 'Loki (Storybook only VRT)', - value: TestingLibraryOptions.LOKI - }, - { name: 'Pa11y', value: TestingLibraryOptions.PA11Y } ], validate: minMaxOptionsValidate({ min: 1 }) } diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index a3b73a4931..208d4adff3 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -4,7 +4,7 @@ import fs from 'fs' import { CustomAnswers, - ConfigurationAnswers, + ConfigurationAnswers, StorybookConfig } from '@phase2/particle-types' import { @@ -113,21 +113,29 @@ module.exports = class extends Generator { */ async initializing() { await this._promptUser() + // const { bundles, designRoot, drupal, nameSpace, projectName } = this.configuration.config // // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html - // if ( - // this.configuration.options.frontendFramework.includes( - // FrontendFrameworkOptions.REACT - // ) - // ) { - // this.composeWith( - // require.resolve('@phase2/generator-particle-storybook'), - // { - // configuration: this.configuration, - // updatePackageJson: this._updatePackageJson, + // bundles.map(bundle => { + // if (bundle.storybook) { + // const { frontendFramework, storybook, name } = bundle; + // const sbConfig: StorybookConfig = { + // frameWork: frontendFramework, + // name: name, + // root: designRoot, + // // @ts-ignore + // dist: storybook.dist, // } - // ) - // } + // this.composeWith( + // require.resolve('@phase2/generator-particle-storybook'), + // { + // configuration: sbConfig, + // updatePackageJson: this._updatePackageJson, + // } + // ) + // } + // }) + // Add other subgenerators here } diff --git a/packages/generator-particle-storybook/src/generators/app/index.ts b/packages/generator-particle-storybook/src/generators/app/index.ts index 8c9e36821f..ab5f5ea52f 100644 --- a/packages/generator-particle-storybook/src/generators/app/index.ts +++ b/packages/generator-particle-storybook/src/generators/app/index.ts @@ -3,7 +3,10 @@ import Generator from 'yeoman-generator' import { white } from 'chalk' import fs from 'fs' -import { CustomAnswers, FrontendFrameworkOptions } from '@phase2/particle-types' +import { + FrontendFrameworkOptions, + StorybookConfig, +} from '@phase2/particle-types' import { main } from './templates/main' export const storybookAddons: string[] = [ @@ -12,6 +15,7 @@ export const storybookAddons: string[] = [ '@storybook/addon-links', '@storybook/addon-viewport', '@storybook/addon-a11y', + '@storybook/preset-typescript' ] const storybookSupportedVersion = '^5.3.19' @@ -23,7 +27,7 @@ const storiesRoot: string[] = ['./stories/**/*.story.tsx'] * Currently only supports react */ module.exports = class extends Generator { - configuration: CustomAnswers + configuration: StorybookConfig updatePackageJson: (newJson: Record) => void constructor(args: any, opts: any) { super(args, opts) @@ -61,14 +65,14 @@ module.exports = class extends Generator { fs.mkdirSync(`${process.cwd()}/${storybookPath}/`, { recursive: true }) // create the files - // fs.writeFileSync( - // this.destinationPath(`${storybookPath}/main.js`), - // main({ - // addons: storybookAddons, - // componentLibraryPath: `../../${this.configuration.componentLibraryPath}`, - // storiesRoot, - // }) - // ) + fs.writeFileSync( + this.destinationPath(`${storybookPath}/main.js`), + main({ + addons: storybookAddons, + componentLibraryPath: `../../${this.configuration.root}/${this.configuration.name}`, + storiesRoot, + }) + ) fs.writeFileSync( this.destinationPath(`${storybookPath}/preview.js`), preview({ diff --git a/packages/generator-particle-storybook/src/generators/app/templates/main.ts b/packages/generator-particle-storybook/src/generators/app/templates/main.ts index 0a7bc3e30e..c99050fea7 100644 --- a/packages/generator-particle-storybook/src/generators/app/templates/main.ts +++ b/packages/generator-particle-storybook/src/generators/app/templates/main.ts @@ -11,6 +11,10 @@ export interface MainConfig { * @TODO require('../../particle) is a placeholder until we have proper base config */ export const main = (config: MainConfig) => `const path = require('path') +const fs = require('fs'); +const path = require('path'); +const CopyPlugin = require('copy-webpack-plugin'); +const WriteFilePlugin = require('write-file-webpack-plugin'); const APP_COMPONENT_LIBRARY = path.resolve(__dirname, '${ config.componentLibraryPath diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index 42dd91e656..85a39f0069 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -6,9 +6,6 @@ export enum FrontendFrameworkOptions { export enum TestingLibraryOptions { CYPRESS = 'cypress', JEST = 'jest', - LOKI = 'loki', - PA11Y = 'pa11y', - SELENIUM = 'selenium', } export interface Dist { @@ -26,18 +23,15 @@ export interface BundleAnswers { frontendFramework: FrontendFrameworkOptions name: string storybook: string - consuming?: boolean drupal?: string } export interface CustomAnswers { bundles: Bundle[] designRoot: string - hasTypescript: boolean nameSpace: string projectName: string testingLibraries: TestingLibraryOptions[] - typescriptEsm?: boolean hasDrupal?: boolean drupal?: string cliVersion?: string @@ -46,3 +40,11 @@ export interface CustomAnswers { export interface ConfigurationAnswers { config: CustomAnswers } + +export interface StorybookConfig { + root: string + name: string + dist: string + frontendFramework: string +} + From a71b9db27a36a20448d9b1da0a5b0b89225e3b9d Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Wed, 2 Dec 2020 14:52:38 -0800 Subject: [PATCH 14/18] chore: Update package.json, and base generator, remove depricated types. --- package.json | 8 +- packages/generator-particle-base/package.json | 1 + .../generators/app/generatePromptOptions.ts | 142 ------------------ .../src/generators/app/index.ts | 96 ++++++------ packages/particle-types/types.ts | 40 ----- 5 files changed, 47 insertions(+), 240 deletions(-) diff --git a/package.json b/package.json index b97fd701c2..ed1509ebf6 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "private": true, "keywords:": [ "Particle", + "Stencil", "Storybook", - "Pattern Lab", "Drupal" ], "homepage": "https://github.com/phase2/particle#readme", @@ -28,9 +28,9 @@ "pretty-check": "prettier --check packages/**/*.js", "test": "jest", "test:watch": "jest --watch", - "build": "lerna run --concurrency 1 --scope='{@phase2/particle-types,@phase2/generator-particle-storybook,@phase2/generator-particle-base,@phase2/particle-cli}' tsc", - "dev:clean:node_modules": "lerna exec --parallel --scope='{@phase2/particle-types,@phase2/generator-particle-storybook,@phase2/generator-particle-base,@phase2/particle-cli}' 'rm -rf node_modules'", - "dev:clean:lib": "lerna exec --parallel --scope='{@phase2/particle-types,@phase2/generator-particle-storybook,@phase2/generator-particle-base,@phase2/particle-cli}' 'rm -rf lib/'", + "build": "lerna run --concurrency 1 --scope='{@phase2/particle-types,@phase2/generator-particle-components,@phase2/generator-particle-storybook,@phase2/generator-particle-base,@phase2/particle-cli}' tsc", + "dev:clean:node_modules": "lerna exec --parallel --scope='{@phase2/particle-types,@phase2/generator-particle-components,@phase2/generator-particle-storybook,@phase2/generator-particle-base,@phase2/particle-cli}' 'rm -rf node_modules'", + "dev:clean:lib": "lerna exec --parallel --scope='{@phase2/particle-types,@phase2/generator-particle-components,@phase2/generator-particle-storybook,@phase2/generator-particle-base,@phase2/particle-cli}' 'rm -rf lib/'", "build:watch": "lerna run --parallel --scope='{@phase2/*,generator-*}' tsc:watch", "update:check": "ncu && lerna exec --concurrency 1 --no-bail -- ncu", "update:start": "npm-upgrade && lerna exec --concurrency 1 -- npm-upgrade; npm run lerna:install" diff --git a/packages/generator-particle-base/package.json b/packages/generator-particle-base/package.json index 001b2b7f12..8559f043be 100644 --- a/packages/generator-particle-base/package.json +++ b/packages/generator-particle-base/package.json @@ -16,6 +16,7 @@ "directory": "packages/particle-cli" }, "dependencies": { + "@phase2/generator-particle-components": "^0.0.1", "@phase2/generator-particle-storybook": "^0.0.1", "@phase2/particle-types": "^0.0.1", "chalk": "^4.1.0", diff --git a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts index 8e0e04a597..25ebf09ab3 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -1,23 +1,3 @@ -import inquirer from 'inquirer'; -import chalk from 'chalk'; - -import { - CustomAnswers, - FrontendFrameworkOptions, - TestingLibraryOptions, - Bundle, -} from '@phase2/particle-types' - -const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( - answer: Record[] -) => { - if (answer.length < min || (!max ? false : answer.length > max)) { - const maxText = max? `and a maximum of ${max} options.` : ' option(s).' - return `You must choose a minimum of ${min}${maxText}` - } - return true -} - const validateString = (length: number, defaultVal?:string ) => ( answer: string ) => { @@ -31,95 +11,6 @@ const validateString = (length: number, defaultVal?:string ) => ( return true } -const nameWork = [ - { - type: 'input', - message:'Enter a component library name using kebab case. (min 4 chars) Ex: "alpha".', - name: 'name', - default: 'default', - validate: validateString(4) - }, - { - type: 'list', - message: 'What frontend framework are you using with Storybook?', - name: 'frontendFramework', - choices: [ - { - name: 'Webcomponents', - checked: true, - value: FrontendFrameworkOptions.WEBCOMPONENTS - }, - { - name: 'React', - value: FrontendFrameworkOptions.REACT - } - ] - }, -] - -const dists = (designRoot: string, usingDrupal: boolean, name: string) => [ - { - type: 'input', - message: `Where does ${chalk.blue(name)}\'s storybook compile to?`, - name: 'storybook', - default:`${designRoot}/${name}/dist/`, - validate: validateString(5) - }, - { - type: 'input', - message: `Where should Drupal compile ${chalk.blue(name)} to?`, - default: './themes/particle/dist/', - name: 'drupal', - validate: validateString(5), - when: usingDrupal - } -] - -const generateBundle = async (prev: Bundle[], root: string, usingDrupal: boolean) => { - const {name, frontendFramework } = await inquirer.prompt(nameWork) - const { storybook, drupal } = await inquirer.prompt(dists(root, usingDrupal, name)) - if (drupal) { - const curr: Bundle = { - name: name, - frontendFramework: frontendFramework, - storybook: { - dist: storybook - }, - drupal: { - dist: drupal - } - } - return [...prev, curr] - - } - const curr: Bundle = { - name: name, - frontendFramework: frontendFramework, - storybook: { - dist: storybook - } - } - - return [...prev, curr] -} - -export const bundleLoop = async (designRoot: string, hasDrupal: boolean) => { - let loop = true - let bundlesArray: any[] = [{name: 'base'}] - const rerun = [{ - type: 'confirm', - name: 'generateTheme', - message: 'Do you want to add another theme?', - default: true - }] - do { - bundlesArray = await generateBundle(bundlesArray, designRoot, hasDrupal) - loop = await inquirer.prompt(rerun).then(answers => answers.generateTheme) - } while (loop) - - return bundlesArray -} - export const propOptions = [ { type: 'input', @@ -133,37 +24,4 @@ export const propOptions = [ name: 'projectName', validate: validateString(4) }, - { - type: 'input', - message: 'Where would you like to place your design system?', - name: 'designRoot', - default: './project/design', - validate: validateString(5, './project/design') - }, - { - type: 'confirm', - message: 'Will you be using Drupal?', - name: 'hasDrupal', - default: true - }, - { - type: 'input', - message: 'Where should your Drupal root exist?', - default: `./project/`, - name: 'drupal', - when: (answer: CustomAnswers) => { - return answer.hasDrupal - }, - validate: validateString(5,`./project/`) - }, - { - type: 'checkbox', - message: 'What testing libraries do you want to use?', - name: 'testingLibraries', - choices: [ - { name: 'Jest', value: TestingLibraryOptions.JEST }, - { name: 'Cypress', value: TestingLibraryOptions.CYPRESS }, - ], - validate: minMaxOptionsValidate({ min: 1 }) - } ] diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index 208d4adff3..73d19a974b 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -3,15 +3,18 @@ import merge from 'lodash.merge' import fs from 'fs' import { - CustomAnswers, - ConfigurationAnswers, StorybookConfig + ConfigurationAnswers } from '@phase2/particle-types' import { - propOptions, - bundleLoop + propOptions } from './generatePromptOptions' + +/** + * List of Dependencies to be installed + */ + module.exports = class extends Generator { // configuration will come from the constructor argument configuration: ConfigurationAnswers @@ -31,7 +34,7 @@ module.exports = class extends Generator { // makes config a required argument this.option('configuration', { type: String, - description: 'stringified configuration object from particle-cli', + description: 'stringified configuration object from particle-cli' }) this.configuration = opts.configuration @@ -39,11 +42,12 @@ module.exports = class extends Generator { : {} this.packageJson = { + client: 'client-abbreviation', name: 'project-name', version: '1.0.0', main: 'index.js', scripts: { - test: 'echo "Error: no test specified" && exit 1', + test: 'echo "Error: no test specified" && exit 1' }, keywords: [], author: '', @@ -51,7 +55,7 @@ module.exports = class extends Generator { description: 'Particle boilerplate project', repository: {}, dependencies: {}, - devDependencies: {}, + devDependencies: {} } this._updatePackageJson = this._updatePackageJson.bind(this) } @@ -60,6 +64,13 @@ module.exports = class extends Generator { this.packageJson = merge(this.packageJson, newValues) } + _updateSubGeneratorPackageJson(newValues: Record, jsonPath: string) :void { + const currentJson = JSON.parse(fs.readFileSync(jsonPath).toString()); + const mergedJson = JSON.stringify(merge(currentJson, newValues)) + + fs.writeFileSync(jsonPath, mergedJson ) + } + /** * Creates the package.json, package.json should never need to be re-written */ @@ -70,17 +81,6 @@ module.exports = class extends Generator { ) } - /** - * Remove unwanted properties generated by CLI prompts - */ - - cleanup = (obj:any, props:string[]) => { - props.map(prop => - !!obj[prop] || obj[prop] === false ? delete obj[prop] : null - ) - return obj - } - /** * Create a particle config file */ @@ -97,15 +97,9 @@ module.exports = class extends Generator { } async _promptUser() { - const cleanOuts = ['hasDrupal'] - const config: CustomAnswers = await this.prompt(propOptions) - const usingDrupal:boolean = config.hasDrupal ? config.hasDrupal : false - - config.bundles = await bundleLoop(config.designRoot, usingDrupal) - - this.configuration.config = this.cleanup(config,cleanOuts) - - this.packageJson.name = config.projectName + this.configuration.config = await this.prompt(propOptions) + this.packageJson.client = this.configuration.config.nameSpace + this.packageJson.name = this.configuration.config.projectName } /** @@ -113,37 +107,31 @@ module.exports = class extends Generator { */ async initializing() { await this._promptUser() - // const { bundles, designRoot, drupal, nameSpace, projectName } = this.configuration.config - - // // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html - // bundles.map(bundle => { - // if (bundle.storybook) { - // const { frontendFramework, storybook, name } = bundle; - // const sbConfig: StorybookConfig = { - // frameWork: frontendFramework, - // name: name, - // root: designRoot, - // // @ts-ignore - // dist: storybook.dist, - // } - // this.composeWith( - // require.resolve('@phase2/generator-particle-storybook'), - // { - // configuration: sbConfig, - // updatePackageJson: this._updatePackageJson, - // } - // ) - // } - // }) - - // Add other subgenerators here + const { nameSpace, projectName } = this.configuration.config + const projectNamespace = `${nameSpace}-${projectName}` + + // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html + this.composeWith( + require.resolve('@phase2/generator-particle-components'), + { + projectNamespace: projectNamespace, + updateJason: this._updateSubGeneratorPackageJson + } + ); + + this.composeWith( + require.resolve('@phase2/generator-particle-storybook'), + { + projectNamespace: projectNamespace, + updateJason: this._updateSubGeneratorPackageJson + } + ); } + // Add other subgenerators here + writing() { this._createPackageJson() this._writeParticleConfig() - - // Installs all dependencies - this.npmInstall() } } diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index 85a39f0069..5bab6f65fb 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -1,39 +1,6 @@ -export enum FrontendFrameworkOptions { - REACT = 'react', - WEBCOMPONENTS = 'webcomponents', -} - -export enum TestingLibraryOptions { - CYPRESS = 'cypress', - JEST = 'jest', -} - -export interface Dist { - dist?: string -} - -export interface Bundle { - name: string - frontendFramework?: FrontendFrameworkOptions, - storybook?: Dist - drupal?: Dist -} - -export interface BundleAnswers { - frontendFramework: FrontendFrameworkOptions - name: string - storybook: string - drupal?: string -} - export interface CustomAnswers { - bundles: Bundle[] - designRoot: string nameSpace: string projectName: string - testingLibraries: TestingLibraryOptions[] - hasDrupal?: boolean - drupal?: string cliVersion?: string } @@ -41,10 +8,3 @@ export interface ConfigurationAnswers { config: CustomAnswers } -export interface StorybookConfig { - root: string - name: string - dist: string - frontendFramework: string -} - From 7f9304e7eea004e5da1fad3e0f135f562ffd9748 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Wed, 2 Dec 2020 14:53:50 -0800 Subject: [PATCH 15/18] chore: update storybook genereator, including adding new test-component template. --- .../generator-particle-storybook/package.json | 3 +- .../src/generators/app/index.ts | 89 +++++++++++-------- .../src/generators/app/templates/main.ts | 47 ++-------- .../src/generators/app/templates/preview.ts | 21 +---- .../app/templates/test-component.ts | 8 ++ .../src/utils/helpers.ts | 5 +- 6 files changed, 75 insertions(+), 98 deletions(-) create mode 100644 packages/generator-particle-storybook/src/generators/app/templates/test-component.ts diff --git a/packages/generator-particle-storybook/package.json b/packages/generator-particle-storybook/package.json index 8d9f3748b1..f6153cc7b3 100644 --- a/packages/generator-particle-storybook/package.json +++ b/packages/generator-particle-storybook/package.json @@ -19,7 +19,8 @@ "dependencies": { "@phase2/particle-types": "^0.0.1", "chalk": "^4.1.0", - "yeoman-generator": "^4.11.0" + "yeoman-generator": "^4.11.0", + "lodash.merge": "^4.6.2" }, "devDependencies": { "@types/inquirer": "^7.3.0", diff --git a/packages/generator-particle-storybook/src/generators/app/index.ts b/packages/generator-particle-storybook/src/generators/app/index.ts index ab5f5ea52f..dad4da4097 100644 --- a/packages/generator-particle-storybook/src/generators/app/index.ts +++ b/packages/generator-particle-storybook/src/generators/app/index.ts @@ -1,12 +1,9 @@ import { preview } from './templates/preview' +import { component } from './templates/test-component' import Generator from 'yeoman-generator' -import { white } from 'chalk' +import { green } from 'chalk' import fs from 'fs' -import { - FrontendFrameworkOptions, - StorybookConfig, -} from '@phase2/particle-types' import { main } from './templates/main' export const storybookAddons: string[] = [ @@ -15,69 +12,83 @@ export const storybookAddons: string[] = [ '@storybook/addon-links', '@storybook/addon-viewport', '@storybook/addon-a11y', - '@storybook/preset-typescript' + '@storybook/preset-typescript', + '@storybook/web-components' ] -const storybookSupportedVersion = '^5.3.19' -const storybookPath = 'app/storybook' -const storiesRoot: string[] = ['./stories/**/*.story.tsx'] +const storiesRoot: string[] = ['../src/**/*.stories.tsx'] /** * @assumption we are already inside the particle root directory * Currently only supports react */ module.exports = class extends Generator { - configuration: StorybookConfig - updatePackageJson: (newJson: Record) => void + projectNamespace: string + updateJason: (newJson: Record, path: string) => void + constructor(args: any, opts: any) { super(args, opts) - this.configuration = opts.configuration - this.updatePackageJson = opts.updatePackageJson + this.projectNamespace = opts.projectNamespace + this.updateJason = opts.updateJason } - addStorybookDependencies() { + _addStorybookDependencies() { // TODO to add support for other frameworks - const dependencies = ['@storybook/react', ...storybookAddons] - - console.log(white('adding storybook dependencies to the packageJson')) + console.log(green('adding storybook dependencies to the packageJson')) + const packageJsonPath = `${this.projectNamespace}/package.json` - // if noInstall flag is passed assume that updatePackageJson was passed and fire off command - // else run npm install - this.updatePackageJson({ + const newPackageData = { scripts: { 'build:storybook': 'build-storybook -c ./apps/storybook', 'dev:storybook': 'start-storybook -p 6006 -c ./apps/storybook', - }, - devDependencies: dependencies.reduce>( - (acc, value: string) => { - acc[value] = storybookSupportedVersion - return acc - }, - {} - ), - }) + "watch-storybook": "wait-on ./dist-stencil && start-storybook -p 6009", + } + } + + this.updateJason(newPackageData, packageJsonPath); + this.npmInstall(storybookAddons, { 'save-dev': true }) } - async createStorybookFiles() { - console.log(white('creating files & folders for storybook')) + writing() { + const isStencil = fs.existsSync(this.projectNamespace); + const storybookPath = `${this.projectNamespace}/.storybook` + + console.log(green('creating files & folders for storybook')) // create the folders - fs.mkdirSync(`${process.cwd()}/${storybookPath}/`, { recursive: true }) + fs.mkdirSync(`${process.cwd()}/${this.projectNamespace}/`, {recursive: true}) + + this.spawnCommandSync( + 'npx', + ['-p', '@storybook/cli', 'sb', 'init', '-t', 'web_components'], + { cwd: `${this.projectNamespace}` }) - // create the files + // remove extraneous stories dir. + if (isStencil) { + this.spawnCommandSync( + 'rm', + ['-rf', 'stories'], + {cwd: `${this.projectNamespace}/src`} + ) + } + + // overwrite boilerplate config files. fs.writeFileSync( this.destinationPath(`${storybookPath}/main.js`), main({ - addons: storybookAddons, - componentLibraryPath: `../../${this.configuration.root}/${this.configuration.name}`, - storiesRoot, + addons: [...storybookAddons], + storiesRoot }) ) fs.writeFileSync( this.destinationPath(`${storybookPath}/preview.js`), - preview({ - frontendFramework: FrontendFrameworkOptions.REACT, - }) + preview() + ) + fs.openSync(`${this.projectNamespace}/src/components/my-component/my-component.stories.tsx`, 'w') + fs.writeFileSync( + this.destinationPath(`${this.projectNamespace}/src/components/my-component/my-component.stories.tsx`), + component() ) + this._addStorybookDependencies() } } diff --git a/packages/generator-particle-storybook/src/generators/app/templates/main.ts b/packages/generator-particle-storybook/src/generators/app/templates/main.ts index c99050fea7..7aa175acf3 100644 --- a/packages/generator-particle-storybook/src/generators/app/templates/main.ts +++ b/packages/generator-particle-storybook/src/generators/app/templates/main.ts @@ -1,7 +1,6 @@ import { stringifyAndSingleQuote } from '../../../utils/helpers' export interface MainConfig { - componentLibraryPath: string // this will have to be resolved based off storybook location in apps/storybook and the path of the component_library addons: string[] storiesRoot: string[] } @@ -10,47 +9,17 @@ export interface MainConfig { * app/storybook/main.js * @TODO require('../../particle) is a placeholder until we have proper base config */ -export const main = (config: MainConfig) => `const path = require('path') -const fs = require('fs'); -const path = require('path'); -const CopyPlugin = require('copy-webpack-plugin'); -const WriteFilePlugin = require('write-file-webpack-plugin'); - -const APP_COMPONENT_LIBRARY = path.resolve(__dirname, '${ - config.componentLibraryPath -}') -// TODO Needs to be replaced with proper base config object. TBD at later date -const particle = require('../../particle') - -const dev = {} -const prod = {} - -const cssMode = process.env.NODE_ENV === 'production' ? 'extract' : 'hot' - +export const main = (config: MainConfig) => ` module.exports = { addons: ${stringifyAndSingleQuote(config.addons)}, stories: ${stringifyAndSingleQuote(config.storiesRoot)}, - webpackFinal: (config) => { - /** - * Delete the CSS management rules from Storybook. - * Also delete the file-loader ruleset from Storybook in favor of Particle - * Particle.js owns that process. - */ - // eslint-disable-next-line no-param-reassign - config.module.rules.splice(2, 2) - /** - * Delete the ProgressPlugin from Storybook for CI to remove - * log file spam. - */ - if (process.env.CI === 'true') { - // eslint-disable-next-line no-param-reassign - config.plugins.splice(4, 1) - } - return particle( - { shared: config, dev, prod }, - { APP_COMPONENT_LIBRARY }, - { cssMode } - ) + webpackFinal: async config => { + config.module.rules.push({ + test: /\\.(ts|tsx)$/, + loader: require.resolve('babel-loader'), + }); + config.resolve.extensions.push('.ts', '.tsx'); + return config; }, } ` diff --git a/packages/generator-particle-storybook/src/generators/app/templates/preview.ts b/packages/generator-particle-storybook/src/generators/app/templates/preview.ts index 87b18866ca..191c6e2957 100644 --- a/packages/generator-particle-storybook/src/generators/app/templates/preview.ts +++ b/packages/generator-particle-storybook/src/generators/app/templates/preview.ts @@ -1,23 +1,10 @@ -import { FrontendFrameworkOptions } from '@phase2/particle-types' -export interface PreviewConfig { - frontendFramework: FrontendFrameworkOptions -} - -export const preview = ( - config: PreviewConfig -) => `import { addDecorator, addParameters } from '@storybook/${config.frontendFramework}' +export const preview = () => ` +import {defineCustomElements} from '../dist-stencil/esm/loader'; +import { addDecorator, addParameters } from '@storybook/web-components' import { withA11y } from '@storybook/addon-a11y' +defineCustomElements().then(); // Enable a11y checks for all stories addDecorator(withA11y) -// sorts stories alphebetically -addParameters({ - options: { - storySort: (a: any, b: any) => - a[1].kind === b[1].kind - ? 0 - : a[1].id.localeCompare(b[1].id, undefined, { numeric: true }), - }, -}) ` diff --git a/packages/generator-particle-storybook/src/generators/app/templates/test-component.ts b/packages/generator-particle-storybook/src/generators/app/templates/test-component.ts new file mode 100644 index 0000000000..06992cf5fb --- /dev/null +++ b/packages/generator-particle-storybook/src/generators/app/templates/test-component.ts @@ -0,0 +1,8 @@ +export const component =()=> ` +export default { + title: 'My Component' +} + +export const Default = () => \`\`; + +` diff --git a/packages/generator-particle-storybook/src/utils/helpers.ts b/packages/generator-particle-storybook/src/utils/helpers.ts index 584022f8f2..f75b3bfb0f 100644 --- a/packages/generator-particle-storybook/src/utils/helpers.ts +++ b/packages/generator-particle-storybook/src/utils/helpers.ts @@ -1,2 +1,3 @@ -export const stringifyAndSingleQuote = (val: string[]) => - JSON.stringify(val).replace('"', "'") +export const stringifyAndSingleQuote = (val: string[]) =>{ + return JSON.stringify(val).replace(/"/gi, "'") +} From 082bf4e65c28d0ff32cf1f4761018568283530b0 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Wed, 2 Dec 2020 14:55:21 -0800 Subject: [PATCH 16/18] chore: Add components generator to incorporate stencilJs. --- .../package.json | 44 +++++++++++++ .../src/generators/app/index.ts | 66 +++++++++++++++++++ .../src/generators/app/templates/config.ts | 29 ++++++++ .../tsconfig.json | 11 ++++ 4 files changed, 150 insertions(+) create mode 100644 packages/generator-particle-components/package.json create mode 100644 packages/generator-particle-components/src/generators/app/index.ts create mode 100644 packages/generator-particle-components/src/generators/app/templates/config.ts create mode 100644 packages/generator-particle-components/tsconfig.json diff --git a/packages/generator-particle-components/package.json b/packages/generator-particle-components/package.json new file mode 100644 index 0000000000..e279b30d1b --- /dev/null +++ b/packages/generator-particle-components/package.json @@ -0,0 +1,44 @@ +{ + "name": "@phase2/generator-particle-components", + "version": "0.0.1", + "description": "Generates the file structure for stencil web components", + "files": [ + "lib/generators" + ], + "keywords": [ + "yeoman-generator", + "particle", + "stencil" + ], + "main": "lib/generators/app/index.js", + "homepage": "https://github.com/phase2/particle#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/phase2/particle.git", + "directory": "packages/particle-cli" + }, + "dependencies": { + "chalk": "^4.1.0", + "yeoman-generator": "^4.11.0", + "lodash.merge": "^4.6.2" + }, + "devDependencies": { + "@types/inquirer": "^7.3.0", + "@types/yeoman-generator": "^4.11.0", + "@types/lodash.merge": "^4.6.6", + "typescript": "^3.9.7" + }, + "scripts": { + "tsc": "tsc", + "tsc:watch": "tsc --watch" + }, + "bugs": { + "url": "https://github.com/phase2/particle/issues" + }, + "directories": { + "lib": "lib", + "test": "test" + }, + "author": "", + "license": "ISC" +} diff --git a/packages/generator-particle-components/src/generators/app/index.ts b/packages/generator-particle-components/src/generators/app/index.ts new file mode 100644 index 0000000000..386bef4a31 --- /dev/null +++ b/packages/generator-particle-components/src/generators/app/index.ts @@ -0,0 +1,66 @@ +import { config } from './templates/config' +import Generator from 'yeoman-generator' +import { blueBright } from 'chalk' +import fs from 'fs' + +const deps = ['typescript', '@stencil/core']; +const devDeps = ['wait-on', 'concurrently'] + + + +module.exports = class extends Generator { + projectNamespace: string + updateJason: (newJson: Record, path: string) => void + constructor(args: any, opts: any) { + super(args, opts); + this.projectNamespace = opts.projectNamespace + this.updateJason = opts.updateJason + } + + _overwriteStencilConfig() { + fs.writeFileSync( + this.destinationPath(`./${this.projectNamespace}/stencil.config.ts`), + config() + ) + } + + _addStencilDependencies() { + // TODO to add support for other frameworks + console.log(blueBright('adding stencil dependencies to the packageJson')) + + const packageJsonPath = `${this.projectNamespace}/package.json` + + const newPackageData = { + distDirs: { + "stencil": "dist-stencil", + "storybook": "dist-storybook" + }, + scripts: { + "build": "stencil build --docs", + "test": "stencil test --spec --e2e", + "test.watch": "stencil test --spec --e2e --watchAll", + "generate": "stencil generate", + "start": "concurrently \"npm:watch-stencil\" \"npm:watch-storybook\"", + "watch-stencil": "stencil build --dev --watch", + }, + } + + this.updateJason(newPackageData, packageJsonPath) + this.npmInstall(deps, {},{cwd: `${this.projectNamespace}`}) + this.npmInstall(devDeps, { 'save-dev': true }, {cwd: `${this.projectNamespace}`}) + } + + writing() { + console.log(blueBright(`building stencil project at${process.cwd()}/${this.projectNamespace}`)) + this.spawnCommandSync('npm', [ + 'init', + 'stencil', + 'component', + `${this.projectNamespace}` + ]) + + this._addStencilDependencies() + this._overwriteStencilConfig() + } + +}; diff --git a/packages/generator-particle-components/src/generators/app/templates/config.ts b/packages/generator-particle-components/src/generators/app/templates/config.ts new file mode 100644 index 0000000000..13815445b1 --- /dev/null +++ b/packages/generator-particle-components/src/generators/app/templates/config.ts @@ -0,0 +1,29 @@ +export const config = () => ` + import { Config } from '@stencil/core'; + + const { name, distDirs } = require('./package.json'); + + export const config: Config = { + buildDist: true, + namespace: name, + taskQueue: 'async', + outputTargets: [ + { + type: 'www', + serviceWorker: null, // disable service workers + }, + { + type: 'dist', + dir: distDirs.stencil, + }, + { + type: 'dist-custom-elements-bundle', + dir: distDirs.stencil, + }, + { + type: 'docs-readme', + dir: distDirs.stencil, + }, + ], + } +` diff --git a/packages/generator-particle-components/tsconfig.json b/packages/generator-particle-components/tsconfig.json new file mode 100644 index 0000000000..da3392ce86 --- /dev/null +++ b/packages/generator-particle-components/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "outDir": "lib", + "rootDir": "./src", + "strict": true, + "baseUrl": "./", + "moduleResolution": "node" + } +} From 83ba3e70c404e1289c0ef02162d87bc48aca43af Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Wed, 2 Dec 2020 18:28:10 -0800 Subject: [PATCH 17/18] chore: R/F build commands to ensure TS installs before SB. Add docs. --- .../src/generators/app/index.ts | 12 +++--- .../src/generators/app/index.ts | 37 +++++++++++++------ .../src/generators/app/index.ts | 8 +++- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index 73d19a974b..6f1777438f 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -10,11 +10,6 @@ import { propOptions } from './generatePromptOptions' - -/** - * List of Dependencies to be installed - */ - module.exports = class extends Generator { // configuration will come from the constructor argument configuration: ConfigurationAnswers @@ -64,6 +59,9 @@ module.exports = class extends Generator { this.packageJson = merge(this.packageJson, newValues) } + /** + * Helper function passed to sub-generators + */ _updateSubGeneratorPackageJson(newValues: Record, jsonPath: string) :void { const currentJson = JSON.parse(fs.readFileSync(jsonPath).toString()); const mergedJson = JSON.stringify(merge(currentJson, newValues)) @@ -111,6 +109,7 @@ module.exports = class extends Generator { const projectNamespace = `${nameSpace}-${projectName}` // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html + this.composeWith( require.resolve('@phase2/generator-particle-components'), { @@ -127,10 +126,9 @@ module.exports = class extends Generator { } ); } - // Add other subgenerators here - writing() { + configuring() { this._createPackageJson() this._writeParticleConfig() } diff --git a/packages/generator-particle-components/src/generators/app/index.ts b/packages/generator-particle-components/src/generators/app/index.ts index 386bef4a31..ddff72e101 100644 --- a/packages/generator-particle-components/src/generators/app/index.ts +++ b/packages/generator-particle-components/src/generators/app/index.ts @@ -1,13 +1,13 @@ import { config } from './templates/config' import Generator from 'yeoman-generator' -import { blueBright } from 'chalk' +import { blueBright, green } from 'chalk' import fs from 'fs' -const deps = ['typescript', '@stencil/core']; +/** +* Required dev-dependencies. +*/ const devDeps = ['wait-on', 'concurrently'] - - module.exports = class extends Generator { projectNamespace: string updateJason: (newJson: Record, path: string) => void @@ -17,16 +17,22 @@ module.exports = class extends Generator { this.updateJason = opts.updateJason } + /** + * Replaces vanilla stencil.config.ts with templates/config.ts + **/ _overwriteStencilConfig() { + console.log('overwrite stencil config') fs.writeFileSync( this.destinationPath(`./${this.projectNamespace}/stencil.config.ts`), config() ) } + /** + * Update package.json and dev-dependencies + **/ _addStencilDependencies() { - // TODO to add support for other frameworks - console.log(blueBright('adding stencil dependencies to the packageJson')) + console.log(blueBright('adding stencil dependencies to the package.json')) const packageJsonPath = `${this.projectNamespace}/package.json` @@ -46,11 +52,14 @@ module.exports = class extends Generator { } this.updateJason(newPackageData, packageJsonPath) - this.npmInstall(deps, {},{cwd: `${this.projectNamespace}`}) - this.npmInstall(devDeps, { 'save-dev': true }, {cwd: `${this.projectNamespace}`}) + this.npmInstall([...devDeps], { 'save-dev': true }, {cwd: `${this.projectNamespace}`}) } - writing() { + /** + * Running in default to be sure to preemptively install typescript + * for proper storybook initialization. + */ + default() { console.log(blueBright(`building stencil project at${process.cwd()}/${this.projectNamespace}`)) this.spawnCommandSync('npm', [ 'init', @@ -58,9 +67,13 @@ module.exports = class extends Generator { 'component', `${this.projectNamespace}` ]) - - this._addStencilDependencies() - this._overwriteStencilConfig() + console.log(green('Installing Typescript')) + this.spawnCommandSync('npm', [ + 'i', + 'typescript', + ], {cwd: `${this.projectNamespace}`}) + this._addStencilDependencies() + this._overwriteStencilConfig() } }; diff --git a/packages/generator-particle-storybook/src/generators/app/index.ts b/packages/generator-particle-storybook/src/generators/app/index.ts index dad4da4097..3e9acd67a7 100644 --- a/packages/generator-particle-storybook/src/generators/app/index.ts +++ b/packages/generator-particle-storybook/src/generators/app/index.ts @@ -32,9 +32,13 @@ module.exports = class extends Generator { this.updateJason = opts.updateJason } + + /** + * Update package.json scripts + */ _addStorybookDependencies() { // TODO to add support for other frameworks - console.log(green('adding storybook dependencies to the packageJson')) + console.log(green('adding storybook dependencies to the package.json')) const packageJsonPath = `${this.projectNamespace}/package.json` const newPackageData = { @@ -46,7 +50,7 @@ module.exports = class extends Generator { } this.updateJason(newPackageData, packageJsonPath); - this.npmInstall(storybookAddons, { 'save-dev': true }) + this.npmInstall([...storybookAddons], { 'save-dev': true }, {cwd: this.projectNamespace}) } writing() { From 466fb3287e1273cf75d206270e53664cfc33c337 Mon Sep 17 00:00:00 2001 From: IMGoodrich Date: Wed, 2 Dec 2020 22:33:28 -0800 Subject: [PATCH 18/18] chore: Remove duplicate storybook addons, and minor cleanup. --- packages/generator-particle-base/src/generators/app/index.ts | 4 ++-- .../src/generators/app/index.ts | 1 + .../generator-particle-storybook/src/generators/app/index.ts | 5 +---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index 6f1777438f..6b717ee583 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -96,8 +96,6 @@ module.exports = class extends Generator { async _promptUser() { this.configuration.config = await this.prompt(propOptions) - this.packageJson.client = this.configuration.config.nameSpace - this.packageJson.name = this.configuration.config.projectName } /** @@ -107,6 +105,8 @@ module.exports = class extends Generator { await this._promptUser() const { nameSpace, projectName } = this.configuration.config const projectNamespace = `${nameSpace}-${projectName}` + this.packageJson.client = nameSpace + this.packageJson.name = projectName // All composed generators must be imported following this syntax https://yeoman.io/authoring/composability.html diff --git a/packages/generator-particle-components/src/generators/app/index.ts b/packages/generator-particle-components/src/generators/app/index.ts index ddff72e101..fe06280579 100644 --- a/packages/generator-particle-components/src/generators/app/index.ts +++ b/packages/generator-particle-components/src/generators/app/index.ts @@ -37,6 +37,7 @@ module.exports = class extends Generator { const packageJsonPath = `${this.projectNamespace}/package.json` const newPackageData = { + repository:{}, distDirs: { "stencil": "dist-stencil", "storybook": "dist-storybook" diff --git a/packages/generator-particle-storybook/src/generators/app/index.ts b/packages/generator-particle-storybook/src/generators/app/index.ts index 3e9acd67a7..9fc83a37c5 100644 --- a/packages/generator-particle-storybook/src/generators/app/index.ts +++ b/packages/generator-particle-storybook/src/generators/app/index.ts @@ -8,12 +8,9 @@ import { main } from './templates/main' export const storybookAddons: string[] = [ '@storybook/addon-knobs', - '@storybook/addon-actions', '@storybook/addon-links', - '@storybook/addon-viewport', '@storybook/addon-a11y', - '@storybook/preset-typescript', - '@storybook/web-components' + '@storybook/addon-essentials' ] const storiesRoot: string[] = ['../src/**/*.stories.tsx']