Skip to content

Commit

Permalink
feat: generate open graph images
Browse files Browse the repository at this point in the history
  • Loading branch information
daKmoR committed Mar 28, 2022
1 parent 1d75d0b commit bf87645
Show file tree
Hide file tree
Showing 27 changed files with 470 additions and 40 deletions.
2 changes: 1 addition & 1 deletion config/rocket.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ export default {
// serviceWorkerName: 'sw.js',
// pathPrefix: '/_site/',

// emptyOutputDir: false,
// clearOutputDir: false,
};
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
"pre-commit": "lint-staged"
}
},
"imports": {
"#pageTree": "./site/pages/__shared/pageTree.js",
"#assets/*": "./site/src/assets/*"
},
"lint-staged": {
"*.js": [
"eslint --fix",
Expand All @@ -140,8 +144,5 @@
"packages/*",
"examples/*",
"presets/*"
],
"imports": {
"#pageTree": "./site/pages/__shared/pageTree.js"
}
]
}
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"rocket:build": "node src/cli.js build -c demo",
"rocket:start": "node src/cli.js start -c demo",
"start": "npm run rocket:start",
"test": "mocha --require ../../scripts/testMochaGlobalHooks.js test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs}",
"test": "mocha --require ../../scripts/testMochaGlobalHooks.js test-node/**/*.test.{js,cjs} test-node/*.test.{js,cjs} --timeout 5000",
"test:watch": "onchange 'src/**/*.{js,cjs}' 'test-node/**/*.{js,cjs}' -- npm test",
"types:copy": "copyfiles \"./types/**/*.d.ts\" dist-types/",
"xtest:watch": "mocha test/**/*.test.js --parallel --watch"
Expand Down Expand Up @@ -60,6 +60,7 @@
"commander": "^9.0.0",
"fs-extra": "^9.0.1",
"gray-matter": "^4.0.3",
"playwright": "^1.15.0",
"plugins-manager": "^0.3.0"
},
"devDependencies": {
Expand Down
93 changes: 92 additions & 1 deletion packages/cli/src/RocketBuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// @ts-nocheck

import { Engine } from '@rocket/engine/server';
import { gatherFiles } from '@rocket/engine';

import { fromRollup } from '@web/dev-server-rollup';

import { rollup } from 'rollup';
import path from 'path';
Expand All @@ -10,7 +13,9 @@ import { rollupPluginHTML } from '@web/rollup-plugin-html';
import { createMpaConfig, createServiceWorkerConfig } from '@rocket/building-rollup';
import { adjustPluginOptions } from 'plugins-manager';
import { existsSync } from 'fs';
import { readFile, writeFile } from 'fs/promises';
import { readFile, unlink, writeFile } from 'fs/promises';

import { chromium } from 'playwright';

/**
* @param {object} config
Expand Down Expand Up @@ -88,9 +93,14 @@ export class RocketBuild {
outputDir: this.cli.options.outputDevDir,
setupPlugins: this.cli.options.setupEnginePlugins,
renderMode: 'production',
clearOutputDir: this.cli.options.clearOutputDir,
});
await this.engine.build({ autoStop: this.cli.options.buildAutoStop });

if (this.cli.options.buildOpenGraphImages) {
await this.buildOpenGraphImages();
}

if (this.cli.options.buildOptimize) {
await productionBuild(this.cli.options);
await this.engine.copyPublicFilesTo(this.cli.options.outputDir);
Expand All @@ -109,4 +119,85 @@ export class RocketBuild {
await writeFile(notFoundHtmlFilePath, notFoundHtml);
}
}

async buildOpenGraphImages() {
const openGraphFiles = await gatherFiles(this.cli.options.outputDevDir, {
fileEndings: ['.opengraph.html'],
});
if (openGraphFiles.length === 0) {
return;
}

// TODO: enable URL support in the Engine and remove this "workaround"
if (
typeof this.cli.options.inputDir !== 'string' ||
typeof this.cli.options.outputDevDir !== 'string'
) {
return;
}

const withWrap = this.cli.options.setupDevServerAndBuildPlugins
? this.cli.options.setupDevServerAndBuildPlugins.map(modFunction => {
modFunction.wrapPlugin = fromRollup;
return modFunction;
})
: [];

this.engine = new Engine();
this.engine.setOptions({
docsDir: this.cli.options.inputDir,
outputDir: this.cli.options.outputDevDir,
setupPlugins: this.cli.options.setupEnginePlugins,
open: false,
clearOutputDir: false,
adjustDevServerOptions: this.cli.options.adjustDevServerOptions,
setupDevServerMiddleware: this.cli.options.setupDevServerMiddleware,
setupDevServerPlugins: [...this.cli.options.setupDevServerPlugins, ...withWrap],
});
try {
await this.engine.start();

const browser = await chromium.launch();
// In 2022 Twitter & Facebook recommend a size of 1200x628 - we capture with 2 dpr for retina displays
const context = await browser.newContext({
viewport: { width: 1200, height: 628 },
deviceScaleFactor: 2,
});
const page = await context.newPage();

for (const openGraphFile of openGraphFiles) {
const relUrl = path.relative(this.cli.options.outputDevDir, openGraphFile);
const imagePath = openGraphFile.replace('.opengraph.html', '.opengraph.png');
const htmlPath = openGraphFile.replace('.opengraph.html', '.html');
const relImageUrl = path.basename(imagePath);

let htmlString = await readFile(htmlPath, 'utf8');
if (!htmlString.includes('<meta property="og:image"')) {
if (htmlString.includes('</head>')) {
htmlString = htmlString.replace(
'</head>',
[
' <meta property="og:image:width" content="2400">',
' <meta property="og:image:height" content="1256">',
` <meta property="og:image" content="./${relImageUrl}">`,
' </head>',
].join('\n'),
);
}
}
const url = `http://localhost:${this.engine.devServer.config.port}/${relUrl}`;
await page.goto(url);
await page.screenshot({ path: imagePath });

await unlink(openGraphFile);
await writeFile(htmlPath, htmlString);
}
await browser.close();

await this.engine.stop();
} catch (e) {
console.log('Could not start dev server to generate open graph images');
console.error(e);
}
}
}
3 changes: 2 additions & 1 deletion packages/cli/src/RocketCli.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ export class RocketCli {
serviceWorkerName: 'service-worker.js',
buildOptimize: true,
buildAutoStop: true,
buildOpenGraphImages: true,

adjustBuildOptions: options => options,
adjustDevServerOptions: options => options,

configFile: '',
absoluteBaseUrl: '',
emptyOutputDir: true,
clearOutputDir: true,

// /** @type {{[key: string]: ImagePreset}} */
// imagePresets: {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/RocketStart.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class RocketStart {
outputDir: this.cli.options.outputDir,
setupPlugins: this.cli.options.setupEnginePlugins,
open: this.cli.options.open,
clearOutputDir: this.cli.options.clearOutputDir,
adjustDevServerOptions: this.cli.options.adjustDevServerOptions,
setupDevServerMiddleware: this.cli.options.setupDevServerMiddleware,
setupDevServerPlugins: [...this.cli.options.setupDevServerPlugins, ...withWrap],
Expand Down
116 changes: 116 additions & 0 deletions packages/cli/test-node/04-open-graph.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import chai from 'chai';
import { setupTestCli } from './test-helpers.js';

const { expect } = chai;

describe('Open Graph', () => {
it('generates the image and adds the meta tags', async () => {
const { build, readOutput, outputExists } = await setupTestCli(
'fixtures/04-open-graph/01-generate-image-and-inject-meta',
undefined,
{
buildOptimize: true,
},
);
await build();

expect(readOutput('index.html', { replaceImageHashes: true })).to.equal(
[
'<!DOCTYPE html>',
'<html lang="en">',
' <head>',
' <meta charset="utf-8" />',
' <meta property="og:image:width" content="2400" />',
' <meta property="og:image:height" content="1256" />',
' <meta property="og:image" content="http://my-site.com/__HASH__.png" />',
' </head>',
' <body>',
' <h1>Hello World!</h1>',
' </body>',
'</html>',
].join('\n'),
);

expect(outputExists('./index.opengraph.html')).to.be.false;
});

it('handles multiple pages', async () => {
const { build, readOutput } = await setupTestCli(
'fixtures/04-open-graph/02-multiple-pages',
undefined,
{
buildOptimize: true,
},
);
await build();

expect(readOutput('index.html', { replaceImageHashes: true })).to.equal(
[
'<!DOCTYPE html>',
'<html lang="en">',
' <head>',
' <meta charset="utf-8" />',
' <meta property="og:image:width" content="2400" />',
' <meta property="og:image:height" content="1256" />',
' <meta property="og:image" content="http://my-site.com/__HASH__.png" />',
' </head>',
' <body>',
' <h1>Hello World!</h1>',
' </body>',
'</html>',
].join('\n'),
);

expect(readOutput('components/index.html', { replaceImageHashes: true })).to.equal(
[
'<!DOCTYPE html>',
'<html lang="en">',
' <head>',
' <meta charset="utf-8" />',
' <meta property="og:image:width" content="2400" />',
' <meta property="og:image:height" content="1256" />',
' <meta property="og:image" content="http://my-site.com/__HASH__.png" />',
' </head>',
' <body>',
' <h1>Components</h1>',
' </body>',
'</html>',
].join('\n'),
);

expect(readOutput('components/accordion/index.html', { replaceImageHashes: true })).to.equal(
[
'<!DOCTYPE html>',
'<html lang="en">',
' <head>',
' <meta charset="utf-8" />',
' <meta property="og:image:width" content="2400" />',
' <meta property="og:image:height" content="1256" />',
' <meta property="og:image" content="http://my-site.com/__HASH__.png" />',
' </head>',
' <body>',
' <h1>Accordion</h1>',
' </body>',
'</html>',
].join('\n'),
);

// This image is "wrong" as it does not output the page title as the page is not added to the page tree
expect(readOutput('components/special.html', { replaceImageHashes: true })).to.equal(
[
'<!DOCTYPE html>',
'<html lang="en">',
' <head>',
' <meta charset="utf-8" />',
' <meta property="og:image:width" content="2400" />',
' <meta property="og:image:height" content="1256" />',
' <meta property="og:image" content="http://my-site.com/__HASH__.png" />',
' </head>',
' <body>',
' <h1>Special</h1>',
' </body>',
'</html>',
].join('\n'),
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default /** @type {import('../../../../../types/main').RocketCliOptions} */ ({
absoluteBaseUrl: 'http://my-site.com',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = 'index.rocket.js';
import { layout, openGraphLayout, html } from './local.data.js';
export { layout, openGraphLayout, html };
/* END - Rocket auto generated - do not touch */

export default () => html`<h1>Hello World!</h1>`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { PageTree } from '@rocket/engine';
import { html } from 'lit';

export const layout = data => html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
${data.content()}
</body>
</html>
`;

const pageTree = new PageTree();
await pageTree.restore(new URL('./pageTreeData.rocketGenerated.json', import.meta.url));

export const openGraphLayout = data => html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>${pageTree.getPage(data.sourceRelativeFilePath)?.model?.name}</h1>
<footer>Generated by <a href="https://next.rocket.modern-web.dev/">Rocket</a></footer>
</body>
</html>
`;

export { html };
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"h1": "Hello World!",
"name": "Hello World!",
"menuLinkText": "Hello World!",
"url": "/",
"outputRelativeFilePath": "index.html",
"sourceRelativeFilePath": "index.rocket.js",
"level": 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default /** @type {import('../../../../../types/main').RocketCliOptions} */ ({
absoluteBaseUrl: 'http://my-site.com',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = 'components/accordion.rocket.js';
import { layout, openGraphLayout, html } from '../recursive.data.js';
export { layout, openGraphLayout, html };
/* END - Rocket auto generated - do not touch */

export default () => html`<h1>Accordion</h1>`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = 'components/index.rocket.js';
import { layout, openGraphLayout, html } from '../recursive.data.js';
export { layout, openGraphLayout, html };
/* END - Rocket auto generated - do not touch */

export default () => html`<h1>Components</h1>`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = 'components/special.html.rocket.js';
import { layout, openGraphLayout, html } from '../recursive.data.js';
export { layout, openGraphLayout, html };
/* END - Rocket auto generated - do not touch */

export default () => html`<h1>Special</h1>`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* START - Rocket auto generated - do not touch */
export const sourceRelativeFilePath = 'index.rocket.js';
import { layout, openGraphLayout, html } from './recursive.data.js';
export { layout, openGraphLayout, html };
/* END - Rocket auto generated - do not touch */

export default () => html`<h1>Hello World!</h1>`;

0 comments on commit bf87645

Please sign in to comment.