Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
ahoendgen committed Dec 11, 2021
0 parents commit 7d2eec0
Show file tree
Hide file tree
Showing 20 changed files with 2,667 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
12 changes: 12 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": ["eslint:recommended", "prettier"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint/eslint-plugin"],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {},
"env": {
"node": true
}
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
.eslintcache
dist
localTest
1 change: 1 addition & 0 deletions .husky/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
21 changes: 21 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"useTabs": true,
"overrides": [
{
"files": "*.md",
"options": {
"printWidth": 70,
"useTabs": false,
"trailingComma": "none",
"arrowParens": "avoid",
"proseWrap": "never"
}
},
{
"files": "*.{json,babelrc,eslintrc,remarkrc,prettierrc}",
"options": {
"useTabs": false
}
}
]
}
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# plop generator react atomic component

_An opinionated [`plop`][plop] generator for [`typescript`][typescript] [`atomic`][atomic] [`react`][react] components._

## Installation

This package is hosted on [`npm`][npm].

```bash
npm install --save-dev @a9g/plop-generator-react-atomic-component
```

## Usage

First, create two interfaces to include classnames or styles (depending on if you are using react-native or not) to include them into your props.

```typescript
import { StyleProp, ViewStyle } from "react-native";

export interface PropsWithClassName {
className?: string;
}

export interface PropsWithStyle {
style?: StyleProp<ViewStyle>;
}
```

Afterwards, be sure you have [`plop`][plop] installed. Then, add the following lines to your `plopfile.js`.

```javascript
const atomicGenerator =
require("@a9g/plop-generator-react-atomic-component").default;

const defaultConfig = {
createIndex: true,
functional: true,
basePath: "src/ui/components",
withClassnameInterfaceImportPath: "/framework/ui", //make sure to configure this path
withStyleInterfaceImportPath: "/framework/ui",
tests: true,
stories: true,
styledComponents: true,
useNative: false, // native and macro can't be used together
useMacro: false
};

atomicGenerator(plop, defaultConfig);
```

Now you'll have access to the `atomic-component` generator as shown below.

```bash
plop atomic-component
```

```text
src
└── ui
└── components
└── $Type
└── $ComponentName
├── $ComponentName.tsx
├── $ComponentName.test.tsx (optional)
├── $ComponentName.stories.tsx (optional)
├── $ComponentName.styles.tsx (optional)
└── index.tsx (optional)
```

## Questions

Report bugs or provide feedback by filing [issues][issues]

## License

MIT see [license.md](license.md)

[npm]: https://www.npmjs.com/package/@a9g/plop-generator-react-atomic-component
[issues]: https://github.com/ahoendgen/plop-generator-react-atomic-component/issues
[plop]: https://plopjs.com
[react]: https://reactjs.org
[typescript]: https://typescriptlang.org
[atomic]: https://atomicdesign.bradfrost.com/
9 changes: 9 additions & 0 deletions license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# The MIT License

Copyright 2021 André Hoendgen, contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
60 changes: 60 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "@a9g/plop-generator-react-atomic-component",
"version": "0.0.1",
"description": "plop generator react atomic component",
"main": "dist/index.js",
"types": "dist/types/index.d.ts",
"author": "André Hoendgen <[email protected]> (andre-hoendgen.de)",
"license": "MIT",
"repository": "https://github.com/ahoendgen/plop-generator-react-atomic-component",
"keywords": [
"node",
"typescript",
"eslint",
"prettier",
"jest",
"plop",
"plop-pack",
"plop-generator",
"react"
],
"files": [
"dist"
],
"scripts": {
"start": "tsc --watch",
"build": "tsc",
"postbuild": "cp -r src/templates dist/templates",
"pretest": "npm run lint && tsc --noEmit",
"format": "prettier --loglevel warn --write \"**/*.{ts,tsx,css,md,json}\"",
"posttest": "npm run format",
"prepare": "husky install",
"prepublishOnly": "rimraf dist && run-s build",
"lint": "eslint . --cache --fix --ext .ts,.tsx"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.6.0",
"@typescript-eslint/parser": "^5.6.0",
"eslint": "^8.4.1",
"eslint-config-prettier": "^8.3.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
"rimraf": "^3.0.2",
"typescript": "^4.5.3"
},
"lint-staged": {
"*.{ts,tsx,css,md,json,js}": "prettier --write"
},
"dependencies": {
"node-plop": "^0.30.0"
},
"volta": {
"node": "16.13.1",
"yarn": "1.22.17"
},
"publishConfig": {
"access": "public"
}
}
7 changes: 7 additions & 0 deletions plopfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const generator = require("./dist/index").default;

const config = plop => {
generator(plop);
}

module.exports = config
164 changes: 164 additions & 0 deletions src/atomicComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { NodePlopAPI } from "node-plop";
import * as path from "path";
import { GeneratorConfig } from "index";

const atomicComponent = (
config: Partial<GeneratorConfig>,
plop: NodePlopAPI
) => {
const defaultConfig: GeneratorConfig = {
createIndex: true,
functional: true,
basePath: "src/ui/components",
withClassnameInterfaceImportPath: "/framework/ui",
withStyleInterfaceImportPath: "/framework/ui",
tests: true,
stories: true,
styledComponents: true,
useNative: false, // native and macro can't be used together
useMacro: false,
};

let fullConfig: GeneratorConfig = {
...defaultConfig,
...config,
};

const prompts = [];

prompts.push({
type: "list",
name: "type",
message: "component type",
choices: ["Atom", "Molecule", "Organism", "Template", "Page"],
});

prompts.push({
type: "input",
name: "name",
message: "component name",
});

const actions = [];
let data = {};

const IS_NATIVE = fullConfig.useNative;
const IS_FUNCTIONAL = fullConfig.functional;
const WITH_STYLED_COMPONENTS = fullConfig.styledComponents;

let styledComponentsType = "styled-components";
let baseComponent = "div";
let withClassNameClassName = `className={${
IS_FUNCTIONAL ? "" : "this."
}props.className} `;
let withClassNameProps = "interface Props extends PropsWithClassName";
let withClassNameImport = `import {PropsWithClassName} from "${fullConfig.withClassnameInterfaceImportPath}";\n`;

if (fullConfig.useMacro) {
styledComponentsType = "styled-components/macro";
}

if (IS_NATIVE) {
styledComponentsType = "styled-components/native";
baseComponent = "Text";
withClassNameClassName = `style={${
IS_FUNCTIONAL ? "" : "this."
}props.style} `;
withClassNameProps = "interface Props extends PropsWithNativeStyle";
withClassNameImport = `import {PropsWithNativeStyle} from "${fullConfig.withStyleInterfaceImportPath}";\n`;
}

let styleImport = `\nimport {Root} from './{{pascalCase name}}.styles';`;
let templateBaseComponent = "Root";

if (!WITH_STYLED_COMPONENTS) {
styleImport = "";
templateBaseComponent = "div";
if (IS_NATIVE) {
templateBaseComponent = "Text";
}
}

data = {
templateBaseComponent,
withClassNameProps,
withClassNameClassName,
baseComponent,
styledComponentsType,
};
plop.setPartial("styleImport", styleImport);
plop.setPartial("withClassNameImport", withClassNameImport);
plop.setPartial("withClassNameClassName", withClassNameClassName);

const CURRENT_DIR = path.resolve(__dirname);

if (fullConfig.createIndex) {
actions.push({
type: "add",
path:
fullConfig.basePath +
"/{{pascalCase type }}//{{pascalCase name}}/index.ts",
templateFile: CURRENT_DIR + "/templates/index.hbs",
data,
});
}

if (fullConfig.functional) {
actions.push({
type: "add",
path:
fullConfig.basePath +
"/{{pascalCase type }}/{{pascalCase name}}/{{pascalCase name}}.tsx",
templateFile: CURRENT_DIR + "/templates/component_functional.hbs",
data,
});
} else {
actions.push({
type: "add",
path:
fullConfig.basePath +
"/{{pascalCase type }}/{{pascalCase name}}/{{pascalCase name}}.tsx",
templateFile: CURRENT_DIR + "/templates/component_class_based.hbs",
data,
});
}

if (fullConfig.tests) {
actions.push({
type: "add",
path:
fullConfig.basePath +
"/{{pascalCase type}}/{{pascalCase name}}/{{pascalCase name}}.test.tsx",
templateFile: CURRENT_DIR + "/templates/test.hbs",
data,
});
}

if (fullConfig.stories) {
actions.push({
type: "add",
path:
fullConfig.basePath +
"/{{pascalCase type }}/{{pascalCase name}}/{{pascalCase name}}.stories.tsx",
templateFile: CURRENT_DIR + "/templates/story.hbs",
data,
});
}

actions.push({
type: "add",
path:
fullConfig.basePath +
"/{{pascalCase type }}/{{pascalCase name}}/{{pascalCase name}}.styles.ts",
templateFile: CURRENT_DIR + "/templates/styles.hbs",
data,
});

return {
description: "⚛ react component",
prompts,
actions,
};
};

export default atomicComponent;
Loading

0 comments on commit 7d2eec0

Please sign in to comment.