From 01a57ba76608c52478d6227b76d8c7ede8b0c1a4 Mon Sep 17 00:00:00 2001 From: Sander Elias Date: Sat, 20 Nov 2021 07:42:24 +0100 Subject: [PATCH] feat(scully-plugin-puppeteer): move Puppeteer to a stand-alone (optional) plugin (#1470) * feat(scully-plugin-puppeteer): move Puppeteer to a stand-alone (optional) plugin ISSUES CLOSED: #1462 * fix(scully-plugin-puppeteer): fix circular dependency issue by ovoinf the name in TEXT messages. ISSUES CLOSED: #1462 * refactor(scully-plugin-puppeteer): don't use a config setting anymore for the 'default' routeRenderer --- .gitignore | 1 + .vscode/settings.json | 3 - jest.config.js | 2 +- libs/plugins/extra/package.json | 2 +- .../src/lib/installInterceptor.ts | 9 +- libs/plugins/scully-plugin-puppeteer/.babelrc | 3 + .../scully-plugin-puppeteer/.eslintrc.json | 18 ++++ .../plugins/scully-plugin-puppeteer/README.md | 10 ++ .../scully-plugin-puppeteer/jest.config.js | 15 +++ .../scully-plugin-puppeteer/package.json | 27 +++++ .../scully-plugin-puppeteer/src/index.ts | 11 ++ .../src/lib}/launchedBrowser.ts | 9 +- .../plugins-scully-plugin-puppeteer.spec.ts | 7 ++ .../lib/plugins-scully-plugin-puppeteer.ts} | 31 ++---- .../scully-plugin-puppeteer/tsconfig.json | 13 +++ .../scully-plugin-puppeteer/tsconfig.lib.json | 11 ++ .../tsconfig.spec.json | 9 ++ libs/scully/src/index.ts | 18 +++- .../src/lib/renderPlugins/executePlugins.ts | 5 +- libs/scully/src/lib/renderPlugins/index.ts | 3 +- libs/scully/src/lib/utils/asyncPool.ts | 4 +- libs/scully/src/lib/utils/config.ts | 1 + .../src/lib/utils/handlers/renderParallel.ts | 2 +- .../src/lib/utils/handlers/renderPlugin.ts | 6 -- .../src/lib/utils/interfacesandenums.ts | 1 + .../src/lib/utils/platform-server/index.ts | 1 + .../platform-server/serverPlatformRender.ts | 5 +- .../lib/utils/platform-server/startupSpS.ts | 18 +++- .../src/lib/utils/startup/scullyInit.ts | 41 +++++++ libs/scully/src/lib/utils/waitForIt.ts | 4 + nx.json | 9 +- scully.sample-blog.config.ts | 4 +- scully.scully-docs.config.ts | 3 +- scully.sps-sample.config.ts | 3 +- scully/plugins/render-once.ts | 6 +- tools/package.json | 2 +- tsconfig.base.json | 1 + workspace.json | 101 ++++++++++++------ 38 files changed, 314 insertions(+), 105 deletions(-) create mode 100644 libs/plugins/scully-plugin-puppeteer/.babelrc create mode 100644 libs/plugins/scully-plugin-puppeteer/.eslintrc.json create mode 100644 libs/plugins/scully-plugin-puppeteer/README.md create mode 100644 libs/plugins/scully-plugin-puppeteer/jest.config.js create mode 100644 libs/plugins/scully-plugin-puppeteer/package.json create mode 100644 libs/plugins/scully-plugin-puppeteer/src/index.ts rename libs/{scully/src/lib/renderPlugins => plugins/scully-plugin-puppeteer/src/lib}/launchedBrowser.ts (93%) create mode 100644 libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.spec.ts rename libs/{scully/src/lib/renderPlugins/puppeteerRenderPlugin.ts => plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.ts} (87%) create mode 100644 libs/plugins/scully-plugin-puppeteer/tsconfig.json create mode 100644 libs/plugins/scully-plugin-puppeteer/tsconfig.lib.json create mode 100644 libs/plugins/scully-plugin-puppeteer/tsconfig.spec.json create mode 100644 libs/scully/src/lib/utils/waitForIt.ts diff --git a/.gitignore b/.gitignore index 79746e88e..7ad0bdd8a 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ testDocs scully/tsconfig.sps-sample.json adsample.js bs.sh +consoleProxy.ts \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index a0d2f24c6..d922d4268 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,9 +20,6 @@ "statusBar.background": "#131f64", "statusBarItem.hoverBackground": "#1b2c8f", "statusBar.foreground": "#e7e7e7", - "panel.border": "#1b2c8f", - "sideBar.border": "#1b2c8f", - "editorGroup.border": "#1b2c8f", "sash.hoverBorder": "#1b2c8f", "statusBarItem.remoteBackground": "#131f64", "statusBarItem.remoteForeground": "#e7e7e7" diff --git a/jest.config.js b/jest.config.js index ff8f0dd5e..762eafb2d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,3 @@ module.exports = { - projects: ['/tests/jest/src', '/libs/platform-server'], + projects: ['/tests/jest/src', '/libs/platform-server', '/libs/plugins/scully-plugin-puppeteer'], }; diff --git a/libs/plugins/extra/package.json b/libs/plugins/extra/package.json index 0cf27d34b..c99b81ef0 100644 --- a/libs/plugins/extra/package.json +++ b/libs/plugins/extra/package.json @@ -21,4 +21,4 @@ "scully-plugin", "plugin" ] -} \ No newline at end of file +} diff --git a/libs/plugins/scully-plugin-local-cache/src/lib/installInterceptor.ts b/libs/plugins/scully-plugin-local-cache/src/lib/installInterceptor.ts index b4ed0ac35..1d9082ab8 100644 --- a/libs/plugins/scully-plugin-local-cache/src/lib/installInterceptor.ts +++ b/libs/plugins/scully-plugin-local-cache/src/lib/installInterceptor.ts @@ -1,10 +1,15 @@ -import { launchedBrowser$ } from '@scullyio/scully'; +import { findPlugin } from '@scullyio/scully'; import { Browser, Page, Target } from 'puppeteer'; +import { Observable } from 'rxjs'; import { config } from './config'; import { handlePuppeteerRequest } from './handlePuppeteerRequest'; import { handlePuppeteerResponse } from './handlePuppeteerResponse'; -export function installInterceptor() { +export async function installInterceptor() { + const launchedBrowser$ = findPlugin('getPPTLaunchedBrowser')() as Observable; + if (!launchedBrowser$) { + throw new Error('No launched browser found'); + } launchedBrowser$.subscribe(async (browser: Browser) => { browser.on('targetcreated', async (ev: Target) => { const page: Page = await ev.page(); diff --git a/libs/plugins/scully-plugin-puppeteer/.babelrc b/libs/plugins/scully-plugin-puppeteer/.babelrc new file mode 100644 index 000000000..cf7ddd99c --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/plugins/scully-plugin-puppeteer/.eslintrc.json b/libs/plugins/scully-plugin-puppeteer/.eslintrc.json new file mode 100644 index 000000000..3456be9b9 --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/plugins/scully-plugin-puppeteer/README.md b/libs/plugins/scully-plugin-puppeteer/README.md new file mode 100644 index 000000000..e475da5b6 --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/README.md @@ -0,0 +1,10 @@ +# scully-plugin-puppeteer + +This is the Puppeteer render plugin for Scully. + +The interface for a renderPlugin is: +```ts + (route:HandledRoute) => Promise +``` + +This plugin will be called for every route that is in the `handledRoute[]` When it throws its retried for 3 times. If it fails after that, the route is skipped. diff --git a/libs/plugins/scully-plugin-puppeteer/jest.config.js b/libs/plugins/scully-plugin-puppeteer/jest.config.js new file mode 100644 index 000000000..53d89b853 --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/jest.config.js @@ -0,0 +1,15 @@ +module.exports = { + displayName: 'plugins-scully-plugin-puppeteer', + preset: '../../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../coverage/libs/plugins/scully-plugin-puppeteer', +}; diff --git a/libs/plugins/scully-plugin-puppeteer/package.json b/libs/plugins/scully-plugin-puppeteer/package.json new file mode 100644 index 000000000..84adaf83b --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/package.json @@ -0,0 +1,27 @@ +{ + "name": "@scullyio/scully-plugin-puppeteer", + "version": "2.0.0", + "description": "Scully Puppeteer render plugin", + "main": "./src/index.js", + "license": "MIT", + "dependencies": { + "tslib": "^1.13.0" + }, + "peerDependencies": { + "@scullyio/scully": "*", + "puppeteer": "^10.2.0" + }, + "repository": { + "type": "GIT", + "url": "https://github.com/scullyio/scully/tree/main/libs/plugins/scully-plugin-puppeteer" + }, + "keywords": [ + "angular", + "scully", + "seo", + "scully-plugin", + "puppeteer", + "plugin", + "render" + ] +} diff --git a/libs/plugins/scully-plugin-puppeteer/src/index.ts b/libs/plugins/scully-plugin-puppeteer/src/index.ts new file mode 100644 index 000000000..82540517b --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/src/index.ts @@ -0,0 +1,11 @@ +import { logOk, routeRenderer, registerPlugin } from '@scullyio/scully'; +import { launchedBrowser } from './lib/launchedBrowser'; +import { puppeteerRender } from './lib/plugins-scully-plugin-puppeteer'; +import { launchedBrowser$ } from './lib/launchedBrowser'; + +registerPlugin('enterprise','getPPTLaunchedBrowser',async () => launchedBrowser$) +registerPlugin('beforeAll', 'startLaunching the browser', async () => { + logOk('Puppeteer is being launched') + launchedBrowser(); +}) +registerPlugin('scullySystem', routeRenderer, puppeteerRender); diff --git a/libs/scully/src/lib/renderPlugins/launchedBrowser.ts b/libs/plugins/scully-plugin-puppeteer/src/lib/launchedBrowser.ts similarity index 93% rename from libs/scully/src/lib/renderPlugins/launchedBrowser.ts rename to libs/plugins/scully-plugin-puppeteer/src/lib/launchedBrowser.ts index 8f04407fe..3ebf45548 100644 --- a/libs/scully/src/lib/renderPlugins/launchedBrowser.ts +++ b/libs/plugins/scully-plugin-puppeteer/src/lib/launchedBrowser.ts @@ -1,11 +1,8 @@ +import { cliOptions, loadConfig, logError, logOk, scullyConfig, waitForIt, yellow } from '@scullyio/scully'; import { Browser, launch } from 'puppeteer'; -import { BehaviorSubject, from, interval, merge, Observable, of, timer } from 'rxjs'; -import { catchError, delayWhen, filter, shareReplay, switchMap, take, throttleTime } from 'rxjs'; -import { showBrowser, serverTimeout } from '../utils/cli-options'; -import { loadConfig, scullyConfig } from '../utils/config'; -import { logOk, logError, yellow } from '../utils/log'; -import { waitForIt } from './puppeteerRenderPlugin'; +import { BehaviorSubject, catchError, delayWhen, filter, from, interval, merge, Observable, of, shareReplay, switchMap, take, throttleTime, timer } from 'rxjs'; +const { showBrowser, serverTimeout } = cliOptions; const launches = new BehaviorSubject(undefined); /** * Returns an Observable with that will fire with the launched puppeteer in there. diff --git a/libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.spec.ts b/libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.spec.ts new file mode 100644 index 000000000..c78445d3b --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.spec.ts @@ -0,0 +1,7 @@ +import { puppeteerRender } from './plugins-scully-plugin-puppeteer'; + +describe('pluginsScullyPluginPuppeteer', () => { + it('should work', () => { + expect(puppeteerRender).toEqual(puppeteerRender) + }); +}); diff --git a/libs/scully/src/lib/renderPlugins/puppeteerRenderPlugin.ts b/libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.ts similarity index 87% rename from libs/scully/src/lib/renderPlugins/puppeteerRenderPlugin.ts rename to libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.ts index 5237b5df5..7be78c191 100644 --- a/libs/scully/src/lib/renderPlugins/puppeteerRenderPlugin.ts +++ b/libs/plugins/scully-plugin-puppeteer/src/lib/plugins-scully-plugin-puppeteer.ts @@ -1,19 +1,13 @@ // tslint:disable: no-string-literal -// const puppeteer = require('puppeteer'); + +import { createFolderFor, HandledRoute, logError, logWarn, scullyConfig, title404, waitForIt, yellow } from '@scullyio/scully'; +import { captureException } from '@scullyio/scully/src/lib/utils/captureMessage'; +import { showBrowser, ssl } from '@scullyio/scully/src/lib/utils/cli-options'; import { readFileSync } from 'fs-extra'; import { jsonc } from 'jsonc'; import { join } from 'path'; import { Browser, Page, Serializable } from 'puppeteer'; -import { interval, Subject } from 'rxjs'; -import { filter, switchMap, take } from 'rxjs'; -import { registerPlugin, scullySystem } from '../pluginManagement'; -import { HandledRoute } from '../routerPlugins/handledRoute.interface'; -import { createFolderFor } from '../utils'; -import { showBrowser, ssl } from '../utils/cli-options'; -import { scullyConfig } from '../utils/config'; -import { logError, logWarn, yellow } from '../utils/log'; -import { captureException } from '../utils/captureMessage'; -import { title404 } from '../utils/serverstuff/title404'; +import { filter, interval, Subject, switchMap, take } from 'rxjs'; import { launchedBrowser, reLaunch } from './launchedBrowser'; const errorredPages = new Map(); @@ -28,16 +22,16 @@ try { // version = jsonc.parse(readFileSync(join(__dirname, '../../../package.json')).toString()).version || '0.0.0'; } -export const puppeteerRender = 'puppeteerRender' as const; -const plugin = async (route: HandledRoute): Promise => { + +export const puppeteerRender = async (route: HandledRoute): Promise => { const timeOutValueInSeconds = 25; const pageLoaded = new Subject(); const path = route.rawRoute ? route.rawRoute : scullyConfig.hostUrl - ? `${scullyConfig.hostUrl}${route.route}` - : `http${ssl ? 's' : ''}://${scullyConfig.hostName}:${scullyConfig.appPort}${route.route}`; + ? `${scullyConfig.hostUrl}${route.route}` + : `http${ssl ? 's' : ''}://${scullyConfig.hostName}:${scullyConfig.appPort}${route.route}`; let pageHtml: string; let browser: Browser; @@ -221,17 +215,13 @@ const plugin = async (route: HandledRoute): Promise => { /** give it a couple of secs */ await waitForIt(3 * 1000); /** retry! */ - return plugin(route); + return puppeteerRender(route); } } return pageHtml; }; -export function waitForIt(milliSeconds: number) { - return new Promise((resolve) => setTimeout(() => resolve(), milliSeconds)); -} - const windowSet = (page: Page, name: string, value: Serializable) => page.evaluateOnNewDocument(` Object.defineProperty(window, '${name}', { @@ -241,4 +231,3 @@ const windowSet = (page: Page, name: string, value: Serializable) => }) `); -registerPlugin(scullySystem, puppeteerRender, plugin); diff --git a/libs/plugins/scully-plugin-puppeteer/tsconfig.json b/libs/plugins/scully-plugin-puppeteer/tsconfig.json new file mode 100644 index 000000000..667a3463d --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/plugins/scully-plugin-puppeteer/tsconfig.lib.json b/libs/plugins/scully-plugin-puppeteer/tsconfig.lib.json new file mode 100644 index 000000000..ca4b70c39 --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/plugins/scully-plugin-puppeteer/tsconfig.spec.json b/libs/plugins/scully-plugin-puppeteer/tsconfig.spec.json new file mode 100644 index 000000000..65aff5094 --- /dev/null +++ b/libs/plugins/scully-plugin-puppeteer/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.spec.js", "**/*.spec.jsx", "**/*.d.ts"] +} diff --git a/libs/scully/src/index.ts b/libs/scully/src/index.ts index b0c41c8de..0830f2191 100644 --- a/libs/scully/src/index.ts +++ b/libs/scully/src/index.ts @@ -4,18 +4,17 @@ import { getPluginConfig, setConfig, setPluginConfig, - setPluginPriority, + setPluginPriority } from './lib/pluginManagement/pluginConfig'; import { configValidator, registerPlugin } from './lib/pluginManagement/pluginRepository'; import './lib/pluginManagement/systemPlugins'; import { ContentMetaData } from './lib/renderPlugins/content-render-utils/readFileAndCheckPrePublishSlug'; import { renderRoute } from './lib/renderPlugins/executePlugins'; -import { launchedBrowser$ } from './lib/renderPlugins/launchedBrowser'; import { ContentTextRoute, HandledRoute, RouteConfig } from './lib/routerPlugins/handledRoute.interface'; import { WriteToStorage } from './lib/systemPlugins/writeToFs.plugin'; import { createFolderFor } from './lib/utils'; import { prod } from './lib/utils/cli-options'; -import { loadConfig, scullyConfig, updateScullyConfig } from './lib/utils/config'; +import { loadConfig, routeRenderer, scullyConfig, updateScullyConfig } from './lib/utils/config'; import './lib/utils/exitHandler'; import { handleTravesal } from './lib/utils/handlers/handleTravesal'; import { routeDiscovery } from './lib/utils/handlers/routeDiscovery'; @@ -24,12 +23,18 @@ import { RouteTypes, ScullyConfig } from './lib/utils/interfacesandenums'; import { replaceFirstRouteParamWithVal } from './lib/utils/replaceFirstRouteParamWithVal'; import { routeSplit } from './lib/utils/routeSplit'; import { staticServer } from './lib/utils/serverstuff/staticServer'; +import { title404 } from './lib/utils/serverstuff/title404'; import { getHandledRoutes } from './lib/utils/services/routeStorage'; import { startScully } from './lib/utils/startup'; +export * as cliOptions from './lib/utils/cli-options'; export * from './lib/utils/log'; +export * from './lib/utils/platform-server'; export * from './lib/utils/procesmanager'; -export * from './lib/utils/platform-server' +export { waitForIt } from './lib/utils/waitForIt'; +export { SPSRouteRenderer } from './lib/utils/platform-server'; export { + title404, + routeRenderer, configValidator, ContentMetaData, ContentTextRoute, @@ -51,7 +56,7 @@ export { updateScullyConfig, /** WIP part, those might be remove again in near future. */ handleTravesal, - launchedBrowser$, + // launchedBrowser$, loadConfig, renderRoute, routeDiscovery, @@ -63,3 +68,6 @@ export { prod, setPluginPriority, }; + + + diff --git a/libs/scully/src/lib/renderPlugins/executePlugins.ts b/libs/scully/src/lib/renderPlugins/executePlugins.ts index e837ab524..69b3b789c 100644 --- a/libs/scully/src/lib/renderPlugins/executePlugins.ts +++ b/libs/scully/src/lib/renderPlugins/executePlugins.ts @@ -1,10 +1,9 @@ import { findPlugin } from '../pluginManagement/pluginConfig'; import { registerPlugin, scullySystem } from '../pluginManagement/pluginRepository'; import { HandledRoute } from '../routerPlugins/handledRoute.interface'; -import { scullyConfig } from '../utils/config'; +import { scullyConfig,routeRenderer } from '../utils/config'; import { logError, yellow, logWarn } from '../utils/log'; import { captureException } from '../utils/captureMessage'; -import { puppeteerRender } from './puppeteerRenderPlugin'; import { toJSDOM, fromJSDOM } from './jsdomPlugins'; import { JSDOM } from 'jsdom'; import { postProcessByDomPlugin, postProcessByHtmlPlugin } from '../pluginManagement'; @@ -30,7 +29,7 @@ const executePluginsForRoute = async (route: HandledRoute) => { } } // this support different renders: puppeteer / imgRender / sps / others... - const InitialHTML = (await (route.renderPlugin ? findPlugin(route.renderPlugin) : findPlugin(puppeteerRender))(route)) as string; + const InitialHTML = (await (route.renderPlugin ? findPlugin(route.renderPlugin) : findPlugin(routeRenderer))(route)) as string; // split out jsDom vs string renderers. const { jsDomRenders, renders: stringRenders } = handlers.reduce( diff --git a/libs/scully/src/lib/renderPlugins/index.ts b/libs/scully/src/lib/renderPlugins/index.ts index 51f5fc8e3..eeec337ff 100644 --- a/libs/scully/src/lib/renderPlugins/index.ts +++ b/libs/scully/src/lib/renderPlugins/index.ts @@ -2,6 +2,5 @@ export * from './contentRenderPlugin'; export * from './contentTextRenderPlugin'; export * from './executePlugins'; export * from './jsdomPlugins'; -export * from './launchedBrowser'; -export * from './puppeteerRenderPlugin'; export * from './seoHrefCompletionPlugin'; + diff --git a/libs/scully/src/lib/utils/asyncPool.ts b/libs/scully/src/lib/utils/asyncPool.ts index 323559a53..cf82e090e 100644 --- a/libs/scully/src/lib/utils/asyncPool.ts +++ b/libs/scully/src/lib/utils/asyncPool.ts @@ -1,6 +1,6 @@ -import { waitForIt } from '../renderPlugins/puppeteerRenderPlugin'; -import { log, logWarn, printProgress } from './log'; import { performance } from 'perf_hooks'; +import { logWarn, printProgress } from './log'; +import { waitForIt } from './waitForIt'; const progressTime = 100; /** diff --git a/libs/scully/src/lib/utils/config.ts b/libs/scully/src/lib/utils/config.ts index 59b75b18d..45546280f 100644 --- a/libs/scully/src/lib/utils/config.ts +++ b/libs/scully/src/lib/utils/config.ts @@ -9,6 +9,7 @@ import { readAngularJson } from './read-angular-json'; import { validateConfig } from './validateConfig'; export const angularRoot = findAngularJsonPath(); export const scullyConfig: ScullyConfig = {} as ScullyConfig; +export const routeRenderer = 'routeRenderer' as const; export const scullyDefaults: Partial = { bareProject: false, diff --git a/libs/scully/src/lib/utils/handlers/renderParallel.ts b/libs/scully/src/lib/utils/handlers/renderParallel.ts index 9e298a70c..170e123b8 100644 --- a/libs/scully/src/lib/utils/handlers/renderParallel.ts +++ b/libs/scully/src/lib/utils/handlers/renderParallel.ts @@ -1,12 +1,12 @@ import { performance } from 'perf_hooks'; import { findPlugin } from '../../pluginManagement/pluginConfig'; import { renderRoute } from '../../renderPlugins/executePlugins'; -import { waitForIt } from '../../renderPlugins/puppeteerRenderPlugin'; import { WriteToStorage } from '../../systemPlugins/writeToFs.plugin'; import { asyncPool } from '../asyncPool'; import { scullyConfig } from '../config'; import { logWarn } from '../log'; import { performanceIds } from '../performanceIds'; +import { waitForIt } from '../waitForIt'; const writeToFs = findPlugin(WriteToStorage); diff --git a/libs/scully/src/lib/utils/handlers/renderPlugin.ts b/libs/scully/src/lib/utils/handlers/renderPlugin.ts index 1578c51cf..796aa1ee8 100644 --- a/libs/scully/src/lib/utils/handlers/renderPlugin.ts +++ b/libs/scully/src/lib/utils/handlers/renderPlugin.ts @@ -1,17 +1,11 @@ import { registerPlugin, scullySystem } from '../../pluginManagement'; -import { launchedBrowser } from '../../renderPlugins/launchedBrowser'; import { HandledRoute } from '../../routerPlugins/handledRoute.interface'; -import { printProgress } from '../log'; import { renderParallel } from './renderParallel'; export const renderPlugin = 'renderPlugin' as const; registerPlugin(scullySystem, renderPlugin, defaultRenderPlugin); async function defaultRenderPlugin(handledRoutes: HandledRoute[]) { - /** update progress to show what's going on */ - printProgress(false, 'Starting puppeteer'); - /** launch the browser, its shared among renderers */ - await launchedBrowser(); /** start handling each route, works in chunked parallel mode */ await renderParallel(handledRoutes); } diff --git a/libs/scully/src/lib/utils/interfacesandenums.ts b/libs/scully/src/lib/utils/interfacesandenums.ts index ee37b48c0..0d0f64698 100644 --- a/libs/scully/src/lib/utils/interfacesandenums.ts +++ b/libs/scully/src/lib/utils/interfacesandenums.ts @@ -1,3 +1,4 @@ +import { ExecFileSyncOptionsWithBufferEncoding } from 'child_process'; import { PuppeteerNodeLaunchOptions } from 'puppeteer'; export enum RouteTypes { diff --git a/libs/scully/src/lib/utils/platform-server/index.ts b/libs/scully/src/lib/utils/platform-server/index.ts index 4388e5c0f..1f33d7b47 100644 --- a/libs/scully/src/lib/utils/platform-server/index.ts +++ b/libs/scully/src/lib/utils/platform-server/index.ts @@ -1,3 +1,4 @@ // export * from './ps-runner'; export { enableSPS } from './startupSpS' export { Deferred } from './deferred' +export { SPSRouteRenderer } from './serverPlatformRender' diff --git a/libs/scully/src/lib/utils/platform-server/serverPlatformRender.ts b/libs/scully/src/lib/utils/platform-server/serverPlatformRender.ts index 7f75244ff..8603f4e07 100644 --- a/libs/scully/src/lib/utils/platform-server/serverPlatformRender.ts +++ b/libs/scully/src/lib/utils/platform-server/serverPlatformRender.ts @@ -53,8 +53,9 @@ async function spsPoolInitPlugin(path) { } -export const renderWithSpS = 'renderWithSpS' as const; -registerPlugin('scullySystem', renderWithSpS, renderWithSpSPlugin); +export const SPSRouteRenderer = 'SPSRouteRenderer' as const; +export const SPSRenderer = 'SPSRenderer' as const; +registerPlugin('scullySystem', SPSRenderer, renderWithSpSPlugin); async function renderWithSpSPlugin(routes: HandledRoute[]) { printProgress(undefined, 'Rendering using Scully Platform Server'); const jobs = routes.map((r, i) => { diff --git a/libs/scully/src/lib/utils/platform-server/startupSpS.ts b/libs/scully/src/lib/utils/platform-server/startupSpS.ts index 03eec59a1..079b925e5 100644 --- a/libs/scully/src/lib/utils/platform-server/startupSpS.ts +++ b/libs/scully/src/lib/utils/platform-server/startupSpS.ts @@ -2,14 +2,14 @@ import { exec } from 'child_process'; import { existsSync, rmSync, writeFileSync } from 'fs'; import { join } from 'path'; import { filter, merge, tap } from 'rxjs'; -import { getHandledRoutes, handleJobs, Job, logOk } from '..'; -import { getPool, green, loadConfig, log, logError, logWarn, printProgress, registerPlugin, scullyConfig, yellow } from '../../..'; +import { getHandledRoutes, handleJobs, Job, routeRenderer } from '..'; +import { getPool, green, loadConfig, log, logError, printProgress, registerPlugin, scullyConfig, yellow } from '../../..'; import { findPlugin } from '../../pluginManagement'; import { renderPlugin } from '../handlers/renderPlugin'; import { terminateAllPools } from '../procesmanager/taskPool'; import { readDotProperty } from '../scullydot'; import { Deferred } from './deferred'; -import { initSpSPool, renderWithSpS } from './serverPlatformRender'; +import { initSpSPool, SPSRenderer } from './serverPlatformRender'; const workerPath = join(__dirname, 'ps-worker.js') @@ -48,7 +48,7 @@ const plugin = async () => { }); } else { const { sourceRoot, homeFolder, spsModulePath } = scullyConfig - if (spsModulePath=== undefined) { + if (spsModulePath === undefined) { logError(`For the SPS renderer the option "spsModulePath" needs to be part of your projects scullyConfig. Aborting run`) process.exit(15) } @@ -82,9 +82,17 @@ const plugin = async () => { } }; +/** + * Set up the Scully Platform Server render + */ export function enableSPS() { + /** do the setup (compile angular app etc) */ registerPlugin('beforeAll', 'compileAngularApp', plugin); - registerPlugin('scullySystem', renderPlugin, findPlugin(renderWithSpS), undefined, { replaceExistingPlugin: true }); + /** replace the render plugin with the SPS specific render plugin */ + registerPlugin('scullySystem', renderPlugin, findPlugin(SPSRenderer), undefined, { replaceExistingPlugin: true }); + /** register dummy routeRenderer, to prevent loading PPT by default */ + registerPlugin('scullySystem', routeRenderer, async () => undefined); + /** make sure tear-down of the workers happens */ registerPlugin('allDone', 'exitAllWorkers', terminateAllPools); } diff --git a/libs/scully/src/lib/utils/startup/scullyInit.ts b/libs/scully/src/lib/utils/startup/scullyInit.ts index 2ef449577..1754914ea 100644 --- a/libs/scully/src/lib/utils/startup/scullyInit.ts +++ b/libs/scully/src/lib/utils/startup/scullyInit.ts @@ -1,7 +1,9 @@ import open from 'open'; import { join } from 'path'; import { captureException, green, hostName, httpGetJson, installExitHandler, isPortTaken, loadConfig, log, logError, logWarn, moveDistAngular, openNavigator, removeStaticDist, ScullyConfig, scullyDefaults, ssl, startScully, waitForServerToBeAvailable, watch, yellow } from '../'; +import { findPlugin } from '../../pluginManagement'; import { project } from '../cli-options'; +import { routeRenderer } from '../config'; import { DotProps, readAllDotProps, readDotProperty, writeDotProperty } from '../scullydot'; import { startBackgroundServer } from './startBackgroundServer'; import { bootServe, isBuildThere, watchMode } from './watchMode'; @@ -25,6 +27,12 @@ export const scullyInit = async () => { updateDotProps(scullyConfig); + /** + * this strange notation is there to prevent circular dependency errors + * when lazy loading the plugin. + */ + await checkIfRenderPluginIsLoaded(scullyConfig); + /** check if the dist folder containes a build */ await isBuildThere(scullyConfig); @@ -72,6 +80,39 @@ You are using "${yellow(scullyConfig.hostUrl)}" as server. }; +async function checkIfRenderPluginIsLoaded(scullyConfig: ScullyConfig) { + const pluginName = `@scullyio/${String.fromCharCode(115)}cully-plugin-puppeteer`; + if (!findPlugin(routeRenderer, undefined, false)) { + try { + await import(pluginName); + + } catch { + logError(` Notice: + ============================================================ + The scully-plugin-puppeteer is not installed. please run: + npm install ${pluginName} + and try again. + ============================================================`); + process.exit(15); + }; + logWarn(` Deprication Notice: + ====================================================================== + From now on, the plugin that is being used to render a route is + able to be changed by the user. You can do this by adding or + enabling the plugin in the scully.json file. For your convenience, + we loaded the Puppeteer plugin for you. + + To disable this warning enable the plugin of your choiche. + for Puppteer please add: + import '${pluginName}'; + to your scully.${scullyConfig.projectName}.config.ts file. + + When you get this waring while not using scully-plugin-puppeteer + you need to set the defaultRouteRenderer to the name of your plugin. + The defaultRouteRenderer is now set to '${routeRenderer}'. + ======================================================================`); + } +} let scullyConfig: ScullyConfig; async function getConfig() { diff --git a/libs/scully/src/lib/utils/waitForIt.ts b/libs/scully/src/lib/utils/waitForIt.ts new file mode 100644 index 000000000..cfca50e68 --- /dev/null +++ b/libs/scully/src/lib/utils/waitForIt.ts @@ -0,0 +1,4 @@ + +export function waitForIt(milliSeconds: number) { + return new Promise((resolve) => setTimeout(() => resolve(), milliSeconds)); +} diff --git a/nx.json b/nx.json index 31ea783a1..6ab4e0c48 100644 --- a/nx.json +++ b/nx.json @@ -58,6 +58,9 @@ "plugins-scully-plugin-flash-prevention": { "tags": [] }, + "plugins-scully-plugin-puppeteer": { + "tags": [] + }, "plugins-scully-plugin-remove-scripts": { "tags": [] }, @@ -76,15 +79,15 @@ "scully-schematics": { "tags": ["schematics"] }, - "universal": { + "sps-sample": { "tags": [] }, - "sps-sample": { + "universal": { "tags": [] } }, "affected": { - "defaultBase": "master" + "defaultBase": "main" }, "targetDependencies": { "build": [ diff --git a/scully.sample-blog.config.ts b/scully.sample-blog.config.ts index 5c87ae0bb..015efadae 100644 --- a/scully.sample-blog.config.ts +++ b/scully.sample-blog.config.ts @@ -1,6 +1,6 @@ /** load the plugins */ // import './demos/plugins/extra-plugin.js'; -import { ContentTextRoute, enableSPS, HandledRoute, httpGetJson, logError, registerPlugin, RouteConfig, ScullyConfig, setPluginConfig } from '@scullyio/scully'; +import { SPSRouteRenderer, ContentTextRoute, enableSPS, HandledRoute, httpGetJson, logError, registerPlugin, RouteConfig, ScullyConfig, setPluginConfig } from '@scullyio/scully'; import { baseHrefRewrite } from '@scullyio/scully-plugin-base-href-rewrite'; import { docLink } from '@scullyio/scully-plugin-docs-link-update'; import '@scullyio/scully-plugin-extra'; @@ -10,6 +10,7 @@ import { removeScripts } from '@scullyio/scully-plugin-remove-scripts'; import './demos/plugins/errorPlugin'; import './demos/plugins/tocPlugin'; import './demos/plugins/voidPlugin'; +// import '@scullyio/scully-plugin-puppeteer'; // import { theVaultReady } from '@herodevs/scully-plugin-the-vault'; @@ -29,6 +30,7 @@ export const config: Promise = (async () => { // }) return { + defaultRouteRenderer: SPSRouteRenderer, /** outDir is where the static distribution files end up */ // bareProject:true, projectName: 'sample-blog', diff --git a/scully.scully-docs.config.ts b/scully.scully-docs.config.ts index ccd657f13..972aacef7 100644 --- a/scully.scully-docs.config.ts +++ b/scully.scully-docs.config.ts @@ -1,4 +1,4 @@ -import { prod, scullyConfig, registerPlugin, ScullyConfig, setPluginConfig, log, logError, enableSPS } from '@scullyio/scully'; +import { SPSRouteRenderer, prod, scullyConfig, registerPlugin, ScullyConfig, setPluginConfig, log, logError, enableSPS } from '@scullyio/scully'; import { docLink } from '@scullyio/scully-plugin-docs-link-update'; import { GoogleAnalytics } from '@scullyio/scully-plugin-google-analytics'; import { LogRocket } from '@scullyio/scully-plugin-logrocket'; @@ -65,6 +65,7 @@ export const config: Promise = createConfig(); async function createConfig(): Promise { // await localCacheReady(); return { + defaultRouteRenderer: SPSRouteRenderer, projectRoot: './apps/scully-docs/src', projectName: 'scully-docs', outDir: './dist/static/doc-sites', diff --git a/scully.sps-sample.config.ts b/scully.sps-sample.config.ts index b6f6f3b38..268ef5f78 100644 --- a/scully.sps-sample.config.ts +++ b/scully.sps-sample.config.ts @@ -1,4 +1,4 @@ -import { ScullyConfig, enableSPS, registerPlugin, HandledRoute } from '@scullyio/scully'; +import { ScullyConfig, enableSPS, registerPlugin, HandledRoute, SPSRouteRenderer } from '@scullyio/scully'; import '@scullyio/scully-plugin-extra'; import '@scullyio/scully-plugin-from-data'; import { JSDOM } from 'jsdom'; @@ -15,6 +15,7 @@ const defaultPostRenderers: string[] = ['blah', 'blahAh', 'seoHrefOptimise']; enableSPS(); export const config: ScullyConfig = { + defaultRouteRenderer: SPSRouteRenderer, projectName: 'sps-sample', outDir: './dist/static/sps-sample', defaultPostRenderers, diff --git a/scully/plugins/render-once.ts b/scully/plugins/render-once.ts index 4816c95bd..b6099639f 100644 --- a/scully/plugins/render-once.ts +++ b/scully/plugins/render-once.ts @@ -1,10 +1,8 @@ -import { findPlugin, HandledRoute, log, registerPlugin, yellow } from '@scullyio/scully'; +import { findPlugin, HandledRoute, log, routeRenderer, registerPlugin, yellow } from '@scullyio/scully'; import { scullySystem } from '@scullyio/scully/src/lib/pluginManagement/pluginRepository'; -import { puppeteerRender } from '@scullyio/scully/src/lib/renderPlugins/puppeteerRenderPlugin'; -//libs/scully/src/lib/renderPlugins/puppeteerRenderPlugin.ts export const renderOnce = 'renderOnce' as const; -const render = findPlugin(puppeteerRender); +const render = findPlugin(routeRenderer); const cache = new Map>(); registerPlugin(scullySystem, renderOnce, (route: HandledRoute, config) => { diff --git a/tools/package.json b/tools/package.json index a01054030..9f2012271 100644 --- a/tools/package.json +++ b/tools/package.json @@ -8,5 +8,5 @@ }, "type": "module", "author": "", - "license": "ISC" + "license": "MIT" } diff --git a/tsconfig.base.json b/tsconfig.base.json index 5a89da138..0f38bd95f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -17,6 +17,7 @@ "@scullyio/scully-plugin-google-analytics": ["libs/plugins/google-analytics/src/index.ts"], "@scullyio/scully-plugin-local-cache": ["libs/plugins/scully-plugin-local-cache/src/index.ts"], "@scullyio/scully-plugin-logrocket": ["libs/plugins/logrocket/src/index.ts"], + "@scullyio/scully-plugin-puppeteer": ["libs/plugins/scully-plugin-puppeteer/src/index.ts"], "@scullyio/scully-plugin-remove-scripts": ["libs/plugins/scully-plugin-remove-scripts/src/index.ts"], "@scullyio/scully-plugin-sentry": ["libs/plugins/sentry/src/index.ts"], "@scullyio/universal": ["libs/universal/src/index.ts"] diff --git a/workspace.json b/workspace.json index c473c5ee8..8842290f6 100644 --- a/workspace.json +++ b/workspace.json @@ -347,6 +347,39 @@ } } }, + "plugins-scully-plugin-puppeteer": { + "root": "libs/plugins/scully-plugin-puppeteer", + "sourceRoot": "libs/plugins/scully-plugin-puppeteer/src", + "projectType": "library", + "architect": { + "build": { + "builder": "@nrwl/node:package", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/scully-plugin-puppeteer", + "tsConfig": "libs/plugins/scully-plugin-puppeteer/tsconfig.lib.json", + "packageJson": "libs/plugins/scully-plugin-puppeteer/package.json", + "main": "libs/plugins/scully-plugin-puppeteer/src/index.ts", + "assets": ["libs/plugins/scully-plugin-puppeteer/*.md"] + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/plugins/scully-plugin-puppeteer/**/*.ts"] + } + }, + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["coverage/libs/plugins/scully-plugin-puppeteer"], + "options": { + "jestConfig": "libs/plugins/scully-plugin-puppeteer/jest.config.js", + "passWithNoTests": true + } + } + } + }, "plugins-scully-plugin-remove-scripts": { "root": "libs/plugins/scully-plugin-remove-scripts", "sourceRoot": "libs/plugins/scully-plugin-remove-scripts/src", @@ -655,40 +688,6 @@ } } }, - "universal": { - "projectType": "library", - "root": "libs/universal", - "sourceRoot": "libs/universal/src", - "prefix": "scullyio", - "architect": { - "build": { - "builder": "@nrwl/angular:package", - "options": { - "tsConfig": "libs/universal/tsconfig.lib.json", - "project": "libs/universal/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "libs/universal/tsconfig.lib.prod.json" - } - } - }, - "lint": { - "builder": "@nrwl/linter:eslint", - "options": { - "lintFilePatterns": ["libs/universal/src/**/*.ts", "libs/universal/src/**/*.html"] - } - }, - "test": { - "builder": "@nrwl/jest:jest", - "outputs": ["coverage/libs/universal"], - "options": { - "jestConfig": "libs/universal/jest.config.js", - "passWithNoTests": true - } - } - } - }, "sps-sample": { "projectType": "application", "root": "apps/sps-sample", @@ -777,6 +776,40 @@ } } } + }, + "universal": { + "projectType": "library", + "root": "libs/universal", + "sourceRoot": "libs/universal/src", + "prefix": "scullyio", + "architect": { + "build": { + "builder": "@nrwl/angular:package", + "options": { + "tsConfig": "libs/universal/tsconfig.lib.json", + "project": "libs/universal/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "libs/universal/tsconfig.lib.prod.json" + } + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": ["libs/universal/src/**/*.ts", "libs/universal/src/**/*.html"] + } + }, + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["coverage/libs/universal"], + "options": { + "jestConfig": "libs/universal/jest.config.js", + "passWithNoTests": true + } + } + } } }, "cli": {