Compare commits

...

25 Commits

Author SHA1 Message Date
Thomas Allmer
0166dc6f04 feat(web-menu): initial release 2021-11-07 13:53:04 +01:00
github-actions[bot]
0197bee621 Version Packages 2021-10-15 19:25:11 +02:00
Thomas Allmer
5c6b9c91eb feat(mdjs-preview): Move platform and sizes settings above preview 2021-10-15 12:33:30 +02:00
Thomas Allmer
6221e5f9ea feat: mdjs support for story-code blocks 2021-10-15 12:33:30 +02:00
Jorge del Casar
06741ed729 chore: update playwright 2021-10-11 19:23:33 +02:00
Thomas Allmer
23c164c822 chore: reduce test output noise 2021-10-03 22:06:22 +02:00
github-actions[bot]
c2a76c3f53 Version Packages 2021-10-03 17:15:38 +02:00
Thomas Allmer
70bb7a128e feat: update all packages to use new plugins-manager api 2021-10-03 17:10:18 +02:00
Thomas Allmer
3c342473d7 feat(plugin-manager): new type safe API
Co-authored-by: Benny Powers <web@bennypowers.com>
2021-10-03 17:10:18 +02:00
Thomas Allmer
9e0579ab19 chore: version linting considering valid semver 2021-09-13 15:22:52 +02:00
Thomas Allmer
de202da0a5 chore: reenable all tests 2021-09-13 15:22:52 +02:00
github-actions[bot]
509a8d9115 Version Packages 2021-09-13 13:33:26 +02:00
Mathieu Puech
42418f2c00 fix: disable service worker for local development 2021-09-13 13:29:05 +02:00
Benny Powers
cadd8588b0 fix(blog): conditionally apply blog overview styles 2021-09-13 12:36:51 +02:00
Benny Powers
aabe011427 Update 30-articles.njk 2021-09-13 12:36:51 +02:00
qa46hx
e1089c5701 fix(blog): add title to blog page 2021-09-13 12:36:38 +02:00
Benny Powers
9f10386eb2 fix: hide 404 page from sitemap.xml 2021-08-10 23:57:52 +02:00
qa46hx
0987a41620 fix(mdjs-viewer): fix styling mdjs-viewer 2021-08-10 23:55:09 +02:00
Thomas Allmer
7301a0f354 feat(cli): enable conditional eleventy config 2021-08-01 16:49:20 +02:00
Thomas Allmer
5ac6aa6693 fix(cli): set the encoding of the simulator to utf8 2021-07-27 21:03:51 +02:00
github-actions[bot]
9f1633cccc Version Packages 2021-07-27 12:54:55 +02:00
Thijs Louisse
00f4a91550 fix(blog): alignment + spacings for article grids 2021-07-27 12:51:30 +02:00
Jorge del Casar
eb62dd9fd5 fix: use semver to lint versions 2021-07-21 20:42:22 +02:00
github-actions[bot]
bb07267289 Version Packages 2021-07-21 16:36:44 +02:00
Thomas Allmer
738941afdd feat(cli): add rollup config function to rocket config 2021-07-21 15:57:31 +02:00
245 changed files with 6628 additions and 1148 deletions

1
.gitignore vendored
View File

@@ -40,3 +40,4 @@ _merged_assets
_merged_includes
__output
__output-dev
docs_backup

View File

@@ -1,4 +1,6 @@
---
layout: layout-404
permalink: 404.html
menu:
exclude: true
---

View File

@@ -1,8 +1,8 @@
:not(rocket-navigation):not(:defined) {
:not(web-menu):not(:defined) {
opacity: 0;
}
rocket-navigation,
web-menu,
header {
font-family: 'Montserrat', sans-serif;
}

View File

@@ -19,6 +19,9 @@ html {
--primary-color: rgb(44, 62, 80);
--primary-lines-color: #ccc;
--primary-text-color: #2c3e50;
--primary-text-inverse-color: #eee;
--switch-unselected-color: #808080;
--switch-selected-color: #42b983;
}
@media (prefers-color-scheme: dark) {
@@ -45,6 +48,9 @@ html {
--rocket-search-highlight-color: #41ffb0;
--rocket-search-hover-background-color: #6b717a;
--rocket-search-fill-color: #fff;
--primary-text-inverse-color: #2c3e50;
--switch-unselected-color: #808080;
--switch-selected-color: #42b983;
/* Markdown */
--markdown-octicon-link: var(--primary-text-color);

View File

@@ -0,0 +1,3 @@
module.exports = {
layout: 'layout-blog-details',
};

View File

@@ -107,3 +107,60 @@ export default ({
});
```
<!-- prettier-ignore-end -->
## Advanced
Sometimes you need even more control over specific settings.
### Rollup
For example if you wanna add an `acron` plugin to rollup
<!-- prettier-ignore-start -->
```js
import { importAssertions } from 'acorn-import-assertions';
/** @type {import('rocket/cli').RocketCliConfig} */
export default ({
rollup: config => ({
...config,
acornInjectPlugins: [importAssertions],
}),
});
```
<!-- prettier-ignore-end -->
### Eleventy
For example to add custom filter you can access the eleventy config directly
<!-- prettier-ignore-start -->
```js
/** @type {import('rocket/cli').RocketCliConfig} */
export default ({
eleventy: eleventyConfig => {
eleventyConfig.addFilter('value', value => `prefix${value}`);
},
});
```
<!-- prettier-ignore-end -->
You even have access to the full rocketConfig if you for example want to create filters that behave differently during start/build.
<!-- prettier-ignore-start -->
```js
/** @type {import('rocket/cli').RocketCliConfig} */
export default ({
eleventy: (config, rocketConfig) => {
config.addFilter('conditional-resolve', value => {
if (rocketConfig.command === 'build') {
return `build:${value}`;
}
if (rocketConfig.command === 'start') {
return `start:${value}`;
}
});
},
});
```
<!-- prettier-ignore-end -->

View File

@@ -123,6 +123,68 @@ export const header = () => {
};
```
```js story-code
// not defined for android
```
```js story-code
// not defined for ios
```
#### Story Code
If your preview is followed by a code blocks marked as `story-code` then those will be shown when switching between multiple platforms
````md
```js preview-story
// will be visible when platform web is selected
export const JsPreviewStory = () => html` <demo-wc-card>JS Preview Story</demo-wc-card> `;
```
```xml story-code
<!-- will be visible when platform android is selected -->
<Button
android:id="@+id/demoWcCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Code"
style="@style/Widget.FooComponents.Demo.Wc.Card"
/>
```
```swift story-code
// will be visible when platform ios is selected
import DemoWc.Card
let card = DemoWcButton()
```
````
See it in action by opening up the code block and switching platforms
```js preview-story
// will be visible when platform web is selected
export const JsPreviewStory = () => html` <demo-wc-card>JS Preview Story</demo-wc-card> `;
```
```xml story-code
<!-- will be visible when platform android is selected -->
<Button
android:id="@+id/demoWcCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Code"
style="@style/Widget.FooComponents.Demo.Wc.Card"
/>
```
```swift story-code
// will be visible when platform ios is selected
import DemoWc.Card
let card = DemoWcButton()
```
## Supported Systems
### Storybook

View File

@@ -40,6 +40,68 @@ will result in
export const foo = () => html` <demo-element></demo-element> `;
```
```js story-code
// not defined for android
```
```js story-code
// not defined for ios
```
#### Story Code
If your preview is followed by a code blocks marked as `story-code` then those will be shown when switching between multiple platforms
````md
```js preview-story
// will be visible when platform web is selected
export const JsPreviewStory = () => html` <demo-element></demo-element> `;
```
```xml story-code
<!-- will be visible when platform android is selected -->
<Button
android:id="@+id/demoElement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Code"
style="@style/Widget.Demo.Element"
/>
```
```swift story-code
// will be visible when platform ios is selected
import Demo.Element
let card = DemoElement()
```
````
See it in action by opening up the code block and switching platforms
```js preview-story
// will be visible when platform web is selected
export const JsPreviewStory = () => html` <demo-element></demo-element> `;
```
```xml story-code
<!-- will be visible when platform android is selected -->
<Button
android:id="@+id/demoElement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Code"
style="@style/Widget.Demo.Element"
/>
```
```swift story-code
// will be visible when platform ios is selected
import Demo.Element
let card = DemoElement()
```
## HTML Story
````md

View File

@@ -1,4 +1,3 @@
# Presets >> Launch || 20
- [Preset](./preset/)
- [Custom Elements](./custom-elements/)

View File

@@ -1,4 +1,3 @@
# Presets >> Search || 10
- [Preset](./preset/)
- [Custom Elements](./custom-elements/)

View File

@@ -3,6 +3,111 @@
The Plugins Manager replaces the specific registration/execution (with options) in a given plugin system by an intend to use a plugin (with these options).
This allows your users to adjust the options before actually applying the plugins.
## Setup
1. Install npm package
```bash
npm i plugins-manager
```
2. Change your public API from an array of plugin "instances" to an array of setup functions
```diff
import myPlugin from 'my-plugin';
+ import { addPlugin } from 'plugins-manager';
export default {
- plugins: [myPlugin],
+ setupPlugins: [addPlugin(myPlugin)]
}
```
3. Convert setup function to plugins
```js
import { applyPlugins } from 'plugins-manager';
const finalConfig = applyPlugins(config); // "converts" setupPlugins to plugins
// work with plugins or pass it on to another tool
const bundle = await rollup(finalConfig);
```
## Usage
As you users in most cases you will need to either add or adjust a given plugin in a config file.
👉 `my-tool.config.js`
```js
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
import json from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
export default {
setupPlugins: [
// add a new plugin with optional plugin options
addPlugin(json, {
/* ... */
}),
// adjust the options of a plugin that is already registered
adjustPluginOptions(nodeResolve, {
/* ... */
}),
],
};
```
## Plugins can be functions or classes
### Function Plugins
```js
function myPlugin({ lastName: 'initial-second' }) {
// ...
}
export default {
setupPlugins: [addPlugin(myPlugin)],
};
// function parameters are type safe
addPlugin(myPlugin, { lastName: 'new name' }); // ts ok
addPlugin(myPlugin, { otherProp: 'new name' }); // ts error
```
### Class Plugins
The options are passed to the constructor.
```js
/**
* @typedef {object} MyClassOptions
* @property {string} lastName
*/
class MyClass {
/** @type {MyClassOptions} */
options = {
lastName: 'initial-second',
};
/**
* @param {Partial<MyClassOptions>} options
*/
constructor(options = {}) {
this.options = { ...this.options, ...options };
}
// ...
}
export default {
setupPlugins: [addPlugin(MyClass)],
};
// constructor parameters are type safe
addPlugin(MyClass, { lastName: 'new name' }); // ts ok
addPlugin(MyClass, { otherProp: 'new name' }); // ts error
```
## Problem
Many plugin systems require you to either execute a plugin function like in `rollup`.
@@ -48,18 +153,14 @@ This means if you wish to define default plugins and allow your user to override
The plugins manager lets you orchestrate a set of "meta plugins" which are defined by
- name
- plugin
- options
- plugin (class or function)
- it's options
```js
import beep from '@rollup/plugin-beep';
import url from '@rollup/plugin-url';
let metaPlugins = [
{ name: 'beep', plugin: beep },
{ name: 'url', plugin: url, options: { limit: 10000 } },
];
let metaPlugins = [{ plugin: beep }, { plugin: url, options: { limit: 10000 } }];
```
This array can be modified by adding/removing or adjusting options.
@@ -94,16 +195,13 @@ export default {
### Adding Helpers
Doing array manipulations is kinda error-prone so we offer encourage to use an array of setup function. Where as each setup function can either add a new plugin (with a unique name) or adjust an already existing plugin.
Doing array manipulations is kinda error-prone so we encourage to use an array of setup function. Where as each setup function can either add a new plugin (with a unique name) or adjust an already existing plugin.
```js
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
const systemSetupFunctions = [
addPlugin({ name: 'first', plugin: first }),
addPlugin({ name: 'second', plugin: second }),
];
const userSetupFunctions = [adjustPluginOptions('first', { my: 'options' })];
const systemSetupFunctions = [addPlugin(first), addPlugin(second)];
const userSetupFunctions = [adjustPluginOptions(first, { my: 'options' })];
```
Arrays of functions can by merged like so
@@ -115,9 +213,9 @@ const finalSetupFunctions = [...systemSetupFunctions, ...userSetupFunctions];
and then converted to the final output.
```js
import { metaPluginsToRollupPlugins } from 'plugins-manager';
import { applyPlugins } from 'plugins-manager';
const plugins = metaPluginsToRollupPlugins(finalSetupFunctions, metaPlugins);
const plugins = applyPlugins(finalSetupFunctions, metaPlugins);
```
## Adding a Plugin
@@ -133,18 +231,27 @@ By default it adds at the bottom.
import json from '@rollup/plugin-json';
import { addPlugin } from 'plugins-manager';
const userSetupFunctions = [
addPlugin({ name: 'json', plugin: json, options: { preferConst: true } }),
];
const userSetupFunctions = [addPlugin(json, { preferConst: true })];
```
Example usage:
```js
addPlugin({ name: 'json', plugin: json }); // Add at the bottom (default)
addPlugin({ name: 'json', plugin: json, location: 'top' }); // Add at the top/beginning of the array
addPlugin({ name: 'json', plugin: json, location: 'beep' }); // Add after (default) plugin 'beep'
addPlugin({ name: 'json', plugin: json, location: 'beep', how: 'before' }); // Add before plugin 'beep'
addPlugin(json); // Add at the bottom (default)
addPlugin(json, {}, { location: 'top' }); // Add at the top/beginning of the array
addPlugin(json, {}, { location: beep }); // Add after (default) plugin 'beep'
addPlugin(json, {}, { location: beep, how: 'before' }); // Add before plugin 'beep'
```
This is type safe and typescript will throw an error if you pass the wrong type.
```js
function myPlugin({ myFlag = false } = {}) {
// ...
}
addPlugin(myPlugin, { myFlag: true }); // ts ok
addPlugin(myPlugin, { notExisting: true }); // ts error
```
## Adjusting Plugin Options
@@ -154,12 +261,14 @@ Adjusting options means to either
- flatly merge objects (e.g. only the first level will be preserved)
- calling a function to do the merge yourself
- setting the raw value (if not an object or function)
- you need to have a reference to the plugin (which is used to auto complete the available options via typescript)
```js
import json from '@rollup/plugin-json';
import { adjustPluginOptions } from 'plugins-manager';
const userSetupFunctions = [
adjustPluginOptions('json', { preferConst: false, anotherOption: 'format' }),
adjustPluginOptions(json, { preferConst: false, anotherOption: 'format' }),
];
```
@@ -167,36 +276,53 @@ Example usage:
```js
// given
addPlugin({
name: 'json',
plugin: json,
options: {
other: {
nested: 'other.nested',
nested2: 'other.nested2',
},
main: true,
addPlugin(json, {
other: {
nested: 'other.nested',
nested2: 'other.nested2',
},
main: true,
});
// merge objects flately
adjustPluginOptions('json', { other: { nested: '--overwritten--' } });
adjustPluginOptions(json, { other: { nested: '--overwritten--' } });
// resulting options = { other: { nested: '--overwritten--' }, main: true }
// NOTE: nested2 is removed
// merge via function
adjustPluginOptions('json', config => ({ other: { ...config.other, nested: '--overwritten--' } }));
adjustPluginOptions(json, config => ({ other: { ...config.other, nested: '--overwritten--' } }));
// resulting options = { other: { nested: '--overwritten--', nested2: 'other.nested2' }, main: true }
// merge via function to override full options
adjustPluginOptions('json', config => ({ only: 'this' }));
adjustPluginOptions(json, config => ({ only: 'this' }));
// resulting options = { only: 'this' }
// setting a raw value
adjustPluginOptions('json', false);
adjustPluginOptions(json, false);
// resulting options = false
```
This is type safe and typescript will throw an error if you pass the wrong type.
```js
function myPlugin({ myFlag = false } = {}) {
// ...
}
adjustPluginOptions(myPlugin, { myFlag: true }); // ts ok
adjustPluginOptions(myPlugin, { notExisting: true }); // ts error
```
## Remove Plugin
Sometimes you would like to remove a default plugin from the config.
```js
export default {
setupPlugins: [removePlugin(json)],
};
```
## Converting metaPlugins to an Actual Plugin
To execute all setup function you can use this little helper
@@ -227,25 +353,9 @@ Rollup has a more specific helper that handles
Note: if you provide `config.plugins` then it will return that directly ignoring `setupPlugins`
```js
import { metaConfigToRollupConfig } from 'plugins-manager';
import { applyPlugins } from 'plugins-manager';
const finalConfig = metaConfigToRollupConfig(currentConfig, defaultMetaPlugins);
```
Web Dev Server has a more specific helper that handles
- `config.setupPlugins`
- `config.setupRollupPlugins`
Note: if you provide `config.plugins` then it will return that directly ignoring `setupPlugins` and `setupRollupPlugins`
```js
import { metaConfigToWebDevServerConfig } from 'plugins-manager';
import { fromRollup } from '@web/dev-server-rollup';
const finalConfig = metaConfigToWebDevServerConfig(currentConfig, defaultMetaPlugins, {
rollupWrapperFunction: fromRollup,
});
const finalConfig = applyPlugins(currentConfig, defaultMetaPlugins);
```
Eleventy

View File

@@ -42,11 +42,13 @@ eleventyExcludeFromCollections: true
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% raw %}{% for page in collections.all %}
{%- if page.url !== '/404.html' -%}
<url>
<loc>{{ rocketConfig.absoluteBaseUrl }}{{ page.url | url }}</loc>
<lastmod>{{ page.date.toISOString() }}</lastmod>
<changefreq>{{ page.data.changeFreq if page.data.changeFreq else "monthly" }}</changefreq>
</url>
{%- endif -%}
{% endfor %}{% endraw %}
</urlset>
```

View File

@@ -29,14 +29,15 @@
"postinstall": "npm run setup",
"release": "changeset publish && yarn format",
"rocket:build": "node packages/cli/src/cli.js build",
"rocket:upgrade": "node packages/cli/src/cli.js upgrade",
"search": "node packages/cli/src/cli.js search",
"setup": "npm run setup:ts-configs && npm run build:packages",
"setup:patches": "npx patch-package",
"setup:ts-configs": "node scripts/generate-ts-configs.mjs",
"prestart": "yarn analyze",
"start": "node packages/cli/src/cli.js start",
"xprestart": "yarn analyze",
"start": "node --trace-warnings packages/cli/src/cli.js start",
"test": "yarn test:node && yarn test:web",
"test:node": "mocha \"packages/*/test-node/**/*.test.{ts,js,mjs,cjs}\" --timeout 5000 --reporter dot --exit",
"test:node": "mocha \"packages/*/test-node/**/*.test.{ts,js,mjs,cjs}\" -- --timeout 5000 --reporter dot --exit",
"test:web": "web-test-runner",
"types": "run-s types:clear types:copy types:build",
"types:build": "tsc --build",
@@ -62,7 +63,7 @@
"@typescript-eslint/parser": "^4.13.0",
"@web/test-runner": "^0.12.2",
"@web/test-runner-commands": "^0.4.0",
"@web/test-runner-playwright": "^0.8.0",
"@web/test-runner-playwright": "^0.8.8",
"cem-plugin-readme": "^0.1.3",
"chai": "^4.2.0",
"concurrently": "^5.3.0",
@@ -80,6 +81,7 @@
"onchange": "^7.1.0",
"prettier": "^2.2.1",
"prettier-plugin-package": "^1.3.0",
"publish-docs": "^0.1.2",
"puppeteer": "^9.0.0",
"remark-emoji": "^2.1.0",
"rimraf": "^3.0.2",

View File

@@ -1,5 +1,44 @@
# @rocket/blog
## 0.4.0
### Minor Changes
- 70bb7a1: BREAKING CHANGE: Update to latest plugins manager to get type safe options
There is no longer a name string as a key for a plugin. It is identified by it's function/class. You will need to adjust your code if you are adding or adjusting plugins.
```diff
- addPlugin({ name: 'my-plugin', plugin: myPlugin, options: { myFlag: true }, location: 'top' });
+ addPlugin(myPlugin, { myFlag: true }, { location: 'top' });
- adjustPluginOptions('my-plugin', { myFlag: true });
+ adjustPluginOptions(myPlugin, { myFlag: true });
```
For more details please see the [Changelog](https://github.com/modernweb-dev/rocket/blob/main/packages/plugins-manager/CHANGELOG.md#030) of the plugins-manager package.
### Patch Changes
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- plugins-manager@0.3.0
## 0.3.3
### Patch Changes
- e1089c5: add title to blog page
## 0.3.2
### Patch Changes
- 00f4a91: alignment + spacings for article grids
## 0.3.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/blog",
"version": "0.3.1",
"version": "0.4.0",
"publishConfig": {
"access": "public"
},
@@ -38,6 +38,6 @@
"testing"
],
"dependencies": {
"plugins-manager": "^0.2.4"
"plugins-manager": "^0.3.0"
}
}

View File

@@ -20,6 +20,8 @@ body[layout='layout-blog-details'] #sidebar-nav li.anchor a:hover::before {
flex-wrap: wrap;
margin: calc(-1 * var(--gap)) 0 0 calc(-1 * var(--gap));
width: calc(100% + var(--gap));
align-items: flex-start;
}
.articles article {
@@ -32,6 +34,12 @@ body[layout='layout-blog-details'] #sidebar-nav li.anchor a:hover::before {
.articles article h2 {
margin: 0;
border: none;
padding-top: 1rem;
}
.articles article .thumbnail {
display: block;
height: 200px;
}
.articles article .read {
@@ -48,7 +56,7 @@ body[layout='layout-blog-details'] #sidebar-nav li.anchor a:hover::before {
}
@media screen and (min-width: 1024px) {
body[layout='layout-blog-details'] #sidebar {
body[layout='layout-blog-details'] #sidebar {
display: block;
}
}
}

View File

@@ -1,3 +1,6 @@
{% if title %}
<h1>{{title}}</h1>
{% endif %}
{% if cover_image %}
<img src="{{ cover_image | url }}" alt="">
{% endif %}

View File

@@ -1,25 +0,0 @@
<rocket-navigation>
<ul>
<li class="current">
<h3>Headings</h3>
{{ collections[section] | rocketPageAnchors({ title: title }) | rocketNavToHtml({
listItemClass: "menu-item",
activeListItemClass: "current",
activeKey: eleventyNavigation.key
}) | safe }}
</li>
</ul>
<div class="sidebar-tags">
<h3>Date</h3>
<div>{{ page.date.toDateString() }}</div>
</div>
<div class="sidebar-tags">
<h3>Tags</h3>
<div class="tags">
{% for tag in tags %}
<span class="tag">{{tag}}</span>
{% endfor %}
</div>
</div>
{% include 'partials/mobile-sidebar-bottom.njk' %}
</rocket-navigation>

View File

@@ -1,20 +1 @@
<div class="articles">
{% for post in posts %}
{% if post.data.published %}
<article>
{% if post.data.cover_image %}
<a href="{{ post.url | url }}">
<img src="{{ post.data.cover_image | url }}" alt="">
</a>
{% endif %}
<div class="content">
<h2>
<a href="{{ post.url | url }}">{{ post.data.title }}</a>
</h2>
<p>{{ post.data.description }}</p>
<a class="read" href="{{ post.url | url }}">...read more</a>
</div>
</article>
{% endif %}
{% endfor %}
</div>
<web-menu name="article-overview"></web-menu>

View File

@@ -1 +1,9 @@
<link rel="stylesheet" href="{{ '/_assets/rocket-blog.css' | asset | url }}">
{%- if layout == 'layout-blog-overview' -%}
<style>
.articles article .thumbnail {
background-size: cover;
background-position: center;
}
</style>
{%- endif -%}

View File

@@ -1,52 +1,52 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { addPlugin } from 'plugins-manager';
// import { addPlugin } from 'plugins-manager';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SECTION = 'blog';
const POST_COLLECTION = 'posts';
// const SECTION = 'blog';
// const POST_COLLECTION = 'posts';
export function rocketBlog({ section = SECTION, postCollection = POST_COLLECTION } = {}) {
const isHiddenCollection = item => ['-', '_'].includes(item.charAt(0));
const isVisibleCollection = item => !isHiddenCollection(item);
const isNotPostCollection = collection => collection !== postCollection;
export function rocketBlog() {
// const isHiddenCollection = item => ['-', '_'].includes(item.charAt(0));
// const isVisibleCollection = item => !isHiddenCollection(item);
// const isNotPostCollection = collection => collection !== postCollection;
const eleventyPluginRocketBlog = {
configFunction: eleventyConfig => {
eleventyConfig.addCollection('posts', collection => {
/*
// It's not working beacuse it's a paginated collection.
const headerDocs = eleventyConfig.collections.header(collection);
headerDocs.filter(page => page.data.section === section).forEach(page => {
page.data.layout = 'blog';
});
*/
if (section === postCollection) {
throw new Error("Rocket blog: section and postCollection couldn't be equal");
}
if (!eleventyConfig.collections[section]) {
const collectionKeys = Object.keys(eleventyConfig.collections);
const availableCollections = collectionKeys
.filter(isVisibleCollection)
.filter(isNotPostCollection);
throw new Error(
`Rocket blog: Collection '${section}' not found. Aviable colections: ${availableCollections.join(
', ',
)}`,
);
}
// const eleventyPluginRocketBlog = {
// configFunction: eleventyConfig => {
// eleventyConfig.addCollection('posts', collection => {
// /*
// // It's not working beacuse it's a paginated collection.
// const headerDocs = eleventyConfig.collections.header(collection);
// headerDocs.filter(page => page.data.section === section).forEach(page => {
// page.data.layout = 'blog';
// });
// */
// if (section === postCollection) {
// throw new Error("Rocket blog: section and postCollection couldn't be equal");
// }
// if (!eleventyConfig.collections[section]) {
// const collectionKeys = Object.keys(eleventyConfig.collections);
// const availableCollections = collectionKeys
// .filter(isVisibleCollection)
// .filter(isNotPostCollection);
// throw new Error(
// `Rocket blog: Collection '${section}' not found. Aviable colections: ${availableCollections.join(
// ', ',
// )}`,
// );
// }
const posts = eleventyConfig.collections[section](collection);
posts.forEach(page => {
page.data.layout = 'layout-blog-details';
});
return posts;
});
},
};
// const posts = eleventyConfig.collections[section](collection);
// posts.forEach(page => {
// page.data.layout = 'layout-blog-details';
// });
// return posts;
// });
// },
// };
return {
path: path.resolve(__dirname),
setupEleventyPlugins: [addPlugin({ name: 'rocket-blog', plugin: eleventyPluginRocketBlog })],
// setupEleventyPlugins: [addPlugin({ name: 'rocket-blog', plugin: eleventyPluginRocketBlog })],
};
}

View File

@@ -1,5 +1,22 @@
# @rocket/building-rollup
## 0.4.0
### Minor Changes
- 70bb7a1: BREAKING CHANGE: Update to latest plugins manager to get type safe options
There is no longer a name string as a key for a plugin. It is identified by it's function/class. You will need to adjust your code if you are adding or adjusting plugins.
```diff
- addPlugin({ name: 'my-plugin', plugin: myPlugin, options: { myFlag: true }, location: 'top' });
+ addPlugin(myPlugin, { myFlag: true }, { location: 'top' });
- adjustPluginOptions('my-plugin', { myFlag: true });
+ adjustPluginOptions(myPlugin, { myFlag: true });
```
For more details please see the [Changelog](https://github.com/modernweb-dev/rocket/blob/main/packages/plugins-manager/CHANGELOG.md#030) of the plugins-manager package.
## 0.3.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/building-rollup",
"version": "0.3.1",
"version": "0.4.0",
"publishConfig": {
"access": "public"
},

View File

@@ -2,13 +2,13 @@ import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
import babelPkg from '@rollup/plugin-babel';
import { metaConfigToRollupConfig } from 'plugins-manager';
import { applyPlugins } from 'plugins-manager';
const { babel } = babelPkg;
export function createBasicConfig(userConfig) {
const { config, metaPlugins } = createBasicMetaConfig(userConfig);
return metaConfigToRollupConfig(config, metaPlugins);
return applyPlugins(config, metaPlugins);
}
export function createBasicMetaConfig(userConfig = { output: {} }) {
@@ -39,14 +39,12 @@ export function createBasicMetaConfig(userConfig = { output: {} }) {
let metaPlugins = [
{
name: 'node-resolve',
plugin: resolve,
options: {
moduleDirectories: ['node_modules', 'web_modules'],
},
},
{
name: 'babel',
plugin: babel,
options: {
babelHelpers: 'bundled',
@@ -73,7 +71,6 @@ export function createBasicMetaConfig(userConfig = { output: {} }) {
},
},
{
name: 'terser',
plugin: terser,
},
];

View File

@@ -1,10 +1,11 @@
import { createSpaMetaConfig } from './createSpaConfig.js';
import { adjustPluginOptions, metaConfigToRollupConfig } from 'plugins-manager';
import { adjustPluginOptions, applyPlugins } from 'plugins-manager';
import { rollupPluginHTML } from '@web/rollup-plugin-html';
export function createMpaConfig(userConfig) {
const { config, metaPlugins } = createMpaMetaConfig(userConfig);
const final = metaConfigToRollupConfig(config, metaPlugins);
const final = applyPlugins(config, metaPlugins);
return final;
}
@@ -12,7 +13,7 @@ export function createMpaMetaConfig(userConfig = { output: {}, setupPlugins: []
const { config, metaPlugins } = createSpaMetaConfig(userConfig);
config.setupPlugins = [
adjustPluginOptions('html', {
adjustPluginOptions(rollupPluginHTML, {
flattenOutput: false,
}),
...config.setupPlugins,

View File

@@ -3,13 +3,13 @@ import { terser } from 'rollup-plugin-terser';
import babelPkg from '@rollup/plugin-babel';
import replace from '@rollup/plugin-replace';
import { metaConfigToRollupConfig } from 'plugins-manager';
import { applyPlugins } from 'plugins-manager';
const { babel } = babelPkg;
export function createServiceWorkerConfig(userConfig) {
const { config, metaPlugins } = createServiceWorkerMetaConfig(userConfig);
return metaConfigToRollupConfig(config, metaPlugins);
return applyPlugins(config, metaPlugins);
}
export function createServiceWorkerMetaConfig(userConfig = { output: {} }) {
@@ -33,21 +33,19 @@ export function createServiceWorkerMetaConfig(userConfig = { output: {} }) {
let metaPlugins = [
{
name: 'node-resolve',
plugin: resolve,
options: {
moduleDirectories: ['node_modules', 'web_modules'],
},
},
{
name: 'replace',
plugin: replace,
options: {
'process.env.NODE_ENV': JSON.stringify(developmentMode ? 'development' : 'production'),
preventAssignment: true,
},
},
{
name: 'babel',
plugin: babel,
options: {
babelHelpers: 'bundled',
@@ -74,7 +72,6 @@ export function createServiceWorkerMetaConfig(userConfig = { output: {} }) {
},
},
{
name: 'terser',
plugin: terser,
options: {
mangle: {

View File

@@ -1,13 +1,13 @@
import { rollupPluginHTML } from '@web/rollup-plugin-html';
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
import { polyfillsLoader } from '@web/rollup-plugin-polyfills-loader';
import { metaConfigToRollupConfig } from 'plugins-manager';
import { applyPlugins } from 'plugins-manager';
import { createBasicMetaConfig } from './createBasicConfig.js';
export function createSpaConfig(userConfig) {
const { config, metaPlugins } = createSpaMetaConfig(userConfig);
return metaConfigToRollupConfig(config, metaPlugins);
return applyPlugins(config, metaPlugins);
}
export function createSpaMetaConfig(userConfig = { output: {} }) {
@@ -30,7 +30,6 @@ export function createSpaMetaConfig(userConfig = { output: {} }) {
const spaMetaPlugins = [
...metaPlugins,
{
name: 'html',
plugin: rollupPluginHTML,
options: {
rootDir,
@@ -38,11 +37,9 @@ export function createSpaMetaConfig(userConfig = { output: {} }) {
},
},
{
name: 'import-meta-assets',
plugin: importMetaAssets,
},
{
name: 'polyfills-loader',
plugin: polyfillsLoader,
options: {
polyfills: {},

View File

@@ -1,5 +1,62 @@
# @rocket/cli
## 0.10.0
### Minor Changes
- 70bb7a1: BREAKING CHANGE: Update to latest plugins manager to get type safe options
There is no longer a name string as a key for a plugin. It is identified by it's function/class. You will need to adjust your code if you are adding or adjusting plugins.
```diff
- addPlugin({ name: 'my-plugin', plugin: myPlugin, options: { myFlag: true }, location: 'top' });
+ addPlugin(myPlugin, { myFlag: true }, { location: 'top' });
- adjustPluginOptions('my-plugin', { myFlag: true });
+ adjustPluginOptions(myPlugin, { myFlag: true });
```
For more details please see the [Changelog](https://github.com/modernweb-dev/rocket/blob/main/packages/plugins-manager/CHANGELOG.md#030) of the plugins-manager package.
### Patch Changes
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- Updated dependencies [70bb7a1]
- plugins-manager@0.3.0
- @rocket/eleventy-plugin-mdjs-unified@0.6.0
- @rocket/building-rollup@0.4.0
## 0.9.11
### Patch Changes
- 7301a0f: Pass on rocketConfig to the eleventy function to enable conditional configurations/filters
- 42418f2: Disable the service worker for local development
- 5ac6aa6: Set the encoding of the simulator to utf8 via a html meta tag
## 0.9.10
### Patch Changes
- 738941a: In `rocket.config.js` you can now supply a rollup config function.
```js
export default {
rollup: config => {
// config will be the fully generated config object after all presets have been applied
if (config.plugins.includes('...')) {
// change some config options
}
return config;
},
};
```
## 0.9.9
### Patch Changes

View File

@@ -1,10 +1,20 @@
const { setComputedConfig, getComputedConfig } = require('./src/public/computedConfig.cjs');
const { generateEleventyComputed } = require('./src/public/generateEleventyComputed.cjs');
const {
generateEleventyComputed,
LayoutPlugin,
SectionPlugin,
SocialMediaImagePlugin,
JoiningBlocksPlugin,
} = require('./src/public/generateEleventyComputed.cjs');
const { createSocialImage } = require('./src/public/createSocialImage.cjs');
module.exports = {
setComputedConfig,
getComputedConfig,
generateEleventyComputed,
LayoutPlugin,
SectionPlugin,
SocialMediaImagePlugin,
JoiningBlocksPlugin,
createSocialImage,
};

View File

@@ -1,3 +1,13 @@
/** @typedef {import('@rocket/cli/types/main').RocketCliOptions} RocketCliOptions */
export { setComputedConfig, getComputedConfig } from './src/public/computedConfig.cjs';
export {
generateEleventyComputed,
LayoutPlugin,
SectionPlugin,
SocialMediaImagePlugin,
JoiningBlocksPlugin,
} from './src/public/generateEleventyComputed.cjs';
export { createSocialImage } from './src/public/createSocialImage.cjs';
export { RocketCli } from './src/RocketCli.js';

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/cli",
"version": "0.9.9",
"version": "0.10.0",
"publishConfig": {
"access": "public"
},
@@ -57,10 +57,9 @@
"dependencies": {
"@11ty/eleventy": "^0.11.1",
"@11ty/eleventy-img": "^0.9.0",
"@rocket/building-rollup": "^0.3.1",
"@rocket/building-rollup": "^0.4.0",
"@rocket/core": "^0.1.2",
"@rocket/eleventy-plugin-mdjs-unified": "^0.5.2",
"@rocket/eleventy-rocket-nav": "^0.3.0",
"@rocket/eleventy-plugin-mdjs-unified": "^0.6.0",
"@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-node-resolve": "^11.0.1",
"@web/config-loader": "^0.1.3",
@@ -71,8 +70,9 @@
"command-line-args": "^5.1.1",
"command-line-usage": "^6.1.1",
"fs-extra": "^9.0.1",
"gray-matter": "^4.0.3",
"micromatch": "^4.0.2",
"plugins-manager": "^0.2.4",
"plugins-manager": "^0.3.0",
"slash": "^3.0.0",
"utf8": "^3.0.0",
"workbox-window": "^6.1.5"

View File

@@ -4,4 +4,6 @@
window.__rocketServiceWorkerUrl = '{{ rocketServiceWorkerUrl | url }}';
</script>
{% if rocketConfig.command == 'build' %}
<script type="module" inject-service-worker="" src="{{ '/_assets/scripts/registerServiceWorker.js' | asset | url }}"></script>
{% endif %}

View File

@@ -1,4 +1,4 @@
<a class="logo-link" href="{{ '/' | url }}">
<img src="{{ '/_assets/logo.svg' | asset | url }}" alt="{{ site.logoAlt }}" />
<span class="sr-only">{{ site.name }}</span>
<span>{{ site.name }}</span>
</a>

View File

@@ -1,6 +1,8 @@
<html theme="light" platform="web" lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="menu:exclude" content="true">
<meta charset="utf-8">
<style type="text/css">
body {
margin: 0;

View File

@@ -5,6 +5,7 @@ import { rollup } from 'rollup';
import fs from 'fs-extra';
import path from 'path';
import { copy } from '@web/rollup-plugin-copy';
import { rollupPluginHTML } from '@web/rollup-plugin-html';
import { createMpaConfig, createServiceWorkerConfig } from '@rocket/building-rollup';
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
@@ -25,18 +26,14 @@ async function buildAndWrite(config) {
async function productionBuild(config) {
const defaultSetupPlugins = [
addPlugin({
name: 'copy',
plugin: copy,
options: {
patterns: ['!(*.md|*.html)*', '_merged_assets/_static/**/*'],
rootDir: config.outputDevDir,
},
addPlugin(copy, {
patterns: ['!(*.md|*.html)*', '_merged_assets/_static/**/*'],
rootDir: config.outputDevDir,
}),
];
if (config.pathPrefix) {
defaultSetupPlugins.push(
adjustPluginOptions('html', { absolutePathPrefix: config.pathPrefix }),
adjustPluginOptions(rollupPluginHTML, { absolutePathPrefix: config.pathPrefix }),
);
}
@@ -54,8 +51,8 @@ async function productionBuild(config) {
...config.setupBuildPlugins,
],
});
await buildAndWrite(mpaConfig);
const finalConfig = typeof config.rollup === 'function' ? config.rollup(mpaConfig) : mpaConfig;
await buildAndWrite(finalConfig);
const serviceWorkerSourcePath = path.resolve('docs/_merged_assets/service-worker.js');
if (fs.existsSync(serviceWorkerSourcePath)) {

View File

@@ -298,7 +298,7 @@ export class RocketCli {
async cleanup() {
setComputedConfig({});
if (this.eleventy) {
this.eleventy.finish();
// this.eleventy.finish();
// await this.eleventy.stopWatch();
}
this.stop();

View File

@@ -1,11 +1,61 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { startDevServer } from '@web/dev-server';
import { fromRollup } from '@web/dev-server-rollup';
import { metaConfigToWebDevServerConfig } from 'plugins-manager';
import { executeSetupFunctions } from 'plugins-manager';
/** @typedef {import('../types/main').RocketCliOptions} RocketCliOptions */
/** @typedef {import('@web/dev-server').DevServerConfig} DevServerConfig */
/**
* @param {any} config
* @param {MetaPluginWrapable[]} metaPlugins
* @param {object} [options]
* @param {function | null} [options.rollupWrapperFunction]
*/
export function metaConfigToWebDevServerConfig(
config,
metaPlugins,
{ rollupWrapperFunction = null } = {},
) {
if (config.plugins) {
delete config.setupPlugins;
delete config.setupRollupPlugins;
return config;
}
const metaPluginsNoWrap = metaPlugins.map(pluginObj => {
pluginObj.__noWrap = true;
return pluginObj;
});
const rollupPlugins = /** @type {MetaPluginWrapable[]} */ (executeSetupFunctions(
config.setupRollupPlugins,
[...metaPluginsNoWrap],
));
const wrappedRollupPlugins = rollupPlugins.map(pluginObj => {
if (typeof rollupWrapperFunction === 'function' && pluginObj.__noWrap !== true) {
pluginObj.plugin = rollupWrapperFunction(pluginObj.plugin);
}
return pluginObj;
});
const _metaPlugins = executeSetupFunctions(config.setupPlugins, [...wrappedRollupPlugins]);
const plugins = _metaPlugins.map(pluginObj => {
if (pluginObj.options) {
return pluginObj.plugin(pluginObj.options);
} else {
return pluginObj.plugin();
}
});
config.plugins = plugins;
delete config.setupPlugins;
delete config.setupRollupPlugins;
return config;
}
export class RocketStart {
static pluginName = 'RocketStart';
commands = ['start'];

187
packages/cli/src/RocketUpgrade.js Executable file
View File

@@ -0,0 +1,187 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { readdir, rename, writeFile } from 'fs/promises';
import path from 'path';
import { upgrade202109menu } from './upgrades/upgrade202109menu.js';
import { copy } from 'fs-extra';
/** @typedef {import('../types/main').RocketCliOptions} RocketCliOptions */
/** @typedef {import('../types/upgrade').UpgradeFile} UpgradeFile */
/** @typedef {import('../types/upgrade').FolderRename} FolderRename */
/** @typedef {import('../types/upgrade').upgrade} upgrade */
/**
* @param {UpgradeFile} options
* @returns {boolean}
*/
function filterMerged({ relPath }) {
return relPath.startsWith('_merged');
}
/**
*
* @param {object} options
* @param {string} options.rootDir
* @param {string} options.currentDir
* @param {(options: UpgradeFile) => Boolean} [options.filter]
* @returns
*/
async function getAllFiles(options) {
const { rootDir, currentDir, filter = filterMerged } = options;
const entries = await readdir(currentDir, { withFileTypes: true });
/** @type {UpgradeFile[]} */
let files = [];
for (const entry of entries) {
const { name: folderName } = entry;
const currentPath = path.join(currentDir, folderName);
if (entry.isFile()) {
const relPath = path.relative(rootDir, currentPath);
/** @type {UpgradeFile} */
const data = {
path: currentPath,
relPath,
name: path.basename(relPath),
extName: path.extname(relPath),
};
if (!filter(data)) {
files.push(data);
}
}
}
for (const entry of entries) {
const { name: folderName } = entry;
const currentPath = path.join(currentDir, folderName);
if (entry.isDirectory()) {
files = [...files, ...(await getAllFiles({ ...options, currentDir: currentPath }))];
}
}
return files;
}
/**
*
* @param {upgrade} options
*/
async function updateFileSystem({ files, folderRenames }) {
// rename files while not touching folders
for (const file of files) {
if (file.updatedName) {
const newPath = path.join(path.dirname(file.path), file.updatedName);
await rename(file.path, newPath);
}
}
// rename folders
for (const renameObj of folderRenames) {
if (renameObj.fromAbsolute && renameObj.toAbsolute) {
await rename(renameObj.fromAbsolute, renameObj.toAbsolute);
}
}
// update file content
for (const file of files) {
if (file.updatedContent) {
await writeFile(file.updatedPath || file.path, file.updatedContent);
}
}
}
/**
* @param {string} relPath
* @param {FolderRename[]} folderRenames
* @returns {string}
*/
function applyFolderRenames(relPath, folderRenames) {
let newRelPath = relPath;
for (const renameObj of folderRenames) {
if (newRelPath.startsWith(renameObj.from)) {
newRelPath = renameObj.to + newRelPath.slice(renameObj.from.length);
}
}
return newRelPath;
}
export class RocketUpgrade {
static pluginName = 'RocketUpgrade';
commands = ['upgrade'];
/**
* @param {object} options
* @param {RocketCliOptions} options.config
* @param {any} options.argv
*/
async setup({ config, argv }) {
this.__argv = argv;
this.config = config;
}
async upgradeCommand() {
if (!this?.config?._inputDirCwdRelative) {
return;
}
const backupPath = path.join(this.config._inputDirCwdRelative, '..', 'docs_backup');
await copy(this.config._inputDirCwdRelative, backupPath);
console.log(`A backup of your docs folder has been created at ${backupPath}.`);
let files = await getAllFiles({
rootDir: this.config._inputDirCwdRelative,
currentDir: this.config._inputDirCwdRelative,
});
/** @type {FolderRename[]} */
let folderRenames = [];
const upgrade = await upgrade202109menu({ files, folderRenames });
files = upgrade.files;
folderRenames = upgrade.folderRenames;
const orderedFolderRenames = [...folderRenames].sort((a, b) => {
return b.from.split('/').length - a.from.split('/').length;
});
// adjust relPath if there is a new filename
let i = 0;
for (const fileData of files) {
if (fileData.updatedName) {
files[i].updatedRelPath = `${path.dirname(fileData.relPath)}/${fileData.updatedName}`;
}
i += 1;
}
// adjust relPath to consider renamed folders
i = 0;
for (const fileData of files) {
const modifiedPath = applyFolderRenames(
fileData.updatedRelPath || fileData.relPath,
orderedFolderRenames,
);
if (modifiedPath !== fileData.relPath) {
files[i].updatedRelPath = modifiedPath;
}
i += 1;
}
// add an updatedPath if needed
i = 0;
for (const file of files) {
if (file.updatedRelPath) {
files[i].updatedPath = path.join(this.config._inputDirCwdRelative, file.updatedRelPath);
}
i += 1;
}
// create absolute paths for renames
i = 0;
for (const renameObj of folderRenames) {
folderRenames[i].fromAbsolute = path.join(this.config._inputDirCwdRelative, renameObj.from);
folderRenames[i].toAbsolute = path.join(this.config._inputDirCwdRelative, renameObj.to);
i += 1;
}
await updateFileSystem({
files,
folderRenames: orderedFolderRenames,
});
}
}

View File

@@ -21,7 +21,7 @@ parser.prepareWasm(saxWasmBuffer);
* @param {string} link
*/
function isRelativeLink(link) {
if (link.startsWith('http') || link.startsWith('/')) {
if (link.startsWith('http') || link.startsWith('/') || link.includes(':')) {
return false;
}
return true;

View File

@@ -14,8 +14,11 @@ import { readConfig } from '@web/config-loader';
import { RocketStart } from './RocketStart.js';
import { RocketBuild } from './RocketBuild.js';
import { RocketUpgrade } from './RocketUpgrade.js';
import { RocketLint } from './RocketLint.js';
import { webMenu } from '@web/menu';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -42,7 +45,7 @@ function ignore({ src }) {
*/
export async function normalizeConfig(inConfig) {
let config = {
presets: [],
presets: [webMenu()],
setupUnifiedPlugins: [],
setupDevAndBuildPlugins: [],
setupDevPlugins: [],
@@ -50,6 +53,7 @@ export async function normalizeConfig(inConfig) {
setupEleventyPlugins: [],
setupEleventyComputedConfig: [],
setupCliPlugins: [],
setupMenus: [],
eleventy: () => {},
command: 'help',
watch: true,
@@ -98,7 +102,7 @@ export async function normalizeConfig(inConfig) {
try {
const fileConfig = await readConfig('rocket.config', userConfigFile, path.resolve(__configDir));
if (fileConfig) {
config = {
const updatedConfig = {
...config,
...fileConfig,
build: {
@@ -111,12 +115,16 @@ export async function normalizeConfig(inConfig) {
},
imagePresets: config.imagePresets,
};
if (fileConfig.presets) {
updatedConfig.presets = [...config.presets, ...fileConfig.presets];
}
if (fileConfig.imagePresets && fileConfig.imagePresets.responsive) {
config.imagePresets.responsive = {
updatedConfig.imagePresets.responsive = {
...config.imagePresets.responsive,
...fileConfig.imagePresets.responsive,
};
}
config = updatedConfig;
}
} catch (error) {
console.error('Could not read rocket config file', error);
@@ -165,6 +173,9 @@ export async function normalizeConfig(inConfig) {
if (preset.setupCliPlugins) {
config.setupCliPlugins = [...config.setupCliPlugins, ...preset.setupCliPlugins];
}
if (preset.setupMenus) {
config.setupMenus = [...config.setupMenus, ...preset.setupMenus];
}
if (typeof preset.before11ty === 'function') {
config.__before11tyFunctions.push(preset.before11ty);
@@ -174,11 +185,7 @@ export async function normalizeConfig(inConfig) {
config._presetPaths.push(path.resolve(_inputDirCwdRelative));
/** @type {MetaPlugin[]} */
let pluginsMeta = [
{ name: 'RocketStart', plugin: RocketStart },
{ name: 'RocketBuild', plugin: RocketBuild },
{ name: 'RocketLint', plugin: RocketLint },
];
let pluginsMeta = [{ plugin: RocketStart }, { plugin: RocketBuild }, { plugin: RocketLint }, { plugin: RocketUpgrade}];
if (Array.isArray(config.setupCliPlugins)) {
for (const setupFn of config.setupCliPlugins) {

View File

@@ -5,8 +5,10 @@ const { createSocialImage: defaultCreateSocialImage } = require('./createSocialI
const { getComputedConfig } = require('./computedConfig.cjs');
const { executeSetupFunctions } = require('plugins-manager');
function titleMetaPlugin() {
return async data => {
class TitleMetaPlugin {
static dataName = 'titleMeta';
async execute(data) {
if (data.titleMeta) {
return data.titleMeta;
}
@@ -17,29 +19,24 @@ function titleMetaPlugin() {
return titleMetaFromContent;
}
return {};
};
}
}
function titlePlugin() {
return async data => {
class TitlePlugin {
static dataName = 'title';
async execute(data) {
if (data.title) {
return data.title;
}
return data.titleMeta?.title;
};
}
}
function eleventyNavigationPlugin() {
return async data => {
if (data.eleventyNavigation) {
return data.eleventyNavigation;
}
return data.titleMeta?.eleventyNavigation;
};
}
class SectionPlugin {
static dataName = 'section';
function sectionPlugin() {
return async data => {
async execute(data) {
if (data.section) {
return data.section;
}
@@ -52,11 +49,17 @@ function sectionPlugin() {
return parts[1];
}
}
};
}
}
function layoutPlugin({ defaultLayout = 'layout-default' } = {}) {
return async data => {
class LayoutPlugin {
static dataName = 'layout';
constructor({ defaultLayout = 'layout-default' } = {}) {
this.defaultLayout = defaultLayout;
}
async execute(data) {
if (data.layout) {
return data.layout;
}
@@ -66,22 +69,29 @@ function layoutPlugin({ defaultLayout = 'layout-default' } = {}) {
return 'layout-index';
}
}
return defaultLayout;
};
return this.defaultLayout;
}
}
function socialMediaImagePlugin(args = {}) {
const { createSocialImage = defaultCreateSocialImage, rocketConfig = {} } = args;
class SocialMediaImagePlugin {
static dataName = 'socialMediaImage';
const cleanedUpArgs = { ...args };
delete cleanedUpArgs.createSocialImage;
constructor(args = {}) {
const { createSocialImage = defaultCreateSocialImage, rocketConfig = {} } = args;
return async data => {
this.cleanedUpArgs = { ...args };
delete this.cleanedUpArgs.createSocialImage;
this.rocketConfig = rocketConfig;
this.createSocialImage = createSocialImage;
}
async execute(data) {
if (data.socialMediaImage) {
return data.socialMediaImage;
}
if (rocketConfig.createSocialMediaImages === false) {
if (this.rocketConfig.createSocialMediaImages === false) {
return;
}
@@ -95,15 +105,15 @@ function socialMediaImagePlugin(args = {}) {
const section = data.section ? ' ' + data.section[0].toUpperCase() + data.section.slice(1) : '';
const footer = `${data.site.name}${section}`;
const imgUrl = await createSocialImage({
const imgUrl = await this.createSocialImage({
title,
subTitle,
footer,
section,
...cleanedUpArgs,
...this.cleanedUpArgs,
});
return imgUrl;
};
}
}
function sortByOrder(a, b) {
@@ -146,26 +156,64 @@ async function dirToTree(sourcePath, extra = '') {
return sortedTree;
}
function joiningBlocksPlugin(rocketConfig) {
const { _inputDirCwdRelative } = rocketConfig;
const partialsSource = path.resolve(_inputDirCwdRelative, '_merged_includes');
return async () => {
const joiningBlocks = await dirToTree(partialsSource, '_joiningBlocks');
class JoiningBlocksPlugin {
static dataName = '_joiningBlocks';
constructor(rocketConfig) {
const { _inputDirCwdRelative } = rocketConfig;
this.partialsSource = path.resolve(_inputDirCwdRelative, '_merged_includes');
}
async execute() {
const joiningBlocks = await dirToTree(this.partialsSource, '_joiningBlocks');
return joiningBlocks;
};
}
}
/**
* Removes the `xx--` prefix that is used for ordering
*
* @returns {string}
*/
class PermalinkPlugin {
static dataName = 'permalink';
execute(data) {
if (data.permalink) {
return data.permalink;
}
let filePath = data.page.filePathStem.replace(/[0-9]+--/g, '');
return filePath.endsWith('index') ? `${filePath}.html` : `${filePath}/index.html`;
}
}
/**
* @returns {Number}
*/
class MenuOrderPlugin {
static dataName = 'menu.order';
execute(data) {
const matches = data.page.fileSlug.match(/([0-9]+)--/);
if (matches) {
return parseInt(matches[1]);
}
return 0;
}
}
function generateEleventyComputed() {
const rocketConfig = getComputedConfig();
let metaPlugins = [
{ name: 'titleMeta', plugin: titleMetaPlugin },
{ name: 'title', plugin: titlePlugin },
{ name: 'eleventyNavigation', plugin: eleventyNavigationPlugin },
{ name: 'section', plugin: sectionPlugin },
{ name: 'socialMediaImage', plugin: socialMediaImagePlugin, options: { rocketConfig } },
{ name: '_joiningBlocks', plugin: joiningBlocksPlugin, options: rocketConfig },
{ name: 'layout', plugin: layoutPlugin },
{ plugin: TitleMetaPlugin, options: {} }, // TODO: remove after search & social media are standalone
{ plugin: TitlePlugin, options: {} }, // TODO: remove after search & social media are standalone
{ plugin: SectionPlugin, options: {} }, // TODO: remove this
{ plugin: SocialMediaImagePlugin, options: { rocketConfig } }, // TODO: convert to standalone tool that can work with html
{ plugin: JoiningBlocksPlugin, options: rocketConfig },
{ plugin: LayoutPlugin, options: {} },
{ plugin: PermalinkPlugin, options: {} },
{ plugin: MenuOrderPlugin, options: {} },
];
const finalMetaPlugins = executeSetupFunctions(
@@ -176,13 +224,23 @@ function generateEleventyComputed() {
const eleventyComputed = {};
for (const pluginObj of finalMetaPlugins) {
if (pluginObj.options) {
eleventyComputed[pluginObj.name] = pluginObj.plugin(pluginObj.options);
const inst = new pluginObj.plugin(pluginObj.options);
eleventyComputed[inst.constructor.dataName] = inst.execute.bind(inst);
} else {
eleventyComputed[pluginObj.name] = pluginObj.plugin();
const inst = new pluginObj.plugin();
eleventyComputed[inst.constructor.dataName] = inst.execute.bind(inst);
}
}
return eleventyComputed;
}
module.exports = { generateEleventyComputed };
module.exports = {
generateEleventyComputed,
LayoutPlugin,
PermalinkPlugin,
MenuOrderPlugin,
SectionPlugin,
SocialMediaImagePlugin,
JoiningBlocksPlugin,
};

View File

@@ -1,5 +1,6 @@
const eleventyPluginMdjsUnified = require('@rocket/eleventy-plugin-mdjs-unified');
const eleventyRocketNav = require('@rocket/eleventy-rocket-nav');
const remark2rehype = require('remark-rehype');
const { getComputedConfig } = require('../public/computedConfig.cjs');
const rocketFilters = require('../eleventy-plugins/rocketFilters.cjs');
@@ -9,7 +10,7 @@ const { adjustPluginOptions } = require('plugins-manager');
const image = require('./mdjsImageHandler.cjs');
const defaultSetupUnifiedPlugins = [
adjustPluginOptions('remark2rehype', {
adjustPluginOptions(remark2rehype, {
handlers: {
image,
},
@@ -23,31 +24,19 @@ module.exports = function (eleventyConfig) {
let metaPlugins = [
{
name: 'rocket-filters',
plugin: rocketFilters,
options: { _inputDirCwdRelative },
},
{
name: 'rocket-copy',
plugin: rocketCopy,
options: { _inputDirCwdRelative },
},
{
name: 'eleventy-plugin-mdjs-unified',
plugin: eleventyPluginMdjsUnified,
options: {
setupUnifiedPlugins: [...defaultSetupUnifiedPlugins, ...config.setupUnifiedPlugins],
},
},
{
name: 'eleventy-rocket-nav',
plugin: eleventyRocketNav,
},
{
name: 'rocket-collections',
plugin: rocketCollections,
options: { _inputDirCwdRelative },
},
];
if (Array.isArray(config.setupEleventyPlugins)) {
@@ -69,7 +58,7 @@ module.exports = function (eleventyConfig) {
}
if (config.eleventy) {
const returnValue = config.eleventy(eleventyConfig);
const returnValue = config.eleventy(eleventyConfig, config);
if (returnValue) {
const returnString = JSON.stringify(returnValue, null, 2);
const msg = [

View File

@@ -0,0 +1,169 @@
import { readFile } from 'fs/promises';
import matter from 'gray-matter';
/** @typedef {import('@rocket/cli/types/upgrade').upgrade} upgrade */
/**
*
* @param {upgrade} options
*/
export async function upgrade202109menu({ files, folderRenames }) {
let i = 0;
const updatedFolderRenames = [...folderRenames];
for (const fileData of files) {
if (fileData.extName === '.md') {
const content = (await readFile(fileData.path)).toString();
const lines = content.split('\n');
const { title, lineNumber } = extractTitle(content);
let order = 0;
if (title && lineNumber >= 0) {
const parsedTitle = parseTitle(title);
order = parsedTitle.order;
lines[lineNumber] = `# ${parsedTitle.title}`;
files[i].updatedContent = lines.join('\n');
}
if (lines[0] === '---') {
const fmObj = matter(content);
if (fmObj.data.eleventyNavigation) {
const eleventyNav = fmObj.data.eleventyNavigation;
if (eleventyNav.order) {
order = eleventyNav.order;
delete fmObj.data.eleventyNavigation.order;
}
if (eleventyNav.key) {
fmObj.data.menu = { ...fmObj.data.menu, linkText: eleventyNav.key };
delete fmObj.data.eleventyNavigation.key;
}
if (eleventyNav.parent) {
delete fmObj.data.eleventyNavigation.parent;
}
if (Object.keys(eleventyNav).length === 0) {
delete fmObj.data.eleventyNavigation;
}
}
if (!title && fmObj.data.title) {
fmObj.content = `\n# ${fmObj.data.title}\n${fmObj.content}`;
delete fmObj.data.title;
}
if (fmObj.data.eleventyExcludeFromCollections) {
fmObj.data.menu = { ...fmObj.data.menu, exclude: true };
delete fmObj.data.eleventyExcludeFromCollections;
}
if (Object.keys(fmObj.data).length > 0) {
files[i].updatedContent = matter.stringify(fmObj.content, fmObj.data);
}
}
if (order !== 0) {
if (fileData.relPath.toLowerCase().endsWith('index.md')) {
const pathParts = fileData.relPath.split('/');
const originDirParts = [...pathParts];
originDirParts.pop();
pathParts[pathParts.length - 2] = `${order}--${pathParts[pathParts.length - 2]}`;
const dirParts = [...pathParts];
dirParts.pop();
updatedFolderRenames.push({ from: originDirParts.join('/'), to: dirParts.join('/') });
} else {
files[i].updatedName = `${order}--${fileData.name}`;
}
}
}
i += 1;
}
return { files, folderRenames: updatedFolderRenames };
}
/**
* Reads a text and extracts a title from it
*
* @param {string} content The text where to extract the title from
* @param {string} engine
*/
export function extractTitle(content, engine = 'md') {
if (engine === 'md') {
let captureHeading = true;
let i = 0;
for (const line of content.split('\n')) {
if (line.startsWith('```')) {
captureHeading = !captureHeading;
}
if (captureHeading && line.startsWith('# ')) {
return { title: line.substring(2), lineNumber: i };
}
i += 1;
}
}
return { title: '', lineNumber: -1 };
}
/**
* Parses a title and extracts the relevante data for it.
* A title can contain
* - ">>" to define a parent => child relationship
* - "||" to define the order for this page
*
* @example
* Foo ||3
* Foo >> Bar ||10
*
* @param {string} inTitle
* @return {{ title: string, order: number }}
*/
export function parseTitle(inTitle) {
if (typeof inTitle !== 'string') {
throw new Error('You need to provide a string to `parseTitle`');
}
let title = inTitle;
let order = 0;
let navigationTitle = title;
if (title.includes('>>')) {
const parts = title
.split('>>')
.map(part => part.trim())
.filter(Boolean);
title = parts.join(' ');
navigationTitle = parts[parts.length - 1];
if (parts.length >= 2) {
title = `${parts[0]}: ${parts[1]}`;
const parentParts = [...parts];
parentParts.pop();
if (parts.length >= 3) {
title = `${parts[parts.length - 2]}: ${parts[parts.length - 1]}`;
}
}
}
if (title.includes('||')) {
const parts = title
.split('||')
.map(part => part.trim())
.filter(Boolean);
if (parts.length !== 2) {
throw new Error('You can use || only once in `parseTitle`');
}
navigationTitle = navigationTitle.split('||').map(part => part.trim())[0];
title = parts[0];
order = parseInt(parts[1]);
}
return {
title: navigationTitle,
order,
};
// data.parts = titleParts;
// data.title = title;
// data.eleventyNavigation = {
// key,
// title: navigationTitle,
// order,
// };
// if (parent) {
// data.eleventyNavigation.parent = parent;
// }
// return data;
}

View File

@@ -2,9 +2,10 @@ import chai from 'chai';
import { RocketCli } from '../src/RocketCli.js';
import path from 'path';
import globby from 'globby';
import fs from 'fs-extra';
import fs, { move, remove } from 'fs-extra';
import prettier from 'prettier';
import { fileURLToPath } from 'url';
import { existsSync } from 'fs';
const { expect } = chai;
@@ -91,86 +92,109 @@ export async function readOutput(
return text;
}
export function startOutputExist(cli, fileName) {
const outputDir = cli.config.outputDevDir;
return fs.existsSync(path.join(outputDir, fileName));
}
export function buildOutputExist(cli, fileName) {
const outputDir = cli.config.outputDir;
return fs.existsSync(path.join(outputDir, fileName));
}
/**
* @param {*} cli
* @param {string} fileName
* @param {readOutputOptions} options
*/
export async function readStartOutput(cli, fileName, options = {}) {
options.type = 'start';
return readOutput(cli, fileName, options);
}
/**
* @param {*} cli
* @param {string} fileName
* @param {readOutputOptions} options
*/
export async function readBuildOutput(cli, fileName, options = {}) {
options.type = 'build';
return readOutput(cli, fileName, options);
}
export async function getfixtureExpectedFiles(pathToDir) {
const cwd = path.join(fixtureDir, pathToDir);
const paths = await globby('**/*', { cwd, absolute: true, dot: true });
return paths;
}
export async function execute(cli, configFileDir) {
export async function execute(pathToConfig, { type = 'start', captureLog = false } = {}) {
let log = [];
const origLog = console.log;
if (captureLog) {
console.log = (...args) => {
log = [...log, ...args];
};
}
const configFile = path.join(fixtureDir, pathToConfig.split('/').join(path.sep));
const configFileDir = path.dirname(configFile);
const cli = new RocketCli({
argv: [type, '--config-file', configFile],
});
await cli.setup();
cli.config.outputDevDir = path.join(configFileDir, '__output-dev');
cli.config.devServer.open = false;
cli.config.devServer.port = 8080;
cli.config.watch = false;
cli.config.outputDir = path.join(configFileDir, '__output');
await fs.emptyDir(cli.config.outputDevDir);
await fs.emptyDir(cli.config.outputDir);
await cli.run();
return cli;
/**
* @param {*} cli
* @param {string} fileName
* @param {readOutputOptions} options
*/
async function readOutput2(fileName, options = {}) {
options.type = type;
return readOutput(cli, fileName, options);
}
function outputExists(fileName) {
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
const filePath = path.join(outputDir, fileName);
return fs.existsSync(filePath);
}
if (captureLog) {
console.log = origLog;
}
return { log, readOutput: readOutput2, cli, outputExists };
}
export async function executeBootstrap(pathToDir) {
const configFileDir = path.join(fixtureDir, pathToDir.split('/').join(path.sep));
const cli = new RocketCli({ argv: ['bootstrap'] });
await cli.setup();
cli.config.outputDevDir = path.join(configFileDir, '__output-dev');
cli.config.devServer.open = false;
cli.config.devServer.port = 8080;
cli.config.watch = false;
cli.config.outputDir = path.join(configFileDir, '__output');
await fs.emptyDir(configFileDir);
await execute(cli, configFileDir);
return cli;
await cli.run();
return { cli };
}
export async function executeStart(pathToConfig) {
export async function executeUpgrade(pathToConfig) {
const configFile = path.join(fixtureDir, pathToConfig.split('/').join(path.sep));
const cli = new RocketCli({
argv: ['start', '--config-file', configFile],
argv: ['upgrade', '--config-file', configFile],
});
await execute(cli, path.dirname(configFile));
return cli;
}
await cli.setup();
export async function executeBuild(pathToConfig) {
const configFile = path.join(fixtureDir, pathToConfig.split('/').join(path.sep));
const cli = new RocketCli({
argv: ['build', '--config-file', configFile],
});
await execute(cli, path.dirname(configFile));
return cli;
}
export async function executeLint(pathToConfig) {
const configFile = path.join(fixtureDir, pathToConfig.split('/').join(path.sep));
const cli = new RocketCli({
argv: ['lint', '--config-file', configFile],
});
await execute(cli, path.dirname(configFile));
return cli;
// restore from backup if available - in cases the test did stop in the middle
if (cli.config._inputDirCwdRelative) {
const backupDir = path.join(cli.config._inputDirCwdRelative, '..', 'docs_backup');
if (existsSync(backupDir)) {
await remove(cli.config._inputDirCwdRelative);
await move(backupDir, cli.config._inputDirCwdRelative);
}
}
await cli.run();
return {
cli,
fileExists: fileName => {
const outputDir = cli.config._inputDirCwdRelative;
return fs.existsSync(path.join(outputDir, fileName));
},
readFile: async fileName => {
// TODO: use readOutput once it's changed to read full file paths
const filePath = path.join(cli.config._inputDirCwdRelative, fileName);
const text = await fs.promises.readFile(filePath);
return text.toString();
},
};
}
export function trimWhiteSpace(inString) {

View File

@@ -1,17 +1,11 @@
import chai from 'chai';
import chalk from 'chalk';
import {
executeBuild,
executeStart,
readBuildOutput,
readStartOutput,
setFixtureDir,
} from '@rocket/cli/test-helpers';
import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
describe('RocketCli computedConfig', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -20,90 +14,84 @@ describe('RocketCli computedConfig', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('will extract a title from markdown and set first folder as section', async () => {
cli = await executeStart('computed-config-fixtures/headlines/rocket.config.js');
it('will not create a social media image in "start"', async () => {
const { cli, readOutput } = await execute(
'computed-config-fixtures/social-images-only-build/rocket.config.js',
{
captureLog: true,
},
);
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const [indexTitle, indexSection] = indexHtml.split('\n');
expect(indexTitle).to.equal('Root');
expect(indexSection).to.be.undefined;
const subHtml = await readStartOutput(cli, 'sub/index.html');
const [subTitle, subSection] = subHtml.split('\n');
expect(subTitle).to.equal('Root: Sub');
expect(subSection).to.equal('sub');
const subSubHtml = await readStartOutput(cli, 'sub/subsub/index.html');
const [subSubTitle, subSubSection] = subSubHtml.split('\n');
expect(subSubTitle).to.equal('Sub: SubSub');
expect(subSubSection).to.equal('sub');
const sub2Html = await readStartOutput(cli, 'sub2/index.html');
const [sub2Title, sub2Section] = sub2Html.split('\n');
expect(sub2Title).to.equal('Root: Sub2');
expect(sub2Section).to.equal('sub2');
const withDataHtml = await readStartOutput(cli, 'with-data/index.html');
const [withDataTitle, withDataSection] = withDataHtml.split('\n');
expect(withDataTitle).to.equal('Set via data');
expect(withDataSection).be.undefined;
});
it('will note create a social media image in "start"', async () => {
cli = await executeStart('computed-config-fixtures/social-images-only-build/rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal('');
});
it('will create a social media image in "build"', async () => {
cli = await executeBuild('computed-config-fixtures/social-images-only-build/rocket.config.js');
const { cli, readOutput } = await execute(
'computed-config-fixtures/social-images-only-build/rocket.config.js',
{
captureLog: true,
type: 'build',
},
);
cleanupCli = cli;
const indexHtml = await readBuildOutput(cli, 'index.html', {
const indexHtml = await readOutput('index.html', {
stripToBody: true,
});
expect(indexHtml).to.equal('/_merged_assets/11ty-img/5893749-1200.png');
});
it('will create a social media image for every page', async () => {
cli = await executeStart('computed-config-fixtures/social-images/rocket.config.js');
const { cli, readOutput } = await execute(
'computed-config-fixtures/social-images/rocket.config.js',
{
captureLog: true,
},
);
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal('/_merged_assets/11ty-img/c4c29ec7-1200.png');
const guidesHtml = await readStartOutput(cli, 'guides/index.html');
const guidesHtml = await readOutput('guides/index.html');
expect(guidesHtml).to.equal('/_merged_assets/11ty-img/c593a8cd-1200.png');
const gettingStartedHtml = await readStartOutput(
cli,
'guides/first-pages/getting-started/index.html',
);
const gettingStartedHtml = await readOutput('guides/first-pages/getting-started/index.html');
expect(gettingStartedHtml).to.equal('/_merged_assets/11ty-img/d989ab1a-1200.png');
});
it('can override the svg function globally to adjust all social media image', async () => {
cli = await executeStart('computed-config-fixtures/social-images-override/rocket.config.js');
const { cli, readOutput } = await execute(
'computed-config-fixtures/social-images-override/rocket.config.js',
{
captureLog: true,
},
);
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal('/_merged_assets/11ty-img/d76265ed-1200.png');
const guidesHtml = await readStartOutput(cli, 'guides/index.html');
const guidesHtml = await readOutput('guides/index.html');
expect(guidesHtml).to.equal('/_merged_assets/11ty-img/d76265ed-1200.png');
const gettingStartedHtml = await readStartOutput(
cli,
'guides/first-pages/getting-started/index.html',
);
const gettingStartedHtml = await readOutput('guides/first-pages/getting-started/index.html');
expect(gettingStartedHtml).to.equal('/_merged_assets/11ty-img/d76265ed-1200.png');
});
it('will add "../" for links and image urls only within named template files', async () => {
cli = await executeStart('computed-config-fixtures/image-link/rocket.config.js');
const {
cli,
readOutput,
} = await execute('computed-config-fixtures/image-link/rocket.config.js', { captureLog: true });
cleanupCli = cli;
const namedMdContent = [
'<p>',
@@ -127,22 +115,22 @@ describe('RocketCli computedConfig', () => {
'</div>',
];
const templateHtml = await readStartOutput(cli, 'template/index.html', { formatHtml: true });
const templateHtml = await readOutput('template/index.html', { formatHtml: true });
expect(templateHtml, 'template/index.html does not match').to.equal(
namedHtmlContent.join('\n'),
);
const guidesHtml = await readStartOutput(cli, 'guides/index.html', { formatHtml: true });
const guidesHtml = await readOutput('guides/index.html', { formatHtml: true });
expect(guidesHtml, 'guides/index.html does not match').to.equal(
[...namedMdContent, ...namedHtmlContent].join('\n'),
);
const noAdjustHtml = await readStartOutput(cli, 'no-adjust/index.html');
const noAdjustHtml = await readOutput('no-adjust/index.html');
expect(noAdjustHtml, 'no-adjust/index.html does not match').to.equal(
'<p>Nothing to adjust in here</p>',
);
const rawHtml = await readStartOutput(cli, 'one-level/raw/index.html');
const rawHtml = await readOutput('one-level/raw/index.html');
expect(rawHtml, 'raw/index.html does not match').to.equal(
[
'<div>',
@@ -159,7 +147,7 @@ describe('RocketCli computedConfig', () => {
);
// for index files no '../' will be added
const indexHtml = await readStartOutput(cli, 'index.html', { formatHtml: true });
const indexHtml = await readOutput('index.html', { formatHtml: true });
expect(indexHtml, 'index.html does not match').to.equal(
[
'<p>',
@@ -188,19 +176,28 @@ describe('RocketCli computedConfig', () => {
});
it('can be configured via setupEleventyComputedConfig', async () => {
cli = await executeStart('computed-config-fixtures/setup/addPlugin.rocket.config.js');
const { cli, readOutput } = await execute(
'computed-config-fixtures/setup/addPlugin.rocket.config.js',
{
captureLog: true,
},
);
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal('test-value');
});
it('always assigns layout-default exept for index.* files who get layout-index', async () => {
cli = await executeStart('computed-config-fixtures/layout/rocket.config.js');
const { cli, readOutput } = await execute('computed-config-fixtures/layout/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.include('<body layout="layout-index">');
const pageHtml = await readStartOutput(cli, 'page/index.html');
const pageHtml = await readOutput('page/index.html');
expect(pageHtml).to.include('<body layout="layout-default">');
});
});

View File

@@ -2,13 +2,9 @@ import chai from 'chai';
import fetch from 'node-fetch';
import chalk from 'chalk';
import {
execute,
executeBootstrap,
executeBuild,
executeLint,
executeStart,
expectThrowsAsync,
readBuildOutput,
readStartOutput,
getfixtureExpectedFiles,
setFixtureDir,
} from '@rocket/cli/test-helpers';
@@ -17,7 +13,7 @@ import fs from 'fs-extra';
const { expect } = chai;
describe('RocketCli e2e', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -26,20 +22,24 @@ describe('RocketCli e2e', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('can add a unified plugin via the config', async () => {
cli = await executeStart('e2e-fixtures/unified-plugin/rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html');
const { cli, readOutput } = await execute('e2e-fixtures/unified-plugin/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal(`<p>See a 🐶</p>`);
});
describe('bootstrap command', () => {
it('can bootstrap a project', async () => {
cli = await executeBootstrap('e2e-fixtures/bootstrap/__output');
const { cli } = await executeBootstrap('e2e-fixtures/bootstrap/__output');
cleanupCli = cli;
for (const p of await getfixtureExpectedFiles('e2e-fixtures/bootstrap/expected')) {
const actual = await fs.readFile(
@@ -54,8 +54,11 @@ describe('RocketCli e2e', () => {
describe('eleventy in config', () => {
it('can modify eleventy via an elventy function in the config', async () => {
cli = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html');
const { cli, readOutput } = await execute('e2e-fixtures/content/eleventy.rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal(
['# BEFORE #', '<p>Content inside <code>docs/index.md</code></p>'].join('\n'),
);
@@ -63,7 +66,8 @@ describe('RocketCli e2e', () => {
it('will throw if you try to set options by returning an object', async () => {
await expectThrowsAsync(
() => executeStart('e2e-fixtures/content/eleventy-return.rocket.config.js'),
() =>
execute('e2e-fixtures/content/eleventy-return.rocket.config.js', { captureLog: true }),
{
errorMatch: /Error in your Eleventy config file.*/,
},
@@ -73,13 +77,23 @@ describe('RocketCli e2e', () => {
describe('setupDevAndBuildPlugins in config', () => {
it('can add a rollup plugin via setupDevAndBuildPlugins for build command', async () => {
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js');
const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
const { cli, readOutput } = await execute(
'e2e-fixtures/rollup-plugin/devbuild.rocket.config.js',
{
captureLog: true,
type: 'build',
},
);
cleanupCli = cli;
const inlineModule = await readOutput('e97af63d.js');
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
});
it('can add a rollup plugin via setupDevAndBuildPlugins for start command', async () => {
cli = await executeStart('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js');
const { cli } = await execute('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const response = await fetch('http://localhost:8080/test-data.json');
expect(response.ok).to.be.true; // no server error
@@ -90,37 +104,56 @@ describe('RocketCli e2e', () => {
});
it('can add a rollup plugin for dev & build and modify a build only plugin via the config', async () => {
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild-build.rocket.config.js');
const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
const { cli, readOutput } = await execute(
'e2e-fixtures/rollup-plugin/devbuild-build.rocket.config.js',
{
captureLog: true,
type: 'build',
},
);
cleanupCli = cli;
const inlineModule = await readOutput('e97af63d.js');
expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
});
it('can adjust the inputDir', async () => {
cli = await executeStart('e2e-fixtures/change-input-dir/rocket.config.js');
const { cli, readOutput } = await execute('e2e-fixtures/change-input-dir/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal('<p>Markdown in <code>docs/page/index.md</code></p>');
});
it('can access main rocket config values via {{rocketConfig.value}}', async () => {
cli = await executeStart('e2e-fixtures/rocket-config-in-template/rocket.config.js');
const { cli, readOutput } = await execute(
'e2e-fixtures/rocket-config-in-template/rocket.config.js',
{
captureLog: true,
},
);
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal(
'<p>You can show Rocket config data like rocketConfig.absoluteBaseUrl = <a href="http://test-domain.com/">http://test-domain.com/</a></p>',
);
});
it('can add a pathPrefix that will not influence the start command', async () => {
cli = await executeStart('e2e-fixtures/content/pathPrefix.rocket.config.js');
const { cli, readOutput } = await execute('e2e-fixtures/content/pathPrefix.rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const linkHtml = await readStartOutput(cli, 'link/index.html');
const linkHtml = await readOutput('link/index.html');
expect(linkHtml).to.equal(
['<p><a href="../">home</a></p>', '<p><a href="/">absolute home</a></p>'].join('\n'),
);
const assetHtml = await readStartOutput(cli, 'use-assets/index.html');
const assetHtml = await readOutput('use-assets/index.html');
expect(assetHtml).to.equal('<link rel="stylesheet" href="/_merged_assets/some.css">');
const imageHtml = await readStartOutput(cli, 'image/index.html', { replaceImageHashes: true });
const imageHtml = await readOutput('image/index.html', { replaceImageHashes: true });
expect(imageHtml).to.equal(
[
'<p>',
@@ -147,9 +180,13 @@ describe('RocketCli e2e', () => {
});
it('can add a pathPrefix that will be used in the build command', async () => {
cli = await executeBuild('e2e-fixtures/content/pathPrefix.rocket.config.js');
const { cli, readOutput } = await execute('e2e-fixtures/content/pathPrefix.rocket.config.js', {
captureLog: true,
type: 'build',
});
cleanupCli = cli;
const linkHtml = await readBuildOutput(cli, 'link/index.html', {
const linkHtml = await readOutput('link/index.html', {
stripToBody: true,
});
expect(linkHtml).to.equal(
@@ -157,11 +194,11 @@ describe('RocketCli e2e', () => {
'\n',
),
);
const assetHtml = await readBuildOutput(cli, 'use-assets/index.html');
const assetHtml = await readOutput('use-assets/index.html');
expect(assetHtml).to.equal(
'<html><head><link rel="stylesheet" href="../41297ffa.css">\n\n</head><body>\n\n</body></html>',
);
let imageHtml = await readBuildOutput(cli, 'image/index.html');
let imageHtml = await readOutput('image/index.html');
imageHtml = imageHtml.replace(/\.\.\/([a-z0-9]+)\./g, '../__HASH__.');
expect(imageHtml).to.equal(
[
@@ -184,8 +221,63 @@ describe('RocketCli e2e', () => {
});
it('smoke test for link checking', async () => {
await expectThrowsAsync(() => executeLint('e2e-fixtures/lint-links/rocket.config.js'), {
errorMatch: /Found 1 missing reference targets/,
await expectThrowsAsync(
() => execute('e2e-fixtures/lint-links/rocket.config.js', { captureLog: true, type: 'lint' }),
{
errorMatch: /Found 1 missing reference targets/,
},
);
});
it('can completely take over the rollup config', async () => {
const { cli, readOutput } = await execute('e2e-fixtures/rollup-override/rocket.config.js', {
captureLog: true,
type: 'build',
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html', {
stripToBody: true,
formatHtml: true,
});
expect(indexHtml).to.equal(
[
'<h1 id="importing-foo">',
' <a aria-hidden="true" tabindex="-1" href="#importing-foo"><span class="icon icon-link"></span></a',
' >Importing foo',
'</h1>',
'',
'<script type="module" src="./7338509a.js" mdjs-setup=""></script>',
].join('\n'),
);
});
describe('can adjust the eleventy config while having access to the rocketConfig', () => {
it('testing start', async () => {
const { cli, readOutput } = await execute(
'e2e-fixtures/adjust-eleventy-config/rocket.config.js',
{
captureLog: true,
},
);
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal('<p><a href="start:/path/to/page/">link</a></p>');
});
it('testing build', async () => {
const { cli, readOutput } = await execute(
'e2e-fixtures/adjust-eleventy-config/rocket.config.js',
{
captureLog: true,
type: 'build',
},
);
cleanupCli = cli;
const indexBuildHtml = await readOutput('index.html', {
stripToBody: true,
});
expect(indexBuildHtml).to.equal('<p><a href="build:/path/to/page/">link</a></p>');
});
});
});

View File

@@ -1,11 +1,11 @@
import chai from 'chai';
import chalk from 'chalk';
import { executeStart, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers';
import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
describe('RocketCli images', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -14,15 +14,18 @@ describe('RocketCli images', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
describe('Images', () => {
it('does render content images responsive', async () => {
cli = await executeStart('e2e-fixtures/images/rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html', {
const { cli, readOutput } = await execute('e2e-fixtures/images/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html', {
formatHtml: true,
replaceImageHashes: true,
});
@@ -57,7 +60,7 @@ describe('RocketCli images', () => {
].join('\n'),
);
const keepSvgHtml = await readStartOutput(cli, 'ignores/index.html', {
const keepSvgHtml = await readOutput('ignores/index.html', {
formatHtml: true,
replaceImageHashes: true,
});
@@ -102,7 +105,7 @@ describe('RocketCli images', () => {
].join('\n'),
);
const tableHtml = await readStartOutput(cli, 'table/index.html', {
const tableHtml = await readOutput('table/index.html', {
formatHtml: true,
replaceImageHashes: true,
});
@@ -150,8 +153,12 @@ describe('RocketCli images', () => {
});
it('can configure more patterns to ignore', async () => {
cli = await executeStart('e2e-fixtures/images/ignore-more.rocket.config.js');
const keepSvgHtml = await readStartOutput(cli, 'ignores/index.html', {
const { cli, readOutput } = await execute(
'e2e-fixtures/images/ignore-more.rocket.config.js',
{ captureLog: true },
);
cleanupCli = cli;
const keepSvgHtml = await readOutput('ignores/index.html', {
formatHtml: true,
replaceImageHashes: true,
});
@@ -178,16 +185,17 @@ describe('RocketCli images', () => {
});
it('renders multiple images in the correct order', async () => {
cli = await executeStart('e2e-fixtures/images/rocket.config.js');
const indexHtml = await readStartOutput(cli, 'two-images/index.html', {
const { cli, readOutput } = await execute('e2e-fixtures/images/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('two-images/index.html', {
formatHtml: true,
replaceImageHashes: true,
});
expect(indexHtml).to.equal(
[
'<h2 id="one">',
' <a aria-hidden="true" tabindex="-1" href="#one"><span class="icon icon-link"></span></a>one',
'</h2>',
'<p>one</p>',
'<p>',
' <picture>',
' <source',
@@ -211,9 +219,7 @@ describe('RocketCli images', () => {
' />',
' </picture>',
'</p>',
'<h2 id="two">',
' <a aria-hidden="true" tabindex="-1" href="#two"><span class="icon icon-link"></span></a>two',
'</h2>',
'<p>two</p>',
'<p>',
' <picture>',
' <source',
@@ -242,8 +248,11 @@ describe('RocketCli images', () => {
});
it('can configure those responsive images', async () => {
cli = await executeStart('e2e-fixtures/images/small.rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html', {
const { cli, readOutput } = await execute('e2e-fixtures/images/small.rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html', {
formatHtml: true,
replaceImageHashes: true,
});
@@ -280,8 +289,11 @@ describe('RocketCli images', () => {
});
it('will only render a figure & figcaption if there is a caption/title', async () => {
cli = await executeStart('e2e-fixtures/images/small.rocket.config.js');
const indexHtml = await readStartOutput(cli, 'no-title/index.html', {
const { cli, readOutput } = await execute('e2e-fixtures/images/small.rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('no-title/index.html', {
formatHtml: true,
replaceImageHashes: true,
});
@@ -315,8 +327,12 @@ describe('RocketCli images', () => {
});
it('will render an img with srcset and sizes if there is only one image format', async () => {
cli = await executeStart('e2e-fixtures/images/single-format.rocket.config.js');
const indexHtml = await readStartOutput(cli, 'no-title/index.html', {
const {
cli,
readOutput,
} = await execute('e2e-fixtures/images/single-format.rocket.config.js', { captureLog: true });
cleanupCli = cli;
const indexHtml = await readOutput('no-title/index.html', {
formatHtml: true,
replaceImageHashes: true,
});

View File

@@ -0,0 +1,77 @@
import chai from 'chai';
import chalk from 'chalk';
import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
describe('RocketCli Menu', () => {
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
chalk.level = 0;
setFixtureDir(import.meta.url);
});
afterEach(async () => {
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('will render a menu', async () => {
const { cli, readOutput } = await execute('e2e-fixtures/menu/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html', {
formatHtml: true,
});
expect(indexHtml).to.equal(
[
'<html>',
' <head> </head>',
' <body>',
' <web-menu name="site">',
' <nav aria-label="site">',
' <a href="/components/">Components</a>',
' <a href="/getting-started/">Getting Started</a>',
' <a href="/blog/">Blog</a>',
' </nav>',
' </web-menu>',
' <h1 id="menu-page">',
' <a aria-hidden="true" tabindex="-1" href="#menu-page"><span class="icon icon-link"></span></a',
' >Menu Page',
' </h1>',
' </body>',
'</html>',
].join('\n'),
);
const accordion = await readOutput('components/content/accordion/index.html', {
formatHtml: true,
});
expect(accordion).to.equal(
[
'<html>',
' <head>',
' <meta name="menu:order" content="10" />',
' </head>',
' <body>',
' <web-menu name="site">',
' <nav aria-label="site">',
' <a href="/components/">Components</a>',
' <a href="/getting-started/">Getting Started</a>',
' <a href="/blog/">Blog</a>',
' </nav>',
' </web-menu>',
' <h1 id="accordion">',
' <a aria-hidden="true" tabindex="-1" href="#accordion"><span class="icon icon-link"></span></a',
' >Accordion',
' </h1>',
' </body>',
'</html>',
].join('\n'),
);
});
});

View File

@@ -1,16 +1,11 @@
import chai from 'chai';
import chalk from 'chalk';
import {
executeStart,
readStartOutput,
trimWhiteSpace,
setFixtureDir,
} from '@rocket/cli/test-helpers';
import { execute, trimWhiteSpace, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
describe('RocketCli mergeTemplates', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -19,15 +14,18 @@ describe('RocketCli mergeTemplates', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('merges it in the defined order', async () => {
cli = await executeStart('merge-templates-fixtures/order/rocket.config.js');
const { cli, readOutput } = await execute('merge-templates-fixtures/order/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(trimWhiteSpace(indexHtml)).to.equal(
[
'<p>30-first</p>',
@@ -40,9 +38,13 @@ describe('RocketCli mergeTemplates', () => {
});
it('presets can overwrite in order', async () => {
cli = await executeStart('merge-templates-fixtures/overwrite/rocket.config.js');
const { cli, readOutput } = await execute(
'merge-templates-fixtures/overwrite/rocket.config.js',
{ captureLog: true },
);
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(trimWhiteSpace(indexHtml)).to.equal(
['<p>overwritten second</p>', '<p>third</p>', '<p>overwritten first to be last</p>'].join(
'\n',
@@ -51,9 +53,12 @@ describe('RocketCli mergeTemplates', () => {
});
it('presets can add inbetween', async () => {
cli = await executeStart('merge-templates-fixtures/add/rocket.config.js');
const { cli, readOutput } = await execute('merge-templates-fixtures/add/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(trimWhiteSpace(indexHtml)).to.equal(
[
'<p>first</p>',

View File

@@ -1,11 +1,11 @@
import chai from 'chai';
import chalk from 'chalk';
import { executeStart, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers';
import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
describe('RocketCli preset', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -14,21 +14,24 @@ describe('RocketCli preset', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('offers a default layout (with head, header, content, footer, bottom) and raw layout', async () => {
cli = await executeStart('preset-fixtures/default/rocket.config.js');
const { cli, readOutput } = await execute('preset-fixtures/default/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const rawHtml = await readStartOutput(cli, 'raw/index.html');
const rawHtml = await readOutput('raw/index.html');
expect(rawHtml).to.equal('<p>Just raw</p>');
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.include('<body layout="layout-index">');
const pageHtml = await readStartOutput(cli, 'page/index.html', {
const pageHtml = await readOutput('page/index.html', {
stripScripts: true,
formatHtml: true,
});
@@ -69,7 +72,7 @@ describe('RocketCli preset', () => {
' <div class="content-area">',
' <a class="logo-link" href="/">',
' <img src="/_merged_assets/logo.svg" alt="" />',
' <span class="sr-only">Rocket</span>',
' <span>Rocket</span>',
' </a>',
' </div>',
' </header>',
@@ -86,12 +89,6 @@ describe('RocketCli preset', () => {
' </div>',
'',
' <footer id="main-footer"></footer>',
'',
' <script',
' type="module"',
' inject-service-worker=""',
' src="/_merged_assets/scripts/registerServiceWorker.js"',
' ></script>',
' </body>',
'</html>',
].join('\n'),
@@ -99,16 +96,22 @@ describe('RocketCli preset', () => {
});
it('allows to add content to the head without overriding', async () => {
cli = await executeStart('preset-fixtures/add-to-head/rocket.config.js');
const { cli, readOutput } = await execute('preset-fixtures/add-to-head/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html');
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.include('<meta name="added" content="at the top" />');
});
it('a preset can provide an adjustImagePresets() function', async () => {
cli = await executeStart('preset-fixtures/use-preset/rocket.config.js');
const { cli, readOutput } = await execute('preset-fixtures/use-preset/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html', {
const indexHtml = await readOutput('index.html', {
formatHtml: true,
replaceImageHashes: true,
});

View File

@@ -1,6 +1,6 @@
import chai from 'chai';
import chalk from 'chalk';
import { executeBuild, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers';
import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
@@ -17,7 +17,7 @@ function getServiceWorkerUrl(text) {
}
describe('RocketCli e2e', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -26,20 +26,28 @@ describe('RocketCli e2e', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('will add a script to inject the service worker', async () => {
cli = await executeBuild('e2e-fixtures/service-worker/rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html');
const { cli, readOutput } = await execute('e2e-fixtures/service-worker/rocket.config.js', {
captureLog: true,
type: 'build',
});
cleanupCli = cli;
// we check the start output here as in the rollup build version it's hard to find
const indexHtml = await readOutput('../__output-dev/index.html');
const indexInject = getInjectServiceWorker(indexHtml);
expect(indexInject).to.equal(
'<script type="module" inject-service-worker="" src="/_merged_assets/scripts/registerServiceWorker.js"></script>',
);
expect(getServiceWorkerUrl(indexHtml)).to.equal('/service-worker.js');
const subHtml = await readStartOutput(cli, 'sub/index.html');
// we check the start output here as in the rollup build version it's hard to find
const subHtml = await readOutput('../__output-dev/sub/index.html');
const subInject = getInjectServiceWorker(subHtml);
expect(subInject).to.equal(
'<script type="module" inject-service-worker="" src="/_merged_assets/scripts/registerServiceWorker.js"></script>',
@@ -49,14 +57,21 @@ describe('RocketCli e2e', () => {
// TODO: find a way to run these test either by forcing pathPrefix in start or skipping asset gathering for build or ...
it.skip('will add a script to inject the service worker', async () => {
cli = await executeBuild('e2e-fixtures/service-worker/pathPrefix.rocket.config.js');
const indexHtml = await readStartOutput(cli, 'index.html');
const { cli, readOutput } = await execute(
'e2e-fixtures/service-worker/pathPrefix.rocket.config.js',
{
captureLog: true,
type: 'build',
},
);
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
const indexInject = getInjectServiceWorker(indexHtml);
expect(indexInject).to.equal(
'<script type="module" inject-service-worker="" src="/my-prefix-folder/_merged_assets/scripts/registerServiceWorker.js"></script>',
);
expect(getServiceWorkerUrl(indexHtml)).to.equal('/my-prefix-folder/service-worker.js');
const subHtml = await readStartOutput(cli, 'sub/index.html');
const subHtml = await readOutput('sub/index.html');
const subInject = getInjectServiceWorker(subHtml);
expect(subInject).to.equal(
'<script type="module" inject-service-worker="" src="/my-prefix-folder/_merged_assets/scripts/registerServiceWorker.js"></script>',

View File

@@ -0,0 +1,57 @@
import chai from 'chai';
import chalk from 'chalk';
import path from 'path';
import { executeUpgrade, setFixtureDir } from '@rocket/cli/test-helpers';
import { move, remove } from 'fs-extra';
import { existsSync } from 'fs';
const { expect } = chai;
describe('Upgrade System', () => {
let cli;
before(() => {
// ignore colors in tests as most CIs won't support it
chalk.level = 0;
setFixtureDir(import.meta.url);
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
}
if (cli?.config._inputDirCwdRelative) {
const backupDir = path.join(cli.config._inputDirCwdRelative, '..', 'docs_backup');
if (existsSync(backupDir)) {
await remove(cli.config._inputDirCwdRelative);
await move(backupDir, cli.config._inputDirCwdRelative);
}
}
});
it('2021-09-menu', async () => {
const run = await executeUpgrade('fixtures-upgrade/2021-09-menu/rocket.config.js');
cli = run.cli;
expect(run.fileExists('index.md')).to.be.true;
expect(run.fileExists('31--components/index.md')).to.be.true;
expect(await run.readFile('31--components/index.md')).to.equal(
[
'---',
'menu:',
' linkText: Components',
'---',
'',
'# Component Directory',
'',
'Here you get started.',
'',
].join('\n'),
);
expect(run.fileExists('31--components/10--content/20--accordion/overview.md')).to.be.false;
expect(run.fileExists('31--components/10--content/20--accordion/10--overview.md')).to.be.true;
expect(await run.readFile('31--components/10--content/20--accordion/10--overview.md')).to.equal(
'# Overview\n',
);
});
});

View File

@@ -1,16 +1,11 @@
import chai from 'chai';
import chalk from 'chalk';
import {
executeStart,
readStartOutput,
setFixtureDir,
startOutputExist,
} from '@rocket/cli/test-helpers';
import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai;
describe('RocketCli use cases', () => {
let cli;
let cleanupCli;
before(() => {
// ignore colors in tests as most CIs won't support it
@@ -19,18 +14,22 @@ describe('RocketCli use cases', () => {
});
afterEach(async () => {
if (cli?.cleanup) {
await cli.cleanup();
if (cleanupCli?.cleanup) {
await cleanupCli.cleanup();
}
});
it('supports dynamic imports', async () => {
cli = await executeStart('use-cases/dynamic-imports/rocket.config.js');
const {
cli,
readOutput,
outputExists,
} = await execute('use-cases/dynamic-imports/rocket.config.js', { captureLog: true });
cleanupCli = cli;
expect(startOutputExist(cli, 'sub/assets/myData.js'), 'static files did not get copied').to.be
.true;
expect(outputExists('sub/assets/myData.js'), 'static files did not get copied').to.be.true;
const aboutHtml = await readStartOutput(cli, 'about/index.html', { formatHtml: true });
const aboutHtml = await readOutput('about/index.html', { formatHtml: true });
expect(aboutHtml).to.equal(
[
'<p><code>about.md</code></p>',
@@ -38,7 +37,7 @@ describe('RocketCli use cases', () => {
].join('\n'),
);
const subHtml = await readStartOutput(cli, 'sub/index.html', { formatHtml: true });
const subHtml = await readOutput('sub/index.html', { formatHtml: true });
expect(subHtml).to.equal(
[
'<p><code>sub/index.md</code></p>',
@@ -46,7 +45,7 @@ describe('RocketCli use cases', () => {
].join('\n'),
);
const subDetailsHtml = await readStartOutput(cli, 'sub/details/index.html', {
const subDetailsHtml = await readOutput('sub/details/index.html', {
formatHtml: true,
});
expect(subDetailsHtml).to.equal(
@@ -56,7 +55,7 @@ describe('RocketCli use cases', () => {
].join('\n'),
);
const indexHtml = await readStartOutput(cli, 'index.html', { formatHtml: true });
const indexHtml = await readOutput('index.html', { formatHtml: true });
expect(indexHtml).to.equal(
[
'<p><code>index.md</code></p>',

View File

@@ -1,3 +1,4 @@
import remark2rehype from 'remark-rehype';
import { adjustPluginOptions } from 'plugins-manager';
function image(h, node) {
@@ -10,7 +11,7 @@ function image(h, node) {
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
setupUnifiedPlugins: [
adjustPluginOptions('remark2rehype', {
adjustPluginOptions(remark2rehype, {
handlers: {
image,
},

View File

@@ -1,8 +1,16 @@
import { addPlugin } from 'plugins-manager';
class Test {
static dataName = 'test';
execute() {
return 'test-value';
}
}
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
setupEleventyComputedConfig: [addPlugin({ name: 'test', plugin: () => 'test-value' })],
setupEleventyComputedConfig: [addPlugin(Test)],
};
export default config;

View File

@@ -1,4 +1,5 @@
import { adjustPluginOptions } from 'plugins-manager';
import { SocialMediaImagePlugin } from '@rocket/cli';
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
@@ -6,7 +7,7 @@ const config = {
createSocialMediaImages: true,
},
setupEleventyComputedConfig: [
adjustPluginOptions('socialMediaImage', {
adjustPluginOptions(SocialMediaImagePlugin, {
createSocialImageSvg: async () => {
return `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630">

View File

@@ -0,0 +1,5 @@
---
layout: layout-raw
---
<a href="{{ '/path/to/page/' | conditional-resolve }}">link</a>

View File

@@ -0,0 +1,15 @@
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
eleventy: (config, rocketConfig) => {
config.addFilter('conditional-resolve', value => {
if (rocketConfig.command === 'build') {
return `build:${value}`;
}
if (rocketConfig.command === 'start') {
return `start:${value}`;
}
});
},
};
export default config;

View File

@@ -2,10 +2,10 @@
layout: layout-raw
---
## one
one
![one](./_assets/my-image.jpg)
## two
two
![two](./_assets/my-image.jpg)

View File

@@ -0,0 +1 @@
# Components

View File

@@ -0,0 +1 @@
# Getting Started

View File

@@ -0,0 +1 @@
# Blog

View File

@@ -0,0 +1,11 @@
<html>
<head>
{% if menu.order %}
<meta name="menu:order" content="{{ menu.order }}" />
{% endif %}
</head>
<body>
<web-menu name="site"></web-menu>
{{ content | safe }}
</body>
</html>

View File

@@ -0,0 +1 @@
# Menu Page

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -0,0 +1 @@
**/*.njk

View File

@@ -0,0 +1,11 @@
---
layout: layout-raw
---
# Importing foo
```js script
import { foo } from './not-foo.js';
console.log(foo);
```

View File

@@ -0,0 +1 @@
export const notFoo = 'not-foo';

View File

@@ -0,0 +1,11 @@
// @ts-no-check
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
rollup: config => ({
...config,
shimMissingExports: true,
}),
};
export default config;

View File

@@ -1,11 +1,14 @@
// @ts-no-check
import json from '@rollup/plugin-json';
import { rollupPluginHTML } from '@web/rollup-plugin-html';
import { addPlugin, adjustPluginOptions } from 'plugins-manager';
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
setupDevAndBuildPlugins: [addPlugin({ name: 'json', plugin: json, location: 'top' })],
setupBuildPlugins: [adjustPluginOptions('html', { absoluteBaseUrl: 'https://test-me.com' })],
setupDevAndBuildPlugins: [addPlugin(json, {}, { location: 'top' })],
setupBuildPlugins: [
adjustPluginOptions(rollupPluginHTML, { absoluteBaseUrl: 'https://test-me.com' }),
],
};
export default config;

View File

@@ -1,10 +1,9 @@
// @ts-no-check
import json from '@rollup/plugin-json';
import { addPlugin } from 'plugins-manager';
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
setupDevAndBuildPlugins: [addPlugin({ name: 'json', plugin: json, location: 'top' })],
setupDevAndBuildPlugins: [addPlugin(json, {}, { location: 'top' })],
devServer: {
mimeTypes: {
// serve all json files as js

View File

@@ -1,10 +1,11 @@
// @ts-check
import emoji from 'remark-emoji';
import { addPlugin } from 'plugins-manager';
import markdown from 'remark-parse';
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {
setupUnifiedPlugins: [addPlugin({ location: 'markdown', name: 'emoji', plugin: emoji })],
setupUnifiedPlugins: [addPlugin(emoji, {}, { location: markdown })],
};
export default config;

View File

@@ -0,0 +1,11 @@
<html>
<head>
{% if menuOrder %}
<meta name="menu:order" content="{{ menuOrder }}"/>
{% endif %}
</head>
<body>
<web-menu name="site"></web-menu>
{{ content | safe }}
</body>
</html>

View File

@@ -0,0 +1 @@
# Components >> Content >> Accordion >> Api || 20

View File

@@ -0,0 +1 @@
# Components >> Content >> Accordion || 20

View File

@@ -0,0 +1 @@
# Components >> Content >> Accordion >> Overview || 10

View File

@@ -0,0 +1 @@
# Components >> Content || 10

View File

@@ -0,0 +1 @@
# Components >> Content >> Tabs || 10

View File

@@ -0,0 +1,8 @@
---
title: Component Directory
eleventyNavigation:
key: Components
order: 31
---
Here you get started.

View File

@@ -0,0 +1,3 @@
---
title: Rocket
---

View File

@@ -0,0 +1,3 @@
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = {};
export default config;

View File

@@ -1,89 +0,0 @@
import chai from 'chai';
import { RocketCli } from '../src/RocketCli.js';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs-extra';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const { expect } = chai;
/**
* @param {function} method
* @param {string} errorMessage
*/
export async function expectThrowsAsync(method, { errorMatch, errorMessage } = {}) {
let error = null;
try {
await method();
} catch (err) {
error = err;
}
expect(error).to.be.an('Error', 'No error was thrown');
if (errorMatch) {
expect(error.message).to.match(errorMatch);
}
if (errorMessage) {
expect(error.message).to.equal(errorMessage);
}
}
export async function readOutput(
cli,
fileName,
{ stripToBody = false, stripStartEndWhitespace = true, type = 'build' } = {},
) {
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
let text = await fs.promises.readFile(path.join(outputDir, fileName));
text = text.toString();
if (stripToBody) {
const bodyOpenTagEnd = text.indexOf('>', text.indexOf('<body') + 1) + 1;
const bodyCloseTagStart = text.indexOf('</body>');
text = text.substring(bodyOpenTagEnd, bodyCloseTagStart);
}
if (stripStartEndWhitespace) {
text = text.trim();
}
return text;
}
export async function readStartOutput(cli, fileName, options = {}) {
options.type = 'start';
return readOutput(cli, fileName, options);
}
export async function execute(cli, configFileDir) {
await cli.setup();
cli.config.outputDevDir = path.join(configFileDir, '__output-dev');
cli.config.devServer.open = false;
cli.config.watch = false;
cli.config.outputDir = path.join(configFileDir, '__output');
await cli.run();
return cli;
}
export async function executeStart(pathToConfig) {
const configFile = path.join(__dirname, pathToConfig.split('/').join(path.sep));
const cli = new RocketCli({
argv: ['start', '--config-file', configFile],
});
await execute(cli, path.dirname(configFile));
return cli;
}
export async function executeLint(pathToConfig) {
const configFile = path.join(__dirname, pathToConfig.split('/').join(path.sep));
const cli = new RocketCli({
argv: ['lint', '--config-file', configFile],
});
await execute(cli, path.dirname(configFile));
return cli;
}
export function trimWhiteSpace(inString) {
return inString
.split('\n')
.map(line => line.trim())
.filter(line => line)
.join('\n');
}

View File

@@ -14,19 +14,32 @@ function cleanup(config) {
delete configNoPaths.eleventy;
delete configNoPaths.outputDevDir;
delete configNoPaths.imagePresets.responsive.ignore;
delete configNoPaths.presets;
delete configNoPaths.setupCliPlugins;
return configNoPaths;
}
const plugins = [
{ commands: ['start'] },
{ commands: ['build'] },
{ commands: ['start', 'build', 'lint'] }, // lint
{ commands: ['upgrade'] },
{ commands: ['start', 'build', 'lint'] }, // web-menu
];
describe('normalizeConfig', () => {
it('makes sure essential settings are there', async () => {
const configFile = path.join(__dirname, 'fixtures', 'empty', 'rocket.config.js');
const config = await normalizeConfig({ configFile });
// testing pathes is always a little more complicted 😅
// testing pathes is always a little more complicated 😅
expect(config._inputDirCwdRelative).to.match(/empty\/docs$/);
expect(config._presetPaths[0]).to.match(/cli\/preset$/);
expect(config._presetPaths[1]).to.match(/empty\/docs$/);
expect(config._presetPaths[1]).to.match(/web-menu\/preset$/);
expect(config._presetPaths[2]).to.match(/empty\/docs$/);
expect(config.outputDevDir).to.match(/_site-dev$/);
expect(config.presets.length).to.equal(1);
expect(config.setupCliPlugins.length).to.equal(1);
expect(cleanup(config)).to.deep.equal({
__before11tyFunctions: [],
@@ -41,14 +54,9 @@ describe('normalizeConfig', () => {
setupDevPlugins: [],
setupEleventyPlugins: [],
setupEleventyComputedConfig: [],
setupCliPlugins: [],
presets: [],
setupMenus: [],
serviceWorkerName: 'service-worker.js',
plugins: [
{ commands: ['start'] },
{ commands: ['build'] },
{ commands: ['start', 'build', 'lint'] },
],
plugins,
imagePresets: {
responsive: {
formats: ['avif', 'jpeg'],
@@ -84,9 +92,8 @@ describe('normalizeConfig', () => {
setupDevAndBuildPlugins: [],
setupDevPlugins: [],
setupEleventyPlugins: [],
setupCliPlugins: [],
setupEleventyComputedConfig: [],
presets: [],
setupMenus: [],
imagePresets: {
responsive: {
formats: ['avif', 'jpeg'],
@@ -95,11 +102,7 @@ describe('normalizeConfig', () => {
},
},
serviceWorkerName: 'service-worker.js',
plugins: [
{ commands: ['start'] },
{ commands: ['build'] },
{ commands: ['start', 'build', 'lint'] },
],
plugins,
inputDir: 'docs',
outputDir: '_site',
});
@@ -125,9 +128,8 @@ describe('normalizeConfig', () => {
setupDevAndBuildPlugins: [],
setupDevPlugins: [],
setupEleventyPlugins: [],
setupCliPlugins: [],
setupEleventyComputedConfig: [],
presets: [],
setupMenus: [],
imagePresets: {
responsive: {
formats: ['avif', 'jpeg'],
@@ -136,11 +138,7 @@ describe('normalizeConfig', () => {
},
},
serviceWorkerName: 'service-worker.js',
plugins: [
{ commands: ['start'] },
{ commands: ['build'] },
{ commands: ['start', 'build', 'lint'] },
],
plugins,
inputDir: 'docs',
outputDir: '_site',
});
@@ -169,9 +167,8 @@ describe('normalizeConfig', () => {
setupDevAndBuildPlugins: [],
setupDevPlugins: [],
setupEleventyPlugins: [],
setupCliPlugins: [],
setupEleventyComputedConfig: [],
presets: [],
setupMenus: [],
imagePresets: {
responsive: {
formats: ['avif', 'jpeg'],
@@ -180,11 +177,7 @@ describe('normalizeConfig', () => {
},
},
serviceWorkerName: 'service-worker.js',
plugins: [
{ commands: ['start'] },
{ commands: ['build'] },
{ commands: ['start', 'build', 'lint'] },
],
plugins,
inputDir: 'docs',
outputDir: '_site',
});

View File

@@ -40,6 +40,7 @@ export interface RocketCliOptions extends Pick<RocketPreset, PresetKeys> {
start?: RocketStartConfig;
// advanced
rollup?: (config: any) => void; // TODO: improve
devServer?: DevServerConfig;
eleventy?: (eleventyConfig: any) => void; // TODO: improve
plugins?: RocketPlugin[];

22
packages/cli/types/upgrade.d.ts vendored Normal file
View File

@@ -0,0 +1,22 @@
export interface UpgradeFile {
path: string;
relPath: string;
name: string;
extName: string;
updatedContent?: string;
updatedPath?: string;
updatedRelPath?: string;
updatedName?: string;
}
export interface FolderRename {
from: string;
to: string;
fromAbsolute?: string;
toAbsolute?: string;
}
export interface upgrade {
files: UpgradeFile[];
folderRenames: FolderRename[];
}

View File

@@ -1,5 +1,16 @@
# @rocket/eleventy-plugin-mdjs-unified
## 0.6.0
### Minor Changes
- 70bb7a1: BREAKING CHANGE: Updating to latest version of `@mdjs/core` which requires the latest version of `plugins-manager`
### Patch Changes
- Updated dependencies [70bb7a1]
- @mdjs/core@0.9.0
## 0.5.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/eleventy-plugin-mdjs-unified",
"version": "0.5.2",
"version": "0.6.0",
"publishConfig": {
"access": "public"
},
@@ -31,7 +31,7 @@
"mdjs"
],
"dependencies": {
"@mdjs/core": "^0.8.2",
"@mdjs/core": "^0.9.0",
"es-module-lexer": "^0.3.26",
"unist-util-visit": "^2.0.3"
},

View File

@@ -5,6 +5,7 @@ const fs = require('fs');
const { mdjsProcess } = require('@mdjs/core');
const visit = require('unist-util-visit');
const { init, parse } = require('es-module-lexer');
const markdown = require('remark-parse');
// @ts-ignore
const { parseTitle } = require('@rocket/core/title');
@@ -45,12 +46,12 @@ function cleanupTitleHeadline() {
* @param {MdjsProcessPlugin[]} plugins
*/
function addCleanupTitleHeadline(plugins) {
if (plugins.findIndex(plugin => plugin.name === 'cleanupTitleHeadline') === -1) {
if (plugins.findIndex(pluginObj => pluginObj.plugin === cleanupTitleHeadline) === -1) {
// add plugins right after markdown
const markdownPluginIndex = plugins.findIndex(plugin => plugin.name === 'markdown');
const markdownPluginIndex = plugins.findIndex(pluginObj => pluginObj.plugin === markdown);
plugins.splice(markdownPluginIndex + 1, 0, {
name: 'cleanupTitleHeadline',
plugin: cleanupTitleHeadline,
options: {},
});
}
return plugins;

View File

@@ -1,8 +1,9 @@
const htmlHeading = require('rehype-autolink-headings');
const pluginMdjs = require('../../../index.js');
function addClassAnchorToHtmlHeading(plugins) {
return plugins.map(pluginObj => {
if (pluginObj.name === 'htmlHeading') {
if (pluginObj.plugin === htmlHeading) {
return {
...pluginObj,
options: {

View File

@@ -1,5 +1,28 @@
# @rocket/launch
## 0.6.0
### Minor Changes
- 70bb7a1: BREAKING CHANGE: Update to latest plugins manager to get type safe options
There is no longer a name string as a key for a plugin. It is identified by it's function/class. You will need to adjust your code if you are adding or adjusting plugins.
```diff
- addPlugin({ name: 'my-plugin', plugin: myPlugin, options: { myFlag: true }, location: 'top' });
+ addPlugin(myPlugin, { myFlag: true }, { location: 'top' });
- adjustPluginOptions('my-plugin', { myFlag: true });
+ adjustPluginOptions(myPlugin, { myFlag: true });
```
For more details please see the [Changelog](https://github.com/modernweb-dev/rocket/blob/main/packages/plugins-manager/CHANGELOG.md#030) of the plugins-manager package.
## 0.5.6
### Patch Changes
- 0987a41: Fix styling in darkmode
## 0.5.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@rocket/launch",
"version": "0.5.5",
"version": "0.6.0",
"publishConfig": {
"access": "public"
},

Some files were not shown because too many files have changed in this diff Show More