Skip to content

Commit cc8dc90

Browse files
authored
Merge pull request #157 from akd-io/feature/154-add-contributing-guidelines
Add contributing guidelines
2 parents 373baf6 + 04fbad1 commit cc8dc90

File tree

5 files changed

+156
-85
lines changed

5 files changed

+156
-85
lines changed

CONTRIBUTING.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Contributing
2+
3+
## Adding support for a new technology
4+
5+
1. Fork the repository on GitHub
6+
2. Create a named feature branch (like `support_x`)
7+
3. Add a new .ts file for your plugin in `src/main/plugins`.
8+
9+
- See the [Writing a plugin section](#writing-a-plugin) below to learn how to write a Create Next Stack plugin.
10+
11+
4. Add new flags to the CLI in [`src/main/index.ts`](src/main/index.ts)
12+
5. Add the plugin to the `plugins` array in [`src/main/setup/setup.ts`](src/main/setup/setup.ts).
13+
6. Add potential plugin steps to the `steps` array in [`src/main/setup/setup.ts`](src/main/setup/setup.ts). Steps are run top-to-bottom.
14+
7. Update the `README.md`:
15+
- Add the technology to the technology list
16+
- Update the `Usage` section to include the new technology
17+
8. Consider expanding some of the e2e tests to include the new technology.
18+
9. Run tests using `yarn test` to ensure they all pass
19+
10. Submit a Pull Request on GitHub
20+
21+
## Writing a plugin
22+
23+
Plugins aren't too scary. The simple Framer Motion plugin that simply adds the `framer-motion` dependency looks like this:
24+
25+
```typescript
26+
export const framerMotionPlugin = createPlugin({
27+
name: "Framer Motion",
28+
description: "Adds support for Framer Motion",
29+
active: ({ flags }) => Boolean(flags["framer-motion"]),
30+
dependencies: {
31+
"framer-motion": {
32+
name: "framer-motion",
33+
version: "^9.0.0",
34+
},
35+
},
36+
technologies: [
37+
{
38+
name: "Framer Motion",
39+
description:
40+
"Framer Motion is a popular React animation library. It allows users to create both simple animations and complex gesture-based interactions. The library implements a declarative API, otherwise known as spring animations, which lets the developer define the animation's end state, letting the library handle the rest.",
41+
links: [
42+
{ title: "Website", url: "https://www.framer.com/motion/" },
43+
{ title: "Docs", url: "https://www.framer.com/docs/" },
44+
{ title: "GitHub", url: "https://github.com/framer/motion" },
45+
],
46+
},
47+
],
48+
} as const)
49+
```
50+
51+
and the [Emotion plugin](src/main/plugins/emotion.ts) easily sets Next.js compiler options using
52+
53+
```typescript
54+
compilerOptions: {
55+
emotion: true,
56+
}
57+
```
58+
59+
and the [Prettier plugin](src/main/plugins/prettier.ts) easily adds formatting scripts to the `package.json` file using
60+
61+
```typescript
62+
scripts: [
63+
{
64+
name: "format",
65+
description: "Formats all source code in the project.",
66+
command: "prettier --write --ignore-path=.gitignore .",
67+
},
68+
{
69+
name: "format:check",
70+
description: "Checks the formatting of all code in the project.",
71+
command: "prettier --check --ignore-path=.gitignore .",
72+
},
73+
],
74+
```
75+
76+
Take a look at the `Plugin` type for documentation on each available property in [`src/main/plugin.ts`](src/main/plugin.ts).
77+
78+
Also take a look at the [other plugins](src/main/plugins) for more advanced examples.

README.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ The table below provides an overview of the technologies currently supported by
5353

5454
| Name | Links |
5555
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
56-
| [Next.js](https://nextjs.org/) | [Docs](https://nextjs.org/docs) - [Learn Next.js](https://nextjs.org/learn) - [GitHub repo](https://github.com/vercel/next.js) |
57-
| [React](https://reactjs.org/) | [Docs](https://reactjs.org/docs/getting-started.html) - [GitHub repo](https://github.com/facebook/react) |
58-
| [TypeScript](https://www.typescriptlang.org/) | [Docs](https://www.typescriptlang.org/docs/) - [GitHub repo](https://github.com/microsoft/TypeScript) |
59-
| [ESLint](https://eslint.org/) | [Configuration](https://eslint.org/docs/user-guide/configuring/) - [Rules](https://eslint.org/docs/rules/) - [GitHub Repo](https://github.com/eslint/eslint) |
56+
| [Next.js](https://nextjs.org/) (Mandatory) | [Docs](https://nextjs.org/docs) - [Learn Next.js](https://nextjs.org/learn) - [GitHub repo](https://github.com/vercel/next.js) |
57+
| [React](https://reactjs.org/) (Mandatory) | [Docs](https://reactjs.org/docs/getting-started.html) - [GitHub repo](https://github.com/facebook/react) |
58+
| [TypeScript](https://www.typescriptlang.org/) (Mandatory) | [Docs](https://www.typescriptlang.org/docs/) - [GitHub repo](https://github.com/microsoft/TypeScript) |
59+
| [ESLint](https://eslint.org/) (Mandatory) | [Configuration](https://eslint.org/docs/user-guide/configuring/) - [Rules](https://eslint.org/docs/rules/) - [GitHub Repo](https://github.com/eslint/eslint) |
6060
| [pnpm](https://pnpm.io/) | [Docs](https://pnpm.io/motivation) - [GitHub repo](https://github.com/pnpm/pnpm) |
6161
| [Yarn](https://yarnpkg.com/) | [CLI Docs](https://yarnpkg.com/cli) - [GitHub repo](https://github.com/yarnpkg/berry) |
6262
| [npm](https://www.npmjs.com/) | [CLI Docs](https://docs.npmjs.com/cli/) |
@@ -75,14 +75,6 @@ The table below provides an overview of the technologies currently supported by
7575
| [lint-staged](https://github.com/okonet/lint-staged) | [GitHub repo](https://github.com/okonet/lint-staged) |
7676
| [GitHub Actions](https://github.com/features/actions) | [Docs](https://docs.github.com/en/actions) - [Workflow syntax](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions) |
7777

78-
#### ⚠ Required
79-
80-
Technologies marked with ⚠ are required. If you don't want to use these technologies, you have three options:
81-
82-
1. Set up your project using Create Next Stack anyway, and make the necessary adjustments manually.
83-
1. Set up your project manually with Create Next App.
84-
1. Find and use a template repo you can clone here on GitHub.
85-
8678
## Usage
8779

8880
Below you see an overview of Create Next Stack's usage, including detailed information about arguments and options. The overview is the result of running `create-next-stack --help`
@@ -110,6 +102,10 @@ OPTIONS
110102
--styling=<styling-method> Sets the preferred styling method. (Required) <styling-method> = emotion|styled-components|tailwind-css|css-modules|css-modules-with-sass
111103
```
112104

105+
## Contributing
106+
107+
Contributions are welcome! Please see the [contributing guidelines](CONTRIBUTING.md) for more information.
108+
113109
## License
114110

115111
Create Next Stack is released under the [MIT License](LICENSE).

src/main/plugin.ts

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,6 @@ import { NextConfig } from "next"
22
import { ValidCNSInputs } from "./create-next-stack-types"
33
import { DeeplyReadonly } from "./helpers/deeply-readonly"
44

5-
export const createPlugin = <TPluginConfig extends PluginConfig>(
6-
pluginConfig: TPluginConfig
7-
): Plugin<TPluginConfig> => {
8-
const plugin = {
9-
...pluginConfig,
10-
}
11-
const enhancements = {
12-
steps:
13-
pluginConfig.steps != null
14-
? Object.entries(pluginConfig.steps).reduce(
15-
(acc, [key, value]) => ({
16-
...acc,
17-
[key]: createStep(value, plugin as Plugin<TPluginConfig>),
18-
}),
19-
{} as Record<string, Step>
20-
)
21-
: undefined,
22-
}
23-
for (const [key, value] of Object.entries(enhancements)) {
24-
Object.defineProperty(plugin, key, {
25-
value,
26-
enumerable: true,
27-
})
28-
}
29-
return plugin as Plugin<TPluginConfig>
30-
}
31-
32-
export const createStep = <TRawStep extends RawStep = RawStep>(
33-
step: TRawStep,
34-
plugin: Plugin
35-
): Step<TRawStep> => {
36-
return {
37-
// defaults
38-
shouldRun: true,
39-
40-
// step
41-
...step,
42-
43-
// enhancements
44-
plugin,
45-
}
46-
}
47-
48-
export type Plugin<TPluginConfig extends PluginConfig = PluginConfig> =
49-
TPluginConfig & {
50-
steps?: {
51-
[key in keyof TPluginConfig["steps"]]: Step<RawStep> // TODO: Fix type. This should be Step<TPluginConfig["steps"][key]>, but that doesn't work.
52-
}
53-
}
54-
55-
export type Step<TStep extends RawStep = RawStep> = TStep & {
56-
shouldRun: NonNullable<RawStep["shouldRun"]>
57-
plugin: Plugin
58-
}
59-
605
type PluginConfig = DeeplyReadonly<{
616
/** Name of the plugin */
627
name: string
@@ -70,7 +15,7 @@ type PluginConfig = DeeplyReadonly<{
7015
devDependencies?: Record<string, Package>
7116
/** Temporary dependencies uninstalled when Create Next Stack is done. */
7217
tmpDependencies?: Record<string, Package>
73-
/** Technology descriptions */
18+
/** Descriptions of the technologies supported by the plugin. */
7419
technologies?: Technology[]
7520
/** Scripts that are added to the package.json file. */
7621
scripts?: Script[]
@@ -153,7 +98,6 @@ type RawStep = {
15398
*/
15499
description: string
155100

156-
// TODO: Consider memoizing shouldRun, as it is sometimes called multiple times. See the lint-staged setup step.
157101
/**
158102
* A boolean or function that determines whether the custom run function should run.
159103
*
@@ -165,6 +109,63 @@ type RawStep = {
165109
run: (inputs: ValidCNSInputs) => Promise<void>
166110
}
167111

112+
export const createPlugin = <TPluginConfig extends PluginConfig>(
113+
pluginConfig: TPluginConfig
114+
): Plugin<TPluginConfig> => {
115+
const plugin = {
116+
...pluginConfig,
117+
}
118+
const enhancements = {
119+
steps:
120+
pluginConfig.steps != null
121+
? Object.entries(pluginConfig.steps).reduce(
122+
(acc, [key, value]) => ({
123+
...acc,
124+
[key]: createStep(value, plugin as Plugin<TPluginConfig>),
125+
}),
126+
{} as Record<string, Step>
127+
)
128+
: undefined,
129+
}
130+
for (const [key, value] of Object.entries(enhancements)) {
131+
Object.defineProperty(plugin, key, {
132+
value,
133+
enumerable: true,
134+
})
135+
}
136+
return plugin as Plugin<TPluginConfig>
137+
}
138+
139+
export const createStep = <TRawStep extends RawStep = RawStep>(
140+
step: TRawStep,
141+
plugin: Plugin
142+
): Step<TRawStep> => {
143+
return {
144+
// defaults
145+
shouldRun: true,
146+
147+
// TODO: Consider memoizing shouldRun, as it is sometimes called multiple times. See the lint-staged setup step.
148+
149+
// step
150+
...step,
151+
152+
// enhancements
153+
plugin,
154+
}
155+
}
156+
157+
export type Plugin<TPluginConfig extends PluginConfig = PluginConfig> =
158+
TPluginConfig & {
159+
steps?: {
160+
[key in keyof TPluginConfig["steps"]]: Step<RawStep> // TODO: Fix type. This should be Step<TPluginConfig["steps"][key]>, but that doesn't work.
161+
}
162+
}
163+
164+
export type Step<TStep extends RawStep = RawStep> = TStep & {
165+
shouldRun: NonNullable<RawStep["shouldRun"]>
166+
plugin: Plugin
167+
}
168+
168169
export const evalActive = (
169170
active: PluginConfig["active"],
170171
inputs: ValidCNSInputs

src/main/plugins/emotion.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,20 @@ export const emotionPlugin = createPlugin({
2525
setup: {
2626
description: "setting up Emotion",
2727
run: async () => {
28-
await addTypeScriptSupportForTheEmotionCssProp()
28+
/*
29+
* Add TypeScript support for the css-prop as per the Emotion docs: https://emotion.sh/docs/typescript#css-prop
30+
*/
31+
await modifyJsonFile("tsconfig.json", (tsConfig) => ({
32+
...tsConfig,
33+
compilerOptions: {
34+
...toObject(tsConfig["compilerOptions"]),
35+
jsxImportSource: "@emotion/react",
36+
},
37+
}))
2938
},
3039
},
3140
},
32-
swcCompilerOptions: {
41+
compilerOptions: {
3342
emotion: true,
3443
},
3544
} as const)
36-
37-
/**
38-
* Add TypeScript support for the css-prop as per the Emotion docs: https://emotion.sh/docs/typescript#css-prop
39-
*/
40-
const addTypeScriptSupportForTheEmotionCssProp = async () => {
41-
await modifyJsonFile("tsconfig.json", (tsConfig) => ({
42-
...tsConfig,
43-
compilerOptions: {
44-
...toObject(tsConfig["compilerOptions"]),
45-
jsxImportSource: "@emotion/react",
46-
},
47-
}))
48-
}

src/main/plugins/styled-components.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const styledComponentsPlugin = createPlugin({
2828
],
2929
},
3030
],
31-
swcCompilerOptions: {
31+
compilerOptions: {
3232
styledComponents: true,
3333
},
3434
} as const)

0 commit comments

Comments
 (0)