diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 280fdbdbd..78c5d706f 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node: [ 16.x] + node: [16.x] steps: - name: Checkout branch @@ -117,6 +117,38 @@ jobs: name: static-sites-docs path: dist/static/doc-sites + buildPWSample: + name: Build playwright Sample-blog + needs: buildlibs + runs-on: ubuntu-latest + + strategy: + matrix: + node: [16.x] + steps: + - name: Checkout branch + uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node }} + - name: Cache Node Modules + id: cache-node-modules + uses: actions/cache@v2 + with: + path: node_modules + key: node-modules-${{ matrix.node }}-${{secrets.CACHEKEY}}-${{ hashFiles('package-lock.json') }} + - name: Install Dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + - name: Build lib using NX cache + run: | + npm run symlinks + npm run nx -- run-many --target=build --all --prod + - name: build pw-sample-blog + run: | + node ./dist/libs/scully/src/scully --project pw-sample-blog --tds --RSD --scan --404=index --ks + jest-test: name: Run Jest tests needs: [buildDocs, buildSample] @@ -213,7 +245,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [ 16.x ] + node: [16.x] steps: - name: Checking out uses: actions/checkout@master @@ -247,7 +279,7 @@ jobs: # Optional options appended to `git-commit` # See https://git-scm.com/docs/git-commit for a list of available options - commit_options: '--no-verify --signoff' + commit_options: "--no-verify --signoff" file_pattern: releaseChecksums.json # Optional commit user and author settings @@ -261,7 +293,7 @@ jobs: # Optional options appended to `git-push` # See git-push documentation for details: https://git-scm.com/docs/git-push#_options - push_options: '--force' + push_options: "--force" # Optional: Disable dirty check and always try to create a commit and push skip_dirty_check: false @@ -275,7 +307,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - prop: [ { node: 14, angular: 12 }] + prop: [{ node: 14, angular: 12 }] steps: - name: Setup angular project and run all tests uses: actions/setup-node@v2 diff --git a/jest.config.js b/jest.config.js index 762eafb2d..889f3e1bc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,3 @@ module.exports = { - projects: ['/tests/jest/src', '/libs/platform-server', '/libs/plugins/scully-plugin-puppeteer'], + projects: ['/tests/jest/src', '/libs/platform-server', '/libs/plugins/scully-plugin-puppeteer', '/libs/plugins/scully-plugin-playwright'], }; diff --git a/libs/plugins/scully-plugin-playwright/.babelrc b/libs/plugins/scully-plugin-playwright/.babelrc new file mode 100644 index 000000000..cf7ddd99c --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/plugins/scully-plugin-playwright/.eslintrc.json b/libs/plugins/scully-plugin-playwright/.eslintrc.json new file mode 100644 index 000000000..3456be9b9 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/.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-playwright/README.md b/libs/plugins/scully-plugin-playwright/README.md new file mode 100644 index 000000000..942d05c2e --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/README.md @@ -0,0 +1,11 @@ +# scully-plugin-playwright + +This is the playwright 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-playwright/jest.config.js b/libs/plugins/scully-plugin-playwright/jest.config.js new file mode 100644 index 000000000..376e99068 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/jest.config.js @@ -0,0 +1,15 @@ +module.exports = { + displayName: 'plugins-scully-plugin-playwright', + 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-playwright', +}; diff --git a/libs/plugins/scully-plugin-playwright/package.json b/libs/plugins/scully-plugin-playwright/package.json new file mode 100644 index 000000000..77b172425 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/package.json @@ -0,0 +1,23 @@ +{ + "name": "@scullyio/scully-plugin-playwright", + "version": "0.0.1", + "repository": { + "type": "GIT", + "url": "https://github.com/scullyio/scully/tree/main/libs/plugins/scully-plugin-playwright" + }, + "keywords": [ + "angular", + "scully", + "seo", + "scully-plugin", + "plugin", + "playwright" + ], + "dependencies": { + "tslib": "^1.13.0" + }, + "peerDependencies": { + "@scullyio/scully": "*", + "playwright": "^1.16.3" + } +} \ No newline at end of file diff --git a/libs/plugins/scully-plugin-playwright/src/index.ts b/libs/plugins/scully-plugin-playwright/src/index.ts new file mode 100644 index 000000000..8c041bc78 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/src/index.ts @@ -0,0 +1,39 @@ +import { green, log, logError, logOk, registerPlugin, routeRenderer } from '@scullyio/scully'; +import { exec } from 'child_process'; +import { LaunchOptions } from 'playwright'; +import { playwrightRenderer } from './lib/plugins-scully-plugin-playwright'; +import { launchedBrowser, launchedBrowser$ } from './lib/plugins-scully-plugin-playwright-utils'; + +async function runScript(cmd: string) { + return new Promise((resolve, reject) => { + exec(cmd, (err, stdout, stderr) => { + if (err) { + log(stderr); + reject(err); + } else { + resolve(stdout); + } + }); + }); +} +const plugin = async () => { + await runScript(`npx playwright install`).catch(() => { + logError(`Playwright install failed. Please fix the above errors in the app, and run Scully again.`); + process.exit(0); + }); + log(` ${green('✔')} Playwright installation successfully`); +} +export function enablePW() { + registerPlugin('beforeAll', 'installPWDeps', plugin); + + registerPlugin('scullySystem', routeRenderer, playwrightRenderer, undefined, { replaceExistingPlugin: true }); + + registerPlugin('enterprise', 'getPWLaunchedBrowser', async () => launchedBrowser$) + registerPlugin('beforeAll', 'startLaunching the browser', async () => { + logOk('Playwright is being launched') + launchedBrowser(); + }) +} + +export { playwrightRender } from './lib/plugins-scully-plugin-playwright'; +export type BrowserLaunchOptions = LaunchOptions & { browser: string }; \ No newline at end of file diff --git a/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright-utils.ts b/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright-utils.ts new file mode 100644 index 000000000..b154df95a --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright-utils.ts @@ -0,0 +1,156 @@ +import { loadConfig, logError, logWarn, white, yellow } from '@scullyio/scully'; +import { showBrowser } from '@scullyio/scully/src/lib/utils/cli-options'; +import * as playwright from "playwright"; +import { Browser, LaunchOptions } from "playwright"; +import { BehaviorSubject, catchError, delayWhen, filter, from, merge, Observable, of, shareReplay, switchMap, take, throttleTime, timer } from 'rxjs'; + +const defaultConfig: LaunchOptions = { + headless: true, + channel: 'chrome', + browser: 'chromium', +} as any; +const options = { ...defaultConfig }; + + +const launches = new BehaviorSubject(undefined); + +export let browser: Browser; +export function waitForIt(milliSeconds: number) { + return new Promise((resolve) => setTimeout(() => resolve(), milliSeconds)); +} + +let usageCounter = 0; +export const launchedBrowser: () => Promise = async () => { + if (++usageCounter > 500) { + launches.next(); + usageCounter = 0; + } + return launchedBrowser$.pipe(take(1)).toPromise(); +}; + +export const reLaunch = (reason?: string): Promise => { + if (reason) { + logWarn( + white(` + ======================================== + Relaunch because of ${reason} + ======================================== + + `) + ); + } + launches.next(); + return launchedBrowser(); +}; + +const launch = async (pluginConfig: any): Promise => { + const browserType = pluginConfig.browser + const playrightBrowser = playwright[browserType]; + const browser = await playrightBrowser.launch({ headless: pluginConfig.headless, channel: pluginConfig.channel }); + return browser; +} +export const launchedBrowser$: Observable = of('').pipe( + /** load config only after a subscription is made */ + switchMap(() => loadConfig()), + /** give the system a bit of breathing room, and prevent race */ + switchMap(() => from(waitForIt(50))), + switchMap(() => merge(obsBrowser(), launches)), + /** use shareReplay so the browser will stay in memory during the lifetime of the program */ + shareReplay({ refCount: false, bufferSize: 1 }), + filter((e) => e !== undefined) +); + +function obsBrowser(): Observable { + let isLaunching = false; + if (showBrowser) { + options.headless = false; + } + options.args = options.args || []; + return new Observable((obs) => { + const startPlaywright = () => { + if (!isLaunching) { + isLaunching = true; + launchPlayWrightWithRetry(options).then((b) => { + /** I will only come here when playwright is actually launched */ + browser = b; + b.on('disconnected', () => reLaunch('disconnect')); + obs.next(b); + /** only allow a relaunch in a next cycle */ + setTimeout(() => (isLaunching = false), 1000); + }); + } + }; + + launches + .pipe( + /** ignore request while the browser is already starting, we can only launch 1 */ + filter(() => !isLaunching), + /** the long throttleTime is to cater for the concurrently running browsers to crash and burn. */ + throttleTime(15000), + // provide enough time for the current async operations to finish before killing the current browser instance + delayWhen(() => + merge( + /** timout at 25 seconds */ + timer(25000) + ).pipe( + /** use take 1 to make sure we complete when one of the above fires */ + take(1), + /** if something _really_ unwieldy happens with the browser, ignore and go ahead */ + catchError(() => of([])) + ) + ) + ) + .subscribe({ + next: () => { + try { + if (browser && browser.contexts() != null) { + browser.close(); + } + } catch { + /** ignored */ + } + startPlaywright(); + }, + }); + return () => { + if (browser) { + browser.close(); + browser = undefined; + } + }; + }); +} +function launchPlayWrightWithRetry(options, failedLaunches = 0): Promise { + const timeout = (millisecs: number) => new Promise((_, reject) => setTimeout(() => reject('timeout'), millisecs)); + return Promise.race([ + /** use a 1 minute timeout to detect a stalled launch of playwright */ + timeout(Math.max(/** serverTimeout,*/ 60 * 1000)), + launch(options).then((b) => { + return b as unknown as Browser; + }), + ]) + .catch((e) => { + /** first stage catch check for retry */ + if (e.message.includes('Could not find browser revision')) { + throw new Error('Failed launch'); + } + if (++failedLaunches < 3) { + return launchPlayWrightWithRetry(options, failedLaunches); + } + throw new Error('failed 3 times to launch'); + }) + .catch((b) => { + /** second stage catch, houston, we have a problem, and will abort */ + logError(` +================================================================================================= +Playwright cannot find or launch the browser. (by default chrome) +This might happen because the default timeout (60 seconds) is to short on this system +this can be fixed by adding the ${yellow('--serverTimeout=x')} cmd line option. + (where x = the new timeout in milliseconds) +When this happens in CI/CD you can find some additional information here: +https://playwright.dev/docs/troubleshooting +================================================================================================= + `); + process.exit(15); + }) as unknown as Promise; +} \ No newline at end of file diff --git a/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright.spec.ts b/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright.spec.ts new file mode 100644 index 000000000..105f7a930 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright.spec.ts @@ -0,0 +1,7 @@ +import { playwrightRenderer } from './plugins-scully-plugin-playwright'; + +describe('playwrightRenderer', () => { + it('should work', () => { + expect(playwrightRenderer).toEqual(playwrightRenderer); + }); +}); diff --git a/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright.ts b/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright.ts new file mode 100644 index 000000000..d10f0c0fa --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/src/lib/plugins-scully-plugin-playwright.ts @@ -0,0 +1,190 @@ +import { createFolderFor, HandledRoute, logError, logWarn, registerPlugin, scullyConfig, yellow } from '@scullyio/scully'; +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 } from 'playwright'; +import { launchedBrowser, reLaunch, waitForIt } from './plugins-scully-plugin-playwright-utils'; + +let version = '0.0.0'; +try { + const pkg = join(__dirname, '../../../package.json'); + // console.log(pkg) + version = jsonc.parse(readFileSync(pkg).toString()).version || '0.0.0'; +} catch (e) { + // this is only for internals builds + // version = jsonc.parse(readFileSync(join(__dirname, '../../../package.json')).toString()).version || '0.0.0'; +} +export const title404 = '404 - URL not provided in the app Scully is serving'; +const errorredPages = new Map(); +export const playwrightRender = 'playwrightRender' as const; + +export const playwrightRenderer = async (route: HandledRoute): Promise => { + const path = route.rawRoute + ? route.rawRoute + : scullyConfig.hostUrl + ? `${scullyConfig.hostUrl}${route.route}` + : `http${ssl ? 's' : ''}://${scullyConfig.hostName}:${scullyConfig.appPort}${route.route}`; + let pageHtml: string; + let browser: Browser; + let page: Page; + try { + browser = await launchedBrowser().catch((e) => { + logError('Playwright died?', e); + return Promise.reject(e); + }); + // open a new page + page = await browser.newPage(); + + let resolve; + const pageReady = new Promise((r) => (resolve = r)); + + if (scullyConfig.ignoreResourceTypes && scullyConfig.ignoreResourceTypes.length > 0) { + await page.route('**/*', route => checkIfRequestShouldBeIgnored.bind(route.request)); + + // eslint-disable-next-line no-inner-declarations + function checkIfRequestShouldBeIgnored(request) { + if (scullyConfig.ignoreResourceTypes.includes(request.resourceType())) { + request.abort(); + } else { + request.continue(); + } + } + } + + /** this will be called from the browser, but runs in node */ + await page.exposeFunction('onCustomEvent', () => { + resolve(); + }); + + windowSet(page, 'scullyVersion', version); + + if (route.config && route.config.manualIdleCheck) { + route.exposeToPage = route.exposeToPage || {}; + route.exposeToPage.manualIdle = true; + } + + if (scullyConfig.inlineStateOnly) { + route.injectToPage = route.injectToPage || {}; + route.injectToPage.inlineStateOnly = true; + } + + if (route.exposeToPage !== undefined) { + windowSet(page, 'ScullyIO-exposed', route.exposeToPage); + } + if (route.injectToPage !== undefined) { + windowSet(page, 'ScullyIO-injected', route.injectToPage); + } + + /** Inject this into the running page, runs in browser */ + await page.addInitScript(() => { + /** set "running" mode */ + window['ScullyIO'] = 'running'; + window.addEventListener('AngularReady', () => { + window['onCustomEvent'](); + }); + }); + + // enter url in page + await page.goto(path); + + await Promise.race([pageReady, waitForIt(25 * 1000)]); + + /** when done, add in some scully content. */ + await page.evaluate(() => { + const head = document.head; + + /** add a small script tag to set "generated" mode */ + const d = document.createElement('script'); + d.innerHTML = `window['ScullyIO']='generated';`; + if (window['ScullyIO-injected']) { + /** and add the injected data there too. */ + d.innerHTML += `window['ScullyIO-injected']=${JSON.stringify(window['ScullyIO-injected'])};`; + } + const m = document.createElement('meta'); + m.name = 'generator'; + m.content = `Scully ${window['scullyVersion']}`; + head.appendChild(d); + head.insertBefore(m, head.firstChild); + /** inject the scully version into the body attribute */ + document.body.setAttribute('scully-version', window['scullyVersion']); + }); + + pageHtml = await page.content(); + + /** Check for undefined content and re-try */ + if ([undefined, null, '', 'undefined', 'null'].includes(pageHtml)) { + throw new Error(`Route "${route.route}" render to ${path}`); + } + + const firstTitle = await page.evaluate(() => { + const d = document.querySelector('h1'); + return (d && d.innerText) || ''; + }); + + if (firstTitle === title404) { + logWarn(`Route "${yellow(route.route)}" not provided by angular app`); + } + + /** save thumbnail to disk code */ + if (scullyConfig.thumbnails) { + const file = join(scullyConfig.outDir, route.route, '/thumbnail.jpg'); + createFolderFor(file); + await page.screenshot({ path: file }); + } + + /** + * when the browser is shown, use a 2 minute timeout, otherwise + * wait for page-read || timeout @ 25 seconds. + */ + if (showBrowser) { + page.evaluate( + "console.log('\\n\\n------------------------------\\nScully is done, page left open for 120 seconds for inspection\\n------------------------------\\n\\n')" + ); + //* don't close the browser, but leave it open for inspection for 120 secs + waitForIt(1200 * 1000).then(() => page.close()); + } else { + // await page.close(); + page.close(); + + } + } catch (err) { + const { message } = err; + // tslint:disable-next-line: no-unused-expression + page && typeof page.close === 'function' && (await page.close()); + logWarn(`Playwright error while rendering "${yellow(route.route)}"`, err, ' we will retry rendering this page up to 3 times.'); + if (message && message.includes('closed')) { + /** signal the launched to relaunch playwright, as it has likely died here. */ + //TODO:c + reLaunch('closed'); + // return playwrightRender(route); + } + if (errorredPages.has(route.route) && errorredPages.get(route.route) > 2) { + logError(`Playwright error while rendering "${yellow(route.route)}"`, err, ' we retried rendering this page 3 times.'); + /** we tried this page before, something is really off. Exit stage left. */ + //TODO: + // captureException(err); + process.exit(15); + } else { + const count = errorredPages.get(route.route) || 0; + errorredPages.set(route.route, count + 1); + /** give it a couple of secs */ + await waitForIt(3 * 1000); + /** retry! */ + return playwrightRenderer(route); + } + } + // logWarn(`done with page ${path}`); + return pageHtml; +}; + +const windowSet = (page: Page, name: string, value: any) => + page.addInitScript(` + Object.defineProperty(window, '${name}', { + get() { + return JSON.parse('${JSON.stringify(value)}') + } + }) + `); + +registerPlugin('scullySystem', playwrightRender, playwrightRenderer); \ No newline at end of file diff --git a/libs/plugins/scully-plugin-playwright/tsconfig.json b/libs/plugins/scully-plugin-playwright/tsconfig.json new file mode 100644 index 000000000..667a3463d --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/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-playwright/tsconfig.lib.json b/libs/plugins/scully-plugin-playwright/tsconfig.lib.json new file mode 100644 index 000000000..ca4b70c39 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/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-playwright/tsconfig.spec.json b/libs/plugins/scully-plugin-playwright/tsconfig.spec.json new file mode 100644 index 000000000..65aff5094 --- /dev/null +++ b/libs/plugins/scully-plugin-playwright/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 0830f2191..a8efe2309 100644 --- a/libs/scully/src/index.ts +++ b/libs/scully/src/index.ts @@ -67,7 +67,4 @@ export { setConfig as setMyConfig, prod, setPluginPriority, -}; - - - +}; \ No newline at end of file diff --git a/libs/scully/src/lib/renderPlugins/index.ts b/libs/scully/src/lib/renderPlugins/index.ts index eeec337ff..6af2401ce 100644 --- a/libs/scully/src/lib/renderPlugins/index.ts +++ b/libs/scully/src/lib/renderPlugins/index.ts @@ -3,4 +3,3 @@ export * from './contentTextRenderPlugin'; export * from './executePlugins'; export * from './jsdomPlugins'; export * from './seoHrefCompletionPlugin'; - diff --git a/libs/scully/src/lib/utils/interfacesandenums.ts b/libs/scully/src/lib/utils/interfacesandenums.ts index 0d0f64698..b5c6d6c41 100644 --- a/libs/scully/src/lib/utils/interfacesandenums.ts +++ b/libs/scully/src/lib/utils/interfacesandenums.ts @@ -1,4 +1,3 @@ -import { ExecFileSyncOptionsWithBufferEncoding } from 'child_process'; import { PuppeteerNodeLaunchOptions } from 'puppeteer'; export enum RouteTypes { @@ -47,8 +46,7 @@ export interface ScullyConfig { /** optional proxy config file, uses the same config file as the CLI */ proxyConfig?: string; /** optional launch-options for puppeteer */ - puppeteerLaunchOptions?: PuppeteerNodeLaunchOptions; - /** hostname to use for local server, defaults to `localhost` */ + puppeteerLaunchOptions?: PuppeteerNodeLaunchOptions; /** hostname to use for local server, defaults to `localhost` */ hostName?: string; /** optional hostURL, if this is provided, we are going to use this server instead of the build-in one. */ hostUrl?: string; diff --git a/libs/scully/src/lib/utils/startup/startup.ts b/libs/scully/src/lib/utils/startup/startup.ts index 0a185c0a6..9182e8e64 100644 --- a/libs/scully/src/lib/utils/startup/startup.ts +++ b/libs/scully/src/lib/utils/startup/startup.ts @@ -39,7 +39,7 @@ export const startScully = async (url?: string) => { let innerResolve; const durationProm = new Promise((r) => (innerResolve = r)); const obs = new PerformanceObserver(measurePerformance(innerResolve)); - obs.observe({ entryTypes: ['measure'], buffered: true }); + obs.observe({ entryTypes: ['measure'] }); const numberOfRoutesProm = findPlugin(generateAll)(url) .then((routes) => { printProgress(false, 'calculate timings'); diff --git a/nx.json b/nx.json index 6ab4e0c48..f68ac7b3f 100644 --- a/nx.json +++ b/nx.json @@ -61,6 +61,9 @@ "plugins-scully-plugin-puppeteer": { "tags": [] }, + "plugins-scully-plugin-playwright": { + "tags": [] + }, "plugins-scully-plugin-remove-scripts": { "tags": [] }, diff --git a/package-lock.json b/package-lock.json index bfa46540a..8cbeec1ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "npm-run-all": "^4.1.5", "opal": "^0.6.4", "os": "^0.1.2", + "playwright": "^1.16.3", "prismjs": "^1.25.0", "puppeteer": "^10.2.0", "rxjs": "^7.3.0", @@ -17917,8 +17918,7 @@ "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "node_modules/ip-regex": { "version": "2.1.0", @@ -19929,6 +19929,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/jpeg-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.3.tgz", + "integrity": "sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -23826,6 +23831,94 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.16.3.tgz", + "integrity": "sha512-nfJx/OpIb/8OexL3rYGxNN687hGyaM3XNpfuMzoPlrekURItyuiHHsNhC9oQCx3JDmCn5O3EyyyFCnrZjH6MpA==", + "hasInstallScript": true, + "dependencies": { + "playwright-core": "=1.16.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/playwright-core": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.16.3.tgz", + "integrity": "sha512-16hF27IvQheJee+DbhC941AUZLjbJgfZFWi9YPS4LKEk/lKFhZI+9TiFD0sboYqb9eaEWvul47uR5xxTVbE4iw==", + "dependencies": { + "commander": "^8.2.0", + "debug": "^4.1.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.0", + "jpeg-js": "^0.4.2", + "mime": "^2.4.6", + "pngjs": "^5.0.0", + "progress": "^2.0.3", + "proper-lockfile": "^4.1.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "socks-proxy-agent": "^6.1.0", + "stack-utils": "^2.0.3", + "ws": "^7.4.6", + "yauzl": "^2.10.0", + "yazl": "^2.5.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/playwright-core/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/playwright-core/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/playwright-core/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/playwright-core/node_modules/ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", @@ -23851,6 +23944,14 @@ "node": ">=0.10.0" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -26325,6 +26426,16 @@ "node": ">= 6" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/protractor": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", @@ -27620,7 +27731,6 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true, "engines": { "node": ">= 4" } @@ -28341,7 +28451,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -28617,7 +28726,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", - "dev": true, "dependencies": { "ip": "^1.1.5", "smart-buffer": "^4.1.0" @@ -28631,7 +28739,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz", "integrity": "sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==", - "dev": true, "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.1", @@ -28880,7 +28987,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -28892,7 +28998,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, "engines": { "node": ">=8" } @@ -33438,6 +33543,14 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -36164,8 +36277,7 @@ "version": "12.2.6", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.6.tgz", "integrity": "sha512-LuQXKVH0m85+GT8tldrSBmmgyHycxXkbLaJNtuYGK358dJ9tBdyOPXn7GZzHvpVL5Zdpu/ahuFN2PbFzN+qX/A==", - "dev": true, - "requires": {} + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -37339,8 +37451,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", - "dev": true, - "requires": {} + "dev": true }, "cliui": { "version": "6.0.0", @@ -39951,8 +40062,7 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -40031,15 +40141,13 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "7.2.0", @@ -40112,8 +40220,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "requires": {} + "dev": true }, "ajv-formats": { "version": "2.1.0", @@ -40128,8 +40235,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "alphanum-sort": { "version": "1.0.2", @@ -41538,8 +41644,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "requires": {} + "dev": true }, "cjs-module-lexer": { "version": "1.2.2", @@ -41884,15 +41989,13 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz", "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==", - "dev": true, - "requires": {} + "dev": true }, "@angular/core": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz", "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", - "dev": true, - "requires": {} + "dev": true }, "aria-query": { "version": "3.0.0", @@ -43623,8 +43726,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", - "dev": true, - "requires": {} + "dev": true }, "csso": { "version": "4.2.0", @@ -45115,8 +45217,7 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "requires": {} + "dev": true }, "eslint-scope": { "version": "5.1.1", @@ -47090,8 +47191,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "identity-obj-proxy": { "version": "3.0.0", @@ -47446,8 +47546,7 @@ "ws": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": {} + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" }, "yargs-parser": { "version": "20.2.9", @@ -47501,8 +47600,7 @@ "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "ip-regex": { "version": "2.1.0", @@ -48476,8 +48574,7 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -48615,8 +48712,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} + "dev": true }, "jest-preset-angular": { "version": "9.0.7", @@ -49020,6 +49116,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "jpeg-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.3.tgz", + "integrity": "sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -52096,6 +52197,59 @@ "find-up": "^4.0.0" } }, + "playwright": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.16.3.tgz", + "integrity": "sha512-nfJx/OpIb/8OexL3rYGxNN687hGyaM3XNpfuMzoPlrekURItyuiHHsNhC9oQCx3JDmCn5O3EyyyFCnrZjH6MpA==", + "requires": { + "playwright-core": "=1.16.3" + } + }, + "playwright-core": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.16.3.tgz", + "integrity": "sha512-16hF27IvQheJee+DbhC941AUZLjbJgfZFWi9YPS4LKEk/lKFhZI+9TiFD0sboYqb9eaEWvul47uR5xxTVbE4iw==", + "requires": { + "commander": "^8.2.0", + "debug": "^4.1.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.0", + "jpeg-js": "^0.4.2", + "mime": "^2.4.6", + "pngjs": "^5.0.0", + "progress": "^2.0.3", + "proper-lockfile": "^4.1.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "socks-proxy-agent": "^6.1.0", + "stack-utils": "^2.0.3", + "ws": "^7.4.6", + "yauzl": "^2.10.0", + "yazl": "^2.5.1" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" + } + } + }, "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", @@ -52117,6 +52271,11 @@ } } }, + "pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -52601,29 +52760,25 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-duplicates": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-empty": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-overridden": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", - "dev": true, - "requires": {} + "dev": true }, "postcss-double-position-gradients": { "version": "1.0.0", @@ -53125,8 +53280,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -53194,8 +53348,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-normalize-display-values": { "version": "5.0.1", @@ -53924,6 +54077,16 @@ "sisteransi": "^1.0.5" } }, + "proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "requires": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "protractor": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", @@ -54264,8 +54427,7 @@ "ws": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "requires": {} + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" } } }, @@ -54931,8 +55093,7 @@ "retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" }, "reusify": { "version": "1.0.4", @@ -55025,8 +55186,7 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz", "integrity": "sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==", - "dev": true, - "requires": {} + "dev": true }, "safe-buffer": { "version": "5.1.2", @@ -55486,8 +55646,7 @@ "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" }, "snapdragon": { "version": "0.8.2", @@ -55724,7 +55883,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", - "dev": true, "requires": { "ip": "^1.1.5", "smart-buffer": "^4.1.0" @@ -55734,7 +55892,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz", "integrity": "sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==", - "dev": true, "requires": { "agent-base": "^6.0.2", "debug": "^4.3.1", @@ -55942,7 +56099,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, "requires": { "escape-string-regexp": "^2.0.0" }, @@ -55950,8 +56106,7 @@ "escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" } } }, @@ -56312,8 +56467,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz", "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", - "dev": true, - "requires": {} + "dev": true }, "stylehacks": { "version": "5.0.1", @@ -59389,8 +59543,7 @@ "ws": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" }, "xdg-basedir": { "version": "4.0.0", @@ -59525,6 +59678,14 @@ "fd-slicer": "~1.1.0" } }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "requires": { + "buffer-crc32": "~0.2.3" + } + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 762541bd1..e3fc85c8c 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "build:code": "nx run-many --target=build --all --configuration production --with-deps", "build:tools": "tsc -p tools/tsconfig.tools.json", "build:sample": "npm run scully -- --project sample-blog --tds --RSD --scan --404=index --ks", + "build:pw-sample": "npm run scully -- --project pw-sample-blog --tds --RSD --scan --404=index --ks", "build:uni": "npm run scully -- --project sps-sample --RSD --tds --scan --ks", "build:docs": "npm run scully -- --project scully-docs --RSD --404=index --scan --ks", "scully": "node ./dist/libs/scully/src/scully.js", @@ -29,6 +30,7 @@ "server:testSample": "npm run server:sample &", "server:testDocs": "npm run server:docs &", "server:sample": "npm run scully -- serve --project sample-blog --tds --404=baseOnly --noCache", + "server:pw-sample": "npm run scully -- serve --project pw-sample-blog --tds --404=baseOnly --noCache", "server:uni": "npm run scully -- serve --project sps-sample --tds --404=baseOnly --noCache", "server:docs": "npm run scully -- serve --project scully-docs --tds --noCache", "server:kill": "npm run scully -- killServer ", @@ -105,6 +107,7 @@ "npm-run-all": "^4.1.5", "opal": "^0.6.4", "os": "^0.1.2", + "playwright": "^1.16.3", "prismjs": "^1.25.0", "puppeteer": "^10.2.0", "rxjs": "^7.3.0", diff --git a/scully.pw-sample-blog.config.ts b/scully.pw-sample-blog.config.ts new file mode 100644 index 000000000..59932358f --- /dev/null +++ b/scully.pw-sample-blog.config.ts @@ -0,0 +1,237 @@ +import { ContentTextRoute, 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'; +import { getFlashPreventionPlugin } from '@scullyio/scully-plugin-flash-prevention'; +import '@scullyio/scully-plugin-from-data'; +import { enablePW } from '@scullyio/scully-plugin-playwright'; +import { removeScripts } from '@scullyio/scully-plugin-remove-scripts'; +import './demos/plugins/errorPlugin'; +import './demos/plugins/tocPlugin'; +import './demos/plugins/voidPlugin'; + +const FlashPrevention = getFlashPreventionPlugin(); +enablePW(); + +setPluginConfig('md', { enableSyntaxHighlighting: true }); +setPluginConfig(baseHrefRewrite, { href: '/' }); +const defaultPostRenderers = ['seoHrefOptimise']; +export const config: Promise = (async () => { + return { + /** outDir is where the static distribution files end up */ + projectName: 'sample-blog', + outDir: './dist/static/sample-blog', + extraRoutes: new Promise((resolve) => { + resolve(['/exclude/present', '/test/fakeBase', '/content/hello', '/content/there', '/rawRoute']); + }), + /** Use only inlined HTML, no data.json will be written/read */ + // inlineStateOnly: true, + defaultPostRenderers, + handle404: 'baseOnly', + thumbnails: true, + proxyConfig: 'proxy.conf.js', + maxRenderThreads: 4, + routes: { + '/demo/:id': { + type: 'extra', + numberOfPages: 5, + }, + '/home/:topLevel': { + type: 'extraData', + data: [ + { title: 'All routes in application', data: 'all' }, + { title: 'Unpublished routes in application', data: 'unpublished' }, + { title: 'Toplevel routes in application', data: '' }, + ], + }, + '/user/:userId': { + // Type is mandatory + type: 'json', + /** + * Every parameter in the route must exist here + */ + userId: { + url: 'http://localhost:8200/users', + resultsHandler: (raw) => raw.filter((row) => row.id < 5), + property: 'id', + }, + }, + '/content/:slug': { + type: 'customContent', + }, + '/content/hello': { + type: 'default', + postRenderers: ['contentText'], + contentType: 'html', + content: '

Hello!

', + }, + '/content/there': { + type: 'default', + postRenderers: ['contentText'], + contentType: 'md', + // content: '# blah' + content: () => { + return '

Content generated from function

'; + }, + }, + '/user/:userId/post/:postId': { + // Type is mandatory + type: 'json', + /** + * Every parameter in the route must exist here + */ + userId: { + url: 'http://localhost:8200/users', + resultsHandler: (raw) => raw.filter((row) => row.id < 3), + property: 'id', + }, + postId: { + url: 'http://localhost:8200/posts?userId=${userId}', + property: 'id', + }, + }, + '/user/:userId/friend/:friendCode': { + type: 'ignored', + // type:'json', + userId: { + url: 'http://localhost:8200/users', + resultsHandler: (raw) => raw.filter((row) => row.id < 3), + property: 'id', + }, + friendCode: { + url: 'http://localhost:8200/users?userId=${userId}', + property: 'id', + }, + }, + '/blog/:slug': { + type: 'contentFolder', + postRenderers: [docLink], + slug: { + folder: './tests/assets/blog-files', + }, + }, + '/slow': { + type: FlashPrevention, + postRenderers: [FlashPrevention], + }, + '/manualIdle': { + type: 'default', + manualIdleCheck: true, + }, + '/someRoute': { + type: 'ignored', + }, + '/basehref': { + type: 'default', + postRenderers: [baseHrefRewrite], + baseHref: '/basehref/', + }, + '/basehref/rewritten': { + type: 'default', + postRenderers: [baseHrefRewrite], + baseHref: '/basehref/rewritten/', + }, + '/basehref/removed': { + type: 'default', + postRenderers: [baseHrefRewrite], + baseHref: '/basehref/removed/', + }, + '/test/fakeBase': { + type: 'addFake', + }, + '/noScript': { + type: 'default', + postRenderers: [removeScripts], + }, + '/rawRoute': { + type: 'rawTest', + url: 'http://localhost:8200/users/1/raw', + }, + }, + guessParserOptions: { + excludedFiles: ['apps/sample-blog/src/app/exclude/exclude-routing.module.ts'], + }, + } as ScullyConfig; +})(); + +registerPlugin('postProcessByDom', 'rawTest', async (dom: JSDOM, r: HandledRoute) => { + const { window: { document } } = dom; + const content = (await httpGetJson(r.config.url, { + headers: { + contentType: 'text/html', + expectedContentType: 'text/html' + } + })) as string; + document.write(content); + return dom; +}) + + +registerPlugin('router', 'rawTest', async (route, options: RouteConfig) => { + return [{ route, type: 'rawTest', rawRoute: options?.url ?? 'https://scully.io/', manualIdleCheck: true }]; +}); + +/** plugin to add routes that are not on the routeconfig, to test 404 */ +const fakeroutePlugin = async (): Promise => [ + { route: '/test/fake1', type: 'addFake' }, + { route: '/test/fake2', type: 'addFake' }, +]; + +registerPlugin('router', 'addFake', fakeroutePlugin); + +registerPlugin( + 'routeProcess', + 'test2', + async (r: HandledRoute[]) => + r.map((route) => { + const { data } = route; + const { nonsense, ...rest } = data; + if (nonsense !== 'do remove this please!') { + logError('things are wrong, test failed on processRoutes test2 (sample-blog.config)'); + process.exit(15); + } + return { ...route, data: { ...rest } }; + }), + 30 +); +registerPlugin( + 'routeProcess', + 'test1', + async (r: HandledRoute[]) => r.map((line) => ({ ...line, data: { ...line.data, nonsense: 'do remove this please!' } })), + 20 +); + +async function getMyRoutes(): Promise { + return new Promise((r) => { + console.log('this line should be visible for 15 seconds'); + setTimeout(() => { + console.log('done waiting'); + r(['/exclude/present']); + }, 15000); + }); +} + +registerPlugin('router', 'customContent', async (url) => { + return ['one', 'two', 'tree', 'four', 'five'].map((key, number, arr) => { + const route: ContentTextRoute = { + type: 'customContent', + postRenderers: ['contentText'], + route: `/content/${key}`, + contentType: 'html', + content: ` +

Sample page ${key}

+

This is sample page number ${number + 1}

+

Blog page 1 +

+ ${addLinks()} + `, + }; + function addLinks() { + return arr + .filter((row) => row !== key) + .map((page) => `${page} `) + .join(''); + } + return route; + }); +}); diff --git a/scully.scully-docs.config.ts b/scully.scully-docs.config.ts index 972aacef7..9081d2149 100644 --- a/scully.scully-docs.config.ts +++ b/scully.scully-docs.config.ts @@ -65,7 +65,6 @@ 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 268ef5f78..ec257a640 100644 --- a/scully.sps-sample.config.ts +++ b/scully.sps-sample.config.ts @@ -15,7 +15,6 @@ 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/tsconfig.base.json b/tsconfig.base.json index 0f38bd95f..8d476346f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -18,6 +18,7 @@ "@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-playwright": ["libs/plugins/scully-plugin-playwright/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 8842290f6..1a2239b61 100644 --- a/workspace.json +++ b/workspace.json @@ -9,18 +9,24 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": ["libs/plugins/scully-plugin-local-cache/**/*.ts"] + "lintFilePatterns": [ + "libs/plugins/scully-plugin-local-cache/**/*.ts" + ] } }, "build": { "builder": "@nrwl/node:package", - "outputs": ["{options.outputPath}"], + "outputs": [ + "{options.outputPath}" + ], "options": { "outputPath": "dist/libs/scully-plugin-local-cache", "tsConfig": "libs/plugins/scully-plugin-local-cache/tsconfig.lib.json", "packageJson": "libs/plugins/scully-plugin-local-cache/package.json", "main": "libs/plugins/scully-plugin-local-cache/src/index.ts", - "assets": ["libs/plugins/scully-plugin-local-cache/*.md"] + "assets": [ + "libs/plugins/scully-plugin-local-cache/*.md" + ] } } } @@ -46,8 +52,14 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["libs/ng-lib/tsconfig.lib.json", "libs/ng-lib/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/ng-lib/**"] + "tsConfig": [ + "libs/ng-lib/tsconfig.lib.json", + "libs/ng-lib/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/ng-lib/**" + ] } } }, @@ -61,7 +73,9 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": ["dist/libs/platform-server"], + "outputs": [ + "dist/libs/platform-server" + ], "options": { "project": "libs/platform-server/ng-package.json" }, @@ -77,7 +91,9 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": ["coverage/libs/platform-server"], + "outputs": [ + "coverage/libs/platform-server" + ], "options": { "jestConfig": "libs/platform-server/jest.config.js", "passWithNoTests": true @@ -86,7 +102,10 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": ["libs/platform-server/src/**/*.ts", "libs/platform-server/src/**/*.html"] + "lintFilePatterns": [ + "libs/platform-server/src/**/*.ts", + "libs/platform-server/src/**/*.html" + ] } } } @@ -102,8 +121,14 @@ "options": { "linter": "eslint", "config": "libs/plugins/base-href-rewrite/.eslintrc", - "tsConfig": ["libs/plugins/base-href-rewrite/tsconfig.lib.json", "libs/plugins/base-href-rewrite/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/base-href-rewrite/**"] + "tsConfig": [ + "libs/plugins/base-href-rewrite/tsconfig.lib.json", + "libs/plugins/base-href-rewrite/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/base-href-rewrite/**" + ] } }, "build": { @@ -113,9 +138,13 @@ "tsConfig": "libs/plugins/base-href-rewrite/tsconfig.lib.json", "packageJson": "libs/plugins/base-href-rewrite/package.json", "main": "libs/plugins/base-href-rewrite/src/index.ts", - "assets": ["libs/plugins/base-href-rewrite/*.md"] + "assets": [ + "libs/plugins/base-href-rewrite/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -129,8 +158,14 @@ "builder": "@nrwl/linter:lint", "options": { "linter": "eslint", - "tsConfig": ["libs/plugins/docs-link-update/tsconfig.lib.json", "libs/plugins/docs-link-update/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/docs-link-update/**/*"] + "tsConfig": [ + "libs/plugins/docs-link-update/tsconfig.lib.json", + "libs/plugins/docs-link-update/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/docs-link-update/**/*" + ] } }, "build": { @@ -140,9 +175,13 @@ "tsConfig": "libs/plugins/docs-link-update/tsconfig.lib.json", "packageJson": "libs/plugins/docs-link-update/package.json", "main": "libs/plugins/docs-link-update/src/index.ts", - "assets": ["libs/plugins/docs-link-update/*.md"] + "assets": [ + "libs/plugins/docs-link-update/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -157,8 +196,14 @@ "options": { "linter": "eslint", "config": "libs/plugins/extra/.eslintrc", - "tsConfig": ["libs/plugins/extra/tsconfig.lib.json", "libs/plugins/extra/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/extra/**"] + "tsConfig": [ + "libs/plugins/extra/tsconfig.lib.json", + "libs/plugins/extra/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/extra/**" + ] } }, "build": { @@ -168,9 +213,13 @@ "tsConfig": "libs/plugins/extra/tsconfig.lib.json", "packageJson": "libs/plugins/extra/package.json", "main": "libs/plugins/extra/src/index.ts", - "assets": ["libs/plugins/extra/*.md"] + "assets": [ + "libs/plugins/extra/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -185,8 +234,14 @@ "options": { "linter": "eslint", "config": "libs/plugins/from-data/.eslintrc", - "tsConfig": ["libs/plugins/from-data/tsconfig.lib.json", "libs/plugins/from-data/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/from-data/**"] + "tsConfig": [ + "libs/plugins/from-data/tsconfig.lib.json", + "libs/plugins/from-data/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/from-data/**" + ] } }, "build": { @@ -196,9 +251,13 @@ "tsConfig": "libs/plugins/from-data/tsconfig.lib.json", "packageJson": "libs/plugins/from-data/package.json", "main": "libs/plugins/from-data/src/index.ts", - "assets": ["libs/plugins/from-data/*.md"] + "assets": [ + "libs/plugins/from-data/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -212,8 +271,14 @@ "builder": "@nrwl/linter:lint", "options": { "linter": "eslint", - "tsConfig": ["libs/plugins/google-analytics/tsconfig.lib.json", "libs/plugins/google-analytics/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/google-analytics/**/*"] + "tsConfig": [ + "libs/plugins/google-analytics/tsconfig.lib.json", + "libs/plugins/google-analytics/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/google-analytics/**/*" + ] } }, "build": { @@ -223,9 +288,13 @@ "tsConfig": "libs/plugins/google-analytics/tsconfig.lib.json", "packageJson": "libs/plugins/google-analytics/package.json", "main": "libs/plugins/google-analytics/src/index.ts", - "assets": ["libs/plugins/google-analytics/*.md"] + "assets": [ + "libs/plugins/google-analytics/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -239,8 +308,14 @@ "builder": "@nrwl/linter:lint", "options": { "linter": "eslint", - "tsConfig": ["libs/plugins/logrocket/tsconfig.lib.json", "libs/plugins/logrocket/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/logrocket/**/*"] + "tsConfig": [ + "libs/plugins/logrocket/tsconfig.lib.json", + "libs/plugins/logrocket/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/logrocket/**/*" + ] } }, "build": { @@ -250,9 +325,13 @@ "tsConfig": "libs/plugins/logrocket/tsconfig.lib.json", "packageJson": "libs/plugins/logrocket/package.json", "main": "libs/plugins/logrocket/src/index.ts", - "assets": ["libs/plugins/logrocket/*.md"] + "assets": [ + "libs/plugins/logrocket/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -270,7 +349,10 @@ "libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.lib.json", "libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.spec.json" ], - "exclude": ["**/node_modules/**", "!libs/plugins/scully-plugin-copy-to-clipboard/**/*"] + "exclude": [ + "**/node_modules/**", + "!libs/plugins/scully-plugin-copy-to-clipboard/**/*" + ] } }, "build": { @@ -280,9 +362,13 @@ "tsConfig": "libs/plugins/scully-plugin-copy-to-clipboard/tsconfig.lib.json", "packageJson": "libs/plugins/scully-plugin-copy-to-clipboard/package.json", "main": "libs/plugins/scully-plugin-copy-to-clipboard/src/index.ts", - "assets": ["libs/plugins/scully-plugin-copy-to-clipboard/*.md"] + "assets": [ + "libs/plugins/scully-plugin-copy-to-clipboard/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -300,7 +386,10 @@ "libs/plugins/scully-plugin-critical-css/tsconfig.lib.json", "libs/plugins/scully-plugin-critical-css/tsconfig.spec.json" ], - "exclude": ["**/node_modules/**", "!libs/plugins/scully-plugin-critical-css/**/*"] + "exclude": [ + "**/node_modules/**", + "!libs/plugins/scully-plugin-critical-css/**/*" + ] } }, "build": { @@ -310,9 +399,13 @@ "tsConfig": "libs/plugins/scully-plugin-critical-css/tsconfig.lib.json", "packageJson": "libs/plugins/scully-plugin-critical-css/package.json", "main": "libs/plugins/scully-plugin-critical-css/src/index.ts", - "assets": ["libs/plugins/scully-plugin-critical-css/*.md"] + "assets": [ + "libs/plugins/scully-plugin-critical-css/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -331,7 +424,10 @@ "libs/plugins/scully-plugin-flash-prevention/tsconfig.lib.json", "libs/plugins/scully-plugin-flash-prevention/tsconfig.spec.json" ], - "exclude": ["**/node_modules/**", "!libs/plugins/scully-plugin-flash-prevention/**"] + "exclude": [ + "**/node_modules/**", + "!libs/plugins/scully-plugin-flash-prevention/**" + ] } }, "build": { @@ -341,9 +437,13 @@ "tsConfig": "libs/plugins/scully-plugin-flash-prevention/tsconfig.lib.json", "packageJson": "libs/plugins/scully-plugin-flash-prevention/package.json", "main": "libs/plugins/scully-plugin-flash-prevention/index.ts", - "assets": ["libs/plugins/scully-plugin-flash-prevention/*.md"] + "assets": [ + "libs/plugins/scully-plugin-flash-prevention/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -354,28 +454,89 @@ "architect": { "build": { "builder": "@nrwl/node:package", - "outputs": ["{options.outputPath}"], + "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"] + "assets": [ + "libs/plugins/scully-plugin-puppeteer/*.md" + ] } }, "lint": { "builder": "@nrwl/linter:eslint", - "outputs": ["{options.outputFile}"], + "outputs": [ + "{options.outputFile}" + ], "options": { - "lintFilePatterns": ["libs/plugins/scully-plugin-puppeteer/**/*.ts"] + "lintFilePatterns": [ + "libs/plugins/scully-plugin-puppeteer/**/*.ts" + ] } }, "test": { "builder": "@nrwl/jest:jest", - "outputs": ["coverage/libs/plugins/scully-plugin-puppeteer"], + "outputs": [ + "coverage/libs/plugins/scully-plugin-puppeteer" + ], "options": { "jestConfig": "libs/plugins/scully-plugin-puppeteer/jest.config.js", - "passWithNoTests": true + "outputs": [ + "coverage/libs/plugins/scully-plugin-puppeteer" + ] + } + } + } + }, + "plugins-scully-plugin-playwright": { + "root": "libs/plugins/scully-plugin-playwright", + "sourceRoot": "libs/plugins/scully-plugin-playwright/src", + "projectType": "library", + "architect": { + "build": { + "builder": "@nrwl/node:package", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/libs/scully-plugin-playwright", + "tsConfig": "libs/plugins/scully-plugin-playwright/tsconfig.lib.json", + "packageJson": "libs/plugins/scully-plugin-playwright/package.json", + "main": "libs/plugins/scully-plugin-playwright/src/index.ts", + "assets": [ + "libs/plugins/scully-plugin-playwright/*.md" + ] + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "libs/plugins/scully-plugin-playwright/**/*.ts" + ] + } + }, + "test": { + "builder": "@nrwl/jest:jest", + "outputs": [ + "coverage/libs/plugins/scully-plugin-playwright" + ], + "options": { + "jestConfig": "libs/plugins/scully-plugin-playwright/jest.config.js", + "outputs": [ + "coverage/libs/plugins/scully-plugin-playwright" + ], + "options": { + "jestConfig": "libs/plugins/scully-plugin-playwright/jest.config.js", + "passWithNoTests": true + } } } } @@ -394,7 +555,10 @@ "libs/plugins/scully-plugin-remove-scripts/tsconfig.lib.json", "libs/plugins/scully-plugin-remove-scripts/tsconfig.spec.json" ], - "exclude": ["**/node_modules/**", "!libs/plugins/scully-plugin-remove-scripts/**/*"] + "exclude": [ + "**/node_modules/**", + "!libs/plugins/scully-plugin-remove-scripts/**/*" + ] } }, "build": { @@ -404,9 +568,13 @@ "tsConfig": "libs/plugins/scully-plugin-remove-scripts/tsconfig.lib.json", "packageJson": "libs/plugins/scully-plugin-remove-scripts/package.json", "main": "libs/plugins/scully-plugin-remove-scripts/src/index.ts", - "assets": ["libs/plugins/scully-plugin-remove-scripts/*.md"] + "assets": [ + "libs/plugins/scully-plugin-remove-scripts/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -420,8 +588,14 @@ "builder": "@nrwl/linter:lint", "options": { "linter": "eslint", - "tsConfig": ["libs/plugins/sentry/tsconfig.lib.json", "libs/plugins/sentry/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/plugins/sentry/**/*"] + "tsConfig": [ + "libs/plugins/sentry/tsconfig.lib.json", + "libs/plugins/sentry/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/plugins/sentry/**/*" + ] } }, "build": { @@ -431,9 +605,13 @@ "tsConfig": "libs/plugins/sentry/tsconfig.lib.json", "packageJson": "libs/plugins/sentry/package.json", "main": "libs/plugins/sentry/src/index.ts", - "assets": ["libs/plugins/sentry/*.md"] + "assets": [ + "libs/plugins/sentry/*.md" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -454,8 +632,13 @@ "polyfills": "apps/sample-blog/src/polyfills.ts", "tsConfig": "apps/sample-blog/tsconfig.app.json", "aot": true, - "assets": ["apps/sample-blog/src/favicon.ico", "apps/sample-blog/src/assets"], - "styles": ["apps/sample-blog/src/styles.css"], + "assets": [ + "apps/sample-blog/src/favicon.ico", + "apps/sample-blog/src/assets" + ], + "styles": [ + "apps/sample-blog/src/styles.css" + ], "scripts": [] }, "configurations": { @@ -487,7 +670,9 @@ ] } }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", @@ -509,8 +694,14 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["apps/sample-blog/tsconfig.app.json", "apps/sample-blog/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!apps/sample-blog/**"] + "tsConfig": [ + "apps/sample-blog/tsconfig.app.json", + "apps/sample-blog/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!apps/sample-blog/**" + ] } } } @@ -526,8 +717,14 @@ "options": { "linter": "eslint", "config": "libs/scully/.eslintrc", - "tsConfig": ["libs/scully/tsconfig.json", "libs/scully/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!libs/scully/**"] + "tsConfig": [ + "libs/scully/tsconfig.json", + "libs/scully/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/scully/**" + ] } }, "build": { @@ -537,9 +734,14 @@ "tsConfig": "libs/scully/tsconfig.json", "packageJson": "libs/scully/package.json", "main": "libs/scully/src/index.ts", - "assets": ["libs/scully/*.md", "libs/scully/.npmrc"] + "assets": [ + "libs/scully/*.md", + "libs/scully/.npmrc" + ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -560,7 +762,10 @@ "tsConfig": "apps/scully-docs/tsconfig.app.json", "aot": true, "statsJson": false, - "assets": ["apps/scully-docs/src/favicon.ico", "apps/scully-docs/src/assets"], + "assets": [ + "apps/scully-docs/src/favicon.ico", + "apps/scully-docs/src/assets" + ], "styles": [ "apps/scully-docs/src/app/app.component.css", "apps/scully-docs/src/app/components/footer/component/footer.component.css", @@ -621,7 +826,9 @@ ] } }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", @@ -643,8 +850,14 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["apps/scully-docs/tsconfig.app.json", "apps/scully-docs/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "!apps/scully-docs/**"] + "tsConfig": [ + "apps/scully-docs/tsconfig.app.json", + "apps/scully-docs/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "!apps/scully-docs/**" + ] } } } @@ -660,8 +873,13 @@ "options": { "linter": "eslint", "config": "libs/scully-schematics/.eslintrc", - "tsConfig": ["libs/scully-schematics/tsconfig.json"], - "exclude": ["**/node_modules/**", "!libs/scully-schematics/**"] + "tsConfig": [ + "libs/scully-schematics/tsconfig.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/scully-schematics/**" + ] } }, "build": { @@ -684,7 +902,9 @@ } ] }, - "outputs": ["{options.outputPath}"] + "outputs": [ + "{options.outputPath}" + ] } } }, @@ -702,8 +922,13 @@ "main": "apps/sps-sample/src/main.ts", "polyfills": "apps/sps-sample/src/polyfills.ts", "tsConfig": "apps/sps-sample/tsconfig.app.json", - "assets": ["apps/sps-sample/src/favicon.ico", "apps/sps-sample/src/assets"], - "styles": ["apps/sps-sample/src/styles.css"], + "assets": [ + "apps/sps-sample/src/favicon.ico", + "apps/sps-sample/src/assets" + ], + "styles": [ + "apps/sps-sample/src/styles.css" + ], "scripts": [], "vendorChunk": true, "extractLicenses": false, @@ -764,12 +989,17 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": ["apps/sps-sample/src/**/*.ts", "apps/sps-sample/src/**/*.html"] + "lintFilePatterns": [ + "apps/sps-sample/src/**/*.ts", + "apps/sps-sample/src/**/*.html" + ] } }, "test": { "builder": "@nrwl/jest:jest", - "outputs": ["coverage/apps/sps-sample"], + "outputs": [ + "coverage/apps/sps-sample" + ], "options": { "jestConfig": "apps/sps-sample/jest.config.js", "passWithNoTests": true @@ -798,12 +1028,17 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": ["libs/universal/src/**/*.ts", "libs/universal/src/**/*.html"] + "lintFilePatterns": [ + "libs/universal/src/**/*.ts", + "libs/universal/src/**/*.html" + ] } }, "test": { "builder": "@nrwl/jest:jest", - "outputs": ["coverage/libs/universal"], + "outputs": [ + "coverage/libs/universal" + ], "options": { "jestConfig": "libs/universal/jest.config.js", "passWithNoTests": true @@ -886,4 +1121,4 @@ "style": "css" } } -} +} \ No newline at end of file