Compare commits

..

26 Commits

Author SHA1 Message Date
github-actions[bot]
30eb822151 Version Packages 2021-11-11 10:11:42 +01:00
Thomas Allmer
15a82c0e4d fix: improve mdjs preview iframe security 2021-11-11 10:05:51 +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
109 changed files with 2472 additions and 925 deletions

View File

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

View File

@@ -107,3 +107,60 @@ export default ({
}); });
``` ```
<!-- prettier-ignore-end --> <!-- 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 ## Supported Systems
### Storybook ### Storybook

View File

@@ -40,6 +40,68 @@ will result in
export const foo = () => html` <demo-element></demo-element> `; 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 ## HTML Story
````md ````md

View File

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

View File

@@ -1,4 +1,3 @@
# Presets >> Search || 10 # Presets >> Search || 10
- [Preset](./preset/) - [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). 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. 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 ## Problem
Many plugin systems require you to either execute a plugin function like in `rollup`. 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 The plugins manager lets you orchestrate a set of "meta plugins" which are defined by
- name - plugin (class or function)
- plugin - it's options
- options
```js ```js
import beep from '@rollup/plugin-beep'; import beep from '@rollup/plugin-beep';
import url from '@rollup/plugin-url'; import url from '@rollup/plugin-url';
let metaPlugins = [ let metaPlugins = [{ plugin: beep }, { plugin: url, options: { limit: 10000 } }];
{ name: 'beep', plugin: beep },
{ name: 'url', plugin: url, options: { limit: 10000 } },
];
``` ```
This array can be modified by adding/removing or adjusting options. This array can be modified by adding/removing or adjusting options.
@@ -94,16 +195,13 @@ export default {
### Adding Helpers ### 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 ```js
import { addPlugin, adjustPluginOptions } from 'plugins-manager'; import { addPlugin, adjustPluginOptions } from 'plugins-manager';
const systemSetupFunctions = [ const systemSetupFunctions = [addPlugin(first), addPlugin(second)];
addPlugin({ name: 'first', plugin: first }), const userSetupFunctions = [adjustPluginOptions(first, { my: 'options' })];
addPlugin({ name: 'second', plugin: second }),
];
const userSetupFunctions = [adjustPluginOptions('first', { my: 'options' })];
``` ```
Arrays of functions can by merged like so Arrays of functions can by merged like so
@@ -115,9 +213,9 @@ const finalSetupFunctions = [...systemSetupFunctions, ...userSetupFunctions];
and then converted to the final output. and then converted to the final output.
```js ```js
import { metaPluginsToRollupPlugins } from 'plugins-manager'; import { applyPlugins } from 'plugins-manager';
const plugins = metaPluginsToRollupPlugins(finalSetupFunctions, metaPlugins); const plugins = applyPlugins(finalSetupFunctions, metaPlugins);
``` ```
## Adding a Plugin ## Adding a Plugin
@@ -133,18 +231,27 @@ By default it adds at the bottom.
import json from '@rollup/plugin-json'; import json from '@rollup/plugin-json';
import { addPlugin } from 'plugins-manager'; import { addPlugin } from 'plugins-manager';
const userSetupFunctions = [ const userSetupFunctions = [addPlugin(json, { preferConst: true })];
addPlugin({ name: 'json', plugin: json, options: { preferConst: true } }),
];
``` ```
Example usage: Example usage:
```js ```js
addPlugin({ name: 'json', plugin: json }); // Add at the bottom (default) addPlugin(json); // Add at the bottom (default)
addPlugin({ name: 'json', plugin: json, location: 'top' }); // Add at the top/beginning of the array addPlugin(json, {}, { location: 'top' }); // Add at the top/beginning of the array
addPlugin({ name: 'json', plugin: json, location: 'beep' }); // Add after (default) plugin 'beep' addPlugin(json, {}, { location: beep }); // Add after (default) plugin 'beep'
addPlugin({ name: 'json', plugin: json, location: 'beep', how: 'before' }); // Add before 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 ## Adjusting Plugin Options
@@ -154,12 +261,14 @@ Adjusting options means to either
- flatly merge objects (e.g. only the first level will be preserved) - flatly merge objects (e.g. only the first level will be preserved)
- calling a function to do the merge yourself - calling a function to do the merge yourself
- setting the raw value (if not an object or function) - 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 ```js
import json from '@rollup/plugin-json';
import { adjustPluginOptions } from 'plugins-manager'; import { adjustPluginOptions } from 'plugins-manager';
const userSetupFunctions = [ const userSetupFunctions = [
adjustPluginOptions('json', { preferConst: false, anotherOption: 'format' }), adjustPluginOptions(json, { preferConst: false, anotherOption: 'format' }),
]; ];
``` ```
@@ -167,36 +276,53 @@ Example usage:
```js ```js
// given // given
addPlugin({ addPlugin(json, {
name: 'json', other: {
plugin: json, nested: 'other.nested',
options: { nested2: 'other.nested2',
other: {
nested: 'other.nested',
nested2: 'other.nested2',
},
main: true,
}, },
main: true,
}); });
// merge objects flately // merge objects flately
adjustPluginOptions('json', { other: { nested: '--overwritten--' } }); adjustPluginOptions(json, { other: { nested: '--overwritten--' } });
// resulting options = { other: { nested: '--overwritten--' }, main: true } // resulting options = { other: { nested: '--overwritten--' }, main: true }
// NOTE: nested2 is removed // NOTE: nested2 is removed
// merge via function // 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 } // resulting options = { other: { nested: '--overwritten--', nested2: 'other.nested2' }, main: true }
// merge via function to override full options // merge via function to override full options
adjustPluginOptions('json', config => ({ only: 'this' })); adjustPluginOptions(json, config => ({ only: 'this' }));
// resulting options = { only: 'this' } // resulting options = { only: 'this' }
// setting a raw value // setting a raw value
adjustPluginOptions('json', false); adjustPluginOptions(json, false);
// resulting options = 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 ## Converting metaPlugins to an Actual Plugin
To execute all setup function you can use this little helper 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` Note: if you provide `config.plugins` then it will return that directly ignoring `setupPlugins`
```js ```js
import { metaConfigToRollupConfig } from 'plugins-manager'; import { applyPlugins } from 'plugins-manager';
const finalConfig = metaConfigToRollupConfig(currentConfig, defaultMetaPlugins); const finalConfig = applyPlugins(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,
});
``` ```
Eleventy Eleventy

View File

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

4
netlify.toml Normal file
View File

@@ -0,0 +1,4 @@
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; script-src 'self' www.googletagmanager.com 'sha256-W6Gq+BvrdAAMbF8E7WHA7UPQxuUOfJM8E9mpKD0oihA=' 'sha256-vFU+IJ5dUUukI5Varwy49dN2d89DmFj7UNewqQv88sw='; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' data: fonts.gstatic.com;"

View File

@@ -33,8 +33,8 @@
"setup": "npm run setup:ts-configs && npm run build:packages", "setup": "npm run setup:ts-configs && npm run build:packages",
"setup:patches": "npx patch-package", "setup:patches": "npx patch-package",
"setup:ts-configs": "node scripts/generate-ts-configs.mjs", "setup:ts-configs": "node scripts/generate-ts-configs.mjs",
"prestart": "yarn analyze", "xprestart": "yarn analyze",
"start": "node packages/cli/src/cli.js start", "start": "node --trace-warnings packages/cli/src/cli.js start",
"test": "yarn test:node && yarn test:web", "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", "test:web": "web-test-runner",
@@ -62,7 +62,7 @@
"@typescript-eslint/parser": "^4.13.0", "@typescript-eslint/parser": "^4.13.0",
"@web/test-runner": "^0.12.2", "@web/test-runner": "^0.12.2",
"@web/test-runner-commands": "^0.4.0", "@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", "cem-plugin-readme": "^0.1.3",
"chai": "^4.2.0", "chai": "^4.2.0",
"concurrently": "^5.3.0", "concurrently": "^5.3.0",
@@ -80,6 +80,7 @@
"onchange": "^7.1.0", "onchange": "^7.1.0",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"prettier-plugin-package": "^1.3.0", "prettier-plugin-package": "^1.3.0",
"publish-docs": "^0.1.2",
"puppeteer": "^9.0.0", "puppeteer": "^9.0.0",
"remark-emoji": "^2.1.0", "remark-emoji": "^2.1.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",

View File

@@ -1,5 +1,44 @@
# @rocket/blog # @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 ## 0.3.1
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@rocket/blog", "name": "@rocket/blog",
"version": "0.3.1", "version": "0.4.0",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -38,6 +38,6 @@
"testing" "testing"
], ],
"dependencies": { "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; flex-wrap: wrap;
margin: calc(-1 * var(--gap)) 0 0 calc(-1 * var(--gap)); margin: calc(-1 * var(--gap)) 0 0 calc(-1 * var(--gap));
width: calc(100% + var(--gap)); width: calc(100% + var(--gap));
align-items: flex-start;
} }
.articles article { .articles article {
@@ -32,6 +34,12 @@ body[layout='layout-blog-details'] #sidebar-nav li.anchor a:hover::before {
.articles article h2 { .articles article h2 {
margin: 0; margin: 0;
border: none; border: none;
padding-top: 1rem;
}
.articles article .thumbnail {
display: block;
height: 200px;
} }
.articles article .read { .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) { @media screen and (min-width: 1024px) {
body[layout='layout-blog-details'] #sidebar { body[layout='layout-blog-details'] #sidebar {
display: block; display: block;
} }
} }

View File

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

View File

@@ -3,8 +3,7 @@
{% if post.data.published %} {% if post.data.published %}
<article> <article>
{% if post.data.cover_image %} {% if post.data.cover_image %}
<a href="{{ post.url | url }}"> <a href="{{ post.url | url }}" class="thumbnail" style="background-image: url({{ post.data.cover_image | url }});">
<img src="{{ post.data.cover_image | url }}" alt="">
</a> </a>
{% endif %} {% endif %}
<div class="content"> <div class="content">

View File

@@ -1 +1,9 @@
<link rel="stylesheet" href="{{ '/_assets/rocket-blog.css' | asset | url }}"> <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

@@ -47,6 +47,6 @@ export function rocketBlog({ section = SECTION, postCollection = POST_COLLECTION
return { return {
path: path.resolve(__dirname), path: path.resolve(__dirname),
setupEleventyPlugins: [addPlugin({ name: 'rocket-blog', plugin: eleventyPluginRocketBlog })], setupEleventyPlugins: [addPlugin(eleventyPluginRocketBlog)],
}; };
} }

View File

@@ -1,5 +1,22 @@
# @rocket/building-rollup # @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 ## 0.3.1
### Patch Changes ### Patch Changes

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,73 @@
# @rocket/cli # @rocket/cli
## 0.10.1
### Patch Changes
- 15a82c0: Enable including script files into the simulator via `<script src=".." mdjs-use>`
- 15a82c0: Allow only a limited set of characters for simulator includes `[a-zA-Z0-9\/\-_]`.
Notably, there is no:
- `:` to prevent `http://...` includes
- `.` so filenames as `this.is.my.js` are not supported. Also includes will be without file endings which will be added automatically
## 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 ## 0.9.9
### Patch Changes ### Patch Changes

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "@rocket/cli", "name": "@rocket/cli",
"version": "0.9.9", "version": "0.10.1",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -57,9 +57,9 @@
"dependencies": { "dependencies": {
"@11ty/eleventy": "^0.11.1", "@11ty/eleventy": "^0.11.1",
"@11ty/eleventy-img": "^0.9.0", "@11ty/eleventy-img": "^0.9.0",
"@rocket/building-rollup": "^0.3.1", "@rocket/building-rollup": "^0.4.0",
"@rocket/core": "^0.1.2", "@rocket/core": "^0.1.2",
"@rocket/eleventy-plugin-mdjs-unified": "^0.5.2", "@rocket/eleventy-plugin-mdjs-unified": "^0.6.0",
"@rocket/eleventy-rocket-nav": "^0.3.0", "@rocket/eleventy-rocket-nav": "^0.3.0",
"@rollup/plugin-babel": "^5.2.2", "@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-node-resolve": "^11.0.1", "@rollup/plugin-node-resolve": "^11.0.1",
@@ -72,7 +72,7 @@
"command-line-usage": "^6.1.1", "command-line-usage": "^6.1.1",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"plugins-manager": "^0.2.4", "plugins-manager": "^0.3.0",
"slash": "^3.0.0", "slash": "^3.0.0",
"utf8": "^3.0.0", "utf8": "^3.0.0",
"workbox-window": "^6.1.5" "workbox-window": "^6.1.5"

View File

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

View File

@@ -1,6 +1,7 @@
<html theme="light" platform="web" lang="en"> <html theme="light" platform="web" lang="en">
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<style type="text/css"> <style type="text/css">
body { body {
margin: 0; margin: 0;
@@ -13,19 +14,37 @@
<script type="module"> <script type="module">
import { render } from '@mdjs/mdjs-story'; import { render } from '@mdjs/mdjs-story';
function sanitize(input, type) {
return `${document.location.origin}/${input.match(/[a-zA-Z0-9\/\-_]*/)[0]}.${type}`;
}
async function onHashChange() { async function onHashChange() {
const urlParts = new URLSearchParams(document.location.hash.substr(1)); const urlParts = new URLSearchParams(document.location.hash.substr(1));
if (urlParts.get('stylesheets')) { if (urlParts.get('stylesheets')) {
for (const stylesheet of urlParts.getAll('stylesheets')) { for (const stylesheet of urlParts.getAll('stylesheets')) {
if (!document.querySelector(`link[rel="stylesheet"][href="${stylesheet}"]`)) { const safeStylesheetUrl = sanitize(stylesheet, 'css');
if (!document.querySelector(`link[rel="stylesheet"][href="${safeStylesheetUrl}"]`)) {
const link = document.createElement('link'); const link = document.createElement('link');
link.rel = 'stylesheet'; link.rel = 'stylesheet';
link.href = stylesheet; link.href = safeStylesheetUrl;
document.head.appendChild(link); document.head.appendChild(link);
} }
} }
} }
if (urlParts.get('moduleUrls')) {
for (const moduleUrl of urlParts.getAll('moduleUrls')) {
const safeModuleUrl = sanitize(moduleUrl, 'js');
if (!document.querySelector(`script[type=module][src="${safeModuleUrl}"]`)) {
const script = document.createElement('script');
script.type = 'module';
script.src = safeModuleUrl;
document.head.appendChild(script);
}
}
}
if (urlParts.get('theme')) { if (urlParts.get('theme')) {
document.documentElement.setAttribute('theme', urlParts.get('theme')); document.documentElement.setAttribute('theme', urlParts.get('theme'));
} }
@@ -45,7 +64,8 @@
document.documentElement.removeAttribute('edge-distance'); document.documentElement.removeAttribute('edge-distance');
} }
const mod = await import(urlParts.get('story-file')); const safeStoryUrl = sanitize(urlParts.get('story-file'), 'js');
const mod = await import(safeStoryUrl);
render(mod[urlParts.get('story-key')]({ shadowRoot: document }), document.body); render(mod[urlParts.get('story-key')]({ shadowRoot: document }), document.body);
} }

View File

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

View File

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

View File

@@ -1,11 +1,61 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-ts-comment */
import { startDevServer } from '@web/dev-server'; import { startDevServer } from '@web/dev-server';
import { fromRollup } from '@web/dev-server-rollup'; 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('../types/main').RocketCliOptions} RocketCliOptions */
/** @typedef {import('@web/dev-server').DevServerConfig} DevServerConfig */ /** @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 { export class RocketStart {
static pluginName = 'RocketStart'; static pluginName = 'RocketStart';
commands = ['start']; commands = ['start'];

View File

@@ -174,11 +174,7 @@ export async function normalizeConfig(inConfig) {
config._presetPaths.push(path.resolve(_inputDirCwdRelative)); config._presetPaths.push(path.resolve(_inputDirCwdRelative));
/** @type {MetaPlugin[]} */ /** @type {MetaPlugin[]} */
let pluginsMeta = [ let pluginsMeta = [{ plugin: RocketStart }, { plugin: RocketBuild }, { plugin: RocketLint }];
{ name: 'RocketStart', plugin: RocketStart },
{ name: 'RocketBuild', plugin: RocketBuild },
{ name: 'RocketLint', plugin: RocketLint },
];
if (Array.isArray(config.setupCliPlugins)) { if (Array.isArray(config.setupCliPlugins)) {
for (const setupFn of 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 { getComputedConfig } = require('./computedConfig.cjs');
const { executeSetupFunctions } = require('plugins-manager'); const { executeSetupFunctions } = require('plugins-manager');
function titleMetaPlugin() { class TitleMetaPlugin {
return async data => { static dataName = 'titleMeta';
async execute(data) {
if (data.titleMeta) { if (data.titleMeta) {
return data.titleMeta; return data.titleMeta;
} }
@@ -17,29 +19,35 @@ function titleMetaPlugin() {
return titleMetaFromContent; return titleMetaFromContent;
} }
return {}; return {};
}; }
} }
function titlePlugin() { class TitlePlugin {
return async data => { static dataName = 'title';
async execute(data) {
if (data.title) { if (data.title) {
return data.title; return data.title;
} }
return data.titleMeta?.title; return data.titleMeta?.title;
}; }
} }
function eleventyNavigationPlugin() { class EleventyNavigationPlugin {
return async data => { static dataName = 'eleventyNavigation';
async execute(data) {
if (data.eleventyNavigation) { if (data.eleventyNavigation) {
return data.eleventyNavigation; return data.eleventyNavigation;
} }
return data.titleMeta?.eleventyNavigation; return data.titleMeta?.eleventyNavigation;
}; }
} }
function sectionPlugin() { class SectionPlugin {
return async data => { static dataName = 'section';
async execute(data) {
if (data.section) { if (data.section) {
return data.section; return data.section;
} }
@@ -52,11 +60,17 @@ function sectionPlugin() {
return parts[1]; return parts[1];
} }
} }
}; }
} }
function layoutPlugin({ defaultLayout = 'layout-default' } = {}) { class LayoutPlugin {
return async data => { static dataName = 'layout';
constructor({ defaultLayout = 'layout-default' } = {}) {
this.defaultLayout = defaultLayout;
}
async execute(data) {
if (data.layout) { if (data.layout) {
return data.layout; return data.layout;
} }
@@ -66,22 +80,29 @@ function layoutPlugin({ defaultLayout = 'layout-default' } = {}) {
return 'layout-index'; return 'layout-index';
} }
} }
return defaultLayout; return this.defaultLayout;
}; }
} }
function socialMediaImagePlugin(args = {}) { class SocialMediaImagePlugin {
const { createSocialImage = defaultCreateSocialImage, rocketConfig = {} } = args; static dataName = 'socialMediaImage';
const cleanedUpArgs = { ...args }; constructor(args = {}) {
delete cleanedUpArgs.createSocialImage; 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) { if (data.socialMediaImage) {
return data.socialMediaImage; return data.socialMediaImage;
} }
if (rocketConfig.createSocialMediaImages === false) { if (this.rocketConfig.createSocialMediaImages === false) {
return; return;
} }
@@ -95,15 +116,15 @@ function socialMediaImagePlugin(args = {}) {
const section = data.section ? ' ' + data.section[0].toUpperCase() + data.section.slice(1) : ''; const section = data.section ? ' ' + data.section[0].toUpperCase() + data.section.slice(1) : '';
const footer = `${data.site.name}${section}`; const footer = `${data.site.name}${section}`;
const imgUrl = await createSocialImage({ const imgUrl = await this.createSocialImage({
title, title,
subTitle, subTitle,
footer, footer,
section, section,
...cleanedUpArgs, ...this.cleanedUpArgs,
}); });
return imgUrl; return imgUrl;
}; }
} }
function sortByOrder(a, b) { function sortByOrder(a, b) {
@@ -146,26 +167,31 @@ async function dirToTree(sourcePath, extra = '') {
return sortedTree; return sortedTree;
} }
function joiningBlocksPlugin(rocketConfig) { class JoiningBlocksPlugin {
const { _inputDirCwdRelative } = rocketConfig; static dataName = '_joiningBlocks';
const partialsSource = path.resolve(_inputDirCwdRelative, '_merged_includes');
return async () => { constructor(rocketConfig) {
const joiningBlocks = await dirToTree(partialsSource, '_joiningBlocks'); const { _inputDirCwdRelative } = rocketConfig;
this.partialsSource = path.resolve(_inputDirCwdRelative, '_merged_includes');
}
async execute() {
const joiningBlocks = await dirToTree(this.partialsSource, '_joiningBlocks');
return joiningBlocks; return joiningBlocks;
}; }
} }
function generateEleventyComputed() { function generateEleventyComputed() {
const rocketConfig = getComputedConfig(); const rocketConfig = getComputedConfig();
let metaPlugins = [ let metaPlugins = [
{ name: 'titleMeta', plugin: titleMetaPlugin }, { plugin: TitleMetaPlugin, options: {} },
{ name: 'title', plugin: titlePlugin }, { plugin: TitlePlugin, options: {} },
{ name: 'eleventyNavigation', plugin: eleventyNavigationPlugin }, { plugin: EleventyNavigationPlugin, options: {} },
{ name: 'section', plugin: sectionPlugin }, { plugin: SectionPlugin, options: {} },
{ name: 'socialMediaImage', plugin: socialMediaImagePlugin, options: { rocketConfig } }, { plugin: SocialMediaImagePlugin, options: { rocketConfig } },
{ name: '_joiningBlocks', plugin: joiningBlocksPlugin, options: rocketConfig }, { plugin: JoiningBlocksPlugin, options: rocketConfig },
{ name: 'layout', plugin: layoutPlugin }, { plugin: LayoutPlugin, options: {} },
]; ];
const finalMetaPlugins = executeSetupFunctions( const finalMetaPlugins = executeSetupFunctions(
@@ -176,13 +202,24 @@ function generateEleventyComputed() {
const eleventyComputed = {}; const eleventyComputed = {};
for (const pluginObj of finalMetaPlugins) { for (const pluginObj of finalMetaPlugins) {
if (pluginObj.options) { 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 { } else {
eleventyComputed[pluginObj.name] = pluginObj.plugin(); const inst = new pluginObj.plugin();
eleventyComputed[inst.constructor.dataName] = inst.execute.bind(inst);
} }
} }
return eleventyComputed; return eleventyComputed;
} }
module.exports = { generateEleventyComputed }; module.exports = {
generateEleventyComputed,
LayoutPlugin,
TitleMetaPlugin,
TitlePlugin,
EleventyNavigationPlugin,
SectionPlugin,
SocialMediaImagePlugin,
JoiningBlocksPlugin,
};

View File

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

View File

@@ -91,86 +91,78 @@ export async function readOutput(
return text; 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) { export async function getfixtureExpectedFiles(pathToDir) {
const cwd = path.join(fixtureDir, pathToDir); const cwd = path.join(fixtureDir, pathToDir);
const paths = await globby('**/*', { cwd, absolute: true, dot: true }); const paths = await globby('**/*', { cwd, absolute: true, dot: true });
return paths; 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(); await cli.setup();
cli.config.outputDevDir = path.join(configFileDir, '__output-dev'); cli.config.outputDevDir = path.join(configFileDir, '__output-dev');
cli.config.devServer.open = false; cli.config.devServer.open = false;
cli.config.devServer.port = 8080; cli.config.devServer.port = 8080;
cli.config.watch = false; cli.config.watch = false;
cli.config.outputDir = path.join(configFileDir, '__output'); cli.config.outputDir = path.join(configFileDir, '__output');
await fs.emptyDir(cli.config.outputDevDir);
await fs.emptyDir(cli.config.outputDir);
await cli.run(); 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) { export async function executeBootstrap(pathToDir) {
const configFileDir = path.join(fixtureDir, pathToDir.split('/').join(path.sep)); const configFileDir = path.join(fixtureDir, pathToDir.split('/').join(path.sep));
const cli = new RocketCli({ argv: ['bootstrap'] }); 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 fs.emptyDir(configFileDir);
await execute(cli, configFileDir); await cli.run();
return cli;
}
export async function executeStart(pathToConfig) { return { cli };
const configFile = path.join(fixtureDir, 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 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;
} }
export function trimWhiteSpace(inString) { export function trimWhiteSpace(inString) {

View File

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

View File

@@ -2,13 +2,9 @@ import chai from 'chai';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import chalk from 'chalk'; import chalk from 'chalk';
import { import {
execute,
executeBootstrap, executeBootstrap,
executeBuild,
executeLint,
executeStart,
expectThrowsAsync, expectThrowsAsync,
readBuildOutput,
readStartOutput,
getfixtureExpectedFiles, getfixtureExpectedFiles,
setFixtureDir, setFixtureDir,
} from '@rocket/cli/test-helpers'; } from '@rocket/cli/test-helpers';
@@ -17,7 +13,7 @@ import fs from 'fs-extra';
const { expect } = chai; const { expect } = chai;
describe('RocketCli e2e', () => { describe('RocketCli e2e', () => {
let cli; let cleanupCli;
before(() => { before(() => {
// ignore colors in tests as most CIs won't support it // ignore colors in tests as most CIs won't support it
@@ -26,20 +22,24 @@ describe('RocketCli e2e', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (cli?.cleanup) { if (cleanupCli?.cleanup) {
await cli.cleanup(); await cleanupCli.cleanup();
} }
}); });
it('can add a unified plugin via the config', async () => { it('can add a unified plugin via the config', async () => {
cli = await executeStart('e2e-fixtures/unified-plugin/rocket.config.js'); const { cli, readOutput } = await execute('e2e-fixtures/unified-plugin/rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'index.html'); captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal(`<p>See a 🐶</p>`); expect(indexHtml).to.equal(`<p>See a 🐶</p>`);
}); });
describe('bootstrap command', () => { describe('bootstrap command', () => {
it('can bootstrap a project', async () => { 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')) { for (const p of await getfixtureExpectedFiles('e2e-fixtures/bootstrap/expected')) {
const actual = await fs.readFile( const actual = await fs.readFile(
@@ -54,8 +54,11 @@ describe('RocketCli e2e', () => {
describe('eleventy in config', () => { describe('eleventy in config', () => {
it('can modify eleventy via an elventy function in the config', async () => { it('can modify eleventy via an elventy function in the config', async () => {
cli = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js'); const { cli, readOutput } = await execute('e2e-fixtures/content/eleventy.rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'index.html'); captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
expect(indexHtml).to.equal( expect(indexHtml).to.equal(
['# BEFORE #', '<p>Content inside <code>docs/index.md</code></p>'].join('\n'), ['# 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 () => { it('will throw if you try to set options by returning an object', async () => {
await expectThrowsAsync( 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.*/, errorMatch: /Error in your Eleventy config file.*/,
}, },
@@ -73,13 +77,23 @@ describe('RocketCli e2e', () => {
describe('setupDevAndBuildPlugins in config', () => { describe('setupDevAndBuildPlugins in config', () => {
it('can add a rollup plugin via setupDevAndBuildPlugins for build command', async () => { it('can add a rollup plugin via setupDevAndBuildPlugins for build command', async () => {
cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js'); const { cli, readOutput } = await execute(
const inlineModule = await readBuildOutput(cli, 'e97af63d.js'); '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);'); expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
}); });
it('can add a rollup plugin via setupDevAndBuildPlugins for start command', async () => { 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'); const response = await fetch('http://localhost:8080/test-data.json');
expect(response.ok).to.be.true; // no server error 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 () => { 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 { cli, readOutput } = await execute(
const inlineModule = await readBuildOutput(cli, 'e97af63d.js'); '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);'); expect(inlineModule).to.equal('var a={test:"data"};console.log(a);');
}); });
it('can adjust the inputDir', async () => { 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>'); 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 () => { 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( 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>', '<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 () => { 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( expect(linkHtml).to.equal(
['<p><a href="../">home</a></p>', '<p><a href="/">absolute home</a></p>'].join('\n'), ['<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">'); 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( expect(imageHtml).to.equal(
[ [
'<p>', '<p>',
@@ -147,9 +180,13 @@ describe('RocketCli e2e', () => {
}); });
it('can add a pathPrefix that will be used in the build command', async () => { 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, stripToBody: true,
}); });
expect(linkHtml).to.equal( expect(linkHtml).to.equal(
@@ -157,11 +194,11 @@ describe('RocketCli e2e', () => {
'\n', '\n',
), ),
); );
const assetHtml = await readBuildOutput(cli, 'use-assets/index.html'); const assetHtml = await readOutput('use-assets/index.html');
expect(assetHtml).to.equal( expect(assetHtml).to.equal(
'<html><head><link rel="stylesheet" href="../41297ffa.css">\n\n</head><body>\n\n</body></html>', '<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__.'); imageHtml = imageHtml.replace(/\.\.\/([a-z0-9]+)\./g, '../__HASH__.');
expect(imageHtml).to.equal( expect(imageHtml).to.equal(
[ [
@@ -184,8 +221,63 @@ describe('RocketCli e2e', () => {
}); });
it('smoke test for link checking', async () => { it('smoke test for link checking', async () => {
await expectThrowsAsync(() => executeLint('e2e-fixtures/lint-links/rocket.config.js'), { await expectThrowsAsync(
errorMatch: /Found 1 missing reference targets/, () => 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 chai from 'chai';
import chalk from 'chalk'; import chalk from 'chalk';
import { executeStart, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers'; import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai; const { expect } = chai;
describe('RocketCli images', () => { describe('RocketCli images', () => {
let cli; let cleanupCli;
before(() => { before(() => {
// ignore colors in tests as most CIs won't support it // ignore colors in tests as most CIs won't support it
@@ -14,15 +14,18 @@ describe('RocketCli images', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (cli?.cleanup) { if (cleanupCli?.cleanup) {
await cli.cleanup(); await cleanupCli.cleanup();
} }
}); });
describe('Images', () => { describe('Images', () => {
it('does render content images responsive', async () => { it('does render content images responsive', async () => {
cli = await executeStart('e2e-fixtures/images/rocket.config.js'); const { cli, readOutput } = await execute('e2e-fixtures/images/rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'index.html', { captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -57,7 +60,7 @@ describe('RocketCli images', () => {
].join('\n'), ].join('\n'),
); );
const keepSvgHtml = await readStartOutput(cli, 'ignores/index.html', { const keepSvgHtml = await readOutput('ignores/index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -102,7 +105,7 @@ describe('RocketCli images', () => {
].join('\n'), ].join('\n'),
); );
const tableHtml = await readStartOutput(cli, 'table/index.html', { const tableHtml = await readOutput('table/index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -150,8 +153,12 @@ describe('RocketCli images', () => {
}); });
it('can configure more patterns to ignore', async () => { it('can configure more patterns to ignore', async () => {
cli = await executeStart('e2e-fixtures/images/ignore-more.rocket.config.js'); const { cli, readOutput } = await execute(
const keepSvgHtml = await readStartOutput(cli, 'ignores/index.html', { 'e2e-fixtures/images/ignore-more.rocket.config.js',
{ captureLog: true },
);
cleanupCli = cli;
const keepSvgHtml = await readOutput('ignores/index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -178,8 +185,11 @@ describe('RocketCli images', () => {
}); });
it('renders multiple images in the correct order', async () => { it('renders multiple images in the correct order', async () => {
cli = await executeStart('e2e-fixtures/images/rocket.config.js'); const { cli, readOutput } = await execute('e2e-fixtures/images/rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'two-images/index.html', { captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('two-images/index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -242,8 +252,11 @@ describe('RocketCli images', () => {
}); });
it('can configure those responsive images', async () => { it('can configure those responsive images', async () => {
cli = await executeStart('e2e-fixtures/images/small.rocket.config.js'); const { cli, readOutput } = await execute('e2e-fixtures/images/small.rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'index.html', { captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -280,8 +293,11 @@ describe('RocketCli images', () => {
}); });
it('will only render a figure & figcaption if there is a caption/title', async () => { 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 { cli, readOutput } = await execute('e2e-fixtures/images/small.rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'no-title/index.html', { captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readOutput('no-title/index.html', {
formatHtml: true, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });
@@ -315,8 +331,12 @@ describe('RocketCli images', () => {
}); });
it('will render an img with srcset and sizes if there is only one image format', async () => { 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 {
const indexHtml = await readStartOutput(cli, 'no-title/index.html', { 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, formatHtml: true,
replaceImageHashes: true, replaceImageHashes: true,
}); });

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import chai from 'chai'; import chai from 'chai';
import chalk from 'chalk'; import chalk from 'chalk';
import { executeBuild, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers'; import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai; const { expect } = chai;
@@ -17,7 +17,7 @@ function getServiceWorkerUrl(text) {
} }
describe('RocketCli e2e', () => { describe('RocketCli e2e', () => {
let cli; let cleanupCli;
before(() => { before(() => {
// ignore colors in tests as most CIs won't support it // ignore colors in tests as most CIs won't support it
@@ -26,20 +26,28 @@ describe('RocketCli e2e', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (cli?.cleanup) { if (cleanupCli?.cleanup) {
await cli.cleanup(); await cleanupCli.cleanup();
} }
}); });
it('will add a script to inject the service worker', async () => { it('will add a script to inject the service worker', async () => {
cli = await executeBuild('e2e-fixtures/service-worker/rocket.config.js'); const { cli, readOutput } = await execute('e2e-fixtures/service-worker/rocket.config.js', {
const indexHtml = await readStartOutput(cli, 'index.html'); 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); const indexInject = getInjectServiceWorker(indexHtml);
expect(indexInject).to.equal( expect(indexInject).to.equal(
'<script type="module" inject-service-worker="" src="/_merged_assets/scripts/registerServiceWorker.js"></script>', '<script type="module" inject-service-worker="" src="/_merged_assets/scripts/registerServiceWorker.js"></script>',
); );
expect(getServiceWorkerUrl(indexHtml)).to.equal('/service-worker.js'); 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); const subInject = getInjectServiceWorker(subHtml);
expect(subInject).to.equal( expect(subInject).to.equal(
'<script type="module" inject-service-worker="" src="/_merged_assets/scripts/registerServiceWorker.js"></script>', '<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 ... // 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 () => { it.skip('will add a script to inject the service worker', async () => {
cli = await executeBuild('e2e-fixtures/service-worker/pathPrefix.rocket.config.js'); const { cli, readOutput } = await execute(
const indexHtml = await readStartOutput(cli, 'index.html'); 'e2e-fixtures/service-worker/pathPrefix.rocket.config.js',
{
captureLog: true,
type: 'build',
},
);
cleanupCli = cli;
const indexHtml = await readOutput('index.html');
const indexInject = getInjectServiceWorker(indexHtml); const indexInject = getInjectServiceWorker(indexHtml);
expect(indexInject).to.equal( expect(indexInject).to.equal(
'<script type="module" inject-service-worker="" src="/my-prefix-folder/_merged_assets/scripts/registerServiceWorker.js"></script>', '<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'); 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); const subInject = getInjectServiceWorker(subHtml);
expect(subInject).to.equal( expect(subInject).to.equal(
'<script type="module" inject-service-worker="" src="/my-prefix-folder/_merged_assets/scripts/registerServiceWorker.js"></script>', '<script type="module" inject-service-worker="" src="/my-prefix-folder/_merged_assets/scripts/registerServiceWorker.js"></script>',

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

@@ -1,5 +1,16 @@
# @rocket/eleventy-plugin-mdjs-unified # @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 ## 0.5.2
### Patch Changes ### Patch Changes

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,28 @@
# @rocket/launch # @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 ## 0.5.5
### Patch Changes ### Patch Changes

View File

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

View File

@@ -5,6 +5,7 @@ html {
--primary-color-accent: #cee5f6; --primary-color-accent: #cee5f6;
--primary-text-color: #2c3e50; --primary-text-color: #2c3e50;
--primary-lines-color: #ccc; --primary-lines-color: #ccc;
--primary-text-inverse-color: #eee;
/* Contrast colors */ /* Contrast colors */
--contrast-color-light: #fff; --contrast-color-light: #fff;
@@ -19,6 +20,10 @@ html {
--primary-font-family: 'Open Sans', sans-serif; --primary-font-family: 'Open Sans', sans-serif;
--secondary-font-family: 'Montserrat', sans-serif; --secondary-font-family: 'Montserrat', sans-serif;
--monospace-font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', 'Courier', monospace; --monospace-font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', 'Courier', monospace;
/* controls */
--switch-unselected-color: #808080;
--switch-selected-color: #42b983;
} }
html.dark { html.dark {
@@ -27,6 +32,7 @@ html.dark {
--primary-color-darker: #a22831; --primary-color-darker: #a22831;
--primary-color-accent: #cee5f6; --primary-color-accent: #cee5f6;
--primary-text-color: #eee; --primary-text-color: #eee;
--primary-text-inverse-color: #2c3e50;
/* Contrast colors */ /* Contrast colors */
--contrast-color-light: #fff; --contrast-color-light: #fff;
@@ -39,6 +45,10 @@ html.dark {
/* typography */ /* typography */
--text-color: white; --text-color: white;
/* controls */
--switch-unselected-color: #808080;
--switch-selected-color: #42b983;
/* markdown */ /* markdown */
--markdown-octicon-link: white; --markdown-octicon-link: white;
--markdown-syntax-background-color: #a0a0a0; --markdown-syntax-background-color: #a0a0a0;

View File

@@ -2,12 +2,14 @@ import path from 'path';
import { adjustPluginOptions } from 'plugins-manager'; import { adjustPluginOptions } from 'plugins-manager';
// import { addPlugin } from 'plugins-manager'; // import { addPlugin } from 'plugins-manager';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { LayoutPlugin } from '@rocket/cli';
import htmlHeading from 'rehype-autolink-headings';
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const __dirname = path.dirname(fileURLToPath(import.meta.url));
function addOcticonToHeadlines(plugins) { function addOcticonToHeadlines(plugins) {
return plugins.map(pluginObj => { return plugins.map(pluginObj => {
if (pluginObj.name === 'htmlHeading') { if (pluginObj.plugin === htmlHeading) {
return { return {
...pluginObj, ...pluginObj,
options: { options: {
@@ -50,7 +52,7 @@ export function rocketLaunch() {
path: path.resolve(__dirname), path: path.resolve(__dirname),
setupUnifiedPlugins: [addOcticonToHeadlines], setupUnifiedPlugins: [addOcticonToHeadlines],
setupEleventyComputedConfig: [ setupEleventyComputedConfig: [
adjustPluginOptions('layout', { defaultLayout: 'layout-sidebar' }), adjustPluginOptions(LayoutPlugin, { defaultLayout: 'layout-sidebar' }),
], ],
adjustImagePresets: imagePresets => ({ adjustImagePresets: imagePresets => ({
...imagePresets, ...imagePresets,

View File

@@ -1,11 +1,11 @@
import chai from 'chai'; import chai from 'chai';
import chalk from 'chalk'; import chalk from 'chalk';
import { executeStart, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers'; import { execute, setFixtureDir } from '@rocket/cli/test-helpers';
const { expect } = chai; const { expect } = chai;
describe('RocketLaunch preset', () => { describe('RocketLaunch preset', () => {
let cli; let cleanupCli;
before(() => { before(() => {
// ignore colors in tests as most CIs won't support it // ignore colors in tests as most CIs won't support it
@@ -14,15 +14,18 @@ describe('RocketLaunch preset', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (cli?.cleanup) { if (cleanupCli?.cleanup) {
await cli.cleanup(); await cleanupCli.cleanup();
} }
}); });
it('sets layout-sidebar as default', async () => { it('sets layout-sidebar as default', async () => {
cli = await executeStart('fixtures/layout-sidebar/rocket.config.js'); const { cli, readOutput } = await execute('fixtures/layout-sidebar/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'page/index.html', { const indexHtml = await readOutput('page/index.html', {
stripScripts: true, stripScripts: true,
formatHtml: true, formatHtml: true,
}); });
@@ -244,12 +247,6 @@ describe('RocketLaunch preset', () => {
' </footer>', ' </footer>',
'', '',
' <script type="module" src="/_merged_assets/scripts/init-navigation.js"></script>', ' <script type="module" src="/_merged_assets/scripts/init-navigation.js"></script>',
'',
' <script',
' type="module"',
' inject-service-worker=""',
' src="/_merged_assets/scripts/registerServiceWorker.js"',
' ></script>',
' </body>', ' </body>',
'</html>', '</html>',
].join('\n'), ].join('\n'),
@@ -257,9 +254,12 @@ describe('RocketLaunch preset', () => {
}); });
it('offers a layout-home', async () => { it('offers a layout-home', async () => {
cli = await executeStart('fixtures/layout-home/rocket.config.js'); const { cli, readOutput } = await execute('fixtures/layout-home/rocket.config.js', {
captureLog: true,
});
cleanupCli = cli;
const indexHtml = await readStartOutput(cli, 'index.html', { const indexHtml = await readOutput('index.html', {
stripScripts: true, stripScripts: true,
formatHtml: true, formatHtml: true,
}); });
@@ -508,12 +508,6 @@ describe('RocketLaunch preset', () => {
' </footer>', ' </footer>',
'', '',
' <script type="module" src="/_merged_assets/scripts/init-navigation.js"></script>', ' <script type="module" src="/_merged_assets/scripts/init-navigation.js"></script>',
'',
' <script',
' type="module"',
' inject-service-worker=""',
' src="/_merged_assets/scripts/registerServiceWorker.js"',
' ></script>',
' </body>', ' </body>',
'</html>', '</html>',
].join('\n'), ].join('\n'),

View File

@@ -1,5 +1,70 @@
# Change Log # Change Log
## 0.9.1
### Patch Changes
- 6221e5f: 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()
```
````
- Updated dependencies [5c6b9c9]
- Updated dependencies [6221e5f]
- @mdjs/mdjs-preview@0.5.3
## 0.9.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.8.2 ## 0.8.2
### Patch Changes ### Patch Changes

View File

@@ -1,5 +1,3 @@
# Markdown with JavaScript (mdjs) # Markdown with JavaScript (mdjs)
Combine Markdown with JavaScript [=> See Source <=](../../docs/docs/markdown-javascript/overview.md)
For docs please see our homepage [https://rocket.modern-web.dev/docs/markdown-javascript/overview/](https://rocket.modern-web.dev/docs/markdown-javascript/overview/).

View File

@@ -3,6 +3,7 @@
/** @typedef {import('@mdjs/core/types/code').MdjsProcessPlugin} MdjsProcessPlugin */ /** @typedef {import('@mdjs/core/types/code').MdjsProcessPlugin} MdjsProcessPlugin */
const { mdjsParse } = require('./src/mdjsParse.js'); const { mdjsParse } = require('./src/mdjsParse.js');
const { mdjsSetupCode } = require('./src/mdjsSetupCode.js');
const { mdjsStoryParse } = require('./src/mdjsStoryParse.js'); const { mdjsStoryParse } = require('./src/mdjsStoryParse.js');
const { mdjsDocPage } = require('./src/mdjsDocPage.js'); const { mdjsDocPage } = require('./src/mdjsDocPage.js');
const { mdjsProcess } = require('./src/mdjsProcess.js'); const { mdjsProcess } = require('./src/mdjsProcess.js');
@@ -14,4 +15,5 @@ module.exports = {
mdjsDocPage, mdjsDocPage,
mdjsProcess, mdjsProcess,
isMdjsContent, isMdjsContent,
mdjsSetupCode,
}; };

View File

@@ -1,5 +1,12 @@
import cjsEntrypoint from './index.js'; import cjsEntrypoint from './index.js';
const { mdjsParse, mdjsStoryParse, mdjsDocPage, mdjsProcess, isMdjsContent } = cjsEntrypoint; const {
mdjsParse,
mdjsStoryParse,
mdjsDocPage,
mdjsProcess,
isMdjsContent,
mdjsSetupCode,
} = cjsEntrypoint;
export { mdjsParse, mdjsStoryParse, mdjsDocPage, mdjsProcess, isMdjsContent }; export { mdjsParse, mdjsStoryParse, mdjsDocPage, mdjsProcess, isMdjsContent, mdjsSetupCode };

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mdjs/core", "name": "@mdjs/core",
"version": "0.8.2", "version": "0.9.1",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -21,6 +21,7 @@
} }
}, },
"scripts": { "scripts": {
"prepublishOnly": "publish-docs --github-url https://github.com/modernweb-dev/rocket/ --git-root-dir ../../",
"start": "npm run start:stories", "start": "npm run start:stories",
"start:script": "web-dev-server -c demo/script/server.js --root-dir ../../", "start:script": "web-dev-server -c demo/script/server.js --root-dir ../../",
"start:stories": "web-dev-server -c demo/stories/server.js --root-dir ../../", "start:stories": "web-dev-server -c demo/stories/server.js --root-dir ../../",
@@ -44,12 +45,12 @@
"remark" "remark"
], ],
"dependencies": { "dependencies": {
"@mdjs/mdjs-preview": "^0.5.1", "@mdjs/mdjs-preview": "^0.5.3",
"@mdjs/mdjs-story": "^0.3.0", "@mdjs/mdjs-story": "^0.3.0",
"@types/unist": "^2.0.3", "@types/unist": "^2.0.3",
"es-module-lexer": "^0.3.26", "es-module-lexer": "^0.3.26",
"github-markdown-css": "^4.0.0", "github-markdown-css": "^4.0.0",
"plugins-manager": "^0.2.4", "plugins-manager": "^0.3.0",
"rehype-autolink-headings": "^5.0.1", "rehype-autolink-headings": "^5.0.1",
"rehype-prism-template": "^0.4.1", "rehype-prism-template": "^0.4.1",
"rehype-raw": "^5.0.0", "rehype-raw": "^5.0.0",

View File

@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-ts-comment */
/** @typedef {import('@mdjs/core/types/code').Story} Story */ /** @typedef {import('../types/code').Story} Story */
/** @typedef {import('@mdjs/core/types/code').ParseResult} ParseResult */ /** @typedef {import('../types/code').ParseResult} ParseResult */
/** @typedef {import('@mdjs/core/types/code').ProcessResult} ProcessResult */ /** @typedef {import('../types/code').ProcessResult} ProcessResult */
/** @typedef {import('@mdjs/core/types/code').MdjsProcessPlugin} MdjsProcessPlugin */ /** @typedef {import('../types/code').MdjsProcessPlugin} MdjsProcessPlugin */
const unified = require('unified'); const unified = require('unified');
const markdown = require('remark-parse'); const markdown = require('remark-parse');
@@ -22,23 +22,23 @@ const { mdjsSetupCode } = require('./mdjsSetupCode.js');
/** @type {MdjsProcessPlugin[]} */ /** @type {MdjsProcessPlugin[]} */
const defaultMetaPlugins = [ const defaultMetaPlugins = [
{ name: 'markdown', plugin: markdown }, { plugin: markdown, options: {} },
{ name: 'gfm', plugin: gfm }, { plugin: gfm, options: {} },
{ name: 'mdjsParse', plugin: mdjsParse }, { plugin: mdjsParse, options: {} },
{ name: 'mdjsStoryParse', plugin: mdjsStoryParse }, { plugin: mdjsStoryParse, options: {} },
{ name: 'mdjsSetupCode', plugin: mdjsSetupCode }, { plugin: mdjsSetupCode, options: {} },
// @ts-ignore // @ts-ignore
{ name: 'remark2rehype', plugin: remark2rehype, options: { allowDangerousHtml: true } }, { plugin: remark2rehype, options: { allowDangerousHtml: true } },
// @ts-ignore // @ts-ignore
{ name: 'rehypePrism', plugin: rehypePrism }, { plugin: rehypePrism, options: {} },
// @ts-ignore // @ts-ignore
{ name: 'raw', plugin: raw }, { plugin: raw, options: {} },
// @ts-ignore // @ts-ignore
{ name: 'htmlSlug', plugin: htmlSlug }, { plugin: htmlSlug, options: {} },
// @ts-ignore // @ts-ignore
{ name: 'htmlHeading', plugin: htmlHeading }, { plugin: htmlHeading, options: {} },
// @ts-ignore // @ts-ignore
{ name: 'htmlStringify', plugin: htmlStringify }, { plugin: htmlStringify, options: {} },
]; ];
/** /**
@@ -57,33 +57,8 @@ async function mdjsProcess(mdjs, { setupUnifiedPlugins = [] } = {}) {
const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins); const metaPlugins = executeSetupFunctions(setupUnifiedPlugins, defaultMetaPlugins);
/**
* @param {string} code
*/
async function highlightCode(code) {
// @ts-ignore
const codePlugins = metaPlugins.filter(pluginObj =>
['markdown', 'remark2rehype', 'rehypePrism', 'htmlStringify'].includes(pluginObj.name),
);
const codeParser = unified();
// @ts-ignore
for (const pluginObj of codePlugins) {
codeParser.use(pluginObj.plugin, pluginObj.options);
}
const codeResult = await codeParser.process(code);
return codeResult.contents;
}
// @ts-ignore
for (const pluginObj of metaPlugins) { for (const pluginObj of metaPlugins) {
if (pluginObj.name === 'mdjsSetupCode') { // @ts-ignore
if (pluginObj.options && !pluginObj.options.highlightCode) {
pluginObj.options.highlightCode = highlightCode;
}
if (!pluginObj.options) {
pluginObj.options = { highlightCode };
}
}
parser.use(pluginObj.plugin, pluginObj.options); parser.use(pluginObj.plugin, pluginObj.options);
} }

View File

@@ -77,17 +77,32 @@ function mdjsStoryParse({
const newValue = previewStoryTag(storyData.name); const newValue = previewStoryTag(storyData.name);
if (newValue.includes('[[CODE SLOT]]')) { if (newValue.includes('[[CODE SLOT]]')) {
const tagParts = newValue.split('[[CODE SLOT]]'); const tagParts = newValue.split('[[CODE SLOT]]');
const inside = [node];
let skipAmount = 1;
const next = parent.children[index + 1];
if (next && next.type === 'code' && next.meta === 'story-code') {
inside.push(next);
skipAmount += 1;
const next2 = parent.children[index + 2];
if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
inside.push(next2);
skipAmount += 1;
}
}
node = { node = {
type: 'root', type: 'root',
children: [ children: [
{ type: 'html', value: tagParts[0] }, { type: 'html', value: tagParts[0] },
{ type: 'text', value: '\n\n' }, { type: 'text', value: '\n\n' },
node, ...inside,
{ type: 'text', value: '\n\n' }, { type: 'text', value: '\n\n' },
{ type: 'html', value: tagParts[1] }, { type: 'html', value: tagParts[1] },
], ],
}; };
parent.children.splice(index, 1, node); parent.children.splice(index, skipAmount, node);
} else { } else {
node.type = 'html'; node.type = 'html';
node.value = previewStoryTag(storyData.name); node.value = previewStoryTag(storyData.name);
@@ -115,17 +130,31 @@ function mdjsStoryParse({
const newValue = previewStoryTag(storyData.name); const newValue = previewStoryTag(storyData.name);
if (newValue.includes('[[CODE SLOT]]')) { if (newValue.includes('[[CODE SLOT]]')) {
const tagParts = newValue.split('[[CODE SLOT]]'); const tagParts = newValue.split('[[CODE SLOT]]');
const inside = [node];
let skipAmount = 1;
const next = parent.children[index + 1];
if (next && next.type === 'code' && next.meta === 'story-code') {
inside.push(next);
skipAmount += 1;
const next2 = parent.children[index + 2];
if (next2 && next2.type === 'code' && next2.meta === 'story-code') {
inside.push(next2);
skipAmount += 1;
}
}
node = { node = {
type: 'root', type: 'root',
children: [ children: [
{ type: 'html', value: tagParts[0] }, { type: 'html', value: tagParts[0] },
{ type: 'text', value: '\n\n' }, { type: 'text', value: '\n\n' },
node, ...inside,
{ type: 'text', value: '\n\n' }, { type: 'text', value: '\n\n' },
{ type: 'html', value: tagParts[1] }, { type: 'html', value: tagParts[1] },
], ],
}; };
parent.children.splice(index, 1, node); parent.children.splice(index, skipAmount, node);
} else { } else {
node.type = 'html'; node.type = 'html';
node.value = previewStoryTag(storyData.name); node.value = previewStoryTag(storyData.name);

View File

@@ -3,6 +3,8 @@
const chai = require('chai'); const chai = require('chai');
const { adjustPluginOptions } = require('plugins-manager'); const { adjustPluginOptions } = require('plugins-manager');
const { mdjsProcess } = require('../src/mdjsProcess.js'); const { mdjsProcess } = require('../src/mdjsProcess.js');
const { mdjsSetupCode } = require('../src/mdjsSetupCode.js');
const { mdjsStoryParse } = require('../src/mdjsStoryParse.js');
const { expect } = chai; const { expect } = chai;
@@ -106,7 +108,7 @@ describe('mdjsProcess', () => {
function replaceStoryTag(plugins) { function replaceStoryTag(plugins) {
return plugins.map(pluginObj => { return plugins.map(pluginObj => {
if (pluginObj.name === 'mdjsStoryParse') { if (pluginObj.plugin === mdjsStoryParse) {
return { return {
...pluginObj, ...pluginObj,
options: { options: {
@@ -136,7 +138,7 @@ describe('mdjsProcess', () => {
function replaceStoryTag2(plugins) { function replaceStoryTag2(plugins) {
return plugins.map(pluginObj => { return plugins.map(pluginObj => {
if (pluginObj.name === 'mdjsStoryParse') { if (pluginObj.plugin === mdjsStoryParse) {
return { return {
...pluginObj, ...pluginObj,
options: { options: {
@@ -202,7 +204,7 @@ describe('mdjsProcess', () => {
].join('\n'); ].join('\n');
const result = await mdjsProcess(input, { const result = await mdjsProcess(input, {
setupUnifiedPlugins: [ setupUnifiedPlugins: [
adjustPluginOptions('mdjsSetupCode', { adjustPluginOptions(mdjsSetupCode, {
simulationSettings: { languages: [{ key: 'en', name: 'English' }] }, simulationSettings: { languages: [{ key: 'en', name: 'English' }] },
}), }),
], ],

View File

@@ -114,4 +114,136 @@ describe('mdjsStoryParse', () => {
const result = await parser.process(input); const result = await parser.process(input);
expect(result.contents).to.equal(expected); expect(result.contents).to.equal(expected);
}); });
it('will wrap following story-code blocks', async () => {
const input = [
'```js preview-story',
'export const foo = () => {};',
'```',
'',
'```swift story-code',
'CODE for iOS',
'```',
'',
'```xml story-code',
'CODE for Android',
'```',
].join('\n');
const expected = [
'<mdjs-preview mdjs-story-name="foo">',
'',
'',
'',
'<pre><code class="language-js">export const foo = () => {};',
'</code></pre>',
'<pre><code class="language-swift">CODE for iOS',
'</code></pre>',
'<pre><code class="language-xml">CODE for Android',
'</code></pre>',
'',
'',
'',
'</mdjs-preview>',
'',
].join('\n');
const parser = unified().use(markdown).use(mdjsStoryParse).use(html);
const result = await parser.process(input);
expect(result.contents).to.equal(expected);
});
it('will wrap following story-code blocks also for html stories', async () => {
const input = [
'```html preview-story',
'<my-el></my-el>',
'```',
'',
'```swift story-code',
'CODE for iOS',
'```',
'',
'```xml story-code',
'CODE for Android',
'```',
].join('\n');
const expected = [
'<mdjs-preview mdjs-story-name="HtmlStory0">',
'',
'',
'',
'<pre><code class="language-html">&#x3C;my-el>&#x3C;/my-el>',
'</code></pre>',
'<pre><code class="language-swift">CODE for iOS',
'</code></pre>',
'<pre><code class="language-xml">CODE for Android',
'</code></pre>',
'',
'',
'',
'</mdjs-preview>',
'',
].join('\n');
const parser = unified().use(markdown).use(mdjsStoryParse).use(html);
const result = await parser.process(input);
expect(result.contents).to.equal(expected);
});
it('will wrap only following story-code blocks', async () => {
const input = [
'```js preview-story',
'export const foo = () => {};',
'```',
'```swift story-code',
'CODE for iOS',
'```',
'# hey',
'```swift story-code',
'SHOULD BE OUTSIDE',
'```',
'```js preview-story',
'export const foo2 = () => {};',
'```',
'```xml story-code',
'CODE for Android',
'```',
].join('\n');
const expected = [
'<mdjs-preview mdjs-story-name="foo">',
'',
'',
'',
'<pre><code class="language-js">export const foo = () => {};',
'</code></pre>',
'<pre><code class="language-swift">CODE for iOS',
'</code></pre>',
'',
'',
'',
'</mdjs-preview>',
'<h1>hey</h1>',
'<pre><code class="language-swift">SHOULD BE OUTSIDE',
'</code></pre>',
'<mdjs-preview mdjs-story-name="foo2">',
'',
'',
'',
'<pre><code class="language-js">export const foo2 = () => {};',
'</code></pre>',
'<pre><code class="language-xml">CODE for Android',
'</code></pre>',
'',
'',
'',
'</mdjs-preview>',
'',
].join('\n');
const parser = unified().use(markdown).use(mdjsStoryParse).use(html);
const result = await parser.process(input);
expect(result.contents).to.equal(expected);
});
}); });

View File

@@ -31,4 +31,4 @@ export interface ParseResult {
}; };
} }
export type MdjsProcessPlugin = MetaPlugin<unified.Plugin, unified.Settings>; export type MdjsProcessPlugin = MetaPlugin<unified.Plugin>;

View File

@@ -1,5 +1,78 @@
# @mdjs/mdjs-preview # @mdjs/mdjs-preview
## 0.5.4
### Patch Changes
- 15a82c0: Enable including script files into the simulator via `<script src=".." mdjs-use>`
- 15a82c0: Allow only a limited set of characters for simulator includes `[a-zA-Z0-9\/\-_]`.
Notably, there is no:
- `:` to prevent `http://...` includes
- `.` so filenames as `this.is.my.js` are not supported. Also includes will be without file endings which will be added automatically
## 0.5.3
### Patch Changes
- 5c6b9c9: The Platform and Size controls are now moved above the preview.
For the web platform we added a special "inline" size.
Only when platform=web & size=webInline it will render to dom.
On all other selections it will render the preview via an iframe.
```js
sizes: [
{
key: 'webInline',
name: 'Inline',
platform: 'web',
width: 360,
height: 640,
dpr: 1,
},
{
// ...
},
];
```
- 6221e5f: 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()
```
````
## 0.5.2
### Patch Changes
- 0987a41: - Make [slot="content"] selector more specific
- 0987a41: Fix styling in darkmode
## 0.5.1 ## 0.5.1
### Patch Changes ### Patch Changes

View File

@@ -1,3 +1,3 @@
# Preview element for mdjs # Preview element for mdjs
For docs please see our homepage [https://rocket.modern-web.dev/docs/markdown-javascript/preview/](https://rocket.modern-web.dev/docs/markdown-javascript/preview/). [=> See Source <=](../../docs/docs/markdown-javascript/preview.md)

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mdjs/mdjs-preview", "name": "@mdjs/mdjs-preview",
"version": "0.5.1", "version": "0.5.4",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -20,6 +20,7 @@
"./define": "./src/define/define.js" "./define": "./src/define/define.js"
}, },
"scripts": { "scripts": {
"prepublishOnly": "publish-docs --github-url https://github.com/modernweb-dev/rocket/ --git-root-dir ../../",
"debug": "cd ../../ && npm run debug -- --group mdjs-preview", "debug": "cd ../../ && npm run debug -- --group mdjs-preview",
"test": "npm run test:web", "test": "npm run test:web",
"test:web": "cd ../../ && npm run test:web -- --group mdjs-preview" "test:web": "cd ../../ && npm run test:web -- --group mdjs-preview"

View File

@@ -10,6 +10,16 @@ import {
} from './mdjsViewerSharedStates.js'; } from './mdjsViewerSharedStates.js';
import { addResizeHandler } from './resizeHandler.js'; import { addResizeHandler } from './resizeHandler.js';
/**
* @param {string} input
* @param {'js'|'css'} type
* @returns {string}
*/
function sanitize(input, type) {
const url = new URL(input);
return url.pathname.slice(1, (type.length + 1) * -1);
}
/** /**
* @typedef {object} StoryOptions * @typedef {object} StoryOptions
* @property {HTMLElement | null} StoryOptions.shadowRoot * @property {HTMLElement | null} StoryOptions.shadowRoot
@@ -47,7 +57,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
contentHeight: { type: Number }, contentHeight: { type: Number },
simulatorUrl: { type: String }, simulatorUrl: { type: String },
// page settings // page settings
platform: { type: String }, platform: { type: String, reflect: true },
platforms: { type: Array }, platforms: { type: Array },
size: { type: String }, size: { type: String },
sizes: { type: Array }, sizes: { type: Array },
@@ -107,8 +117,16 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
// { key: 'ios', name: 'iOS' }, // { key: 'ios', name: 'iOS' },
]; ];
this.size = 'webSmall'; this.size = 'webInline';
this.sizes = [ this.sizes = [
{
key: 'webInline',
name: 'Inline',
platform: 'web',
width: 360,
height: 640,
dpr: 1,
},
{ {
key: 'webSmall', key: 'webSmall',
name: 'Small', name: 'Small',
@@ -241,6 +259,10 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
if (this.lightDomRenderTarget && changeProps.has('story')) { if (this.lightDomRenderTarget && changeProps.has('story')) {
render(this.story({ shadowRoot: this }), this.lightDomRenderTarget); render(this.story({ shadowRoot: this }), this.lightDomRenderTarget);
} }
if (changeProps.has('platform') || changeProps.has('size')) {
this.deviceMode = this.platform === 'web' && this.size === 'webInline' ? false : true;
}
} }
disconnectedCallback() { disconnectedCallback() {
@@ -261,9 +283,8 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
if (!mdjsSetupScript) { if (!mdjsSetupScript) {
throw new Error('Could not find a <script type="module" src="..." mdjs-setup></script>'); throw new Error('Could not find a <script type="module" src="..." mdjs-setup></script>');
} }
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set('story-file', mdjsSetupScript.src); params.set('story-file', sanitize(mdjsSetupScript.src, 'js'));
params.set('story-key', this.key); params.set('story-key', this.key);
params.set('theme', this.theme); params.set('theme', this.theme);
params.set('platform', this.platform); params.set('platform', this.platform);
@@ -275,7 +296,16 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
]); ]);
for (const link of links) { for (const link of links) {
if (link.href) { if (link.href) {
params.append('stylesheets', link.href); params.append('stylesheets', sanitize(link.href, 'css'));
}
}
const moduleUrls = /** @type {HTMLScriptElement[]} */ ([
...document.querySelectorAll('script[type=module][mdjs-use]'),
]);
for (const moduleUrl of moduleUrls) {
if (moduleUrl.src) {
params.append('moduleUrls', sanitize(moduleUrl.src, 'js'));
} }
} }
@@ -292,8 +322,15 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
} }
async onCopy() { async onCopy() {
if (this.textContent) { let nodeToConsider = this.children[0];
await navigator.clipboard.writeText(this.textContent.trim()); if (this.platform === 'android') {
nodeToConsider = this.children[1];
}
if (this.platform === 'ios') {
nodeToConsider = this.children[2];
}
if (nodeToConsider && nodeToConsider.textContent) {
await navigator.clipboard.writeText(nodeToConsider.textContent.trim());
this.__copyButtonText = 'Copied ✅'; this.__copyButtonText = 'Copied ✅';
setTimeout(() => { setTimeout(() => {
this.__copyButtonText = 'Copy code'; this.__copyButtonText = 'Copy code';
@@ -304,7 +341,6 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
renderPlatforms() { renderPlatforms() {
if (this.platforms.length) { if (this.platforms.length) {
return html` return html`
<h4>Platform</h4>
<div <div
class="segmented-control" class="segmented-control"
@change=${ @change=${
@@ -337,17 +373,27 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
if (this.platforms.length) { if (this.platforms.length) {
return html` return html`
<div> <div>
<h3>Platform</h3> <h4>Platform</h4>
${this.renderPlatforms()} ${this.renderPlatforms()}
</div> </div>
`; `;
} }
} }
renderSize() {
if (this.sizes.length) {
return html`
<div>
<h4>Size</h4>
${this.renderSizes()}
</div>
`;
}
}
renderSizes() { renderSizes() {
if (this.sizes.length) { if (this.sizes.length) {
return html` return html`
<h4>Size</h4>
<div <div
class="segmented-control" class="segmented-control"
@change=${ @change=${
@@ -380,7 +426,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
return html` return html`
<div> <div>
<h3>Viewport</h3> <h3>Viewport</h3>
${this.renderSizes()} ${this.renderAutoHeight()} ${this.renderAutoHeight()}
</div> </div>
`; `;
} }
@@ -564,6 +610,11 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
render() { render() {
return html` return html`
${this.simulatorUrl
? html`
<div class="platform-size-controls">${this.renderPlatform()} ${this.renderSize()}</div>
`
: ``}
<div id="wrapper"> <div id="wrapper">
<slot name="story"></slot> <slot name="story"></slot>
${this.deviceMode === true ${this.deviceMode === true
@@ -581,19 +632,28 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
: nothing} : nothing}
</div> </div>
<lion-accordion class="options"> <lion-accordion class="options">
${this.deviceMode ${this.simulatorUrl
? html` ? html`
<h3 slot="invoker"> <h3 slot="invoker">
<button>Settings</button> <button>Settings</button>
</h3> </h3>
<div slot="content"> <div slot="content">
${this.deviceMode
? ``
: html`<div>
Note: Additional settings become available when not in web inline mode
</div>`}
<div class="settings-wrapper"> <div class="settings-wrapper">
${this.renderPlatform()} ${this.renderViewport()} ${this.renderVisual()} ${this.deviceMode
${this.renderLocalization()} ${this.renderSyncSettings()} ? html`
${this.renderViewport()} ${this.renderVisual()} ${this.renderLocalization()}
${this.renderSyncSettings()}
`
: html` ${this.renderSyncSettings()} `}
</div> </div>
</div> </div>
` `
: ''} : ``}
<h3 slot="invoker"> <h3 slot="invoker">
<button>Code</button> <button>Code</button>
</h3> </h3>
@@ -608,12 +668,6 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
? html` ? html`
<div class="controls"> <div class="controls">
<a href=${this.iframeUrl} target="_blank">Open simulation in new window</a> <a href=${this.iframeUrl} target="_blank">Open simulation in new window</a>
<button
@click=${() => (this.deviceMode = !this.deviceMode)}
class="simulation-toggle"
>
${this.deviceMode ? html`Disable` : html`Enable`} device simulation
</button>
</div> </div>
` `
: ''} : ''}
@@ -631,6 +685,10 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
display: none; display: none;
} }
:host(:not([device-mode])) #wrapper {
border: 2px solid #4caf50;
}
iframe { iframe {
border: 2px solid #4caf50; border: 2px solid #4caf50;
background: #fff; background: #fff;
@@ -652,7 +710,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
[part='copy-button']:hover { [part='copy-button']:hover {
background-color: var(--primary-color, #3f51b5); background-color: var(--primary-color, #3f51b5);
color: #fff; color: var(--primary-text-inverse-color, #eee);
} }
.switch { .switch {
@@ -668,7 +726,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
[part='switch-button'] { [part='switch-button'] {
display: inline-block; display: inline-block;
width: 44px; width: 44px;
background: #808080; background: var(--switch-unselected-color, #808080);
height: 25px; height: 25px;
border-radius: 15px; border-radius: 15px;
position: relative; position: relative;
@@ -687,7 +745,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
} }
.switch.selected [part='switch-button'] { .switch.selected [part='switch-button'] {
background: var(--primary-color, #008000); background: var(--switch-selected-color, #42b983);
} }
.switch.selected [part='switch-button']::after { .switch.selected [part='switch-button']::after {
@@ -699,7 +757,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
margin: -5px 0 10px 0; margin: -5px 0 10px 0;
text-align: right; text-align: right;
font-size: 12px; font-size: 12px;
color: #333; color: var(--primary-text-color, #2c3e50);
} }
.settings-wrapper { .settings-wrapper {
@@ -725,6 +783,15 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
padding: 15px 0; padding: 15px 0;
} }
.platform-size-controls {
display: flex;
justify-content: flex-start;
}
.platform-size-controls > * {
margin-right: 25px;
}
.controls { .controls {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -748,7 +815,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
.simulation-toggle:hover { .simulation-toggle:hover {
background-color: var(--primary-color, #3f51b5); background-color: var(--primary-color, #3f51b5);
color: #fff; color: var(--primary-text-inverse-color, #eee);
} }
h3[slot='invoker'] button { h3[slot='invoker'] button {
@@ -759,6 +826,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
border: none; border: none;
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
width: 100%; width: 100%;
color: var(--primary-text-color, #2c3e50);
background: none; background: none;
text-align: left; text-align: left;
font-weight: bold; font-weight: bold;
@@ -780,7 +848,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
border-bottom: none; border-bottom: none;
} }
[slot='content'] { .options > [slot='content'] {
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
padding: 10px; padding: 10px;
} }
@@ -820,7 +888,7 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
.segmented-control label.selected span { .segmented-control label.selected span {
background: var(--primary-color, #3f51b5); background: var(--primary-color, #3f51b5);
color: #fff; color: var(--primary-text-inverse-color, #eee);
} }
.segmented-control label:focus-within span { .segmented-control label:focus-within span {
@@ -844,6 +912,21 @@ export class MdJsPreview extends ScopedElementsMixin(LitElement) {
border: 1px solid #333; border: 1px solid #333;
border-radius: 3px; border-radius: 3px;
} }
/** Showing/Hiding additional code blocks **/
::slotted(pre) {
display: none;
}
:host([platform='web']) ::slotted(pre:nth-child(1)) {
display: block;
}
:host([platform='android']) ::slotted(pre:nth-child(2)) {
display: block;
}
:host([platform='ios']) ::slotted(pre:nth-child(3)) {
display: block;
}
`; `;
} }
} }

View File

@@ -28,11 +28,8 @@ describe('mdjs-preview', () => {
expect(preview1.edgeDistance).to.be.true; expect(preview1.edgeDistance).to.be.true;
expect(preview2.edgeDistance).to.be.true; expect(preview2.edgeDistance).to.be.true;
preview1.platform = 'android';
preview1.edgeDistance = false; preview1.edgeDistance = false;
await preview1.updateComplete; await preview1.updateComplete;
expect(preview1.platform).to.equal('android');
expect(preview2.platform).to.equal('android');
expect(preview1.edgeDistance).to.be.false; expect(preview1.edgeDistance).to.be.false;
expect(preview2.edgeDistance).to.be.false; expect(preview2.edgeDistance).to.be.false;
}); });

View File

@@ -1,5 +1,92 @@
# plugins-manager # plugins-manager
## 0.3.0
### Minor Changes
- 70bb7a1: BREAKING CHANGE: `addPlugin` API changed
```diff
- addPlugin({ name: 'my-plugin', plugin: myPlugin, options: { myFlag: true }, location: 'top' });
+ addPlugin(myPlugin, { myFlag: true }, { location: 'top' });
```
This is now 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
```
- 70bb7a1: BREAKING CHANGE: `adjustPluginOptions` API changed
```diff
- adjustPluginOptions('my-plugin', { myFlag: true });
+ adjustPluginOptions(myPlugin, { myFlag: true });
```
This is now 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
```
- 70bb7a1: Add `removePlugin` functionality
```js
export default {
setupPlugins: [removePlugin(json)],
};
```
- 70bb7a1: BREAKING CHANGE: `metaConfigToRollupConfig` has been renamed to `applyPlugins`
```diff
- const finalConfig = metaConfigToRollupConfig(currentConfig, defaultMetaPlugins);
+ const finalConfig = applyPlugins(currentConfig, defaultMetaPlugins);
```
- 70bb7a1: BREAKING CHANGE: `metaConfigToWebDevServerConfig` has been removed
- 70bb7a1: Plugins can now be classes as well. 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
```
## 0.2.4 ## 0.2.4
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,3 @@
# Plugins Manager # Plugins Manager
The Plugins Manager replaces the specific registration/execution (with options) in a given plugin system by an intent to use a plugin (with these options). [=> See Source <=](../../docs/docs/tools/plugins-manager.md)
This allows your users to adjust the options before actually applying the plugins.
For docs please see our homepage [https://rocket.modern-web.dev/docs/plugins-manager/overview/](https://rocket.modern-web.dev/docs/plugins-manager/overview/).

View File

@@ -1,9 +1,11 @@
/** @typedef {import('./types/main').MetaPlugin} MetaPlugin */ /**
/** @typedef {import('./types/main').AddPluginFn} AddPluginFn */ * @template T
/** @typedef {import('./types/main').AddPluginType} AddPluginType */ * @typedef {import('./types/main').MetaPlugin<T>} MetaPlugin
**/
/** @typedef {import('./types/main').Plugin} Plugin */
export { addPlugin } from './src/addPlugin.js'; export { addPlugin } from './src/addPlugin.js';
export { removePlugin } from './src/removePlugin.js';
export { adjustPluginOptions } from './src/adjustPluginOptions.js'; export { adjustPluginOptions } from './src/adjustPluginOptions.js';
export { metaConfigToRollupConfig } from './src/metaConfigToRollupConfig.js'; export { applyPlugins } from './src/applyPlugins.js';
export { metaConfigToWebDevServerConfig } from './src/metaConfigToWebDevServerConfig.js';
export { executeSetupFunctions } from './src/executeSetupFunctions.js'; export { executeSetupFunctions } from './src/executeSetupFunctions.js';

View File

@@ -1,6 +1,6 @@
{ {
"name": "plugins-manager", "name": "plugins-manager",
"version": "0.2.4", "version": "0.3.0",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -23,6 +23,7 @@
}, },
"scripts": { "scripts": {
"build:package": "rimraf dist && esbuild --platform=node --format=cjs --bundle --outfile=dist/index.cjs ./index.js", "build:package": "rimraf dist && esbuild --platform=node --format=cjs --bundle --outfile=dist/index.cjs ./index.js",
"prepublishOnly": "publish-docs --github-url https://github.com/modernweb-dev/rocket/ --git-root-dir ../../",
"test": "mocha --timeout 5000 test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}", "test": "mocha --timeout 5000 test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}",
"test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.{js,cjs}' -- npm test", "test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.{js,cjs}' -- npm test",
"types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/" "types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/"

View File

@@ -1,26 +1,19 @@
/** @typedef {import('../types/main').MetaPlugin} MetaPlugin */
/** /**
* @template {Function} T * @template {import('../types/main').Plugin} T
* @param {import('../types/main').AddPluginOptions<T>} metaPluginAndOptions * @param {T} plugin
* @param {import('../types/main').GetPluginOptions<T>} [options]
* @param {import('../types/main').ManagerOptions} [managerOptions]
*/ */
export function addPlugin(metaPluginAndOptions) { export function addPlugin(plugin, options = {}, { how = 'after', location = 'bottom' } = {}) {
const {
name,
plugin,
options = undefined,
how = 'after',
location = 'bottom',
} = metaPluginAndOptions;
/** /**
* @param {MetaPlugin[]} plugins * @param {import('../types/main').MetaPlugin<T>[]} plugins
*/ */
const addPluginFn = plugins => { const addPluginFn = plugins => {
if (plugins === undefined) { if (plugins === undefined) {
plugins = []; plugins = [];
} }
// only add if name is not already in the meta plugin list // only add if name is not already in the meta plugin list
if (plugins.findIndex(pluginObj => pluginObj.name === name) === -1) { if (plugins.findIndex(pluginObj => pluginObj.plugin === plugin) === -1) {
let index = -1; let index = -1;
let _how = how; let _how = how;
switch (location) { switch (location) {
@@ -33,11 +26,12 @@ export function addPlugin(metaPluginAndOptions) {
_how = 'fixed'; _how = 'fixed';
break; break;
default: default:
index = plugins.findIndex(plugin => plugin.name === location); index = plugins.findIndex(pluginObj => pluginObj.plugin === location);
} }
if (index < 0) { if (index < 0) {
const errorName = location === 'top' || location === 'bottom' ? location : location.name;
throw new Error( throw new Error(
`Could not find a plugin with the name "${location}" to insert "${name}" ${how} it.`, `Could not find a plugin with the name "${errorName}" to insert "${plugin.name}" ${how} it.`,
); );
} }
@@ -46,7 +40,6 @@ export function addPlugin(metaPluginAndOptions) {
} }
plugins.splice(index, 0, { plugins.splice(index, 0, {
name,
plugin, plugin,
options, options,
}); });

View File

@@ -1,4 +1,4 @@
/** @typedef {import('../types/main').MetaPlugin} MetaPlugin */ /** @typedef {import('../types/main').Plugin} Plugin */
/** /**
* @param {any} obj * @param {any} obj
@@ -8,23 +8,35 @@ function isObject(obj) {
} }
/** /**
* @param {string} pluginName * @param {*} x
* @param {any} mergeOptions * @returns {x is function}
*/ */
export function adjustPluginOptions(pluginName, mergeOptions) { function isFunction(x) {
return typeof x === 'function';
}
/**
* @template {import('../types/main').Plugin} T
* @param {T} plugin
* @param {import('../types/main').adjustPluginOptionsOptions<T>} mergeOptions
*/
export function adjustPluginOptions(plugin, mergeOptions) {
/** /**
* @param {MetaPlugin[]} plugins * @template {Function} T
* @param {import('../types/main').MetaPlugin<T>[]} plugins
*/ */
const adjustPluginOptionsFn = plugins => { const adjustPluginOptionsFn = plugins => {
const index = plugins.findIndex(plugin => plugin.name === pluginName); const index = plugins.findIndex(pluginObj => pluginObj.plugin === plugin);
if (index === -1) { if (index === -1) {
throw new Error( throw new Error(
`Could not find a plugin with the name "${pluginName}" to adjust the options.`, `Could not find a plugin with the name "${
plugin.name
}" to adjust it's options with:\n${JSON.stringify(mergeOptions, null, 2)}`,
); );
} }
if (typeof mergeOptions === 'function') { if (isFunction(mergeOptions)) {
plugins[index].options = mergeOptions(plugins[index].options); plugins[index].options = mergeOptions(plugins[index].options);
} else if (isObject(plugins[index].options)) { } else if (isObject(plugins[index].options)) {
plugins[index].options = { ...plugins[index].options, ...mergeOptions }; plugins[index].options = { ...plugins[index].options, ...mergeOptions };

View File

@@ -0,0 +1,46 @@
import { executeSetupFunctions } from './executeSetupFunctions.js';
/** @typedef {import('../types/main').Constructor} Constructor */
/** @typedef {import('../types/main').AnyFn} AnyFn */
/**
* @param {unknown} func
* @returns {boolean}
*/
function isClass(func) {
if (typeof func === 'function' && func.prototype) {
try {
func.arguments && func.caller;
} catch (error) {
return true;
}
}
return false;
}
/**
* @template {import('../types/main').Plugin} T
* @param {any} config
* @param {import('../types/main').MetaPlugin<T>[]} [defaultPlugins]
*/
export function applyPlugins(config, defaultPlugins = []) {
if (config.plugins) {
delete config.setupPlugins;
return config;
}
const _metaPlugins = executeSetupFunctions(config.setupPlugins, [...defaultPlugins]);
const plugins = _metaPlugins.map(pluginObj => {
if (isClass(pluginObj.plugin)) {
const ClassPlugin = /** @type {Constructor} */ (pluginObj.plugin);
return pluginObj.options ? new ClassPlugin(pluginObj.options) : new ClassPlugin();
} else {
const fnPlugin = /** @type {AnyFn} */ (pluginObj.plugin);
return pluginObj.options ? fnPlugin(pluginObj.options) : fnPlugin();
}
});
config.plugins = plugins;
delete config.setupPlugins;
return config;
}

View File

@@ -1,9 +1,8 @@
/** @typedef {import('../types/main').MetaPlugin} MetaPlugin */
/** /**
* @template T
* @param {function[]} setupFunctions * @param {function[]} setupFunctions
* @param {MetaPlugin[]} metaPlugins * @param {import('../types/main').MetaPlugin<T>[]} metaPlugins
* @return {MetaPlugin[]} * @return {import('../types/main').MetaPlugin<T>[]}
*/ */
export function executeSetupFunctions(setupFunctions, metaPlugins = []) { export function executeSetupFunctions(setupFunctions, metaPlugins = []) {
let _metaPlugins = [...metaPlugins]; let _metaPlugins = [...metaPlugins];

View File

@@ -1,27 +0,0 @@
/** @typedef {import('../types/main').MetaPlugin} MetaPlugin */
import { executeSetupFunctions } from 'plugins-manager';
/**
* @param {any} config
* @param {MetaPlugin[]} [metaPlugins]
*/
export function metaConfigToRollupConfig(config, metaPlugins = []) {
if (config.plugins) {
delete config.setupPlugins;
return config;
}
const _metaPlugins = executeSetupFunctions(config.setupPlugins, [...metaPlugins]);
const plugins = _metaPlugins.map(pluginObj => {
if (pluginObj.options) {
return pluginObj.plugin(pluginObj.options);
} else {
return pluginObj.plugin();
}
});
config.plugins = plugins;
delete config.setupPlugins;
return config;
}

View File

@@ -1,53 +0,0 @@
/** @typedef {import('../types/main').MetaPluginWrapable} MetaPluginWrapable */
import { executeSetupFunctions } from 'plugins-manager';
/**
* @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;
}

View File

@@ -0,0 +1,23 @@
/** @typedef {import('../types/main').Plugin} Plugin */
/**
* @param {import('../types/main').Plugin} plugin
*/
export function removePlugin(plugin) {
/**
* @template {Function} T
* @param {import('../types/main').MetaPlugin<T>[]} plugins
*/
const removePluginFn = plugins => {
const index = plugins.findIndex(pluginObj => pluginObj.plugin === plugin);
if (index === -1) {
throw new Error(`Could not find a plugin with the name "${plugin.name}" to remove.`);
}
plugins.splice(index, 1);
return plugins;
};
return removePluginFn;
}

View File

@@ -1,82 +1,93 @@
import chai from 'chai'; import chai from 'chai';
import { addPlugin, metaConfigToRollupConfig } from 'plugins-manager'; import { addPlugin, applyPlugins } from '../index.js';
const { expect } = chai; const { expect } = chai;
describe('addPlugin', () => { describe('addPlugin', () => {
const insertPlugin = (options = 'insert') => `-- ${options}Plugin --`; const insertPlugin = ({ firstName = 'first', lastName = 'last' } = {}) =>
const oneExistingPlugin = [{ name: 'first', plugin: () => 'firstPlugin' }]; `-- ${firstName} ${lastName} Plugin --`;
const firstPlugin = () => 'firstPlugin';
const secondPlugin = () => 'secondPlugin';
const thirdPlugin = () => 'thirdPlugin';
/**
* @template T
* @type {import('../types/main.js').MetaPlugin<T>[]}
*/
const oneExistingPlugin = [{ plugin: firstPlugin, options: {} }];
/**
* @template T
* @type {import('../types/main.js').MetaPlugin<T>[]}
*/
const threeExistingPlugins = [ const threeExistingPlugins = [
{ name: 'first', plugin: () => 'firstPlugin' }, { plugin: firstPlugin, options: {} },
{ name: 'second', plugin: () => 'secondPlugin' }, { plugin: secondPlugin, options: {} },
{ name: 'third', plugin: () => 'thirdPlugin' }, { plugin: thirdPlugin, options: {} },
]; ];
it('adds plugins at the bottom by default', async () => { it('adds plugins at the bottom by default', async () => {
const config = metaConfigToRollupConfig({ const config = applyPlugins({
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin })], setupPlugins: [addPlugin(insertPlugin)],
}); });
expect(config.plugins).to.deep.equal(['-- insertPlugin --']); expect(config.plugins).to.deep.equal(['-- first last Plugin --']);
const config2 = metaConfigToRollupConfig( const config2 = applyPlugins(
{ {
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin })], setupPlugins: [addPlugin(insertPlugin)],
}, },
oneExistingPlugin, oneExistingPlugin,
); );
expect(config2.plugins).to.deep.equal(['firstPlugin', '-- insertPlugin --']); expect(config2.plugins).to.deep.equal(['firstPlugin', '-- first last Plugin --']);
}); });
it('can add at the top', async () => { it('can add at the top', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin, location: 'top' })], setupPlugins: [addPlugin(insertPlugin, undefined, { location: 'top' })],
}, },
oneExistingPlugin, oneExistingPlugin,
); );
expect(config.plugins).to.deep.equal(['-- insertPlugin --', 'firstPlugin']); expect(config.plugins).to.deep.equal(['-- first last Plugin --', 'firstPlugin']);
}); });
it('handles inserting "before" the 0 index ', async () => { it('handles inserting "before" the 0 index ', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [ setupPlugins: [addPlugin(insertPlugin, undefined, { location: 'top', how: 'before' })],
addPlugin({ name: 'insert', plugin: insertPlugin, location: 'top', how: 'before' }),
],
}, },
oneExistingPlugin, oneExistingPlugin,
); );
expect(config.plugins).to.deep.equal(['-- insertPlugin --', 'firstPlugin']); expect(config.plugins).to.deep.equal(['-- first last Plugin --', 'firstPlugin']);
}); });
it('adds after a given location by default', async () => { it('adds after a given location by default', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin, location: 'second' })], setupPlugins: [addPlugin(insertPlugin, undefined, { location: secondPlugin })],
}, },
threeExistingPlugins, threeExistingPlugins,
); );
expect(config.plugins).to.deep.equal([ expect(config.plugins).to.deep.equal([
'firstPlugin', 'firstPlugin',
'secondPlugin', 'secondPlugin',
'-- insertPlugin --', '-- first last Plugin --',
'thirdPlugin', 'thirdPlugin',
]); ]);
}); });
it('can adds before a given location', async () => { it('can adds before a given location', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [ setupPlugins: [
addPlugin({ name: 'insert', plugin: insertPlugin, location: 'second', how: 'before' }), addPlugin(insertPlugin, undefined, { location: secondPlugin, how: 'before' }),
], ],
}, },
threeExistingPlugins, threeExistingPlugins,
); );
expect(config.plugins).to.deep.equal([ expect(config.plugins).to.deep.equal([
'firstPlugin', 'firstPlugin',
'-- insertPlugin --', '-- first last Plugin --',
'secondPlugin', 'secondPlugin',
'thirdPlugin', 'thirdPlugin',
]); ]);
@@ -84,16 +95,18 @@ describe('addPlugin', () => {
it('throws if given location does not exist', async () => { it('throws if given location does not exist', async () => {
expect(() => { expect(() => {
metaConfigToRollupConfig({ applyPlugins({
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin, location: 'not-found' })], setupPlugins: [addPlugin(insertPlugin, undefined, { location: firstPlugin })],
}); });
}).to.throw('Could not find a plugin with the name "not-found" to insert "insert" after it.'); }).to.throw(
'Could not find a plugin with the name "firstPlugin" to insert "insertPlugin" after it.',
);
}); });
it('accepts options', async () => { it('accepts options', async () => {
const config = metaConfigToRollupConfig({ const config = applyPlugins({
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin, options: 'extra' })], setupPlugins: [addPlugin(insertPlugin, { firstName: 'newFirst' })],
}); });
expect(config.plugins).to.deep.equal(['-- extraPlugin --']); expect(config.plugins).to.deep.equal(['-- newFirst last Plugin --']);
}); });
}); });

View File

@@ -1,33 +1,43 @@
// @ts-check
import chai from 'chai'; import chai from 'chai';
import { adjustPluginOptions, metaConfigToRollupConfig } from 'plugins-manager'; import { adjustPluginOptions, applyPlugins } from '../index.js';
const { expect } = chai; const { expect } = chai;
describe('adjustPluginOptions', () => { describe('adjustPluginOptions', () => {
const firstPlugin = ({ flag = 'default-flag' } = {}) => `firstPlugin-${flag}`;
/**
* @param {object} options
* @param {object} [options.other]
* @param {string} [options.other.nested]
* @param {string} [options.other.nested2]
* @returns
*/
const secondPlugin = ({ other = { nested: 'other.nested', nested2: 'other.nested2' } } = {}) =>
`secondPlugin-${other.nested}-${other.nested2}`;
const thirdPlugin = ({ name = 'name' }) => `thirdPlugin-${name}`;
const defaultCurrentMetaPlugins = [ const defaultCurrentMetaPlugins = [
{ plugin: firstPlugin, options: { flag: 'firstSettings' } },
{ {
name: 'first', plugin: secondPlugin,
plugin: options => `firstPlugin-${options.flag}`,
options: { flag: 'firstSettings' },
},
{
name: 'second',
plugin: options => `secondPlugin-${options.other.nested}-${options.other.nested2}`,
options: { other: { nested: 'other.nested', nested2: 'other.nested2' } }, options: { other: { nested: 'other.nested', nested2: 'other.nested2' } },
}, },
{ name: 'third', plugin: options => `thirdPlugin-${options}`, options: 'aString' }, { plugin: thirdPlugin, options: { name: 'name' } },
]; ];
function newCurrentMetaPlugins() { function newCurrentMetaPlugins() {
return defaultCurrentMetaPlugins.map(obj => ({ ...obj })); return defaultCurrentMetaPlugins.map(obj => ({ ...obj }));
} }
it('will merge options objects (flatly)', async () => { it('will merge options objects (flatly)', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [ setupPlugins: [
adjustPluginOptions('first', { flag: '#mod#FirstSettings' }), adjustPluginOptions(firstPlugin, { flag: '#mod#FirstSettings' }),
adjustPluginOptions('second', { other: { nested: '#mod#other.nested' } }), adjustPluginOptions(secondPlugin, { other: { nested: '#mod#other.nested' } }),
], ],
}, },
newCurrentMetaPlugins(), newCurrentMetaPlugins(),
@@ -35,14 +45,14 @@ describe('adjustPluginOptions', () => {
expect(config.plugins).to.deep.equal([ expect(config.plugins).to.deep.equal([
'firstPlugin-#mod#FirstSettings', 'firstPlugin-#mod#FirstSettings',
'secondPlugin-#mod#other.nested-undefined', 'secondPlugin-#mod#other.nested-undefined',
'thirdPlugin-aString', 'thirdPlugin-name',
]); ]);
}); });
it('will override non object settings', async () => { it('will override non object settings', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [adjustPluginOptions('third', '#mod#aString')], setupPlugins: [adjustPluginOptions(thirdPlugin, { name: '#mod#aString' })],
}, },
newCurrentMetaPlugins(), newCurrentMetaPlugins(),
); );
@@ -54,11 +64,11 @@ describe('adjustPluginOptions', () => {
}); });
it('accepts a function as a setting to manually merge objects', async () => { it('accepts a function as a setting to manually merge objects', async () => {
const config = metaConfigToRollupConfig( const config = applyPlugins(
{ {
setupPlugins: [ setupPlugins: [
adjustPluginOptions('second', config => ({ adjustPluginOptions(secondPlugin, config => ({
other: { ...config.other, nested: '#mod#other.nested' }, other: { ...config?.other, nested: '#mod#other.nested' },
})), })),
], ],
}, },
@@ -67,15 +77,76 @@ describe('adjustPluginOptions', () => {
expect(config.plugins).to.deep.equal([ expect(config.plugins).to.deep.equal([
'firstPlugin-firstSettings', 'firstPlugin-firstSettings',
'secondPlugin-#mod#other.nested-other.nested2', 'secondPlugin-#mod#other.nested-other.nested2',
'thirdPlugin-aString', 'thirdPlugin-name',
]); ]);
}); });
it('throws if given location does not exist', async () => { it('throws if given plugin does not exist', async () => {
expect(() => { expect(() => {
metaConfigToRollupConfig({ applyPlugins({
setupPlugins: [adjustPluginOptions('not-found', '#mod#aString')], setupPlugins: [adjustPluginOptions(firstPlugin, { flag: 'newFlag' })],
}); });
}).to.throw('Could not find a plugin with the name "not-found" to adjust the options.'); }).to.throw(
[
'Could not find a plugin with the name "firstPlugin" to adjust it\'s options with:',
'{',
' "flag": "newFlag"',
'}',
].join('\n'),
);
});
it('works with classes', async () => {
class FirstClass {
constructor({ firstName = 'initial-first' } = {}) {
this.options = { firstName };
}
render() {
return `[[ firstName: ${this.options.firstName} ]]`;
}
}
/**
* @typedef {object} SecondClassOptions
* @property {string} lastName
*/
class SecondClass {
/** @type {SecondClassOptions} */
options = {
lastName: 'initial-second',
};
/**
* @param {Partial<SecondClassOptions>} options
*/
constructor(options = {}) {
this.options = { ...this.options, ...options };
}
render() {
return `[[ lastName: ${this.options.lastName} ]]`;
}
}
const config = applyPlugins(
{
setupPlugins: [
adjustPluginOptions(SecondClass, { lastName: 'set-via-adjustPluginOptions' }),
],
},
[
{ plugin: FirstClass, options: {} },
{ plugin: SecondClass, options: {} },
],
);
expect(
config.plugins.map(/** @param {FirstClass | SecondClass} cls */ cls => cls.render()),
).to.deep.equal([
'[[ firstName: initial-first ]]',
'[[ lastName: set-via-adjustPluginOptions ]]',
]);
}); });
}); });

View File

@@ -0,0 +1,92 @@
import chai from 'chai';
import { applyPlugins, addPlugin } from '../index.js';
const { expect } = chai;
describe('applyPlugins', () => {
const insertPlugin = () => `-- insertPlugin --`;
/**
* @template T
* @type {import('../types/main.js').MetaPlugin<T>[]}
*/
const oneExistingPlugin = [{ plugin: () => 'firstPlugin', options: {} }];
/**
* @template T
* @type {import('../types/main.js').MetaPlugin<T>[]}
*/
const threeExistingPlugin = [
{ plugin: () => 'firstPlugin', options: {} },
{ plugin: () => 'secondPlugin', options: {} },
{ plugin: () => 'thirdPlugin', options: {} },
];
it('converts meta config by executing the plugins and assigning it to the config', async () => {
const config = applyPlugins({}, threeExistingPlugin);
expect(config.plugins).to.deep.equal(['firstPlugin', 'secondPlugin', 'thirdPlugin']);
});
it('incorporates "setupPlugin" functions in the config & removes "setupPlugins"', async () => {
const config = applyPlugins(
{
setupPlugins: [addPlugin(insertPlugin)],
},
oneExistingPlugin,
);
expect(config.plugins).to.deep.equal(['firstPlugin', '-- insertPlugin --']);
expect(config.setupPlugins).to.be.undefined;
});
it('a provided plugins property will always win even if it is an empty array', async () => {
const config = applyPlugins({
setupPlugins: [addPlugin(insertPlugin)],
plugins: [],
});
expect(config.plugins).to.deep.equal([]);
expect(config.setupPlugins).to.be.undefined;
});
it('prefers a user set config.plugins', async () => {
const config = applyPlugins(
{
setupPlugins: [addPlugin(insertPlugin)],
plugins: ['user-set'],
},
threeExistingPlugin,
);
expect(config.plugins).to.deep.equal(['user-set']);
expect(config.setupPlugins).to.be.undefined;
});
it('works with classes', async () => {
class FirstClass {
constructor({ firstName = 'initial-first' } = {}) {
this.options = { firstName };
}
render() {
return `[[ firstName: ${this.options.firstName} ]]`;
}
}
class SecondClass {
constructor({ lastName = 'initial-second' } = {}) {
this.options = { lastName };
}
render() {
return `[[ lastName: ${this.options.lastName} ]]`;
}
}
const config = applyPlugins({
setupPlugins: [
addPlugin(FirstClass),
addPlugin(SecondClass, { lastName: 'set-via-addPlugin' }),
],
});
expect(
config.plugins.map(/** @param {FirstClass | SecondClass} cls */ cls => cls.render()),
).to.deep.equal(['[[ firstName: initial-first ]]', '[[ lastName: set-via-addPlugin ]]']);
});
});

View File

@@ -1,6 +1,6 @@
import chai from 'chai'; import chai from 'chai';
import { executeSetupFunctions, addPlugin } from 'plugins-manager'; import { executeSetupFunctions, addPlugin } from '../index.js';
const { expect } = chai; const { expect } = chai;
@@ -9,27 +9,28 @@ describe('executeSetupFunctions', () => {
const secondPlugin = () => 'secondPlugin'; const secondPlugin = () => 'secondPlugin';
const thirdPlugin = () => 'thirdPlugin'; const thirdPlugin = () => 'thirdPlugin';
/**
* @template T
* @type {import('../types/main.js').MetaPlugin<T>[]}
*/
const threeExistingPlugin = [ const threeExistingPlugin = [
{ name: 'first', plugin: firstPlugin }, { plugin: firstPlugin, options: {} },
{ name: 'second', plugin: secondPlugin }, { plugin: secondPlugin, options: {} },
{ name: 'third', plugin: thirdPlugin }, { plugin: thirdPlugin, options: {} },
]; ];
it('executes and returns a new array not adjusting the original', async () => { it('executes and returns a new array not adjusting the original', async () => {
const metaPlugins = executeSetupFunctions( const metaPlugins = executeSetupFunctions(
[ [addPlugin(() => 'a'), addPlugin(() => 'b')],
addPlugin({ name: 'add-a', plugin: () => 'a' }),
addPlugin({ name: 'add-b', plugin: () => 'b' }),
],
threeExistingPlugin, threeExistingPlugin,
); );
expect(metaPlugins.length).to.equal(5); expect(metaPlugins.length).to.equal(5);
// does not change original array // does not change original array
expect(threeExistingPlugin).to.deep.equal([ expect(threeExistingPlugin).to.deep.equal([
{ name: 'first', plugin: firstPlugin }, { plugin: firstPlugin, options: {} },
{ name: 'second', plugin: secondPlugin }, { plugin: secondPlugin, options: {} },
{ name: 'third', plugin: thirdPlugin }, { plugin: thirdPlugin, options: {} },
]); ]);
}); });
}); });

View File

@@ -1,43 +0,0 @@
import chai from 'chai';
import { metaConfigToRollupConfig, addPlugin } from 'plugins-manager';
const { expect } = chai;
describe('metaConfigToRollupConfig', () => {
const insertPlugin = () => `-- insertPlugin --`;
const oneExistingPlugin = [{ name: 'first', plugin: () => 'firstPlugin' }];
const threeExistingPlugin = [
{ name: 'first', plugin: () => 'firstPlugin' },
{ name: 'second', plugin: () => 'secondPlugin' },
{ name: 'third', plugin: () => 'thirdPlugin' },
];
it('converts meta config by executing the plugins and assigning it to the config', async () => {
const config = metaConfigToRollupConfig({}, threeExistingPlugin);
expect(config.plugins).to.deep.equal(['firstPlugin', 'secondPlugin', 'thirdPlugin']);
});
it('incorporates "setupPlugin" functions in the config & removes "setupPlugins"', async () => {
const config = metaConfigToRollupConfig(
{
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin })],
},
oneExistingPlugin,
);
expect(config.plugins).to.deep.equal(['firstPlugin', '-- insertPlugin --']);
expect(config.setupPlugins).to.be.undefined;
});
it('prefers a user set config.plugins', async () => {
const config = metaConfigToRollupConfig(
{
setupPlugins: [addPlugin({ name: 'insert', plugin: insertPlugin })],
plugins: ['user-set'],
},
threeExistingPlugin,
);
expect(config.plugins).to.deep.equal(['user-set']);
expect(config.setupPlugins).to.be.undefined;
});
});

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