Compare commits

...

4 Commits

Author SHA1 Message Date
github-actions[bot]
98d6aad12a Version Packages 2021-02-05 13:30:10 +01:00
Thomas Allmer
ee6b404aaa fix: mdjs element pass on shadowRoot to story function 2021-02-05 13:26:42 +01:00
Thomas Allmer
8ba8939c67 chore: use test-helper everywhere 2021-02-04 20:48:08 +01:00
Thomas Allmer
8e095b792e fix(cli): watching user preset files to trigger updates automatically 2021-02-04 19:56:56 +01:00
13 changed files with 131 additions and 205 deletions

View File

@@ -1,5 +1,11 @@
# @rocket/cli # @rocket/cli
## 0.5.2
### Patch Changes
- 8e095b7: Watching `_assets`, `_data`, `_includes` for changes to trigger updated automatically
## 0.5.1 ## 0.5.1
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@rocket/cli", "name": "@rocket/cli",
"version": "0.5.1", "version": "0.5.2",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

View File

@@ -33,6 +33,35 @@ export class RocketEleventy extends Eleventy {
await super.write(); await super.write();
await this.__rocketCli.update(); await this.__rocketCli.update();
} }
// forks it so we can watch for changes but don't include them while building
getChokidarConfig() {
let ignores = this.eleventyFiles.getGlobWatcherIgnores();
const keepWatching = [
path.join(this.__rocketCli.config._inputDirCwdRelative, '_assets', '**'),
path.join(this.__rocketCli.config._inputDirCwdRelative, '_data', '**'),
path.join(this.__rocketCli.config._inputDirCwdRelative, '_includes', '**'),
];
ignores = ignores.filter(ignore => !keepWatching.includes(ignore));
// debug("Ignoring watcher changes to: %o", ignores);
let configOptions = this.config.chokidarConfig;
// cant override these yet
// TODO maybe if array, merge the array?
delete configOptions.ignored;
return Object.assign(
{
ignored: ignores,
ignoreInitial: true,
// also interesting: awaitWriteFinish
},
configOptions,
);
}
} }
export class RocketCli { export class RocketCli {

View File

@@ -55,6 +55,10 @@ export async function readOutput(
type = 'build', type = 'build',
} = {}, } = {},
) { ) {
if (!cli || !cli.config) {
throw new Error(`No valid cli provided to readOutput - you passed a ${typeof cli}: ${cli}`);
}
const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir; const outputDir = type === 'build' ? cli.config.outputDir : cli.config.outputDevDir;
let text = await fs.promises.readFile(path.join(outputDir, fileName)); let text = await fs.promises.readFile(path.join(outputDir, fileName));
text = text.toString(); text = text.toString();
@@ -116,6 +120,7 @@ export async function execute(cli, configFileDir) {
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.watch = false; cli.config.watch = false;
cli.config.outputDir = path.join(configFileDir, '__output'); cli.config.outputDir = path.join(configFileDir, '__output');
await cli.run(); await cli.run();

View File

@@ -4,7 +4,6 @@ import {
executeBuild, executeBuild,
executeStart, executeStart,
readBuildOutput, readBuildOutput,
readOutput,
readStartOutput, readStartOutput,
setFixtureDir, setFixtureDir,
} from '@rocket/cli/test-helpers'; } from '@rocket/cli/test-helpers';
@@ -29,37 +28,27 @@ describe('RocketCli computedConfig', () => {
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'); cli = await executeStart('computed-config-fixtures/headlines/rocket.config.js');
const indexHtml = await readOutput(cli, 'index.html', { const indexHtml = await readStartOutput(cli, 'index.html');
type: 'start',
});
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 readOutput(cli, 'sub/index.html', { const subHtml = await readStartOutput(cli, 'sub/index.html');
type: 'start',
});
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 readOutput(cli, 'sub/subsub/index.html', { const subSubHtml = await readStartOutput(cli, 'sub/subsub/index.html');
type: 'start',
});
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 readOutput(cli, 'sub2/index.html', { const sub2Html = await readStartOutput(cli, 'sub2/index.html');
type: 'start',
});
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 readOutput(cli, 'with-data/index.html', { const withDataHtml = await readStartOutput(cli, 'with-data/index.html');
type: 'start',
});
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;
@@ -199,9 +188,7 @@ 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'); cli = await executeStart('computed-config-fixtures/setup/addPlugin.rocket.config.js');
const indexHtml = await readOutput(cli, 'index.html', { const indexHtml = await readStartOutput(cli, 'index.html');
type: 'start',
});
expect(indexHtml).to.equal('test-value'); expect(indexHtml).to.equal('test-value');
}); });

View File

@@ -1,86 +1,25 @@
import chai from 'chai'; import chai from 'chai';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { RocketCli } from '../src/RocketCli.js';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs-extra';
import chalk from 'chalk'; import chalk from 'chalk';
import {
const __dirname = path.dirname(fileURLToPath(import.meta.url)); executeBuild,
executeLint,
executeStart,
expectThrowsAsync,
readBuildOutput,
readStartOutput,
setFixtureDir,
} from '@rocket/cli/test-helpers';
const { expect } = chai; const { expect } = chai;
/**
* @param {function} method
* @param {string} errorMessage
*/
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);
}
}
describe('RocketCli e2e', () => { describe('RocketCli e2e', () => {
let cli; let cli;
async function readOutput(
fileName,
{
stripServiceWorker = false,
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 (stripServiceWorker) {
const scriptOpenTagEnd = text.indexOf('<script inject-service-worker');
const scriptCloseTagStart = text.indexOf('</script>', scriptOpenTagEnd) + 9;
text = text.substring(0, scriptOpenTagEnd) + text.substring(scriptCloseTagStart);
}
if (stripStartEndWhitespace) {
text = text.trim();
}
return text;
}
async function execute() {
await cli.setup();
cli.config.outputDevDir = path.join(__dirname, 'e2e-fixtures', '__output-dev');
cli.config.devServer.open = false;
cli.config.devServer.port = 8080;
cli.config.watch = false;
cli.config.outputDir = path.join(__dirname, 'e2e-fixtures', '__output');
await cli.run();
}
async function executeLint(pathToConfig) {
cli = new RocketCli({
argv: ['lint', '--config-file', path.join(__dirname, pathToConfig.split('/').join(path.sep))],
});
await execute();
}
before(() => { before(() => {
// ignore colors in tests as most CIs won't support it // ignore colors in tests as most CIs won't support it
chalk.level = 0; chalk.level = 0;
setFixtureDir(import.meta.url);
}); });
afterEach(async () => { afterEach(async () => {
@@ -90,79 +29,40 @@ describe('RocketCli e2e', () => {
}); });
it('can add a unified plugin via the config', async () => { it('can add a unified plugin via the config', async () => {
cli = new RocketCli({ cli = await executeStart('e2e-fixtures/unified-plugin/rocket.config.js');
argv: [ const indexHtml = await readStartOutput(cli, 'index.html');
'build',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'unified-plugin', 'rocket.config.js'),
],
});
await execute();
const indexHtml = await readOutput('index.html', {
stripServiceWorker: true,
stripToBody: true,
});
expect(indexHtml).to.equal('<p>See a 🐶</p>'); expect(indexHtml).to.equal('<p>See a 🐶</p>');
}); });
describe('eleventy in config', () => { describe('eleventy in config', () => {
// TODO: find out while this has a side effect and breaks other tests // TODO: find out while this has a side effect and breaks other tests
it.skip('can modify eleventy via an elventy function in the config', async () => { it.skip('can modify eleventy via an elventy function in the config', async () => {
cli = new RocketCli({ cli = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js');
argv: [ const indexHtml = await readStartOutput(cli, 'index.html');
'start',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'content', 'eleventy.rocket.config.js'),
],
});
await execute();
const indexHtml = await readOutput('index.html', {
type: 'start',
});
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'),
); );
}); });
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 () => {
cli = new RocketCli({ await expectThrowsAsync(
argv: [ () => executeStart('e2e-fixtures/content/eleventy-return.rocket.config.js'),
'start', {
'--config-file', errorMatch: /Error in your Eleventy config file.*/,
path.join(__dirname, 'e2e-fixtures', 'content', 'eleventy-return.rocket.config.js'), },
], );
});
await expectThrowsAsync(() => execute(), {
errorMatch: /Error in your Eleventy config file.*/,
});
}); });
}); });
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 = new RocketCli({ cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js');
argv: [ const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
'build',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'rollup-plugin', 'devbuild.rocket.config.js'),
],
});
await execute();
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 = new RocketCli({ cli = await executeStart('e2e-fixtures/rollup-plugin/devbuild.rocket.config.js');
argv: [
'start',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'rollup-plugin', 'devbuild.rocket.config.js'),
],
});
await execute();
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
@@ -173,88 +73,45 @@ 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 = new RocketCli({ cli = await executeBuild('e2e-fixtures/rollup-plugin/devbuild-build.rocket.config.js');
argv: [ const inlineModule = await readBuildOutput(cli, 'e97af63d.js');
'build',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'rollup-plugin', 'devbuild-build.rocket.config.js'),
],
});
await execute();
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);');
const swCode = await readOutput('my-service-worker.js'); const swCode = await readBuildOutput(cli, 'my-service-worker.js');
expect(swCode).to.not.be.undefined; expect(swCode).to.not.be.undefined;
}); });
it('can adjust the inputDir', async () => { it('can adjust the inputDir', async () => {
cli = new RocketCli({ cli = await executeStart('e2e-fixtures/change-input-dir/rocket.config.js');
argv: [
'start',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'change-input-dir', 'rocket.config.js'),
],
});
await execute();
const indexHtml = await readOutput('index.html', { const indexHtml = await readStartOutput(cli, 'index.html');
type: 'start',
});
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 = new RocketCli({ cli = await executeStart('e2e-fixtures/rocket-config-in-template/rocket.config.js');
argv: [
'start',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'rocket-config-in-template', 'rocket.config.js'),
],
});
await execute();
const indexHtml = await readOutput('index.html', { const indexHtml = await readStartOutput(cli, 'index.html');
type: 'start',
});
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 = new RocketCli({ cli = await executeStart('e2e-fixtures/content/pathPrefix.rocket.config.js');
argv: [
'start',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'content', 'pathPrefix.rocket.config.js'),
],
});
await execute();
const linkHtml = await readOutput('link/index.html', { const linkHtml = await readStartOutput(cli, 'link/index.html');
type: 'start',
});
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 readOutput('use-assets/index.html', { const assetHtml = await readStartOutput(cli, 'use-assets/index.html');
type: 'start',
});
expect(assetHtml).to.equal('<link rel="stylesheet" href="/_merged_assets/some.css">'); expect(assetHtml).to.equal('<link rel="stylesheet" href="/_merged_assets/some.css">');
}); });
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 = new RocketCli({ cli = await executeBuild('e2e-fixtures/content/pathPrefix.rocket.config.js');
argv: [
'build',
'--config-file',
path.join(__dirname, 'e2e-fixtures', 'content', 'pathPrefix.rocket.config.js'),
],
});
await execute();
const linkHtml = await readOutput('link/index.html', { const linkHtml = await readBuildOutput(cli, 'link/index.html', {
stripServiceWorker: true, stripServiceWorker: true,
stripToBody: true, stripToBody: true,
}); });
@@ -263,7 +120,7 @@ describe('RocketCli e2e', () => {
'\n', '\n',
), ),
); );
const assetHtml = await readOutput('use-assets/index.html', { const assetHtml = await readBuildOutput(cli, 'use-assets/index.html', {
stripServiceWorker: true, stripServiceWorker: true,
}); });
expect(assetHtml).to.equal( expect(assetHtml).to.equal(

View File

@@ -6,7 +6,7 @@ import json from '@rollup/plugin-json';
import { addPlugin, adjustPluginOptions } from 'plugins-manager'; import { addPlugin, adjustPluginOptions } from 'plugins-manager';
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const __dirname = path.dirname(fileURLToPath(import.meta.url));
const outputDir = path.join(__dirname, '..', '__output'); const outputDir = path.join(__dirname, '__output');
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */ /** @type {Partial<import("../../../types/main").RocketCliOptions>} */
const config = { const config = {

View File

@@ -1,6 +1,13 @@
# @mdjs/mdjs-preview # @mdjs/mdjs-preview
## 0.3.1
### Patch Changes
- ee6b404: Pass on the shadowRoot to the story function
## 0.3.0 ## 0.3.0
### Minor Changes ### Minor Changes
- 15e0abe: Clean up dependencies - add Types - 15e0abe: Clean up dependencies - add Types

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mdjs/mdjs-preview", "name": "@mdjs/mdjs-preview",
"version": "0.3.0", "version": "0.3.1",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

View File

@@ -1,6 +1,19 @@
import { LitElement, html, css } from 'lit-element'; import { LitElement, html, css } from 'lit-element';
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'; import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
/**
* @typedef {object} StoryOptions
* @property {ShadowRoot | null} StoryOptions.shadowRoot
*/
/** @typedef {(options?: StoryOptions) => ReturnType<LitElement['render']>} LitHtmlStoryFn */
/**
* Renders a story within a preview frame
*
* @element mdjs-preview
* @prop {StoryFn} [story=(() => TemplateResult)] Function that returns the story
*/
export class MdJsPreview extends LitElement { export class MdJsPreview extends LitElement {
static get properties() { static get properties() {
return { return {
@@ -28,6 +41,7 @@ export class MdJsPreview extends LitElement {
constructor() { constructor() {
super(); super();
this.code = ''; this.code = '';
/** @type {LitHtmlStoryFn} */
this.story = () => html` <p>Loading...</p> `; this.story = () => html` <p>Loading...</p> `;
this.codeHasHtml = false; this.codeHasHtml = false;
} }
@@ -35,7 +49,7 @@ export class MdJsPreview extends LitElement {
render() { render() {
return html` return html`
<div id="wrapper"> <div id="wrapper">
<div>${this.story()}</div> <div>${this.story({ shadowRoot: this.shadowRoot })}</div>
<button id="showCodeButton" @click=${this.toggleShowCode}>show code</button> <button id="showCodeButton" @click=${this.toggleShowCode}>show code</button>
</div> </div>
${this.codeHasHtml ? unsafeHTML(this.code) : html`<pre><code>${this.code}</code></pre>`} ${this.codeHasHtml ? unsafeHTML(this.code) : html`<pre><code>${this.code}</code></pre>`}

View File

@@ -1,6 +1,13 @@
# @mdjs/mdjs-story # @mdjs/mdjs-story
## 0.1.1
### Patch Changes
- ee6b404: Pass on the shadowRoot to the story function
## 0.1.0 ## 0.1.0
### Minor Changes ### Minor Changes
- 15e0abe: Clean up dependencies - add Types - 15e0abe: Clean up dependencies - add Types

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mdjs/mdjs-story", "name": "@mdjs/mdjs-story",
"version": "0.1.0", "version": "0.1.1",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

View File

@@ -1,5 +1,18 @@
import { LitElement, html } from 'lit-element'; import { LitElement, html } from 'lit-element';
/**
* @typedef {object} StoryOptions
* @property {ShadowRoot | null} StoryOptions.shadowRoot
*/
/** @typedef {(options?: StoryOptions) => ReturnType<LitElement['render']>} LitHtmlStoryFn */
/**
* Renders a story
*
* @element mdjs-story
* @prop {StoryFn} [story=(() => TemplateResult)] Function that returns the story
*/
export class MdJsStory extends LitElement { export class MdJsStory extends LitElement {
static get properties() { static get properties() {
return { return {
@@ -11,10 +24,11 @@ export class MdJsStory extends LitElement {
constructor() { constructor() {
super(); super();
this.story = () => html` <p>Loading...</p> `; /** @type {LitHtmlStoryFn} */
this.story = () => html`<p>Loading...</p>`;
} }
render() { render() {
return this.story(); return this.story({ shadowRoot: this.shadowRoot });
} }
} }