mirror of
https://github.com/modernweb-dev/rocket.git
synced 2026-03-21 15:54:57 +00:00
Compare commits
23 Commits
@rocket/cl
...
@rocket/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d44a23af0c | ||
|
|
18a79589c2 | ||
|
|
b7727b0e10 | ||
|
|
5edc40fed5 | ||
|
|
be0d0b3ca1 | ||
|
|
ef8ebb0098 | ||
|
|
2fa61e1377 | ||
|
|
b23e37f38e | ||
|
|
cf45e32702 | ||
|
|
b5965c6c37 | ||
|
|
e39cc45d23 | ||
|
|
f0434cb12c | ||
|
|
c87caaed2d | ||
|
|
04af7ecf53 | ||
|
|
98d6aad12a | ||
|
|
ee6b404aaa | ||
|
|
8ba8939c67 | ||
|
|
8e095b792e | ||
|
|
b58ac27658 | ||
|
|
f44a0f4fd4 | ||
|
|
750418bb51 | ||
|
|
bc2698c1ba | ||
|
|
74f7ddf478 |
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
## The Goal for Rocket
|
## The Goal for Rocket
|
||||||
|
|
||||||
> Our goal is to provide developers with a meta framework for static websites with a spricle of JavaScript.
|
> Our goal is to provide developers with a meta framework for static websites with a sprinkle of JavaScript.
|
||||||
|
|
||||||
Get a site up and running in no time and focus on the content.
|
Get a site up and running in no time and focus on the content.
|
||||||
You can still tweak every detail of every underlying tool that gets used.
|
You can still tweak every detail of every underlying tool that gets used.
|
||||||
@@ -54,7 +54,7 @@ Rocket is part of the [Modern Web Family](https://twitter.com/modern_web_dev).
|
|||||||
|
|
||||||
We are always looking for contributors of all skill levels! If you're looking to ease your way into the project, try out a [good first issue](https://github.com/modernweb-dev/rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
We are always looking for contributors of all skill levels! If you're looking to ease your way into the project, try out a [good first issue](https://github.com/modernweb-dev/rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||||
|
|
||||||
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md). Also, feel free to drop into [slack](https://rocket.modern-web.dev/discover/slack/) and say hi. 👋
|
If you are interested in helping contribute to Modern Web, please take a look at our [Contributing Guide](https://github.com/modernweb-dev/rocket/blob/main/CONTRIBUTING.md). Also, feel free to drop into [slack](https://rocket.modern-web.dev/about/slack/) and say hi. 👋
|
||||||
|
|
||||||
### Financial Contributors
|
### Financial Contributors
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
# Tools >> Check HTML Links ||30
|
# Tools >> Check HTML Links ||30
|
||||||
|
|
||||||
|
```js
|
||||||
|
import '@rocket/launch/inline-notification/inline-notification.js';
|
||||||
|
```
|
||||||
|
|
||||||
A fast checker for broken links/references in HTML.
|
A fast checker for broken links/references in HTML.
|
||||||
|
|
||||||
|
<inline-notification type="tip">
|
||||||
|
|
||||||
|
Read the [Introducing Check HTMl Links - no more bad links](../../blog/introducing-check-html-links.md) Blog post to find out how it came to be and how it works.
|
||||||
|
|
||||||
|
</inline-notification>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Checks all html files for broken local links/references (in href, src, srcset)
|
- Checks all html files for broken local links/references (in href, src, srcset)
|
||||||
@@ -16,10 +26,25 @@ A fast checker for broken links/references in HTML.
|
|||||||
npm i -D check-html-links
|
npm i -D check-html-links
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## CLI flags
|
||||||
|
|
||||||
```
|
| Name | Type | Description |
|
||||||
|
| ------------------- | ------- | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| root-dir | string | the root directory to serve files from. Defaults to the current working directory |
|
||||||
|
| ignore-link-pattern | string | do not check links matching the pattern |
|
||||||
|
| continue-on-error | boolean | if present it will not exit with an error code - useful while writing or for temporary passing a ci |
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# check a folder _site
|
||||||
npx check-html-links _site
|
npx check-html-links _site
|
||||||
|
|
||||||
|
# ignore all links like <a href="/users/123">
|
||||||
|
npx check-html-links _site --ignore-link-pattern "/users/*" "/users/**/*"
|
||||||
|
|
||||||
|
# ignore all links like <a href="/users/123"> & <a href="/users/123/details">
|
||||||
|
npx check-html-links _site --ignore-link-pattern "/users/*" "/users/**/*"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Output
|
## Example Output
|
||||||
|
|||||||
@@ -129,3 +129,25 @@ const config = {
|
|||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Enabling / Disabling
|
||||||
|
|
||||||
|
Generating images from SVG is quite fast but it can still add that's why by default during `rocket start` there will be no social media images created.
|
||||||
|
|
||||||
|
If you with so create them also during start you can
|
||||||
|
|
||||||
|
```js
|
||||||
|
const config = {
|
||||||
|
start: {
|
||||||
|
createSocialMediaImages: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, if you never want to create social media images even during build then you can globally disable it via
|
||||||
|
|
||||||
|
```js
|
||||||
|
const config = {
|
||||||
|
createSocialMediaImages: true,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @rocket/building-rollup
|
# @rocket/building-rollup
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.1.2
|
## 0.1.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/building-rollup",
|
"name": "@rocket/building-rollup",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||||
"homepage": "https://rocket.modern-web.dev/docs/tools/building-rollup/",
|
"homepage": "https://rocket.modern-web.dev/docs/tools/building-rollup/",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js"
|
".": "./index.js"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function createSpaMetaConfig(userConfig = { output: {} }) {
|
|||||||
// directory to match patterns against to be precached
|
// directory to match patterns against to be precached
|
||||||
globDirectory: path.join(config.output.dir),
|
globDirectory: path.join(config.output.dir),
|
||||||
// cache any html js and css by default
|
// cache any html js and css by default
|
||||||
globPatterns: ['**/*.{html,js,css,webmanifest}'],
|
globPatterns: ['**/*.{html,js,css,webmanifest}', '**/*-search-index.json'],
|
||||||
skipWaiting: true,
|
skipWaiting: true,
|
||||||
clientsClaim: true,
|
clientsClaim: true,
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
# check-html-links
|
# check-html-links
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 750418b: Uses a class for the CLI and adding the following options:
|
||||||
|
|
||||||
|
- `--root-dir` the root directory to serve files from. Defaults to the current working directory
|
||||||
|
- `--ignore-link-pattern` do not check links matching the pattern
|
||||||
|
- `--continue-on-error` if present it will not exit with an error code - useful while writing or for temporary passing a ci
|
||||||
|
|
||||||
|
BREAKING CHANGE:
|
||||||
|
|
||||||
|
- Exists with an error code if a broken link is found
|
||||||
|
|
||||||
## 0.1.2
|
## 0.1.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export { validateFolder } from './src/validateFolder.js';
|
export { validateFolder } from './src/validateFolder.js';
|
||||||
export { formatErrors } from './src/formatErrors.js';
|
export { formatErrors } from './src/formatErrors.js';
|
||||||
|
export { CheckHtmlLinksCli } from './src/CheckHtmlLinksCli.js';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "check-html-links",
|
"name": "check-html-links",
|
||||||
"version": "0.1.2",
|
"version": "0.2.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"check-html-links": "src/cli.js"
|
"check-html-links": "src/cli.js"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js"
|
".": "./index.js"
|
||||||
},
|
},
|
||||||
@@ -33,7 +34,9 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
|
"command-line-args": "^5.1.1",
|
||||||
"glob": "^7.0.0",
|
"glob": "^7.0.0",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
"sax-wasm": "^2.0.0"
|
"sax-wasm": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
100
packages/check-html-links/src/CheckHtmlLinksCli.js
Normal file
100
packages/check-html-links/src/CheckHtmlLinksCli.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
|
||||||
|
/** @typedef {import('../types/main').CheckHtmlLinksCliOptions} CheckHtmlLinksCliOptions */
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import commandLineArgs from 'command-line-args';
|
||||||
|
import { validateFiles } from './validateFolder.js';
|
||||||
|
import { formatErrors } from './formatErrors.js';
|
||||||
|
import { listFiles } from './listFiles.js';
|
||||||
|
|
||||||
|
export class CheckHtmlLinksCli {
|
||||||
|
/** @type {CheckHtmlLinksCliOptions} */
|
||||||
|
options;
|
||||||
|
|
||||||
|
constructor({ argv } = { argv: undefined }) {
|
||||||
|
const mainDefinitions = [
|
||||||
|
{ name: 'ignore-link-pattern', type: String, multiple: true },
|
||||||
|
{ name: 'root-dir', type: String, defaultOption: true },
|
||||||
|
{ name: 'continue-on-error', type: Boolean, defaultOption: false },
|
||||||
|
];
|
||||||
|
const options = commandLineArgs(mainDefinitions, {
|
||||||
|
stopAtFirstUnknown: true,
|
||||||
|
argv,
|
||||||
|
});
|
||||||
|
this.options = {
|
||||||
|
printOnError: true,
|
||||||
|
continueOnError: options['continue-on-error'],
|
||||||
|
rootDir: options['root-dir'],
|
||||||
|
ignoreLinkPatterns: options['ignore-link-pattern'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Partial<CheckHtmlLinksCliOptions>} newOptions
|
||||||
|
*/
|
||||||
|
setOptions(newOptions) {
|
||||||
|
this.options = {
|
||||||
|
...this.options,
|
||||||
|
...newOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const { ignoreLinkPatterns, rootDir: userRootDir } = this.options;
|
||||||
|
const rootDir = userRootDir ? path.resolve(userRootDir) : process.cwd();
|
||||||
|
const performanceStart = process.hrtime();
|
||||||
|
|
||||||
|
console.log('👀 Checking if all internal links work...');
|
||||||
|
const files = await listFiles('**/*.html', rootDir);
|
||||||
|
|
||||||
|
const filesOutput =
|
||||||
|
files.length == 0
|
||||||
|
? '🧐 No files to check. Did you select the correct folder?'
|
||||||
|
: `🔥 Found a total of ${chalk.green.bold(files.length)} files to check!`;
|
||||||
|
console.log(filesOutput);
|
||||||
|
|
||||||
|
const { errors, numberLinks } = await validateFiles(files, rootDir, { ignoreLinkPatterns });
|
||||||
|
|
||||||
|
console.log(`🔗 Found a total of ${chalk.green.bold(numberLinks)} links to validate!\n`);
|
||||||
|
|
||||||
|
const performance = process.hrtime(performanceStart);
|
||||||
|
/** @type {string[]} */
|
||||||
|
let output = [];
|
||||||
|
let message = '';
|
||||||
|
if (errors.length > 0) {
|
||||||
|
let referenceCount = 0;
|
||||||
|
for (const error of errors) {
|
||||||
|
referenceCount += error.usage.length;
|
||||||
|
}
|
||||||
|
output = [
|
||||||
|
`❌ Found ${chalk.red.bold(
|
||||||
|
errors.length.toString(),
|
||||||
|
)} missing reference targets (used by ${referenceCount} links) while checking ${
|
||||||
|
files.length
|
||||||
|
} files:`,
|
||||||
|
...formatErrors(errors)
|
||||||
|
.split('\n')
|
||||||
|
.map(line => ` ${line}`),
|
||||||
|
`Checking links duration: ${performance[0]}s ${performance[1] / 1000000}ms`,
|
||||||
|
];
|
||||||
|
message = output.join('\n');
|
||||||
|
if (this.options.printOnError === true) {
|
||||||
|
console.error(message);
|
||||||
|
}
|
||||||
|
if (this.options.continueOnError === false) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`✅ All internal links are valid. (executed in ${performance[0]}s ${
|
||||||
|
performance[1] / 1000000
|
||||||
|
}ms)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { errors, message };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import path from 'path';
|
import { CheckHtmlLinksCli } from './CheckHtmlLinksCli.js';
|
||||||
import chalk from 'chalk';
|
|
||||||
import { validateFiles } from './validateFolder.js';
|
|
||||||
import { formatErrors } from './formatErrors.js';
|
|
||||||
import { listFiles } from './listFiles.js';
|
|
||||||
|
|
||||||
async function main() {
|
const cli = new CheckHtmlLinksCli();
|
||||||
const userRootDir = process.argv[2];
|
cli.run();
|
||||||
const rootDir = userRootDir ? path.resolve(userRootDir) : process.cwd();
|
|
||||||
const performanceStart = process.hrtime();
|
|
||||||
|
|
||||||
console.log('👀 Checking if all internal links work...');
|
|
||||||
const files = await listFiles('**/*.html', rootDir);
|
|
||||||
|
|
||||||
const filesOutput =
|
|
||||||
files.length == 0
|
|
||||||
? '🧐 No files to check. Did you select the correct folder?'
|
|
||||||
: `🔥 Found a total of ${chalk.green.bold(files.length)} files to check!`;
|
|
||||||
console.log(filesOutput);
|
|
||||||
|
|
||||||
const { errors, numberLinks } = await validateFiles(files, rootDir);
|
|
||||||
|
|
||||||
console.log(`🔗 Found a total of ${chalk.green.bold(numberLinks)} links to validate!\n`);
|
|
||||||
|
|
||||||
const performance = process.hrtime(performanceStart);
|
|
||||||
if (errors.length > 0) {
|
|
||||||
let referenceCount = 0;
|
|
||||||
for (const error of errors) {
|
|
||||||
referenceCount += error.usage.length;
|
|
||||||
}
|
|
||||||
const output = [
|
|
||||||
`❌ Found ${chalk.red.bold(
|
|
||||||
errors.length.toString(),
|
|
||||||
)} missing reference targets (used by ${referenceCount} links) while checking ${
|
|
||||||
files.length
|
|
||||||
} files:`,
|
|
||||||
...formatErrors(errors)
|
|
||||||
.split('\n')
|
|
||||||
.map(line => ` ${line}`),
|
|
||||||
`Checking links duration: ${performance[0]}s ${performance[1] / 1000000}ms`,
|
|
||||||
];
|
|
||||||
console.error(output.join('\n'));
|
|
||||||
process.exit(1);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
`✅ All internal links are valid. (executed in %ds %dms)`,
|
|
||||||
performance[0],
|
|
||||||
performance[1] / 1000000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import saxWasm from 'sax-wasm';
|
import saxWasm from 'sax-wasm';
|
||||||
|
import minimatch from 'minimatch';
|
||||||
import { createRequire } from 'module';
|
import { createRequire } from 'module';
|
||||||
|
|
||||||
import { listFiles } from './listFiles.js';
|
import { listFiles } from './listFiles.js';
|
||||||
@@ -10,6 +11,7 @@ import path from 'path';
|
|||||||
/** @typedef {import('../types/main').LocalFile} LocalFile */
|
/** @typedef {import('../types/main').LocalFile} LocalFile */
|
||||||
/** @typedef {import('../types/main').Usage} Usage */
|
/** @typedef {import('../types/main').Usage} Usage */
|
||||||
/** @typedef {import('../types/main').Error} Error */
|
/** @typedef {import('../types/main').Error} Error */
|
||||||
|
/** @typedef {import('../types/main').Options} Options */
|
||||||
/** @typedef {import('sax-wasm').Attribute} Attribute */
|
/** @typedef {import('sax-wasm').Attribute} Attribute */
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
@@ -185,8 +187,9 @@ function getValueAndAnchor(inValue) {
|
|||||||
* @param {object} options
|
* @param {object} options
|
||||||
* @param {string} options.htmlFilePath
|
* @param {string} options.htmlFilePath
|
||||||
* @param {string} options.rootDir
|
* @param {string} options.rootDir
|
||||||
|
* @param {function(string): boolean} options.ignoreUsage
|
||||||
*/
|
*/
|
||||||
async function resolveLinks(links, { htmlFilePath, rootDir }) {
|
async function resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage }) {
|
||||||
for (const hrefObj of links) {
|
for (const hrefObj of links) {
|
||||||
const { value, anchor } = getValueAndAnchor(hrefObj.value);
|
const { value, anchor } = getValueAndAnchor(hrefObj.value);
|
||||||
|
|
||||||
@@ -201,7 +204,9 @@ async function resolveLinks(links, { htmlFilePath, rootDir }) {
|
|||||||
|
|
||||||
let valueFile = value.endsWith('/') ? path.join(value, 'index.html') : value;
|
let valueFile = value.endsWith('/') ? path.join(value, 'index.html') : value;
|
||||||
|
|
||||||
if (value.includes('mailto:')) {
|
if (ignoreUsage(value)) {
|
||||||
|
// ignore
|
||||||
|
} else if (value.includes('mailto:')) {
|
||||||
// ignore for now - could add a check to validate if the email address is valid
|
// ignore for now - could add a check to validate if the email address is valid
|
||||||
} else if (valueFile === '' && anchor !== '') {
|
} else if (valueFile === '' && anchor !== '') {
|
||||||
addLocalFile(htmlFilePath, anchor, usageObj);
|
addLocalFile(htmlFilePath, anchor, usageObj);
|
||||||
@@ -261,8 +266,9 @@ async function validateLocalFiles(checkLocalFiles) {
|
|||||||
/**
|
/**
|
||||||
* @param {string[]} files
|
* @param {string[]} files
|
||||||
* @param {string} rootDir
|
* @param {string} rootDir
|
||||||
|
* @param {Options} opts?
|
||||||
*/
|
*/
|
||||||
export async function validateFiles(files, rootDir) {
|
export async function validateFiles(files, rootDir, opts) {
|
||||||
await parserReferences.prepareWasm(saxWasmBuffer);
|
await parserReferences.prepareWasm(saxWasmBuffer);
|
||||||
await parserIds.prepareWasm(saxWasmBuffer);
|
await parserIds.prepareWasm(saxWasmBuffer);
|
||||||
|
|
||||||
@@ -270,10 +276,20 @@ export async function validateFiles(files, rootDir) {
|
|||||||
checkLocalFiles = [];
|
checkLocalFiles = [];
|
||||||
idCache = new Map();
|
idCache = new Map();
|
||||||
let numberLinks = 0;
|
let numberLinks = 0;
|
||||||
|
|
||||||
|
const ignoreLinkPatternRegExps = opts
|
||||||
|
? opts.ignoreLinkPatterns?.map(pattern => minimatch.makeRe(pattern))
|
||||||
|
: null;
|
||||||
|
|
||||||
|
/** @type {function(string): boolean} */
|
||||||
|
const ignoreUsage = ignoreLinkPatternRegExps
|
||||||
|
? usage => !!ignoreLinkPatternRegExps.find(regExp => usage.match(regExp))
|
||||||
|
: () => false;
|
||||||
|
|
||||||
for (const htmlFilePath of files) {
|
for (const htmlFilePath of files) {
|
||||||
const { links } = await extractReferences(htmlFilePath);
|
const { links } = await extractReferences(htmlFilePath);
|
||||||
numberLinks += links.length;
|
numberLinks += links.length;
|
||||||
await resolveLinks(links, { htmlFilePath, rootDir });
|
await resolveLinks(links, { htmlFilePath, rootDir, ignoreUsage });
|
||||||
}
|
}
|
||||||
await validateLocalFiles(checkLocalFiles);
|
await validateLocalFiles(checkLocalFiles);
|
||||||
|
|
||||||
@@ -282,10 +298,11 @@ export async function validateFiles(files, rootDir) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} inRootDir
|
* @param {string} inRootDir
|
||||||
|
* @param {Options} opts?
|
||||||
*/
|
*/
|
||||||
export async function validateFolder(inRootDir) {
|
export async function validateFolder(inRootDir, opts) {
|
||||||
const rootDir = path.resolve(inRootDir);
|
const rootDir = path.resolve(inRootDir);
|
||||||
const files = await listFiles('**/*.html', rootDir);
|
const files = await listFiles('**/*.html', rootDir);
|
||||||
const { errors } = await validateFiles(files, rootDir);
|
const { errors } = await validateFiles(files, rootDir, opts);
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- ignore known subsystems -->
|
||||||
|
<a href="/docs/"></a>
|
||||||
|
<a href="/developer/getting-started.html#js"></a>
|
||||||
|
<a href="/developer/language-guides/"></a>
|
||||||
|
<a href="/developer/javascript.html"></a>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<a href="/absolute/index.html"></a>
|
||||||
|
<a href="./relative/index.html"></a>
|
||||||
|
<a href="./relative/subfolder/index.html"></a>
|
||||||
|
|
||||||
|
<!-- valid -->
|
||||||
|
<a href="./page.html"></a>
|
||||||
|
<a href=" ./page.html "></a>
|
||||||
|
<a href=" /page.html "></a>
|
||||||
@@ -5,9 +5,9 @@ import { validateFolder } from 'check-html-links';
|
|||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export async function execute(inPath) {
|
export async function execute(inPath, opts) {
|
||||||
const testDir = path.join(__dirname, inPath.split('/').join(path.sep));
|
const testDir = path.join(__dirname, inPath.split('/').join(path.sep));
|
||||||
const errors = await validateFolder(testDir);
|
const errors = await validateFolder(testDir, opts);
|
||||||
return {
|
return {
|
||||||
cleanup: items => {
|
cleanup: items => {
|
||||||
const newItems = [];
|
const newItems = [];
|
||||||
|
|||||||
@@ -183,6 +183,28 @@ describe('validateFolder', () => {
|
|||||||
expect(cleanup(errors)).to.deep.equal([]);
|
expect(cleanup(errors)).to.deep.equal([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('ignoring a folder', async () => {
|
||||||
|
const { errors, cleanup } = await execute('fixtures/internal-link-ignore', {
|
||||||
|
ignoreLinkPatterns: ['./relative/*', './relative/**/*'],
|
||||||
|
});
|
||||||
|
expect(cleanup(errors)).to.deep.equal([
|
||||||
|
{
|
||||||
|
filePath: 'fixtures/internal-link-ignore/absolute/index.html',
|
||||||
|
onlyAnchorMissing: false,
|
||||||
|
usage: [
|
||||||
|
{
|
||||||
|
anchor: '',
|
||||||
|
attribute: 'href',
|
||||||
|
character: 9,
|
||||||
|
file: 'fixtures/internal-link-ignore/index.html',
|
||||||
|
line: 0,
|
||||||
|
value: '/absolute/index.html',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle img src', async () => {
|
it('can handle img src', async () => {
|
||||||
const { errors, cleanup } = await execute('fixtures/internal-images');
|
const { errors, cleanup } = await execute('fixtures/internal-images');
|
||||||
expect(cleanup(errors)).to.deep.equal([
|
expect(cleanup(errors)).to.deep.equal([
|
||||||
|
|||||||
11
packages/check-html-links/types/main.d.ts
vendored
11
packages/check-html-links/types/main.d.ts
vendored
@@ -25,3 +25,14 @@ export interface Error {
|
|||||||
onlyAnchorMissing: boolean;
|
onlyAnchorMissing: boolean;
|
||||||
usage: Usage[];
|
usage: Usage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
ignoreLinkPatterns: string[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckHtmlLinksCliOptions {
|
||||||
|
printOnError: boolean;
|
||||||
|
rootDir: string;
|
||||||
|
ignoreLinkPatterns: string[] | null;
|
||||||
|
continueOnError: boolean;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,48 @@
|
|||||||
# @rocket/cli
|
# @rocket/cli
|
||||||
|
|
||||||
|
## 0.6.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- 5edc40f: Make sure each rocket instance has it's own eleventy config'
|
||||||
|
- ef8ebb0: To support dynamically created content to be part of the anchor navigation of the page we now analyze the final html output instead of `entry.templateContent`.
|
||||||
|
|
||||||
|
BREAKING CHANGE:
|
||||||
|
|
||||||
|
- only add anchors for the currently active pages (before it added anchor for every page)
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
- Updated dependencies [be0d0b3]
|
||||||
|
- Updated dependencies [ef8ebb0]
|
||||||
|
- @rocket/building-rollup@0.1.3
|
||||||
|
- check-html-links@0.2.1
|
||||||
|
- @rocket/core@0.1.2
|
||||||
|
- plugins-manager@0.2.1
|
||||||
|
- @rocket/eleventy-rocket-nav@0.3.0
|
||||||
|
|
||||||
|
## 0.5.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 8e095b7: Watching `_assets`, `_data`, `_includes` for changes to trigger updated automatically
|
||||||
|
|
||||||
|
## 0.5.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- f44a0f4: Rewrite dynamic imports with "`"
|
||||||
|
- 74f7ddf: Adds performance improvements for social media images by:
|
||||||
|
- creating social media images only in `rocket build` phase
|
||||||
|
- adds a config `createSocialMediaImages` to enable (default) or disable it globally
|
||||||
|
- adds config `start.createSocialMediaImages` to enable or disable (default) it during `rocket start`
|
||||||
|
- 750418b: Use class-based node API of check-html-links
|
||||||
|
- Updated dependencies [f44a0f4]
|
||||||
|
- Updated dependencies [750418b]
|
||||||
|
- @rocket/eleventy-plugin-mdjs-unified@0.3.1
|
||||||
|
- check-html-links@0.2.0
|
||||||
|
|
||||||
## 0.5.0
|
## 0.5.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/cli",
|
"name": "@rocket/cli",
|
||||||
"version": "0.5.0",
|
"version": "0.6.0",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"rocket": "src/cli.js"
|
"rocket": "src/cli.js"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./index.cjs",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"require": "./index.cjs",
|
"require": "./index.cjs",
|
||||||
@@ -56,21 +57,22 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@11ty/eleventy": "^0.11.1",
|
"@11ty/eleventy": "^0.11.1",
|
||||||
"@11ty/eleventy-img": "^0.7.4",
|
"@11ty/eleventy-img": "^0.7.4",
|
||||||
"@rocket/building-rollup": "^0.1.2",
|
"@rocket/building-rollup": "^0.1.3",
|
||||||
"@rocket/core": "^0.1.1",
|
"@rocket/core": "^0.1.2",
|
||||||
"@rocket/eleventy-plugin-mdjs-unified": "^0.3.0",
|
"@rocket/eleventy-plugin-mdjs-unified": "^0.3.1",
|
||||||
"@rocket/eleventy-rocket-nav": "^0.2.1",
|
"@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",
|
||||||
"@web/config-loader": "^0.1.3",
|
"@web/config-loader": "^0.1.3",
|
||||||
"@web/dev-server": "^0.1.4",
|
"@web/dev-server": "^0.1.4",
|
||||||
"@web/dev-server-rollup": "^0.3.2",
|
"@web/dev-server-rollup": "^0.3.2",
|
||||||
"@web/rollup-plugin-copy": "^0.2.0",
|
"@web/rollup-plugin-copy": "^0.2.0",
|
||||||
"check-html-links": "^0.1.2",
|
"check-html-links": "^0.2.1",
|
||||||
"command-line-args": "^5.1.1",
|
"command-line-args": "^5.1.1",
|
||||||
"command-line-usage": "^6.1.1",
|
"command-line-usage": "^6.1.1",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"plugins-manager": "^0.2.0",
|
"micromatch": "^4.0.2",
|
||||||
|
"plugins-manager": "^0.2.1",
|
||||||
"utf8": "^3.0.0"
|
"utf8": "^3.0.0"
|
||||||
},
|
},
|
||||||
"types": "dist-types/index.d.ts"
|
"types": "dist-types/index.d.ts"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import computedConfigPkg from './public/computedConfig.cjs';
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import Eleventy from '@11ty/eleventy';
|
import Eleventy from '@11ty/eleventy';
|
||||||
|
import TemplateConfig from '@11ty/eleventy/src/TemplateConfig.js';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
@@ -33,6 +34,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;
|
||||||
|
|
||||||
|
// can’t 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 {
|
||||||
@@ -70,11 +100,16 @@ export class RocketCli {
|
|||||||
await this.mergePresets();
|
await this.mergePresets();
|
||||||
|
|
||||||
const elev = new RocketEleventy(_inputDirCwdRelative, outputDevDir, this);
|
const elev = new RocketEleventy(_inputDirCwdRelative, outputDevDir, this);
|
||||||
elev.isVerbose = false;
|
|
||||||
// 11ty always wants a relative path to cwd - why?
|
// 11ty always wants a relative path to cwd - why?
|
||||||
const rel = path.relative(process.cwd(), path.join(__dirname));
|
const rel = path.relative(process.cwd(), path.join(__dirname));
|
||||||
const relCwdPathToConfig = path.join(rel, 'shared', '.eleventy.cjs');
|
const relCwdPathToConfig = path.join(rel, 'shared', '.eleventy.cjs');
|
||||||
|
|
||||||
|
const config = new TemplateConfig(null, relCwdPathToConfig);
|
||||||
|
elev.config = config.getConfig();
|
||||||
|
elev.resetConfig();
|
||||||
elev.setConfigPathOverride(relCwdPathToConfig);
|
elev.setConfigPathOverride(relCwdPathToConfig);
|
||||||
|
|
||||||
|
elev.isVerbose = false;
|
||||||
await elev.init();
|
await elev.init();
|
||||||
|
|
||||||
this.eleventy = elev;
|
this.eleventy = elev;
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
/** @typedef {import('../types/main').RocketCliOptions} RocketCliOptions */
|
/** @typedef {import('../types/main').RocketCliOptions} RocketCliOptions */
|
||||||
|
|
||||||
import chalk from 'chalk';
|
import { CheckHtmlLinksCli } from 'check-html-links';
|
||||||
import { validateFolder, formatErrors } from 'check-html-links';
|
|
||||||
|
|
||||||
export class RocketLint {
|
export class RocketLint {
|
||||||
static pluginName = 'RocketLint';
|
static pluginName = 'RocketLint';
|
||||||
@@ -49,31 +48,20 @@ export class RocketLint {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const performanceStart = process.hrtime();
|
const checkLinks = new CheckHtmlLinksCli();
|
||||||
console.log('👀 Checking if all internal links work...');
|
checkLinks.setOptions({
|
||||||
const errors = await validateFolder(this.config.lintInputDir);
|
rootDir: this.config.lintInputDir,
|
||||||
const performance = process.hrtime(performanceStart);
|
printOnError: false,
|
||||||
|
continueOnError: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { errors, message } = await checkLinks.run();
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
let referenceCount = 0;
|
if (this.config.command === 'start') {
|
||||||
for (const error of errors) {
|
console.log(message);
|
||||||
referenceCount += error.usage.length;
|
|
||||||
}
|
|
||||||
const output = [
|
|
||||||
`❌ Found ${chalk.red.bold(
|
|
||||||
errors.length.toString(),
|
|
||||||
)} missing reference targets (used by ${referenceCount} links):`,
|
|
||||||
...formatErrors(errors)
|
|
||||||
.split('\n')
|
|
||||||
.map(line => ` ${line}`),
|
|
||||||
`Checking links duration: ${performance[0]}s ${performance[1] / 1000000}ms`,
|
|
||||||
];
|
|
||||||
if (this.config.watch) {
|
|
||||||
console.log(output.join('\n'));
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(output.join('\n'));
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log('✅ All internal links are valid.');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export class RocketStart {
|
|||||||
*/
|
*/
|
||||||
setupCommand(config) {
|
setupCommand(config) {
|
||||||
delete config.pathPrefix;
|
delete config.pathPrefix;
|
||||||
|
config.createSocialMediaImages = !!config?.start?.createSocialMediaImages;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export async function normalizeConfig(inConfig) {
|
|||||||
eleventy: () => {},
|
eleventy: () => {},
|
||||||
command: 'help',
|
command: 'help',
|
||||||
watch: true,
|
watch: true,
|
||||||
|
createSocialMediaImages: true,
|
||||||
inputDir: 'docs',
|
inputDir: 'docs',
|
||||||
outputDir: '_site',
|
outputDir: '_site',
|
||||||
outputDevDir: '_site-dev',
|
outputDevDir: '_site-dev',
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const Image = require('@11ty/eleventy-img');
|
const Image = require('@11ty/eleventy-img');
|
||||||
const { getComputedConfig } = require('./computedConfig.cjs');
|
const { getComputedConfig } = require('./computedConfig.cjs');
|
||||||
const { createSocialImageSvg: defaultcreateSocialImageSvg } = require('./createSocialImageSvg.cjs');
|
const { createSocialImageSvg: defaultCreateSocialImageSvg } = require('./createSocialImageSvg.cjs');
|
||||||
|
|
||||||
async function createSocialImage(args) {
|
async function createSocialImage(args) {
|
||||||
const {
|
const {
|
||||||
title = '',
|
title = '',
|
||||||
subTitle = '',
|
subTitle = '',
|
||||||
footer = '',
|
footer = '',
|
||||||
createSocialImageSvg = defaultcreateSocialImageSvg,
|
createSocialImageSvg = defaultCreateSocialImageSvg,
|
||||||
} = args;
|
} = args;
|
||||||
const cleanedUpArgs = { ...args };
|
const cleanedUpArgs = { ...args };
|
||||||
delete cleanedUpArgs.createSocialImageSvg;
|
delete cleanedUpArgs.createSocialImageSvg;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { processContentWithTitle } = require('@rocket/core/title');
|
const { processContentWithTitle } = require('@rocket/core/title');
|
||||||
const { createSocialImage: defaultcreateSocialImage } = require('./createSocialImage.cjs');
|
const { createSocialImage: defaultCreateSocialImage } = require('./createSocialImage.cjs');
|
||||||
const { getComputedConfig } = require('./computedConfig.cjs');
|
const { getComputedConfig } = require('./computedConfig.cjs');
|
||||||
const { executeSetupFunctions } = require('plugins-manager');
|
const { executeSetupFunctions } = require('plugins-manager');
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ function layoutPlugin({ defaultLayout = 'layout-default' } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function socialMediaImagePlugin(args = {}) {
|
function socialMediaImagePlugin(args = {}) {
|
||||||
const { createSocialImage = defaultcreateSocialImage } = args;
|
const { createSocialImage = defaultCreateSocialImage, rocketConfig = {} } = args;
|
||||||
|
|
||||||
const cleanedUpArgs = { ...args };
|
const cleanedUpArgs = { ...args };
|
||||||
delete cleanedUpArgs.createSocialImage;
|
delete cleanedUpArgs.createSocialImage;
|
||||||
@@ -80,6 +80,11 @@ function socialMediaImagePlugin(args = {}) {
|
|||||||
if (data.socialMediaImage) {
|
if (data.socialMediaImage) {
|
||||||
return data.socialMediaImage;
|
return data.socialMediaImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rocketConfig.createSocialMediaImages === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.title) {
|
if (!data.title) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -158,7 +163,7 @@ function generateEleventyComputed() {
|
|||||||
{ name: 'title', plugin: titlePlugin },
|
{ name: 'title', plugin: titlePlugin },
|
||||||
{ name: 'eleventyNavigation', plugin: eleventyNavigationPlugin },
|
{ name: 'eleventyNavigation', plugin: eleventyNavigationPlugin },
|
||||||
{ name: 'section', plugin: sectionPlugin },
|
{ name: 'section', plugin: sectionPlugin },
|
||||||
{ name: 'socialMediaImage', plugin: socialMediaImagePlugin },
|
{ name: 'socialMediaImage', plugin: socialMediaImagePlugin, options: { rocketConfig } },
|
||||||
{ name: '_joiningBlocks', plugin: joiningBlocksPlugin, options: rocketConfig },
|
{ name: '_joiningBlocks', plugin: joiningBlocksPlugin, options: rocketConfig },
|
||||||
{ name: 'layout', plugin: layoutPlugin },
|
{ name: 'layout', plugin: layoutPlugin },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export function setFixtureDir(importMetaUrl) {
|
|||||||
* @property {boolean} stripToBody
|
* @property {boolean} stripToBody
|
||||||
* @property {boolean} stripStartEndWhitespace
|
* @property {boolean} stripStartEndWhitespace
|
||||||
* @property {boolean} stripScripts
|
* @property {boolean} stripScripts
|
||||||
|
* @property {boolean} formatHtml
|
||||||
* @property {start|build} type
|
* @property {start|build} type
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -54,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();
|
||||||
@@ -81,6 +86,16 @@ 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 {*} cli
|
||||||
* @param {string} fileName
|
* @param {string} fileName
|
||||||
@@ -91,10 +106,21 @@ export async function readStartOutput(cli, fileName, options = {}) {
|
|||||||
return readOutput(cli, fileName, options);
|
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 execute(cli, configFileDir) {
|
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();
|
||||||
@@ -110,6 +136,15 @@ export async function executeStart(pathToConfig) {
|
|||||||
return cli;
|
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) {
|
export async function executeLint(pathToConfig) {
|
||||||
const configFile = path.join(fixtureDir, pathToConfig.split('/').join(path.sep));
|
const configFile = path.join(fixtureDir, pathToConfig.split('/').join(path.sep));
|
||||||
const cli = new RocketCli({
|
const cli = new RocketCli({
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import chai from 'chai';
|
import chai from 'chai';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { executeStart, readOutput, readStartOutput, setFixtureDir } from '@rocket/cli/test-helpers';
|
import {
|
||||||
|
executeBuild,
|
||||||
|
executeStart,
|
||||||
|
readBuildOutput,
|
||||||
|
readStartOutput,
|
||||||
|
setFixtureDir,
|
||||||
|
} from '@rocket/cli/test-helpers';
|
||||||
|
|
||||||
const { expect } = chai;
|
const { expect } = chai;
|
||||||
|
|
||||||
@@ -22,42 +28,49 @@ 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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('will note create a social media image in "start"', async () => {
|
||||||
|
cli = await executeStart('computed-config-fixtures/social-images-only-build/rocket.config.js');
|
||||||
|
|
||||||
|
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||||
|
expect(indexHtml).to.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('will create a social media image in "build"', async () => {
|
||||||
|
cli = await executeBuild('computed-config-fixtures/social-images-only-build/rocket.config.js');
|
||||||
|
|
||||||
|
const indexHtml = await readBuildOutput(cli, 'index.html', {
|
||||||
|
stripToBody: true,
|
||||||
|
stripServiceWorker: true,
|
||||||
|
});
|
||||||
|
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');
|
cli = await executeStart('computed-config-fixtures/social-images/rocket.config.js');
|
||||||
|
|
||||||
@@ -175,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');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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,39 @@ 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
|
it('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 = await executeStart('e2e-fixtures/content/eleventy.rocket.config.js');
|
||||||
cli = new RocketCli({
|
const indexHtml = await readStartOutput(cli, 'index.html');
|
||||||
argv: [
|
|
||||||
'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 +72,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 +119,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(
|
||||||
|
|||||||
87
packages/cli/test-node/RocketCli.useCases.test.js
Normal file
87
packages/cli/test-node/RocketCli.useCases.test.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import chai from 'chai';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import {
|
||||||
|
executeStart,
|
||||||
|
readStartOutput,
|
||||||
|
setFixtureDir,
|
||||||
|
startOutputExist,
|
||||||
|
} from '@rocket/cli/test-helpers';
|
||||||
|
|
||||||
|
const { expect } = chai;
|
||||||
|
|
||||||
|
describe('RocketCli use cases', () => {
|
||||||
|
let cli;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
// ignore colors in tests as most CIs won't support it
|
||||||
|
chalk.level = 0;
|
||||||
|
setFixtureDir(import.meta.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
if (cli?.cleanup) {
|
||||||
|
await cli.cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports dynamic imports', async () => {
|
||||||
|
cli = await executeStart('use-cases/dynamic-imports/rocket.config.js');
|
||||||
|
|
||||||
|
expect(startOutputExist(cli, 'sub/assets/myData.js'), 'static files did not get copied').to.be
|
||||||
|
.true;
|
||||||
|
|
||||||
|
const aboutHtml = await readStartOutput(cli, 'about/index.html', { formatHtml: true });
|
||||||
|
expect(aboutHtml).to.equal(
|
||||||
|
[
|
||||||
|
'<p><code>about.md</code></p>',
|
||||||
|
'<script type="module">',
|
||||||
|
' import { myData } from "../sub/assets/myData.js";',
|
||||||
|
' import("../sub/assets/myData.js");',
|
||||||
|
' const name = "myData";',
|
||||||
|
' import(`../sub/assets/${name}.js`);',
|
||||||
|
'</script>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const subHtml = await readStartOutput(cli, 'sub/index.html', { formatHtml: true });
|
||||||
|
expect(subHtml).to.equal(
|
||||||
|
[
|
||||||
|
'<p><code>sub/index.md</code></p>',
|
||||||
|
'<script type="module">',
|
||||||
|
' import { myData } from "./assets/myData.js";',
|
||||||
|
' import("./assets/myData.js");',
|
||||||
|
' const name = "myData";',
|
||||||
|
' import(`./assets/${name}.js`);',
|
||||||
|
'</script>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const subDetailsHtml = await readStartOutput(cli, 'sub/details/index.html', {
|
||||||
|
formatHtml: true,
|
||||||
|
});
|
||||||
|
expect(subDetailsHtml).to.equal(
|
||||||
|
[
|
||||||
|
'<p><code>sub/details.md</code></p>',
|
||||||
|
'<script type="module">',
|
||||||
|
' import { myData } from "../assets/myData.js";',
|
||||||
|
' import("../assets/myData.js");',
|
||||||
|
' const name = "myData";',
|
||||||
|
' import(`../assets/${name}.js`);',
|
||||||
|
'</script>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const indexHtml = await readStartOutput(cli, 'index.html', { formatHtml: true });
|
||||||
|
expect(indexHtml).to.equal(
|
||||||
|
[
|
||||||
|
'<p><code>index.md</code></p>',
|
||||||
|
'<script type="module">',
|
||||||
|
' import { myData } from "./sub/assets/myData.js";',
|
||||||
|
' import("./sub/assets/myData.js");',
|
||||||
|
' const name = "myData";',
|
||||||
|
' import(`./sub/assets/${name}.js`);',
|
||||||
|
'</script>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
**/*.njk
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Rocket
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -2,6 +2,9 @@ import { adjustPluginOptions } from 'plugins-manager';
|
|||||||
|
|
||||||
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
||||||
const config = {
|
const config = {
|
||||||
|
start: {
|
||||||
|
createSocialMediaImages: true,
|
||||||
|
},
|
||||||
setupEleventyComputedConfig: [
|
setupEleventyComputedConfig: [
|
||||||
adjustPluginOptions('socialMediaImage', {
|
adjustPluginOptions('socialMediaImage', {
|
||||||
createSocialImageSvg: async () => {
|
createSocialImageSvg: async () => {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
module.exports = 'layout.njk';
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{{ socialMediaImage }}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
||||||
const config = {};
|
const config = {
|
||||||
|
start: {
|
||||||
|
createSocialMediaImages: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ describe('normalizeConfig', () => {
|
|||||||
|
|
||||||
expect(cleanup(config)).to.deep.equal({
|
expect(cleanup(config)).to.deep.equal({
|
||||||
command: 'help',
|
command: 'help',
|
||||||
|
createSocialMediaImages: true,
|
||||||
devServer: {},
|
devServer: {},
|
||||||
build: {},
|
build: {},
|
||||||
watch: true,
|
watch: true,
|
||||||
@@ -61,6 +62,7 @@ describe('normalizeConfig', () => {
|
|||||||
|
|
||||||
expect(cleanup(config)).to.deep.equal({
|
expect(cleanup(config)).to.deep.equal({
|
||||||
command: 'help',
|
command: 'help',
|
||||||
|
createSocialMediaImages: true,
|
||||||
devServer: {
|
devServer: {
|
||||||
more: 'settings',
|
more: 'settings',
|
||||||
},
|
},
|
||||||
@@ -92,6 +94,7 @@ describe('normalizeConfig', () => {
|
|||||||
|
|
||||||
expect(cleanup(config)).to.deep.equal({
|
expect(cleanup(config)).to.deep.equal({
|
||||||
command: 'help',
|
command: 'help',
|
||||||
|
createSocialMediaImages: true,
|
||||||
devServer: {
|
devServer: {
|
||||||
more: 'from-file',
|
more: 'from-file',
|
||||||
},
|
},
|
||||||
@@ -128,6 +131,7 @@ describe('normalizeConfig', () => {
|
|||||||
|
|
||||||
expect(cleanup(config)).to.deep.equal({
|
expect(cleanup(config)).to.deep.equal({
|
||||||
command: 'help',
|
command: 'help',
|
||||||
|
createSocialMediaImages: true,
|
||||||
devServer: {},
|
devServer: {},
|
||||||
build: {},
|
build: {},
|
||||||
watch: true,
|
watch: true,
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
**/*.njk
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{{ content | safe }}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
`about.md`
|
||||||
|
|
||||||
|
```js script
|
||||||
|
import { myData } from './sub/assets/myData.js';
|
||||||
|
import('./sub/assets/myData.js');
|
||||||
|
const name = 'myData';
|
||||||
|
import(`./sub/assets/${name}.js`);
|
||||||
|
```
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
`index.md`
|
||||||
|
|
||||||
|
```js script
|
||||||
|
import { myData } from './sub/assets/myData.js';
|
||||||
|
import('./sub/assets/myData.js');
|
||||||
|
const name = 'myData';
|
||||||
|
import(`./sub/assets/${name}.js`);
|
||||||
|
```
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export const myData = 'The answer to everything is 42';
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
`sub/details.md`
|
||||||
|
|
||||||
|
```js script
|
||||||
|
import { myData } from './assets/myData.js';
|
||||||
|
import('./assets/myData.js');
|
||||||
|
const name = 'myData';
|
||||||
|
import(`./assets/${name}.js`);
|
||||||
|
```
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
`sub/index.md`
|
||||||
|
|
||||||
|
```js script
|
||||||
|
import { myData } from './assets/myData.js';
|
||||||
|
import('./assets/myData.js');
|
||||||
|
const name = 'myData';
|
||||||
|
import(`./assets/${name}.js`);
|
||||||
|
```
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/** @type {Partial<import("../../../types/main").RocketCliOptions>} */
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
export default config;
|
||||||
9
packages/cli/types/main.d.ts
vendored
9
packages/cli/types/main.d.ts
vendored
@@ -13,14 +13,21 @@ export interface RocketPreset {
|
|||||||
setupEleventyComputedConfig: function[];
|
setupEleventyComputedConfig: function[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RocketStartConfig {
|
||||||
|
createSocialMediaImages?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RocketCliOptions {
|
export interface RocketCliOptions {
|
||||||
presets: Array<RocketPreset>;
|
presets: Array<RocketPreset>;
|
||||||
pathPrefix?: string;
|
pathPrefix?: string;
|
||||||
inputDir: string;
|
inputDir: string;
|
||||||
outputDir: string;
|
outputDir: string;
|
||||||
emptyOutputDir?: boolen;
|
emptyOutputDir?: boolean;
|
||||||
absoluteBaseUrl?: string;
|
absoluteBaseUrl?: string;
|
||||||
watch: boolean;
|
watch: boolean;
|
||||||
|
createSocialMediaImages?: boolean;
|
||||||
|
|
||||||
|
start?: RocketStartConfig;
|
||||||
|
|
||||||
// TODO: improve all setup functions
|
// TODO: improve all setup functions
|
||||||
setupUnifiedPlugins?: function[];
|
setupUnifiedPlugins?: function[];
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @rocket/core
|
# @rocket/core
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.1.1
|
## 0.1.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/core",
|
"name": "@rocket/core",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||||
"homepage": "https://rocket.modern-web.dev/",
|
"homepage": "https://rocket.modern-web.dev/",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./dist/title.cjs",
|
||||||
"exports": {
|
"exports": {
|
||||||
"./title": {
|
"./title": {
|
||||||
"require": "./dist/title.cjs",
|
"require": "./dist/title.cjs",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
return {
|
return {
|
||||||
useOverlay: { type: Boolean, reflect: true },
|
useOverlay: { type: Boolean, reflect: true },
|
||||||
useOverlayMediaQuery: { type: String },
|
useOverlayMediaQuery: { type: String },
|
||||||
|
mediaMatcher: { type: Object },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +90,20 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (changedProperties.has('useOverlayMediaQuery')) {
|
||||||
|
this.mediaMatcher.removeEventListener('change', this.onMatchMedia);
|
||||||
|
|
||||||
|
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||||
|
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||||
|
this.useOverlay = !!this.mediaMatcher.matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { MediaQueryListEvent } query
|
||||||
|
*/
|
||||||
|
onMatchMedia(query) {
|
||||||
|
this.useOverlay = !!query.matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupOpenCloseListeners() {
|
_setupOpenCloseListeners() {
|
||||||
@@ -118,11 +133,15 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
|
|
||||||
this.__toggle = this.__toggle.bind(this);
|
this.__toggle = this.__toggle.bind(this);
|
||||||
|
|
||||||
|
this.onMatchMedia = this.onMatchMedia.bind(this);
|
||||||
this.onGestureStart = this.onGestureStart.bind(this);
|
this.onGestureStart = this.onGestureStart.bind(this);
|
||||||
this.onGestureMove = this.onGestureMove.bind(this);
|
this.onGestureMove = this.onGestureMove.bind(this);
|
||||||
this.onGestureEnd = this.onGestureEnd.bind(this);
|
this.onGestureEnd = this.onGestureEnd.bind(this);
|
||||||
this.updateFromTouch = this.updateFromTouch.bind(this);
|
this.updateFromTouch = this.updateFromTouch.bind(this);
|
||||||
|
|
||||||
|
this.mediaMatcher = window.matchMedia(this.useOverlayMediaQuery);
|
||||||
|
this.mediaMatcher.addEventListener('change', this.onMatchMedia);
|
||||||
|
|
||||||
this._startX = 0;
|
this._startX = 0;
|
||||||
this._currentX = 0;
|
this._currentX = 0;
|
||||||
this._velocity = 0;
|
this._velocity = 0;
|
||||||
@@ -133,10 +152,7 @@ export class RocketDrawer extends OverlayMixin(LitElement) {
|
|||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.useOverlay = !!window.matchMedia(this.useOverlayMediaQuery).matches;
|
this.useOverlay = !!this.mediaMatcher.matches;
|
||||||
window.matchMedia(this.useOverlayMediaQuery).addListener(query => {
|
|
||||||
this.useOverlay = !!query.matches;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @rocket/eleventy-plugin-mdjs-unified
|
# @rocket/eleventy-plugin-mdjs-unified
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- f44a0f4: Rewrite dynamic imports with "`"
|
||||||
|
|
||||||
## 0.3.0
|
## 0.3.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/eleventy-plugin-mdjs-unified",
|
"name": "@rocket/eleventy-plugin-mdjs-unified",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,10 +72,14 @@ async function processImports(source, inputPath) {
|
|||||||
newSource += '.' + importSrc;
|
newSource += '.' + importSrc;
|
||||||
} else if (importSrc.startsWith("'./")) {
|
} else if (importSrc.startsWith("'./")) {
|
||||||
newSource += "'." + importSrc.substring(1);
|
newSource += "'." + importSrc.substring(1);
|
||||||
|
} else if (importSrc.startsWith('`./')) {
|
||||||
|
newSource += '`.' + importSrc.substring(1);
|
||||||
} else if (importSrc.startsWith('../')) {
|
} else if (importSrc.startsWith('../')) {
|
||||||
newSource += '../' + importSrc;
|
newSource += '../' + importSrc;
|
||||||
} else if (importSrc.startsWith("'../")) {
|
} else if (importSrc.startsWith("'../")) {
|
||||||
newSource += "'../" + importSrc.substring(1);
|
newSource += "'../" + importSrc.substring(1);
|
||||||
|
} else if (importSrc.startsWith('`../')) {
|
||||||
|
newSource += '`../' + importSrc.substring(1);
|
||||||
} else {
|
} else {
|
||||||
newSource += importSrc;
|
newSource += importSrc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const RocketNav = require('./eleventy-rocket-nav');
|
const RocketNav = require('./eleventy-rocket-nav');
|
||||||
|
const { addPageAnchors } = require('./src/addPageAnchors.js');
|
||||||
|
|
||||||
// export the configuration function for plugin
|
// export the configuration function for plugin
|
||||||
module.exports = function (eleventyConfig) {
|
module.exports = function (eleventyConfig) {
|
||||||
@@ -8,6 +9,10 @@ module.exports = function (eleventyConfig) {
|
|||||||
eleventyConfig.addNunjucksFilter('rocketNavToHtml', function (pages, options) {
|
eleventyConfig.addNunjucksFilter('rocketNavToHtml', function (pages, options) {
|
||||||
return RocketNav.toHtml.call(eleventyConfig, pages, options);
|
return RocketNav.toHtml.call(eleventyConfig, pages, options);
|
||||||
});
|
});
|
||||||
|
eleventyConfig.addTransform('rocket-nav-add-page-anchors', async function (content) {
|
||||||
|
const newContent = await addPageAnchors(content);
|
||||||
|
return newContent;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.navigation = {
|
module.exports.navigation = {
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
# @rocket/eleventy-rocket-nav
|
# @rocket/eleventy-rocket-nav
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- ef8ebb0: To support dynamically created content to be part of the anchor navigation of the page we now analyze the final html output instead of `entry.templateContent`.
|
||||||
|
|
||||||
|
BREAKING CHANGE:
|
||||||
|
|
||||||
|
- only add anchors for the currently active pages (before it added anchor for every page)
|
||||||
|
|
||||||
## 0.2.1
|
## 0.2.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -94,21 +94,8 @@ function findNavigationEntries(nodes = [], key = '') {
|
|||||||
entry.title = entry.key;
|
entry.title = entry.key;
|
||||||
}
|
}
|
||||||
if (entry.key) {
|
if (entry.key) {
|
||||||
if (!headingsCache.has(entry.templateContent)) {
|
|
||||||
headingsCache.set(entry.templateContent, getHeadingsOfHtml(entry.templateContent));
|
|
||||||
}
|
|
||||||
const headings = /** @type {Heading[]} */ (headingsCache.get(entry.templateContent));
|
|
||||||
const anchors = headings.map(heading => ({
|
|
||||||
key: heading.text + Math.random(),
|
|
||||||
parent: entry.key,
|
|
||||||
url: `${entry.url}#${heading.id}`,
|
|
||||||
pluginType: 'eleventy-navigation',
|
|
||||||
parentKey: entry.key,
|
|
||||||
title: heading.text,
|
|
||||||
anchor: true,
|
|
||||||
}));
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
entry.children = [...anchors, ...findNavigationEntries(nodes, entry.key)];
|
entry.children = findNavigationEntries(nodes, entry.key);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
});
|
});
|
||||||
@@ -227,43 +214,36 @@ function navigationToHtml(pages, _options = {}) {
|
|||||||
}>${pages
|
}>${pages
|
||||||
.map(entry => {
|
.map(entry => {
|
||||||
const liClass = [];
|
const liClass = [];
|
||||||
const aClass = [];
|
|
||||||
if (options.listItemClass) {
|
if (options.listItemClass) {
|
||||||
liClass.push(options.listItemClass);
|
liClass.push(options.listItemClass);
|
||||||
}
|
}
|
||||||
if (options.anchorClass) {
|
if (options.activeKey === entry.key && options.activeListItemClass) {
|
||||||
aClass.push(options.anchorClass);
|
liClass.push(options.activeListItemClass);
|
||||||
}
|
|
||||||
if (options.activeKey === entry.key) {
|
|
||||||
if (options.activeListItemClass) {
|
|
||||||
liClass.push(options.activeListItemClass);
|
|
||||||
}
|
|
||||||
if (options.activeAnchorClass) {
|
|
||||||
aClass.push(options.activeAnchorClass);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (options.activeTreeListClass && activePages && activePages.includes(entry.key)) {
|
if (options.activeTreeListClass && activePages && activePages.includes(entry.key)) {
|
||||||
liClass.push(options.activeTreeListClass);
|
liClass.push(options.activeTreeListClass);
|
||||||
}
|
}
|
||||||
if (options.activeAnchorListClass && activePages && activePages.includes(entry.key)) {
|
|
||||||
aClass.push(options.activeAnchorListClass);
|
|
||||||
}
|
|
||||||
if (options.listItemHasChildrenClass && entry.children && entry.children.length) {
|
if (options.listItemHasChildrenClass && entry.children && entry.children.length) {
|
||||||
liClass.push(options.listItemHasChildrenClass);
|
liClass.push(options.listItemHasChildrenClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.anchor) {
|
const output = [];
|
||||||
liClass.push('anchor');
|
output.push(
|
||||||
aClass.push('anchor');
|
`<${options.listItemElement}${liClass.length ? ` class="${liClass.join(' ')}"` : ''}>`,
|
||||||
|
);
|
||||||
|
output.push(`<a href="${urlFilter(entry.url)}">${entry.title}</a>`);
|
||||||
|
if (options.showExcerpt && entry.excerpt) {
|
||||||
|
output.push(`: ${entry.excerpt}`);
|
||||||
}
|
}
|
||||||
|
if (options.activeKey === entry.key && options.activeListItemClass) {
|
||||||
|
output.push('<!-- ADD PAGE ANCHORS -->');
|
||||||
|
}
|
||||||
|
if (entry.children) {
|
||||||
|
output.push(navigationToHtml(entry.children, options));
|
||||||
|
}
|
||||||
|
output.push(`</${options.listItemElement}>`);
|
||||||
|
|
||||||
return `<${options.listItemElement}${
|
return output.join('\n');
|
||||||
liClass.length ? ` class="${liClass.join(' ')}"` : ''
|
|
||||||
}><a href="${urlFilter(entry.url)}"${
|
|
||||||
aClass.length ? ` class="${aClass.join(' ')}"` : ''
|
|
||||||
}>${entry.title}</a>${options.showExcerpt && entry.excerpt ? `: ${entry.excerpt}` : ''}${
|
|
||||||
entry.children ? navigationToHtml(entry.children, options) : ''
|
|
||||||
}</${options.listItemElement}>`;
|
|
||||||
})
|
})
|
||||||
.join('\n')}</${options.listElement}>`
|
.join('\n')}</${options.listElement}>`
|
||||||
: '';
|
: '';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/eleventy-rocket-nav",
|
"name": "@rocket/eleventy-rocket-nav",
|
||||||
"version": "0.2.1",
|
"version": "0.3.0",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"main": ".eleventy.js",
|
"main": ".eleventy.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha test-node/**/*.test.js test-node/*.test.js",
|
"test": "mocha test-node/**/*.test.js test-node/*.test.js --timeout 5000",
|
||||||
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
|
"test:watch": "mocha test-node/**/*.test.js test-node/*.test.js --watch"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
136
packages/eleventy-rocket-nav/src/addPageAnchors.js
Normal file
136
packages/eleventy-rocket-nav/src/addPageAnchors.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
const fs = require('fs');
|
||||||
|
const { SaxEventType, SAXParser } = require('sax-wasm');
|
||||||
|
|
||||||
|
const saxPath = require.resolve('sax-wasm/lib/sax-wasm.wasm');
|
||||||
|
const saxWasmBuffer = fs.readFileSync(saxPath);
|
||||||
|
|
||||||
|
/** @typedef {import('../types').Heading} Heading */
|
||||||
|
|
||||||
|
/** @typedef {import('sax-wasm').Text} Text */
|
||||||
|
/** @typedef {import('sax-wasm').Tag} Tag */
|
||||||
|
/** @typedef {import('sax-wasm').Position} Position */
|
||||||
|
|
||||||
|
// Instantiate
|
||||||
|
const parser = new SAXParser(
|
||||||
|
SaxEventType.CloseTag | SaxEventType.Comment,
|
||||||
|
{ highWaterMark: 256 * 1024 }, // 256k chunks
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} options.content
|
||||||
|
* @param {Position} options.start
|
||||||
|
* @param {Position} options.end
|
||||||
|
* @param {string} options.insert
|
||||||
|
*/
|
||||||
|
function removeBetween({ content, start, end, insert = '' }) {
|
||||||
|
const lines = content.split('\n');
|
||||||
|
const i = start.line;
|
||||||
|
const line = lines[i];
|
||||||
|
const upToChange = line.slice(0, start.character - 1);
|
||||||
|
const afterChange = line.slice(end.character + 2);
|
||||||
|
|
||||||
|
lines[i] = `${upToChange}${insert}${afterChange}`;
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Tag} data
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
function getAttribute(data, name) {
|
||||||
|
if (data.attributes) {
|
||||||
|
const { attributes } = data;
|
||||||
|
const foundIndex = attributes.findIndex(entry => entry.name.value === name);
|
||||||
|
if (foundIndex !== -1) {
|
||||||
|
return attributes[foundIndex].value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Tag} data
|
||||||
|
*/
|
||||||
|
function getText(data) {
|
||||||
|
if (data.textNodes) {
|
||||||
|
return data.textNodes.map(textNode => textNode.value).join('');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} html
|
||||||
|
*/
|
||||||
|
function getHeadingsOfHtml(html) {
|
||||||
|
/** @type {Heading[]} */
|
||||||
|
const headings = [];
|
||||||
|
/** @type {Text} */
|
||||||
|
let insertPoint;
|
||||||
|
parser.eventHandler = (ev, _data) => {
|
||||||
|
if (ev === SaxEventType.Comment) {
|
||||||
|
const data = /** @type {Text} */ (/** @type {any} */ (_data));
|
||||||
|
// NOTE: we NEED to access data internal value so sax-wasm does not reuse it's value
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const tmp = data.start.line + data.end.line;
|
||||||
|
if (data.value.trim() === 'ADD PAGE ANCHORS' || data.value.trim() === '-->ADD PAGE ANCHORS') {
|
||||||
|
insertPoint = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev === SaxEventType.CloseTag) {
|
||||||
|
const data = /** @type {Tag} */ (/** @type {any} */ (_data));
|
||||||
|
if (data.name === 'h2') {
|
||||||
|
const id = getAttribute(data, 'id');
|
||||||
|
const text = getText(data);
|
||||||
|
if (id && text) {
|
||||||
|
headings.push({ text, id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parser.write(Buffer.from(html, 'utf8'));
|
||||||
|
parser.end();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return { headings, insertPoint };
|
||||||
|
}
|
||||||
|
|
||||||
|
let isSetup = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} content
|
||||||
|
*/
|
||||||
|
async function addPageAnchors(content) {
|
||||||
|
if (!isSetup) {
|
||||||
|
await parser.prepareWasm(saxWasmBuffer);
|
||||||
|
isSetup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { headings, insertPoint } = getHeadingsOfHtml(content);
|
||||||
|
const pageAnchorsHtml = [];
|
||||||
|
if (headings.length > 0) {
|
||||||
|
pageAnchorsHtml.push('<ul>');
|
||||||
|
for (const heading of headings) {
|
||||||
|
pageAnchorsHtml.push(' <li class="menu-item anchor">');
|
||||||
|
pageAnchorsHtml.push(` <a href="#${heading.id}" class="anchor">${heading.text}</a>`);
|
||||||
|
pageAnchorsHtml.push(' </li>');
|
||||||
|
}
|
||||||
|
pageAnchorsHtml.push('</ul>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertPoint) {
|
||||||
|
return removeBetween({
|
||||||
|
content,
|
||||||
|
start: insertPoint.start,
|
||||||
|
end: insertPoint.end,
|
||||||
|
insert: pageAnchorsHtml.join('\n'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addPageAnchors,
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const { expect } = require('chai');
|
||||||
|
const prettier = require('prettier');
|
||||||
|
const { addPageAnchors } = require('../src/addPageAnchors.js');
|
||||||
|
|
||||||
|
const format = code => prettier.format(code, { parser: 'html' }).trim();
|
||||||
|
|
||||||
|
describe('addPageAnchors', () => {
|
||||||
|
it('finds and adds anchors for each h2 as an unordered list', async () => {
|
||||||
|
const input = [
|
||||||
|
'<body>',
|
||||||
|
' <!-- ADD PAGE ANCHORS -->',
|
||||||
|
' <div id="content">',
|
||||||
|
' <h2 id="first">👉 First Headline</h2>',
|
||||||
|
' </div>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n');
|
||||||
|
const expected = [
|
||||||
|
'<body>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item anchor">',
|
||||||
|
' <a href="#first" class="anchor">👉 First Headline</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' <div id="content">',
|
||||||
|
' <h2 id="first">👉 First Headline</h2>',
|
||||||
|
' </div>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n');
|
||||||
|
const result = await addPageAnchors(input);
|
||||||
|
expect(format(result)).to.deep.equal(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const eleventyNavigationPlugin = require('@rocket/eleventy-rocket-nav');
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPlugin(eleventyNavigationPlugin);
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<body>
|
||||||
|
{{ collections.all | rocketNav | rocketNavToHtml({
|
||||||
|
listItemClass: "menu-item",
|
||||||
|
activeListItemClass: "current",
|
||||||
|
activeKey: eleventyNavigation.key
|
||||||
|
}) | safe }}
|
||||||
|
|
||||||
|
{{ content | safe }}
|
||||||
|
</body>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: Bats
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Bats
|
||||||
|
parent: Mammals
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
🦇 can fly.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: Humans
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Humans
|
||||||
|
parent: Mammals
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
<h2 id="anatomy">Anatomy</h2>
|
||||||
|
<p>Has arms.</p>
|
||||||
|
<h2 id="age">📖 Age</h2>
|
||||||
|
<p>Up to 130 years.</p>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: Mammals
|
||||||
|
eleventyNavigation:
|
||||||
|
key: Mammals
|
||||||
|
---
|
||||||
|
|
||||||
|
Mammals need air.
|
||||||
96
packages/eleventy-rocket-nav/test-node/integration.test.js
Normal file
96
packages/eleventy-rocket-nav/test-node/integration.test.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const { expect } = require('chai');
|
||||||
|
const Eleventy = require('@11ty/eleventy');
|
||||||
|
const TemplateConfig = require('@11ty/eleventy/src/TemplateConfig');
|
||||||
|
const prettier = require('prettier');
|
||||||
|
|
||||||
|
async function execute(fixtureDir) {
|
||||||
|
const relPath = path.relative(process.cwd(), __dirname);
|
||||||
|
const relativeInputPath = path.join(relPath, fixtureDir.split('/').join(path.sep));
|
||||||
|
const relativeOutputPath = path.join(relPath, 'fixtures', '__output');
|
||||||
|
const relativeConfigPath = path.join(relativeInputPath, '.eleventy.js');
|
||||||
|
|
||||||
|
await fs.emptyDir(relativeOutputPath);
|
||||||
|
|
||||||
|
const elev = new Eleventy(relativeInputPath, relativeOutputPath);
|
||||||
|
const config = new TemplateConfig(null, relativeConfigPath);
|
||||||
|
elev.config = config.getConfig();
|
||||||
|
elev.setConfigPathOverride(relativeConfigPath);
|
||||||
|
elev.resetConfig();
|
||||||
|
|
||||||
|
await elev.init();
|
||||||
|
await elev.write();
|
||||||
|
return {
|
||||||
|
readOutput: async readPath => {
|
||||||
|
const relativeReadPath = path.join(relativeOutputPath, readPath);
|
||||||
|
let text = await fs.promises.readFile(relativeReadPath);
|
||||||
|
text = text.toString();
|
||||||
|
text = prettier.format(text, { parser: 'html', printWidth: 100 });
|
||||||
|
return text.trim();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('eleventy-rocket-nav', () => {
|
||||||
|
it('renders a menu with anchors for h2 content', async () => {
|
||||||
|
const { readOutput } = await execute('fixtures/three-pages');
|
||||||
|
|
||||||
|
const bats = await readOutput('bats/index.html');
|
||||||
|
expect(bats).to.deep.equal(
|
||||||
|
[
|
||||||
|
'<body>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item active">',
|
||||||
|
' <a href="/mammals/">Mammals</a>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item">',
|
||||||
|
' <a href="/humans/">Humans</a>',
|
||||||
|
' </li>',
|
||||||
|
' <li class="menu-item current">',
|
||||||
|
' <a href="/bats/">Bats</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
'',
|
||||||
|
' <p>🦇 can fly.</p>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const humans = await readOutput('humans/index.html');
|
||||||
|
expect(humans).to.deep.equal(
|
||||||
|
[
|
||||||
|
'<body>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item active">',
|
||||||
|
' <a href="/mammals/">Mammals</a>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item current">',
|
||||||
|
' <a href="/humans/">Humans</a>',
|
||||||
|
' <ul>',
|
||||||
|
' <li class="menu-item anchor">',
|
||||||
|
' <a href="#anatomy" class="anchor">Anatomy</a>',
|
||||||
|
' </li>',
|
||||||
|
' <li class="menu-item anchor">',
|
||||||
|
' <a href="#age" class="anchor">📖 Age</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' </li>',
|
||||||
|
' <li class="menu-item">',
|
||||||
|
' <a href="/bats/">Bats</a>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
' </li>',
|
||||||
|
' </ul>',
|
||||||
|
'',
|
||||||
|
' <h2 id="anatomy">Anatomy</h2>',
|
||||||
|
' <p>Has arms.</p>',
|
||||||
|
' <h2 id="age">📖 Age</h2>',
|
||||||
|
' <p>Up to 130 years.</p>',
|
||||||
|
'</body>',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -43,8 +43,8 @@
|
|||||||
"remark"
|
"remark"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdjs/mdjs-preview": "^0.3.0",
|
"@mdjs/mdjs-preview": "^0.3.1",
|
||||||
"@mdjs/mdjs-story": "^0.1.0",
|
"@mdjs/mdjs-story": "^0.1.1",
|
||||||
"@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",
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
# @mdjs/mdjs-preview
|
# @mdjs/mdjs-preview
|
||||||
|
|
||||||
|
## 0.3.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mdjs/mdjs-preview",
|
"name": "@mdjs/mdjs-preview",
|
||||||
"version": "0.3.0",
|
"version": "0.3.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||||
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/preview/",
|
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/preview/",
|
||||||
|
"main": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js",
|
".": "./index.js",
|
||||||
|
|||||||
@@ -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>`}
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
# @mdjs/mdjs-story
|
# @mdjs/mdjs-story
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mdjs/mdjs-story",
|
"name": "@mdjs/mdjs-story",
|
||||||
"version": "0.1.0",
|
"version": "0.1.2",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||||
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/story/",
|
"homepage": "https://rocket.modern-web.dev/docs/markdown-javascript/story/",
|
||||||
|
"main": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js",
|
".": "./index.js",
|
||||||
|
|||||||
@@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# plugins-manager
|
# plugins-manager
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "plugins-manager",
|
"name": "plugins-manager",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||||
"homepage": "https://rocket.modern-web.dev/docs/tools/plugins-manager/",
|
"homepage": "https://rocket.modern-web.dev/docs/tools/plugins-manager/",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./dist/index.cjs",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"require": "./dist/index.cjs",
|
"require": "./dist/index.cjs",
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# @rocket/search
|
# @rocket/search
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- be0d0b3: fix: add missing main entry to the packages
|
||||||
|
- Updated dependencies [be0d0b3]
|
||||||
|
- plugins-manager@0.2.1
|
||||||
|
|
||||||
## 0.3.0
|
## 0.3.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rocket/search",
|
"name": "@rocket/search",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
"author": "Modern Web <hello@modern-web.dev> (https://modern-web.dev/)",
|
||||||
"homepage": "https://rocket.modern-web.dev/docs/presets/search/",
|
"homepage": "https://rocket.modern-web.dev/docs/presets/search/",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "./node.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./node.js",
|
".": "./node.js",
|
||||||
"./node": "./node.js",
|
"./node": "./node.js",
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
"@open-wc/scoped-elements": "^1.3.2",
|
"@open-wc/scoped-elements": "^1.3.2",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"minisearch": "^3.0.2",
|
"minisearch": "^3.0.2",
|
||||||
"plugins-manager": "^0.2.0",
|
"plugins-manager": "^0.2.1",
|
||||||
"sax-wasm": "^2.0.0"
|
"sax-wasm": "^2.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ function getTitle({ result, search }) {
|
|||||||
function getText({ result, search }) {
|
function getText({ result, search }) {
|
||||||
const { terms, body } = result;
|
const { terms, body } = result;
|
||||||
|
|
||||||
return highlightSearchTerms({ text: body, search, terms });
|
return highlightSearchTerms({ text: body, search, terms, addEllipsis: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
|
||||||
@@ -40,6 +40,7 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
|||||||
search: { type: String },
|
search: { type: String },
|
||||||
results: { type: Array },
|
results: { type: Array },
|
||||||
maxResults: { type: Number, attribute: 'max-results' },
|
maxResults: { type: Number, attribute: 'max-results' },
|
||||||
|
noResultsText: { type: String },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
|||||||
this.jsonUrl = '';
|
this.jsonUrl = '';
|
||||||
this.search = '';
|
this.search = '';
|
||||||
this.maxResults = 10;
|
this.maxResults = 10;
|
||||||
|
this.noResultsText = 'No results found';
|
||||||
/**
|
/**
|
||||||
* @type {RocketSearchResult[]}
|
* @type {RocketSearchResult[]}
|
||||||
*/
|
*/
|
||||||
@@ -132,6 +134,9 @@ export class RocketSearch extends ScopedElementsMixin(LitElement) {
|
|||||||
></rocket-search-option>
|
></rocket-search-option>
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
|
${this.results.length <= 0 && this.search.length > 0
|
||||||
|
? html` <rocket-search-option .title=${this.noResultsText}></rocket-search-option> `
|
||||||
|
: ''}
|
||||||
</rocket-search-combobox>
|
</rocket-search-combobox>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ export class RocketSearchCombobox extends LionCombobox {
|
|||||||
font: inherit;
|
font: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: var(--rocket-search-fill-color, #000);
|
fill: var(--rocket-search-fill-color, #000);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
::slotted([slot='prefix'][close-btn]) {
|
::slotted([slot='prefix'][close-btn]) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class RocketSearchOption extends LinkMixin(LionOption) {
|
|||||||
|
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
cursor: default;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 12px 10px;
|
padding: 12px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ function defaultHighlight(term) {
|
|||||||
* @param {number} [options.before]
|
* @param {number} [options.before]
|
||||||
* @param {number} [options.length]
|
* @param {number} [options.length]
|
||||||
* @param {function} [options.highlight]
|
* @param {function} [options.highlight]
|
||||||
|
* @param {boolean} [options.addEllipsis]
|
||||||
*/
|
*/
|
||||||
export function highlightSearchTerms({
|
export function highlightSearchTerms({
|
||||||
search,
|
search,
|
||||||
@@ -34,6 +35,7 @@ export function highlightSearchTerms({
|
|||||||
before = 15,
|
before = 15,
|
||||||
length = 100,
|
length = 100,
|
||||||
highlight = defaultHighlight,
|
highlight = defaultHighlight,
|
||||||
|
addEllipsis = false,
|
||||||
}) {
|
}) {
|
||||||
if (!search || !text) {
|
if (!search || !text) {
|
||||||
return '';
|
return '';
|
||||||
@@ -70,5 +72,9 @@ export function highlightSearchTerms({
|
|||||||
} while (startIndex !== -1);
|
} while (startIndex !== -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newText.substr(truncateStart, length + extraLength);
|
let textResult = newText.substr(truncateStart, length + extraLength);
|
||||||
|
if (addEllipsis && truncateStart > 0) {
|
||||||
|
textResult = `...${textResult}`;
|
||||||
|
}
|
||||||
|
return textResult;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user