Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guides/issue 689 theme pack development guide clarifcations #694

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added www/assets/greenwood-starter-presentation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 119 additions & 45 deletions www/pages/guides/theme-packs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,32 @@ index: 2

## Creating a Theme Pack

Introduced as a concept in the [Context Plugin docs](/plugins/context/), a theme pack is what Greenwood uses to refer to a plugin that aims to provide a set of reasuale templates, pages and more to a user (think of [**CSS Zen Garden**](http://www.csszengarden.com/)). A good example (and the one used in this guide!) is greenwood-starter-presentation, which provides the starting point for creating a slide deck entirely from markdown, using Greenwood!
Introduced as a concept in the [Context Plugin docs](/plugins/context/), a theme pack is what Greenwood uses to refer to a plugin that aims to provide a set of reasuale templates, pages and more to a user (think of [**CSS Zen Garden**](http://www.csszengarden.com/)). A good example (and the one this guide is based on) is [**greenwood-starter-presentation**](https://github.com/thescientist13/greenwood-starter-presentation), which provides the starting point for creating a [slide deck entirely from markdown](https://github.com/thescientist13/knowing-your-tco), using Greenwood!

> _Support for pages [coming soon](https://github.com/ProjectEvergreen/greenwood/issues/681)_!
![greenwood-starter-presentation](/assets/greenwood-starter-presentation.png)

### Prerequistes
This guide will walk through the process of setting up Greenwood to support the developing and publishing of your package (theme pack) to npm.
This guide will walk through the process of setting up Greenwood to support the developing and publishing of your package (theme pack) to **npm**.

To try and focus on just the theme pack aspects, this guide assumes a couple things:
- You are already familiar with [setting up](/getting-started/) a Greenwood project.
- You are familiar with [publishing packages to npm](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages).
- Assumes a Unix "like" environment (in regards to commands and file path examples used), though the same can definifitely be done on Windows.
1. You are already familiar with [setting up](/getting-started/) a Greenwood project.
1. You are familiar with [publishing packages to npm](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages).
1. Assumes a Unix "like" environment (in regards to commands and file path examples used), though the same can definifitely be done on Windows.

We encourage using Greenwood to develop your theme pack mainly so that you can ensure a seamless experience when publishing to npm knowing that things should just work. ™️

### Project Setup
For the sake of development, you can create as much as you need to recreate a user workspace and to simulate what your theme pack would look like. Think of it like creating a [Storybook](https://storybook.js.org/) for your theme pack.

For this guide, we will be publishing _templates/_ and _styles/_ to npm, and so _pages/_ will just be used as a way to pull in the template for local development and testing purposes.

For this guide, we will be publishing _layouts/_ (templates) and _styles/_ to **npm**. The _pages/_ diretory is just being used to pull in the template for local development and testing purposes for you as the plugin author.
```shell
src/
pages/
index.md
styles/
theme.css
templates/
layouts/
blog-post.html
package.json
my-theme-pack.js
Expand Down Expand Up @@ -61,19 +62,23 @@ module.exports = () => [{
provider: () => {
return {
templates: [
path.join(__dirname, 'dist/templates')
// __dirname will be _node_modules/your-package/_
// when your plugin is run in a users project
path.join(__dirname, 'dist/layouts')
]
};
}
}];
```

_blog-post.html_
_src/layouts/blog-post.html_
```html
<html>

<!-- we're using the npm publishing paths here which will come up again in the development section -->

<head>
<!-- reference JS or assets too! -->
<!-- reference JS or assets/ too! -->
<link rel="stylesheet" href="/node_modules/my-theme-pack/dist/styles/theme.css">
</head>

Expand All @@ -86,14 +91,14 @@ _blog-post.html_
</html>
```

_theme.css_
_src/styles/theme.css_
```css
* {
color: red
}
```

_index.md_
_src/pages/index.md_
```md
---
template: 'blog-post'
Expand All @@ -104,36 +109,25 @@ template: 'blog-post'
Lorum Ipsum, this is a test.
```

You should then be able to run `yarn develop` and load `/` in your browser and the color of the text should be red.

You're all ready for development! 🙌

### Development

The main consideration needed for development is that your files won't be in _node_modules_, which is what the case would be for users when you publish. So for that reason, we need to add a little boilerplate to _my-theme-pack.js_. There might be others way to solve it, but it just checks if the packages is installed and:
1. If it _is_ installed, then use `__dirname` (which would resolve to somewhere inside _node_modules_) as the base path
1. If it _is not_ installed (like for local development) then you can use use whatever location you have defined in your repository. Most common would just be to use `process.cwd`
The main consideration needed for development is that your files won't be in _node_modules_, which is what the case would be for users when you publish. So for that reason, we need to add a little boilerplate to _my-theme-pack.js_. There might be others way to solve it, but for right now, accepting a "developer only" flag can easily make the plugin pivot into local or "published" modes.

1. If the flag _is_ passed, then use `__dirname` (which would resolve to somewhere inside _node_modules_) as the base path
1. If the flag _is not_ installed (like we want for local development) then you can use use whatever location you have defined in your repository. Most common would just be to use `process.cwd`

So using our current example, our final _my-theme-pack.js_ would look like this:
```js
const os = require('os');
const path = require('path');
const packageJson = require('./package.json');
const { spawnSync } = require('child_process');

module.exports = () => [{
type: 'context',
name: 'my-theme-pack:context',
provider: () => {
const { name } = packageJson;
const baseDistDir = `node_modules/${name}/dist`;
const command = os.platform() === 'win32' ? 'npm.cmd' : 'npm';
const ls = spawnSync(command, ['ls', name]);
const isInstalled = ls.stdout.toString().indexOf('(empty)') < 0;

const templateLocation = isInstalled
? path.join(__dirname, `${baseDistDir}/templates`)
: path.join(process.cwd(), 'src/templates');
provider: (options = {}) => {
// you can use other directory names besides templates/ this way!
const templateLocation = options.__isDevelopment
? path.join(process.cwd(), 'src/layouts')
: path.join(__dirname, 'dist/layouts');

return {
templates: [
Expand All @@ -144,7 +138,9 @@ module.exports = () => [{
}];
```

And our final _greenwood.config.js_ would look like this, which add a "one-off" [resource plugin](/plugins/resource/) to tell Greenwood to route requests to your theme pack files from their _node_modules_ path to the source directory of your repository for development.
And our final _greenwood.config.js_ would look like this, which add a "one-off" [resource plugin](/plugins/resource/) to tell Greenwood to route requests to your theme pack files away from _node_modules+ and to the location of your projects files for development.

Additionally, we make sure to pass the flag from above for `__isDevelopment` to our plugin.
```js
// shared from another test
const myThemePackPlugin = require('./my-theme-pack');
Expand All @@ -169,7 +165,9 @@ class MyThemePackDevelopmentResource extends ResourceInterface {

module.exports = {
plugins: [
...myThemePackPlugin(),
...myThemePackPlugin({
__isDevelopment: true
}),
{
type: 'resource',
name: 'my-theme-pack:resource',
Expand All @@ -179,13 +177,15 @@ module.exports = {
};
```

> _We realize this current workflow is a bit clunky at the moment, so please follow [this discussion](https://github.com/ProjectEvergreen/greenwood/discussions/682) for ways we can try and make this more elegant!_ 🙏🏻
You should then be able to run `yarn develop` and load `/` in your browser and the color of the text should be red.

You're all ready for development now! 🙌


### Publishing
When it comes to publishing, it should be fairly straightforward, you'll just want to do the following
When it comes to publishing, it should be fairly straightforward, and you'll just want to do the following:
1. Add _dist/_ to _.gitignore_ (or whatever `files` location you want to use for publishing)
1. Add a `prepublish` script to your _package.json_ to create the _dist/_ directory with all the needed _templates/_ and _styles/_
1. Add a `prepublish` script to your _package.json_ to create the _dist/_ directory with all the needed _layouts_ (templates) /_ and _styles/_
```json
{
"name": "my-theme-pack",
Expand All @@ -196,21 +196,23 @@ When it comes to publishing, it should be fairly straightforward, you'll just wa
"dist/"
],
"scripts": {
"prepublish": "rm -rf dist/ && mkdir dist/ && cd src/ && cp -rv templates ../dist && cp -rv pages ../dist"
"prepublish": "rm -rf dist/ && mkdir dist/ && rsync -rv --exclude 'pages/' src/ dist"
}
}
```
1. Now, when you run `npm publish` a fresh _dist/_ folder will be made and [included in your package](https://unpkg.com/browse/greenwood-starter-presentation/)

### Installation and Usage for Users
With the above in place and the package published, you're now ready to share your theme pack with other Greenwood users!

### Installation and Usage
With the above in place the package published, user's would just need to do the following
For users, they would just need to do the following:

1. Install the plugin from npm
```shell
$ npm install my-theme-pack --save-dev
```
1. Add the plugin to their _greenwood.config.js_
```js
// shared with another test develop.plugins.context
const myThemePackPlugin = require('my-theme-pack');

module.exports = {
Expand All @@ -219,15 +221,87 @@ With the above in place the package published, user's would just need to do the
]
};
```
1. Then in any of their markdown files, users would just reference the published template's filename
1. Then in any of their markdown files, users would just need to reference the published template's filename
```md
---
template: 'blog-post'
---

My Blog Post using Theme Packs!
My Blog Post using Theme Packs! 💯
```

Success! 🥳

> _Don't forget, user's can also [include additional CSS / JS files in their frontmatter](/docs/front-matter/#imports), to further extend, customize, and override your templates!_
> _Don't forget, user's can also [include additional CSS / JS files in their frontmatter](/docs/front-matter/#imports), to further extend, customize, and override your templates!_


### FAQ

#### _I'm getting an (Rollup) error when trying to build or test my theme pack for production_
If you try and run `yarn build` or `yarn serve` in a repo where you are creating the theme pack, as per the guide here, you may see this error if you reference assets like `<script>`, `<link>`, etc in your templates. ex:

```shell
prerendering complete for page /slides/7/.
prerendering complete for page /slides/6/.
prerendering complete for page /.
done prerendering all pages
Error: ENOENT: no such file or directory, open '/Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/greenwood-starter-presentation/dist/components/presenter-mode.js'
at Object.openSync (fs.js:476:3)
at Object.readFileSync (fs.js:377:35)
at /Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/@greenwood/cli/src/config/rollup.config.js:185:35
at Array.forEach (<anonymous>)
at Object.buildStart (/Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/@greenwood/cli/src/config/rollup.config.js:171:23)
at /Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/rollup/dist/shared/rollup.js:18870:25
at async Promise.all (index 2)
at async rollupInternal (/Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/rollup/dist/shared/rollup.js:20239:9)
at async /Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/@greenwood/cli/src/lifecycles/bundle.js:12:24 {
errno: -2,
syscall: 'open',
code: 'ENOENT',
path: '/Users/owenbuckley/Workspace/github/repos/greenwood-starter-presentation/node_modules/greenwood-starter-presentation/dist/components/presenter-mode.js'
}
```

Although within your theme pack project you can use `yarn develop` to create a theme pack like any other Greenwood project, there are a couple limitations. Mainly from your theme pack templates you must explicitely reference _node_modules/<pacakge-name>/path/to/asset/_ as the starting prefix, but we are tracking a solution and `yarn develop` should be sufficient to be able to succesfully develop and publish for now.


#### _Can I include pages as part of a theme pack?_

Support for [including pages as part of a theme pack](https://github.com/ProjectEvergreen/greenwood/issues/681) is planned and coming soon, pretty much as soon as we can support [external data sources](https://github.com/ProjectEvergreen/greenwood/issues/21) in the CLI.


#### _Will there be less development boilerplate in the future for plugin authors?_

Yes, we do realize this current workflow is a bit clunky at the moment, so please follow [this discussion](https://github.com/ProjectEvergreen/greenwood/discussions/682) for ways we can try and make this more elegant! 🙏🏻


#### Why can't I just use relative paths in my templates to help avoid the boilerplate?

ex.
```html
<html>

<!-- we're using the npm publishing paths here which will come up again in the development section -->

<head>
<!-- reference JS or assets/ too! -->
<link rel="stylesheet" href="../styles/theme.css">
</head>

<body>
...
</body>

</html>
```

Good question! We tried that approach initially as it would help alleivate the open issues and needing to work around local development vs published development identified above, but the issue that was faced was that relative paths like the above [don't preserve their location / context on disk](https://github.com/ProjectEvergreen/greenwood/issues/689#issuecomment-895519561) when coming through the development server
```html
# with explicit path that includes node_modules (is exactly the same)
url -> /node_modules/my-theme-pack/dist/styles/theme.css

# with relative paths
url -> < pagesDir >/styles/theme.css
```

And so at this time Greenwood only looks in the user's workspace, not in _node_modules) and so it will `404`.
7 changes: 5 additions & 2 deletions www/pages/plugins/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Similar in spirit to [**CSS Zen Garden**](http://www.csszengarden.com/)
At present, Greenwood allows for configuring the following locations as array of (absolute) paths
- Templates directory - where additional custom page templates can be found

> _Support for pages [coming soon](https://github.com/ProjectEvergreen/greenwood/issues/681)_!
> _We plan to expand the scope of this as use cases are identified._

### Templates
By providing paths to directories of templates, plugin authors can share complete pages, themes, and UI complete with JavaScript and CSS to Greenwood users, and all a user has to do (besides installing the plugin), is specify a template filename in their frontmatter.
Expand All @@ -33,7 +33,9 @@ template: 'acme-theme-blog-layout'
Your plugin might look like this:
```js
/*
* Asssumes a (simplified) npm publishing structure like so
* For context, when your plugin is installed via npm or Yarn, __dirname will be /path/to/node_modules/ <your-package-name>/
*
* You can then choose how to organize and publish your files. In this case, we have published the template under a _dist/_ folder, which was specified in the package.json `files` field.
*
* node_modules/
* acme-theme-pack/
Expand All @@ -51,6 +53,7 @@ module.exports = () => [{
provider: () => {
return {
templates: [
// when the plugin is installed __dirname will be /path/to/node_modules/<your-package>/
path.join(__dirname, 'dist/layouts')
]
};
Expand Down