diff --git a/.gitignore b/.gitignore index f1fc82cf1e..dab821aeb7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ node_modules # Errors npm-debug.log php_errors.log +lerna-debug.log -# Typescript -dist \ No newline at end of file +# Typescript outdir +packages/*/lib \ No newline at end of file diff --git a/README.md b/README.md index 1ed9bb6037..98778e0dfd 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,14 @@ TBD 1. Clone the repo 1. Run `npm install`, This will install all dev dependencies and run the postinstall script `lerna:install` which runs `lerna bootstrap --nohoist` and installs all package dependencies. 1. If you update subdependencies, simply run `npm install` or `npm run lerna:install` to re-install lerna package dependencies. This is especially helpful when you are pulling in new code (with sub dependency additionas) from another branch. -1. `npm run build:watch` build the project in the dist folder +1. `npm run build:watch` build the project in the `packages//lib` folder, packages that do not contain a .tsconfig and do not have a `tsc` and `tsc:build` script will be ignored lerna executes packages that contain only these scripts. +1. For all packages, simply execute the target file such as `node packages/particle-cli/lib/bin/particle-cli.js init` to run things, typescript must be compiled for typescript packages to execute 1. `npm run test:watch` to start jest in watch mode (recommended) ### Installing A Dependency 1. Run `npm run build`, build will fire off the `tsc` build script and also copy the package.json and README.md files from the `packages/*` directories directly into the dist folder. Alternatively have the compiler in watch mode `npm run build:watch` and run `npm run postbuild` to copy the files in. -1. Cd into `dist/` and run `npm link`, this will link the **bin** alias as an alias in your terminal. Example the bin is named `@phase2/particle-cli` therefore running `npx @phase2/particle-cli -v` will invoke the binary file `particle-cli`. +1. Cd into `package/` and run `npm link`, this will link the **lib/bin** or `main/index.js` alias as an alias in your terminal. Example the bin is named (or aliased) `@phase2/particle-cli` therefore running `npx @phase2/particle-cli -v` will invoke the binary file `particle-cli`. #### Example @@ -41,7 +42,7 @@ particle-cli -V; // works ### Clean the repo -To remove package-lock.json from all levels of the repo simply run this command. PS is used to prevent grep from exiting as this throws an error with `lerna exec` even with the `--no-bail` flag. +To remove package-lock.json from all levels of the repo simply run this command. `ps` is used to prevent grep from exiting as this throws an error with `lerna exec` even with the `--no-bail` flag. ```bash ps -ef | (grep -q -s -R ^$1 package-lock.json && rm -rf package-lock.json) | { grep -v grep || true; }; lerna exec -- ps -ef | (grep -q -s -R ^$1 package-lock.json && rm -rf package-lock.json) | { grep -v grep || true; } diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 1bbde700a7..0000000000 --- a/babel.config.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - useBuiltIns: 'entry', - target: { node: 12 }, - }, - ], - '@babel/preset-typescript', - ], - env: { - test: { - presets: [['@babel/preset-env']], - }, - production: { - plugins: ['transform-remove-console'], - }, - }, -} diff --git a/package.json b/package.json index 49f0b0b05d..a6ec141a02 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "private": true, "keywords:": [ + "Particle", "Storybook", "Pattern Lab", "Drupal" @@ -27,17 +28,16 @@ "pretty-check": "prettier --check packages/**/*.js", "test": "jest", "test:watch": "jest --watch", - "build": "tsc -p tsconfig.json && npm run postbuild", + "build": "lerna run --parallel --scope='@phase2/*' tsc && npm run postbuild", + "build:watch": "lerna run --parallel --scope='@phase2/*' tsc:watch", "postbuild": "ts-node ./src/scripts/build.ts", - "build:watch": "tsc -p tsconfig.json --watch", "update:check": "npx ncu && npx lerna exec --concurrency 1 --no-bail -- npx ncu", "update:start": "npm-upgrade && lerna exec --concurrency 1 -- npm-upgrade; npm run lerna:install" }, "devDependencies": { - "@babel/core": "^7.10.2", - "@babel/preset-env": "^7.10.2", + "@lerna/link": "^3.21.0", "@types/jest": "^26.0.0", - "babel-plugin-transform-remove-console": "^6.9.4", + "@types/node": "^14.0.14", "eslint": "^7.2.0", "jest": "^26.0.1", "jest-watch-typeahead": "^0.6.0", @@ -48,8 +48,5 @@ "ts-jest": "^26.1.0", "ts-node": "^8.10.2", "typescript": "^3.9.5" - }, - "files": [ - "dist/**/*" - ] + } } diff --git a/packages/eslint-config/index.ts b/packages/eslint-config/index.js similarity index 99% rename from packages/eslint-config/index.ts rename to packages/eslint-config/index.js index d796f0cdb1..ebbd5da659 100644 --- a/packages/eslint-config/index.ts +++ b/packages/eslint-config/index.js @@ -34,4 +34,4 @@ module.exports = { rules: { 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], }, -}; +} diff --git a/packages/generator-particle-drupal/generators/app/index.ts b/packages/generator-particle-drupal/generators/app/index.js similarity index 100% rename from packages/generator-particle-drupal/generators/app/index.ts rename to packages/generator-particle-drupal/generators/app/index.js diff --git a/packages/particle-cli/bin/particle-cli.ts b/packages/particle-cli/bin/particle-cli.ts index de5e9e7020..6dcb466002 100755 --- a/packages/particle-cli/bin/particle-cli.ts +++ b/packages/particle-cli/bin/particle-cli.ts @@ -1,9 +1,10 @@ #!/usr/bin/env node -const program = require('commander') -// const pkg = require('../package'); // can't do since this is not copied over into dist unless its an import +import program from 'commander' + import pkg from '../package.json' -const create = require('../lib/create') +import { generatePromptOptions } from '../src/generatePromptOptions' +import create from '../src/create' /** * Initialize Commander program with version. @@ -16,7 +17,7 @@ program .description('Scaffold your project from a set of prompts.') .action(function () { // @TODO Implement Create Function. - create() + generatePromptOptions().then(create) }) // allow commander to parse `process.argv` diff --git a/packages/particle-cli/package.json b/packages/particle-cli/package.json index 23eac4d5af..f922483e42 100644 --- a/packages/particle-cli/package.json +++ b/packages/particle-cli/package.json @@ -13,8 +13,8 @@ "init" ], "bin": { - "@phase2/particle-cli": "bin/particle-cli.js", - "particle-cli": "bin/particle-cli.js" + "@phase2/particle-cli": "lib/bin/particle-cli.js", + "particle-cli": "lib/bin/particle-cli.js" }, "homepage": "https://github.com/phase2/particle#readme", "repository": { @@ -27,10 +27,16 @@ }, "license": "GPL-2.0", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "tsc": "tsc", + "tsc:watch": "tsc --watch" }, "dependencies": { "commander": "^5.1.0", - "esm": "^3.2.25" + "esm": "^3.2.25", + "inquirer": "^7.2.0" + }, + "devDependencies": { + "@types/inquirer": "^6.5.0" } } diff --git a/packages/particle-cli/lib/create.ts b/packages/particle-cli/src/create.ts similarity index 89% rename from packages/particle-cli/lib/create.ts rename to packages/particle-cli/src/create.ts index 805cb848db..1108bc649a 100644 --- a/packages/particle-cli/lib/create.ts +++ b/packages/particle-cli/src/create.ts @@ -24,8 +24,8 @@ * */ -const create = () => { - console.log('Create Particle Project') +const create = (data: any) => { + console.log('Create Particle Project with the data:', data) return true } diff --git a/packages/particle-cli/src/generatePromptOptions.ts b/packages/particle-cli/src/generatePromptOptions.ts new file mode 100644 index 0000000000..c0095300df --- /dev/null +++ b/packages/particle-cli/src/generatePromptOptions.ts @@ -0,0 +1,197 @@ +import { + CustomAnswers, + ConfigOptions, + ConfigurationAnswers, + CSSLibraryOptions, + DesignSystemPatternLibraryOptions, + FrontendFrameworkOptions, + TestingLibraryOptions, +} from './types' +import inquirer from 'inquirer' + +const prompt = inquirer.createPromptModule() + +const minMaxOptionsValidate = ({ min, max }: { min: number; max?: number }) => ( + answer: Record[] +) => { + if (answer.length < min || (!max ? false : answer.length > max)) { + return `You must choose a minimum of ${min} option(s)${ + max ? ` and a maximum of ${max} option(s)` : '' + }` + } + return true +} + +const configurationPrompt = (): Promise => + prompt([ + { + type: 'input', + message: 'choose a project name using kebab case, example: "my-project"', + name: 'projectName', + validate: (name: string) => { + if (!name || name.length < 4) { + return 'Please enter a project name of more than 4 characters length' + } + if (name.indexOf(' ') > 0) { + return 'Please enter a project name with no spaces' + } + return true + }, + }, + { + type: 'input', + message: 'choose a design system name', + name: 'designSystemName', + default: 'default', + validate: (name: string) => { + if (!name || name.length < 4) { + return 'Please enter a repo name of more than 4 characters length' + } + return true + }, + }, + { + type: 'list', + message: 'Choose a configuration', + name: 'config', + choices: [ + { + name: + 'modern react (storybook, tailwind, react, typescript, jest | cypress, svgs)', + value: 'modern-react', + }, + { name: 'drupal only (Pattern Lab, Tailwind, Svgs)', value: 'drupal' }, + { name: 'custom', value: 'custom' }, + ], + }, + ]) + +const customPromptOptions = (): Promise => { + return prompt([ + { + type: 'checkbox', + message: 'choose a Component/Pattern Library or a Design System', + name: 'designSystem', + choices: [ + new inquirer.Separator('-- Design System choose(1 or both)--'), + { + name: 'Storybook', + value: DesignSystemPatternLibraryOptions.STORYBOOK, + checked: true, + }, + { + name: 'Pattern Lab', + value: DesignSystemPatternLibraryOptions.PATTERN_LAB, + }, + ], + validate: minMaxOptionsValidate({ min: 1 }), + }, + { + type: 'checkbox', + message: 'What frontend framework are you using with Storybook?', + name: 'frontendFramework', + choices: [ + { + name: 'React', + checked: true, + value: FrontendFrameworkOptions.REACT, + }, + { + name: 'Webcomponents', + value: FrontendFrameworkOptions.WEBCOMPONENTS, + }, + ], + // PR up for docs on inquirer to annotate second param answers https://github.com/SBoudrias/Inquirer.js/pull/936 + filter: (value: FrontendFrameworkOptions[], answers: CustomAnswers) => { + if ( + answers.designSystem.includes( + DesignSystemPatternLibraryOptions.PATTERN_LAB + ) + ) { + return [FrontendFrameworkOptions.TWIG, ...value] + } + return value + // throw new Error(answers.designSystem) + // input will { answers, values } as we are modifying the return value in the choices section + }, + when: (answers: CustomAnswers) => { + // Checks to see if we enabled typescript previously then asks the prompt + if ( + new Set(answers.designSystem).has( + DesignSystemPatternLibraryOptions.STORYBOOK + ) + ) { + return true + } + + // Mutates answers object adds twig to selected choice (does not prompt user) + answers.frontendFramework = [FrontendFrameworkOptions.TWIG] + + return false + }, + }, + { + type: 'list', + message: 'Choose a CSS library', + name: 'cssLibrary', + choices: [ + { name: 'Tailwind', checked: true, value: CSSLibraryOptions.TAILWIND }, + { name: 'Sass', value: CSSLibraryOptions.SASS }, + { + name: 'Bootstrap', + disabled: true, + value: CSSLibraryOptions.BOOTSTRAP, + }, + ], + }, + { + type: 'confirm', + message: 'Do you want to use typescript?', + name: 'hasTypescript', + }, + { + type: 'confirm', + message: 'Do you want ESModule support for typescript?', + name: 'typescriptEsm', + when: (answer: CustomAnswers) => { + // Checks to see if we enabled typescript previously then asks the prompt + if (answer.hasTypescript) { + return true + } + return false + }, + }, + { + type: 'confirm', + name: 'Are you using SVGs?', + }, + { + type: 'checkbox', + message: 'What testing libraries do you want to use?', + name: 'testingLibraries', + choices: [ + { name: 'Jest', value: TestingLibraryOptions.JEST }, + { name: 'Cypress', value: TestingLibraryOptions.CYPRESS }, + { name: 'Selenium', value: TestingLibraryOptions.SELENIUM }, + { + name: 'Loki (Storybook only VRT)', + value: TestingLibraryOptions.LOKI, + }, + ], + validate: minMaxOptionsValidate({ min: 1 }), + }, + ]) +} + +/** + * Returns a promise with a json schema + * The JSON schema will be used for the create method to generate files based off of the options selected in the prompt + */ +export const generatePromptOptions = async () => { + const results = await configurationPrompt() + if (results.config === ConfigOptions.CUSTOM) { + return customPromptOptions() + } + + return Promise.resolve(results) +} diff --git a/packages/particle-cli/src/types.ts b/packages/particle-cli/src/types.ts new file mode 100644 index 0000000000..eb0a599344 --- /dev/null +++ b/packages/particle-cli/src/types.ts @@ -0,0 +1,48 @@ +export enum CSSLibraryOptions { + TAILWIND = 'tailwind', + SASS = 'sass', + BOOTSTRAP = 'bootstrap', +} + +export enum DesignSystemPatternLibraryOptions { + 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', +} + +interface Naming { + projectName: string + designSystemName: string +} + +export enum TestingLibraryOptions { + JEST = 'jest', + CYPRESS = 'cypress', + LOKI = 'loki', + SELENIUM = 'selenium', +} + +export interface ConfigurationAnswers extends Naming { + config: ConfigOptions +} + +export interface CustomAnswers { + cssLibrary: CSSLibraryOptions + designSystem: DesignSystemPatternLibraryOptions[] + frontendFramework: FrontendFrameworkOptions[] + hasSVG: boolean + hasTypescript: boolean + testingLibraries: TestingLibraryOptions[] + typescriptEsm?: boolean +} diff --git a/packages/particle-cli/__tests__/create.test.ts b/packages/particle-cli/test/create.test.ts similarity index 66% rename from packages/particle-cli/__tests__/create.test.ts rename to packages/particle-cli/test/create.test.ts index d2560fd68a..46f0daccf1 100644 --- a/packages/particle-cli/__tests__/create.test.ts +++ b/packages/particle-cli/test/create.test.ts @@ -1,10 +1,10 @@ -import create from '../lib/create' +import create from '../src/create' import repoPackage from '../package.json' const { name } = repoPackage describe(`${name}/create`, () => { it('show log', () => { - expect(create()).toBeTruthy() + expect(create({})).toBeTruthy() }) }) diff --git a/packages/particle-cli/tsconfig.json b/packages/particle-cli/tsconfig.json new file mode 100644 index 0000000000..32387ae18d --- /dev/null +++ b/packages/particle-cli/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./lib" + }, + "include": ["./src", "./bin"] +} diff --git a/packages/stylelint-config/index.js b/packages/stylelint-config/index.js new file mode 100644 index 0000000000..cef8306aa4 --- /dev/null +++ b/packages/stylelint-config/index.js @@ -0,0 +1,24 @@ +module.exports = { + extends: [ + 'stylelint-config-prettier' + ], + plugins: [ + 'stylelint-scss', + ], + rules: { + 'indentation': 2, + 'declaration-colon-space-after': 'always', + 'declaration-no-important': true, + 'max-nesting-depth': 3, + 'selector-max-specificity': '0,3,3', + 'selector-max-id': 0, + 'scss/at-extend-no-missing-placeholder': true, + 'scss/selector-no-redundant-nesting-selector': true, + 'at-rule-no-vendor-prefix': true, + 'media-feature-name-no-vendor-prefix': true, + 'property-no-vendor-prefix': true, + 'selector-no-vendor-prefix': true, + 'value-no-vendor-prefix': true + }, +}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/src/scripts/build.ts b/src/scripts/build.ts index 6388c6c33a..34293d1517 100644 --- a/src/scripts/build.ts +++ b/src/scripts/build.ts @@ -1,8 +1,8 @@ import fs from 'fs' import { exec } from 'child_process' -const distFolder = './dist' // add your scripts to folder named scripts -const packagesFolder = './packages' +const libFolder = '/lib' // add your scripts to folder named scripts +const packagesFolder = '/packages' const files = fs.readdirSync(distFolder) // reading files from folders enum CopyFiles { @@ -13,17 +13,16 @@ enum CopyFiles { /** * iterates through all dist packages and references the dist folder to the packages folder and grabs files unrelated to JS or TS that are required for publishing the package * */ - files.forEach((packageName: string) => { const path = `${packagesFolder}/${packageName}` const b = fs.readdirSync(path).forEach((item: string) => { if (item === CopyFiles.README || item === CopyFiles.PACKAGE) { exec( - `cp ${path}/${item} ${distFolder}/${packageName}/${item}`, + `cp ${path}/${item} ${path}/${libFolder}/${item}`, { shell: '/bin/bash' }, (err: any, stdout: any, stderr: any) => { console.log( - `successfully wrote ${path}/${item} to dist`, + `successfully wrote ${path}/${item} to lib`, stdout, stderr ) diff --git a/tsconfig.json b/tsconfig.json index 3e82e01e4a..e92f6fbd09 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,16 +2,15 @@ "compilerOptions": { "module": "commonjs", "esModuleInterop": true, - "target": "ES2020", + "target": "ES2019", "moduleResolution": "node", "sourceMap": true, - "outDir": "dist", - "allowJs": true, "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, "declaration": true, - "noImplicitAny": true + "noImplicitAny": true, + "removeComments": true, + "noLib": false }, - "include": ["packages/**/*"], "exclude": ["node_modules", "**/__tests__/*"] }