diff --git a/.c8rc.json b/.c8rc.json index fe91f7331..5d44abeee 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -20,7 +20,7 @@ "statements": 80, "branches": 85, - "functions": 90, + "functions": 85, "lines": 80, "watermarks": { diff --git a/greenwood.config.js b/greenwood.config.js index a87785498..e913c0891 100644 --- a/greenwood.config.js +++ b/greenwood.config.js @@ -4,6 +4,7 @@ import { greenwoodPluginImportCss } from '@greenwood/plugin-import-css'; import { greenwoodPluginImportJson } from '@greenwood/plugin-import-json'; import { greenwoodPluginPolyfills } from '@greenwood/plugin-polyfills'; import { greenwoodPluginPostCss } from '@greenwood/plugin-postcss'; +import { greenwoodPluginRendererPuppeteer } from '@greenwood/plugin-renderer-puppeteer'; import rollupPluginAnalyzer from 'rollup-plugin-analyzer'; import { fileURLToPath, URL } from 'url'; @@ -33,7 +34,8 @@ export default { ]; } }, - ...greenwoodPluginIncludeHTML() + ...greenwoodPluginIncludeHTML(), + ...greenwoodPluginRendererPuppeteer() ], markdown: { plugins: [ diff --git a/packages/cli/package.json b/packages/cli/package.json index 668f18b6d..aa7e9c3ad 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -23,18 +23,9 @@ "publishConfig": { "access": "public" }, - "peerDependencies": { - "puppeteer": "^13.5.2" - }, - "peerDependenciesMeta": { - "puppeteer": { - "optional": true - } - }, "dependencies": { "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-replace": "^2.3.4", - "@webcomponents/webcomponentsjs": "^2.6.0", "acorn": "^8.0.1", "acorn-walk": "^8.0.0", "commander": "^2.20.0", @@ -56,7 +47,7 @@ "rollup": "^2.58.0", "rollup-plugin-terser": "^7.0.0", "unified": "^9.2.0", - "wc-compiler": "~0.3.1" + "wc-compiler": "~0.4.0" }, "devDependencies": { "@babel/runtime": "^7.10.4", @@ -70,7 +61,6 @@ "lit-redux-router": "~0.20.0", "lodash-es": "^4.17.20", "postcss-nested": "^4.1.2", - "puppeteer": "^13.5.2", "pwa-helpers": "^0.9.1", "redux": "^4.0.5", "redux-thunk": "^2.3.0", diff --git a/packages/cli/src/commands/build.js b/packages/cli/src/commands/build.js index 7181ffe96..27f78eaf5 100644 --- a/packages/cli/src/commands/build.js +++ b/packages/cli/src/commands/build.js @@ -1,8 +1,7 @@ import { bundleCompilation } from '../lifecycles/bundle.js'; import { copyAssets } from '../lifecycles/copy.js'; -import { getDevServer } from '../lifecycles/serve.js'; import fs from 'fs'; -import { preRenderCompilationCustom, preRenderCompilationDefault, staticRenderCompilation } from '../lifecycles/prerender.js'; +import { preRenderCompilationWorker, preRenderCompilationCustom, staticRenderCompilation } from '../lifecycles/prerender.js'; import { ServerInterface } from '../lib/server-interface.js'; const runProductionBuild = async (compilation) => { @@ -11,8 +10,10 @@ const runProductionBuild = async (compilation) => { try { const { prerender } = compilation.config; - const port = compilation.config.devServer.port; const outputDir = compilation.context.outputDir; + const defaultPrerender = (compilation.config.plugins.filter(plugin => plugin.type === 'renderer' && plugin.isGreenwoodDefaultPlugin) || []).length === 1 + ? compilation.config.plugins.filter(plugin => plugin.type === 'renderer')[0].provider(compilation) + : {}; const customPrerender = (compilation.config.plugins.filter(plugin => plugin.type === 'renderer' && !plugin.isGreenwoodDefaultPlugin) || []).length === 1 ? compilation.config.plugins.filter(plugin => plugin.type === 'renderer')[0].provider(compilation) : {}; @@ -22,45 +23,40 @@ const runProductionBuild = async (compilation) => { } if (prerender || customPrerender.prerender) { - if (customPrerender.prerender) { + // start any servers if needed + const servers = [...compilation.config.plugins.filter((plugin) => { + return plugin.type === 'server'; + }).map((plugin) => { + const provider = plugin.provider(compilation); + + if (!(provider instanceof ServerInterface)) { + console.warn(`WARNING: ${plugin.name}'s provider is not an instance of ServerInterface.`); + } + + return provider; + })]; + + await Promise.all(servers.map(async (server) => { + await server.start(); + + return Promise.resolve(server); + })); + + if (customPrerender.workerUrl) { + await preRenderCompilationWorker(compilation, customPrerender); + } else if (customPrerender.customUrl) { await preRenderCompilationCustom(compilation, customPrerender); + } else if (defaultPrerender && prerender) { + await preRenderCompilationWorker(compilation, defaultPrerender); } else { - await new Promise(async (resolve, reject) => { - try { - (await getDevServer(compilation)).listen(port, async () => { - console.info(`Started prerender server at localhost:${port}`); - - const servers = [...compilation.config.plugins.filter((plugin) => { - return plugin.type === 'server'; - }).map((plugin) => { - const provider = plugin.provider(compilation); - - if (!(provider instanceof ServerInterface)) { - console.warn(`WARNING: ${plugin.name}'s provider is not an instance of ServerInterface.`); - } - - return provider; - })]; - - await Promise.all(servers.map(async (server) => { - server.start(); - - return Promise.resolve(server); - })); - - await preRenderCompilationDefault(compilation); - - resolve(); - }); - } catch (e) { - reject(e); - } - }); + reject('This is an unhandled pre-rendering case! Please report.'); } } else { await staticRenderCompilation(compilation); } + console.info('success, done generating all pages!'); + await bundleCompilation(compilation); await copyAssets(compilation); diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 2976351a9..042b80d51 100755 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -7,7 +7,6 @@ process.setMaxListeners(0); import { generateCompilation } from './lifecycles/compile.js'; import fs from 'fs'; -import path from 'path'; import program from 'commander'; import { URL } from 'url'; @@ -60,40 +59,6 @@ if (program.parse.length === 0) { program.help(); } -// auto install puppeteer if user has enabled prerendering and not installed it already -async function checkForPuppeteer(compilation) { - if (compilation.config.prerender && !fs.existsSync(path.join(process.cwd(), '/node_modules/puppeteer'))) { - const puppeteerVersion = greenwoodPackageJson.peerDependencies.puppeteer; - console.log('prerender configuration detected but puppeteer is not detected.'); - console.log(`attempting to auto-install puppeteer@${puppeteerVersion} ...`); - - try { - await new Promise(async (resolve, reject) => { - const os = await import('os'); - const spawn = (await import('child_process')).spawn; - const pkgMng = fs.existsSync(path.join(process.cwd(), 'yarn.lock')) ? 'yarn' : 'npm'; - const command = pkgMng === 'yarn' ? 'add' : 'install'; - const commandFlags = pkgMng === 'yarn' ? '--dev' : '--save-dev'; - const pkgCommand = os.platform() === 'win32' ? `${pkgMng}.cmd` : pkgMng; - const args = [command, `puppeteer@${puppeteerVersion}`, commandFlags]; - const childProcess = spawn(pkgCommand, args, { stdio: 'ignore' }); - - childProcess.on('close', code => { - if (code !== 0) { - reject(); - return; - } - console.log('auto installation successful!'); - resolve(); - }); - }); - } catch (e) { - console.error(`Sorry, we were unable to auto-install puppeteer@${puppeteerVersion}.`); - console.log('Please visit our website for more information on self-installation: https://www.greenwoodjs.io/docs/configuration/#prerender'); - } - } -} - const run = async() => { const compilation = await generateCompilation(); @@ -104,7 +69,6 @@ const run = async() => { switch (command) { case 'build': - await checkForPuppeteer(compilation); await (await import('./commands/build.js')).runProductionBuild(compilation); break; @@ -115,7 +79,6 @@ const run = async() => { case 'serve': process.env.__GWD_COMMAND__ = 'build'; - await checkForPuppeteer(compilation); await (await import('./commands/build.js')).runProductionBuild(Object.assign({}, compilation)); await (await import('./commands/serve.js')).runProdServer(compilation); diff --git a/packages/cli/src/lib/ssr-route-worker.js b/packages/cli/src/lib/ssr-route-worker.js index 3a30e0b8b..3b8f62f14 100644 --- a/packages/cli/src/lib/ssr-route-worker.js +++ b/packages/cli/src/lib/ssr-route-worker.js @@ -1,34 +1,42 @@ // https://github.com/nodejs/modules/issues/307#issuecomment-858729422 import { pathToFileURL } from 'url'; import { workerData, parentPort } from 'worker_threads'; -import { renderToString } from 'wc-compiler'; +import { renderToString, renderFromHTML } from 'wc-compiler'; -async function executeRouteModule({ modulePath, compilation, route, label, id }) { - const moduleURL = pathToFileURL(modulePath); - const module = await import(moduleURL).then(module => module); - const { getFrontmatter = null, getBody = null, getTemplate = null } = module; +async function executeRouteModule({ modulePath, compilation, route, label, id, prerender, htmlContents, scripts }) { const parsedCompilation = JSON.parse(compilation); const data = { template: null, body: null, - frontmatter: null + frontmatter: null, + html: null }; - if (getFrontmatter) { - data.frontmatter = await getFrontmatter(parsedCompilation, route, label, id); - } + if (prerender) { + const scriptURLs = JSON.parse(scripts).map(scriptFile => new URL(scriptFile)); + const { html } = await renderFromHTML(htmlContents, scriptURLs); - if (getTemplate) { - data.template = await getTemplate(parsedCompilation, route); - } + data.html = html; + } else { + const module = await import(pathToFileURL(modulePath)).then(module => module); + const { getTemplate = null, getBody = null, getFrontmatter = null } = module; - if (module.default) { - const { html } = await renderToString(moduleURL); + if (module.default) { + const { html } = await renderToString(pathToFileURL(modulePath)); - data.body = html; - } else { - if (getBody) { - data.body = await getBody(parsedCompilation, route); + data.body = html; + } else { + if (getBody) { + data.body = await getBody(parsedCompilation, route); + } + } + + if (getTemplate) { + data.template = await getTemplate(parsedCompilation, route); + } + + if (getFrontmatter) { + data.frontmatter = await getFrontmatter(parsedCompilation, route, label, id); } } diff --git a/packages/cli/src/lifecycles/prerender.js b/packages/cli/src/lifecycles/prerender.js index 0d605a563..b9f52bf57 100644 --- a/packages/cli/src/lifecycles/prerender.js +++ b/packages/cli/src/lifecycles/prerender.js @@ -56,11 +56,13 @@ async function optimizePage(compilation, contents, route, outputPath, outputDir) return htmlOptimized; } -async function preRenderCompilationCustom(compilation, customPrerender) { +async function preRenderCompilationWorker(compilation, workerPrerender) { const pages = compilation.graph.filter(page => !page.isSSR); const outputDir = compilation.context.scratchDir; - for (const page of pages) { + console.info('pages to generate', `\n ${pages.map(page => page.route).join('\n ')}`); + + await Promise.all(pages.map(async (page) => { const { outputPath, route } = page; const outputPathDir = path.join(outputDir, route); const htmlResource = compilation.config.plugins.filter((plugin) => { @@ -87,7 +89,7 @@ async function preRenderCompilationCustom(compilation, customPrerender) { }); await new Promise((resolve, reject) => { - const worker = new Worker(customPrerender.workerUrl, { + const worker = new Worker(workerPrerender.workerUrl, { workerData: { modulePath: null, compilation: JSON.stringify(compilation), @@ -119,75 +121,25 @@ async function preRenderCompilationCustom(compilation, customPrerender) { }); } + console.info('generated page...', route); + await fs.promises.writeFile(path.join(outputDir, outputPath), html); - } + })); } -async function preRenderCompilationDefault(compilation) { - const BrowserRunner = (await import('../lib/browser.js')).BrowserRunner; - const browserRunner = new BrowserRunner(); - - const runBrowser = async (serverUrl, pages, outputDir) => { - try { - return Promise.all(pages.map(async(page) => { - const { outputPath, route } = page; - console.info('prerendering page...', route); - - return await browserRunner - .serialize(`${serverUrl}${route}`) - .then(async (indexHtml) => { - console.info(`prerendering complete for page ${route}.`); - - const html = await optimizePage(compilation, indexHtml, route, outputPath, outputDir); - await fs.promises.writeFile(path.join(outputDir, outputPath), html); - }); - })); - } catch (e) { - // eslint-disable-next-line no-console - console.error(err); - return false; - } - }; - - // gracefully handle if puppeteer is not installed correctly - // like may happen in a stackblitz environment and just reject early - // otherwise we can feel confident attempting to prerender all pages - // https://github.com/ProjectEvergreen/greenwood/discussions/639 - try { - await browserRunner.init(); - } catch (e) { - console.error(e); - - console.error('*******************************************************************'); - console.error('*******************************************************************'); - - console.error('There was an error trying to initialize puppeteer for pre-rendering.'); - - console.info('To troubleshoot, please check your environment for any npm install or postinstall errors, as may be the case in a Stackblitz or other sandbox like environment.'); - console.info('For more information please see this guide - https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md'); - - return Promise.reject(); - } - - return new Promise(async (resolve, reject) => { - try { - const pages = compilation.graph.filter(page => !page.isSSR); - const port = compilation.config.devServer.port; - const outputDir = compilation.context.scratchDir; - const serverAddress = `http://127.0.0.1:${port}`; +async function preRenderCompilationCustom(compilation, customPrerender) { + const { scratchDir } = compilation.context; + const renderer = (await import(customPrerender.customUrl)).default; - console.debug('pages to render', `\n ${pages.map(page => page.route).join('\n ')}`); - console.info(`Prerendering pages at ${serverAddress}`); + console.info('pages to generate', `\n ${compilation.graph.map(page => page.route).join('\n ')}`); - await runBrowser(serverAddress, pages, outputDir); - browserRunner.close(); + await renderer(compilation, async (page, contents) => { + const { outputPath, route } = page; - console.info('done prerendering all pages'); + console.info('generated page...', route); - resolve(); - } catch (err) { - reject(err); - } + const html = await optimizePage(compilation, contents, route, outputPath, scratchDir); + await fs.promises.writeFile(path.join(scratchDir, outputPath), html); }); } @@ -200,7 +152,7 @@ async function staticRenderCompilation(compilation) { return plugin.provider(compilation); })[0]; - console.info('pages to generate', `\n ${pages.map(page => page.path).join('\n ')}`); + console.info('pages to generate', `\n ${pages.map(page => page.route).join('\n ')}`); await Promise.all(pages.map(async (page) => { const { route, outputPath } = page; @@ -211,14 +163,14 @@ async function staticRenderCompilation(compilation) { await fs.promises.writeFile(path.join(scratchDir, outputPath), html); + console.info('generated page...', route); + return Promise.resolve(); })); - - console.info('success, done generating all pages!'); } export { + preRenderCompilationWorker, preRenderCompilationCustom, - preRenderCompilationDefault, staticRenderCompilation }; \ No newline at end of file diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js index 289784400..0195e4227 100644 --- a/packages/cli/src/plugins/resource/plugin-standard-html.js +++ b/packages/cli/src/plugins/resource/plugin-standard-html.js @@ -263,7 +263,6 @@ const getAppTemplate = (contents, templatesDir, customImports = [], contextPlugi }; const getUserScripts = (contents, context) => { - // polyfill chromium for WC support // https://lit.dev/docs/tools/requirements/#polyfills if (process.env.__GWD_COMMAND__ === 'build') { // eslint-disable-line no-underscore-dangle const { projectDirectory, userWorkspace } = context; @@ -280,7 +279,6 @@ const getUserScripts = (contents, context) => { contents = contents.replace('', ` ${litPolyfill} - `); } return contents; @@ -507,7 +505,6 @@ class StandardHtmlResource extends ResourceInterface { let contents = hasHead[0]; contents = contents.replace(/ - @@ -15,13 +10,4 @@ - - - - - - - - - \ No newline at end of file diff --git a/packages/cli/test/cases/build.default.quick-start-npx/build.default.quick-start-npx.spec.js b/packages/cli/test/cases/build.default.quick-start-npx/build.default.quick-start-npx.spec.js index 0321b6c36..03c6d2344 100644 --- a/packages/cli/test/cases/build.default.quick-start-npx/build.default.quick-start-npx.spec.js +++ b/packages/cli/test/cases/build.default.quick-start-npx/build.default.quick-start-npx.spec.js @@ -55,7 +55,7 @@ describe('Build Greenwood With: ', function() { dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './index.html')); }); - xdescribe('head section tags', function() { + describe('head section tags', function() { let metaTags; before(function() { diff --git a/packages/cli/test/cases/build.default.spa/build.default.spa.spec.js b/packages/cli/test/cases/build.default.spa/build.default.spa.spec.js index f99381ca8..f44518c5b 100644 --- a/packages/cli/test/cases/build.default.spa/build.default.spa.spec.js +++ b/packages/cli/test/cases/build.default.spa/build.default.spa.spec.js @@ -163,7 +163,7 @@ describe('Build Greenwood With: ', function() { `${process.cwd()}/node_modules/symbol-observable/es/*.js`, `${outputPath}/node_modules/symbol-observable/es` ); - const symobolLibsPackageJson = await getDependencyFiles( + const symbolLibsPackageJson = await getDependencyFiles( `${process.cwd()}/node_modules/symbol-observable/package.json`, `${outputPath}/node_modules/symbol-observable/` ); @@ -206,7 +206,7 @@ describe('Build Greenwood With: ', function() { ...tokensLibs, ...tokensLibsPackageJson, ...symbolLibs, - ...symobolLibsPackageJson, + ...symbolLibsPackageJson, ...reduxThunkPackageJson, ...reduxThunk ]); diff --git a/packages/cli/test/cases/build.default.ssr-static-export/greenwood.config.js b/packages/cli/test/cases/build.default.ssr-static-export/greenwood.config.js deleted file mode 100644 index b31c8b19b..000000000 --- a/packages/cli/test/cases/build.default.ssr-static-export/greenwood.config.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - prerender: false // TODO should allow this to be false and mix and match rendering options -}; \ No newline at end of file diff --git a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/build.default.workspace-frontmatter-imports.spec.js b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/build.default.workspace-frontmatter-imports.spec.js index 58916c211..9e8295f4d 100644 --- a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/build.default.workspace-frontmatter-imports.spec.js +++ b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/build.default.workspace-frontmatter-imports.spec.js @@ -30,7 +30,7 @@ import fs from 'fs'; import glob from 'glob-promise'; import { JSDOM } from 'jsdom'; import path from 'path'; -import { copyDirectory, getSetupFiles, getOutputTeardownFiles } from '../../../../../test/utils.js'; +import { getSetupFiles, getOutputTeardownFiles } from '../../../../../test/utils.js'; import { runSmokeTest } from '../../../../../test/smoke-test.js'; import { Runner } from 'gallinago'; import { fileURLToPath, URL } from 'url'; @@ -53,21 +53,18 @@ describe('Build Greenwood With: ', function() { describe(LABEL, function() { before(async function() { - // stub puppeteer dependency to avoid package manager installation when running specs that need prerendering - await copyDirectory(`${process.cwd()}/node_modules/puppeteer`, `${outputPath}node_modules/puppeteer`); - await runner.setup(outputPath, getSetupFiles(outputPath)); await runner.runCommand(cliPath, 'build'); }); - + runSmokeTest(['public', 'index'], LABEL); - - describe('Content and file output for the Counter page', function() { + + describe('Content and file output for the Demo page', function() { let dom; let html; before(async function() { - const htmlPath = path.resolve(this.context.publicDir, 'examples/counter', 'index.html'); + const htmlPath = path.resolve(this.context.publicDir, 'examples/demo', 'index.html'); dom = await JSDOM.fromFile(path.resolve(htmlPath)); html = await fs.promises.readFile(htmlPath, 'utf-8'); @@ -85,14 +82,20 @@ describe('Build Greenwood With: ', function() { expect(jsFiles).to.have.lengthOf(1); }); + it('should output a multi-hyphen.js file from frontmatter import', async function() { + const jsFiles = await glob.promise(`${this.context.publicDir}**/**/multi-hyphen.*.js`); + + expect(jsFiles).to.have.lengthOf(1); + }); + it('should a page heading', function() { const heading = dom.window.document.querySelectorAll('body h2'); expect(heading.length).to.be.equal(1); - expect(heading[0].textContent).to.be.equal('Counter Page Example'); + expect(heading[0].textContent).to.be.equal('Demo Page Example'); }); - describe('Counter component from front matter', () => { + describe('Counter component from front matter that is prerendered', () => { it('should output a custom tag that', function() { const counter = dom.window.document.querySelectorAll('body x-counter'); @@ -105,43 +108,25 @@ describe('Build Greenwood With: ', function() { }); it('should output a heading tag from the custom element', function() { - const heading = dom.window.document.querySelectorAll('body h3'); - - expect(heading.length).to.be.equal(1); - expect(heading[0].textContent).to.be.equal('My Counter'); - }); - }); - - describe('Custom component', () => { - it('should output a custom tag that', function() { - const header = dom.window.document.querySelectorAll('body app-header'); - - expect(header.length).to.be.equal(1); - }); - - it('should output a tag that is _not_ wrapped in a

tag', function() { - expect((/

/).test(html)).to.be.false; - expect((/<\/app-header><\/p>/).test(html)).to.be.false; - }); - - it('should output a tag with expected content', function() { - const header = dom.window.document.querySelectorAll('body app-header'); - - expect(header[0].textContent).to.be.equal('I am a header'); + expect(html).to.contain('

My Counter

'); }); }); - describe('Custom Multihypen component', () => { - it('should output a custom tag that', function() { - const header = dom.window.document.querySelectorAll('body multihyphen-custom-element'); + describe('Custom Multihyphen component', () => { + it('should output a custom tag', function() { + const hyphen = dom.window.document.querySelectorAll('body multihyphen-custom-element'); - expect(header.length).to.be.equal(1); + expect(hyphen.length).to.be.equal(1); }); it('should output a tag that is _not_ wrapped in a

tag', function() { expect((/

/).test(html)).to.be.false; expect((/<\/multihyphen-custom-element><\/p>/).test(html)).to.be.false; }); + + it('should have the expected prerendered content', function() { + expect(html).to.contain('I have multiple hyphens in my tag name!'); + }); }); }); }); diff --git a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/counter/counter.js b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/counter/counter.js index b102faa27..e1296f7b1 100644 --- a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/counter/counter.js +++ b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/counter/counter.js @@ -12,18 +12,17 @@ template.innerHTML = ` `; -class MyCounter extends HTMLElement { +export default class MyCounter extends HTMLElement { constructor() { super(); this.count = 0; - this.attachShadow({ mode: 'open' }); } - async connectedCallback() { - this.shadowRoot.appendChild(template.content.cloneNode(true)); - this.shadowRoot.getElementById('inc').onclick = () => this.inc(); - this.shadowRoot.getElementById('dec').onclick = () => this.dec(); - this.update(); + async connectedCallback() { + if (!this.shadowRoot) { + this.attachShadow({ mode: 'open' }); + this.shadowRoot.appendChild(template.content.cloneNode(true)); + } } inc() { diff --git a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/multi-hyphen/multi-hyphen.js b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/multi-hyphen/multi-hyphen.js new file mode 100644 index 000000000..e48a71f3b --- /dev/null +++ b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/components/multi-hyphen/multi-hyphen.js @@ -0,0 +1,16 @@ +const template = document.createElement('template'); + +template.innerHTML = ` +

I have multiple hyphens in my tag name!

+`; + +export default class MultiHyphen extends HTMLElement { + async connectedCallback() { + if (!this.shadowRoot) { + this.attachShadow({ mode: 'open' }); + this.shadowRoot.appendChild(template.content.cloneNode(true)); + } + } +} + +customElements.define('multihyphen-custom-element', MultiHyphen); \ No newline at end of file diff --git a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/pages/examples/counter.md b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/pages/examples/demo.md similarity index 67% rename from packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/pages/examples/counter.md rename to packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/pages/examples/demo.md index c77fc15f7..c67290c5f 100644 --- a/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/pages/examples/counter.md +++ b/packages/cli/test/cases/build.default.workspace-frontmatter-imports/src/pages/examples/demo.md @@ -1,14 +1,13 @@ --- -title: Counter Page +title: Demo Page imports: - /components/counter/counter.js - /components/counter/counter.css + - /components/multi-hyphen/multi-hyphen.js --- -## Counter Page Example +## Demo Page Example -I am a header - \ No newline at end of file diff --git a/packages/cli/test/cases/build.default.workspace-javascript-css/build.default.workspace-javascript-css.spec.js b/packages/cli/test/cases/build.default.workspace-javascript-css/build.default.workspace-javascript-css.spec.js index b81acca3c..19579c28a 100644 --- a/packages/cli/test/cases/build.default.workspace-javascript-css/build.default.workspace-javascript-css.spec.js +++ b/packages/cli/test/cases/build.default.workspace-javascript-css/build.default.workspace-javascript-css.spec.js @@ -1,7 +1,6 @@ /* * Use Case - * Run Greenwood with various usages of JavaScript ( tags in the ', function() { - it('should have one tag in the with mixed attribute ordering', function() { @@ -112,23 +87,11 @@ describe('Build Greenwood With: ', function() { expect(scriptTagSrcOne.textContent).to.be.contain('document.getElementsByClassName("output-script-inline-one")[0].innerHTML="script tag module inline one"'); }); - it('should have the expected output from inline + `); + + return Promise.resolve({ body }); + } + + async shouldOptimize(url = '', body, headers = {}) { + return Promise.resolve(path.extname(url) === '.html' || (headers.request && headers.request['content-type'].indexOf(this.contentType) >= 0)); + } + + async optimize(url, body) { + return new Promise((resolve, reject) => { + try { + const hasHead = body.match(/\(.*)<\/head>/s); + + if (hasHead && hasHead.length > 0) { + body = body.replace(/ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/plugin-renderer-puppeteer/test/cases/build.default/src/scripts/main.js b/packages/plugin-renderer-puppeteer/test/cases/build.default/src/scripts/main.js new file mode 100644 index 000000000..8276a7960 --- /dev/null +++ b/packages/plugin-renderer-puppeteer/test/cases/build.default/src/scripts/main.js @@ -0,0 +1,13 @@ +import { LitElement } from 'lit'; +import { defaults } from 'lodash-es'; +import { lazyReducerEnhancer } from 'pwa-helpers'; +import { createStore } from 'redux'; + +try { + document.getElementsByClassName('output-lit')[0].innerHTML = `import from lit ${btoa(LitElement.prototype).slice(0, 16)}`; + document.getElementsByClassName('output-lodash')[0].innerHTML = `import from lodash-es ${JSON.stringify(defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }))}`; + document.getElementsByClassName('output-pwa')[0].innerHTML = `import from pwa-helpers ${btoa(lazyReducerEnhancer).slice(0, 16)}`; + document.getElementsByClassName('output-redux')[0].innerHTML = `import from redux ${btoa(createStore).slice(0, 16)}`; +} catch (e) { + document.getElementsByClassName('output-error')[0].innerHTML = e; +} \ No newline at end of file diff --git a/packages/plugin-typescript/README.md b/packages/plugin-typescript/README.md index 20a2792ba..301b95693 100644 --- a/packages/plugin-typescript/README.md +++ b/packages/plugin-typescript/README.md @@ -5,6 +5,10 @@ A Greenwood plugin for writing [**TypeScript**](https://www.typescriptlang.org/) > This package assumes you already have `@greenwood/cli` installed. +## Caveats + +As of now, this transformation is only supported for client side (browser) code and will not run correctly in NodeJS until [support for this is introduced into Greenwood](https://github.com/ProjectEvergreen/greenwood/issues/878), or natively by NodeJS. This means it will not work when using `prerender` option with WCC. + ## Installation You can use your favorite JavaScript package manager to install this package. diff --git a/test/smoke-test.js b/test/smoke-test.js index 348383fe7..3ce3b5722 100644 --- a/test/smoke-test.js +++ b/test/smoke-test.js @@ -66,7 +66,6 @@ function commonIndexSpecs(dom, html, label) { expect(tagsMatch('link', html)).to.be.equal(true); }); - // note: one will always be present when using puppeteer it('should have matching opening and closing