diff --git a/README.md b/README.md index 3e936be4f9..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` @@ -46,9 +45,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 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 4c06371886..25ebf09ab3 100644 --- a/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts +++ b/packages/generator-particle-base/src/generators/app/generatePromptOptions.ts @@ -1,217 +1,27 @@ -import inquirer from 'inquirer' - -import { - CustomAnswers, - ConfigOptions, - ConfigurationAnswers, - CSSLibraryOptions, - ComponentLibraryOptions, - FrontendFrameworkOptions, - TestingLibraryOptions, -} from '@phase2/particle-types' - -const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( - answer: Record[] +const validateString = (length: number, defaultVal?:string ) => ( + answer: 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)` : '' - }` + const defaultText = defaultVal ? `\n Recommended: ${defaultVal}` : '' + if (!answer || answer.length < length) { + return `Please enter a value of at least ${length} characters length.${defaultText}` + } + if (answer.indexOf(' ') > 0) { + return 'Please enter a value name with no spaces' } 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 configurationPrompt = [ - { - type: 'input', - message: 'choose a project name using kebab case, example: "p2-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 two word project name with no spaces' - } - return true - }, - }, +export const propOptions = [ { type: 'input', - message: - 'choose a component library name using kebab case. example "project-default"', - name: 'componentLibraryName', - default: 'project-default', - validate: (name: string) => { - if (!name || name.length < 4) { - return 'Please enter a library name of more than 4 characters length' - } - return true - }, + message: 'Choose a abbreviation for your/client\'s name. (min 3 chars)', + name: 'nameSpace', + validate: validateString(2) }, { type: 'input', - message: - 'Where does your component library exist relative to the root of the project', - default: (answers: ConfigurationAnswers) => - `./source/${answers.componentLibraryName}/`, - name: 'componentLibraryPath', - }, - { - 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' }, - ], - }, -] - -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, - }, - ], - }, - { - 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, - }, - { name: 'Pa11y', value: TestingLibraryOptions.PA11Y }, - ], - validate: minMaxOptionsValidate({ min: 1 }), + message: 'Choose a name for the overall project using kebab case. (min 4 chars) Ex: "website", or "saphire-dagger"', + name: 'projectName', + validate: validateString(4) }, ] diff --git a/packages/generator-particle-base/src/generators/app/index.ts b/packages/generator-particle-base/src/generators/app/index.ts index b7dbb303fa..6b717ee583 100644 --- a/packages/generator-particle-base/src/generators/app/index.ts +++ b/packages/generator-particle-base/src/generators/app/index.ts @@ -3,21 +3,16 @@ import merge from 'lodash.merge' import fs from 'fs' import { - Answers, - ConfigurationAnswers, - ConfigOptions, - FrontendFrameworkOptions, + ConfigurationAnswers } from '@phase2/particle-types' import { - configurationPrompt, - customPromptOptions, - options as configOptions, + propOptions } from './generatePromptOptions' module.exports = class extends Generator { // configuration will come from the constructor argument - configuration: Answers + configuration: ConfigurationAnswers packageJson: Record cliVersion = '' @@ -34,7 +29,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 @@ -42,11 +37,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: '', @@ -54,7 +50,7 @@ module.exports = class extends Generator { description: 'Particle boilerplate project', repository: {}, dependencies: {}, - devDependencies: {}, + devDependencies: {} } this._updatePackageJson = this._updatePackageJson.bind(this) } @@ -63,6 +59,16 @@ 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)) + + fs.writeFileSync(jsonPath, mergedJson ) + } + /** * Creates the package.json, package.json should never need to be re-written */ @@ -74,14 +80,14 @@ module.exports = class extends Generator { } /** - * Creeate a particle config file + * Create a particle config file */ - _writeParticleConfig() { + async _writeParticleConfig() { fs.writeFileSync( '.particlerc', JSON.stringify( - { ...this.configuration, ...{ 'cli-version': this.cliVersion } }, + { ...{ 'cli-version': this.cliVersion }, ...this.configuration }, null, 2 ) @@ -89,24 +95,7 @@ module.exports = class extends Generator { } async _promptUser() { - // Initialize storybook - const results: ConfigurationAnswers = await this.prompt(configurationPrompt) - - // if custom exit here - if (results.config === ConfigOptions.CUSTOM) { - const customOptions = await this.prompt(customPromptOptions) - - this.configuration = { - ...results, - options: customOptions, - } - } else { - this.configuration = { - ...results, - options: configOptions[results.config], - } - } - this.packageJson.name = results.projectName + this.configuration.config = await this.prompt(propOptions) } /** @@ -114,29 +103,33 @@ module.exports = class extends Generator { */ async initializing() { 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 - 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 + + 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() { + configuring() { this._createPackageJson() this._writeParticleConfig() - - // Installs all dependencies - this.npmInstall() } } 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..fe06280579 --- /dev/null +++ b/packages/generator-particle-components/src/generators/app/index.ts @@ -0,0 +1,80 @@ +import { config } from './templates/config' +import Generator from 'yeoman-generator' +import { blueBright, green } from 'chalk' +import fs from 'fs' + +/** +* Required dev-dependencies. +*/ +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 + } + + /** + * 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() { + console.log(blueBright('adding stencil dependencies to the package.json')) + + const packageJsonPath = `${this.projectNamespace}/package.json` + + const newPackageData = { + repository:{}, + 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([...devDeps], { 'save-dev': true }, {cwd: `${this.projectNamespace}`}) + } + + /** + * 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', + 'stencil', + 'component', + `${this.projectNamespace}` + ]) + console.log(green('Installing Typescript')) + this.spawnCommandSync('npm', [ + 'i', + 'typescript', + ], {cwd: `${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" + } +} 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 4b4c678c0d..9fc83a37c5 100644 --- a/packages/generator-particle-storybook/src/generators/app/index.ts +++ b/packages/generator-particle-storybook/src/generators/app/index.ts @@ -1,79 +1,95 @@ 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 { Answers, FrontendFrameworkOptions } from '@phase2/particle-types' 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/addon-essentials' ] -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: Answers - 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() { - // TODO to add support for other frameworks - const dependencies = ['@storybook/react', ...storybookAddons] - console.log(white('adding storybook dependencies to the packageJson')) + /** + * Update package.json scripts + */ + _addStorybookDependencies() { + // TODO to add support for other frameworks + console.log(green('adding storybook dependencies to the package.json')) + 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 }, {cwd: this.projectNamespace}) } - 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}) - // create the files + this.spawnCommandSync( + 'npx', + ['-p', '@storybook/cli', 'sb', 'init', '-t', 'web_components'], + { cwd: `${this.projectNamespace}` }) + + // 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.componentLibraryPath}`, - 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 0a7bc3e30e..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,43 +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 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, "'") +} diff --git a/packages/particle-types/types.ts b/packages/particle-types/types.ts index cc10351dd1..5bab6f65fb 100644 --- a/packages/particle-types/types.ts +++ b/packages/particle-types/types.ts @@ -1,54 +1,10 @@ -export enum CSSLibraryOptions { - TAILWIND = 'tailwind', - SASS = 'sass', - 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 { +export interface CustomAnswers { + nameSpace: string projectName: string - componentLibraryName: string - componentLibraryPath: string -} - -export enum TestingLibraryOptions { - CYPRESS = 'cypress', - JEST = 'jest', - LOKI = 'loki', - PA11Y = 'pa11y', - SELENIUM = 'selenium', + cliVersion?: string } -export interface ConfigurationAnswers extends Naming { - config: ConfigOptions +export interface ConfigurationAnswers { + config: CustomAnswers } -export interface CustomAnswers { - cssLibrary: CSSLibraryOptions - componentLibraryTypes: ComponentLibraryOptions[] - frontendFramework: FrontendFrameworkOptions[] - hasSVG: boolean - hasTypescript: boolean - testingLibraries: TestingLibraryOptions[] - typescriptEsm?: boolean -} - -export interface Answers extends Naming { - options: CustomAnswers -}