From b92021866aba179dbc7d6c10e2b14f240286efaf Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 18 Jul 2020 15:55:06 -0400 Subject: [PATCH 01/26] enable spa mode and ssg by default --- greenwood.config.js | 1 + packages/cli/src/lifecycles/config.js | 10 +++++++++- packages/cli/src/lifecycles/serialize.js | 7 ++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/greenwood.config.js b/greenwood.config.js index dd0707819..a1484f3f9 100644 --- a/greenwood.config.js +++ b/greenwood.config.js @@ -6,6 +6,7 @@ const META_DESCRIPTION = 'A modern and performant static site generator supporti const FAVICON_HREF = '/assets/favicon.ico'; module.exports = { + mode: 'spa', workspace: path.join(__dirname, 'www'), title: 'Greenwood', meta: [ diff --git a/packages/cli/src/lifecycles/config.js b/packages/cli/src/lifecycles/config.js index 93c70ff19..4f389c646 100644 --- a/packages/cli/src/lifecycles/config.js +++ b/packages/cli/src/lifecycles/config.js @@ -4,6 +4,7 @@ const url = require('url'); let defaultConfig = { workspace: path.join(process.cwd(), 'src'), + mode: 'ssg', devServer: { port: 1984, host: 'localhost' @@ -25,7 +26,8 @@ module.exports = readAndMergeConfig = async() => { if (await fs.exists(path.join(process.cwd(), 'greenwood.config.js'))) { const userCfgFile = require(path.join(process.cwd(), 'greenwood.config.js')); - const { workspace, devServer, publicPath, title, meta, plugins, themeFile, markdown } = userCfgFile; + const modes = ['ssg', 'spa']; + const { workspace, devServer, mode, publicPath, title, meta, plugins, themeFile, markdown } = userCfgFile; // workspace validation if (workspace) { @@ -43,6 +45,12 @@ module.exports = readAndMergeConfig = async() => { customConfig.workspace = workspace; } + if (typeof mode === 'string' && modes.indexOf(mode.toLowerCase()) >= 0) { + customConfig.mode = mode; + } else if (mode) { + reject(`Error: provided ${mode} not supported, plase use one of: ${modes.join()}`); + } + if (!await fs.exists(customConfig.workspace)) { reject('Error: greenwood.config.js workspace doesn\'t exist! \n' + 'common issues to check might be: \n' + diff --git a/packages/cli/src/lifecycles/serialize.js b/packages/cli/src/lifecycles/serialize.js index 52c4ff22b..982f489e4 100644 --- a/packages/cli/src/lifecycles/serialize.js +++ b/packages/cli/src/lifecycles/serialize.js @@ -21,13 +21,18 @@ module.exports = serializeBuild = async (compilation) => { return await browserRunner.serialize(`http://127.0.0.1:${PORT}${route}`).then(async (content) => { const target = path.join(publicDir, route); - const html = content + let html = content .replace(polyfill, '') .replace('', ` `); + + if (compilation.config.mode === 'ssg') { + // + html = html.replace(/', ` @@ -29,9 +30,10 @@ module.exports = serializeBuild = async (compilation) => { `); - if (compilation.config.mode === 'ssg') { - // - html = html.replace(/ `); - if (mode === 'ssg') { // no javascript + if (mode === 'strict') { // no javascript html = html.replace(/ + +
+ + +
+ \`; + } +} + +customElements.define('page-template', HomeTemplate); +``` + +Caveats: +- Code like [`fetch` and `setTimeout` in your will not be available](https://github.com/ProjectEvergreen/greenwood/blob/v0.8.0/www/components/banner/banner.js#L37) +- Your [CSS selectors cannot target custom elements](https://github.com/thegreenhouseio/www.thegreenhouse.io/pull/158/) (since `customElements.define` will not run) + + +#### SPA (expiremental) +This mode will include all JavaScript, including a client side router provided by Greenwood (just like in development mode). This will pre-render your site _and_ also ship a full "SPA" experience for your users and try and hydrate from that, but at the cost of shipping a little more JavaScript, so choose wisely! + +Here are some of the known issues we are tracking at this time as we refine support for this feature: +- [double rendering](https://github.com/ProjectEvergreen/greenwood/issues/348) +- [meta not changing on route change](https://github.com/ProjectEvergreen/greenwood/issues/306) +- [_cache.json_ hydration](https://github.com/ProjectEvergreen/greenwood/issues/349) + + ### Evergreen Build Greenwood promotes an "evergreen" build that ensures that the code delivered to users is as modern as the code all based on real browser usage and support statistics. Automatically! diff --git a/www/pages/docs/configuration.md b/www/pages/docs/configuration.md index 820ed00b9..a0e864f6c 100644 --- a/www/pages/docs/configuration.md +++ b/www/pages/docs/configuration.md @@ -98,6 +98,20 @@ Which would be equivalent to: ``` +### Mode +Greenwood supports a couple different options for how it will generate a production build, depending on how much JavaScript you will need to serve your users. +- **strict (default)**: What you write will only be used to pre-render your application. No JavaScript is shipped at all* and will typically yield the best results in regards to performance. +- **spa** (expiremental): This will pre-render your site _and_ also ship a full "SPA" experience for your users. + +> _You can learn more about modes in our [How It Works](/about/how-it-works) docs._ + +#### Example +```js +module.exports = { + mode: 'spa' +} +``` + ### Public Path The `publicPath` options allows configuring additional URL segments to customize the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) for your site. From d873e66f41db84fa101e7cffa272fa1577a6bf50 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Thu, 30 Jul 2020 19:47:41 -0400 Subject: [PATCH 09/26] spa mode TODO tracking documentation --- www/pages/about/how-it-works.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/www/pages/about/how-it-works.md b/www/pages/about/how-it-works.md index dbf8eccaf..16b15475c 100644 --- a/www/pages/about/how-it-works.md +++ b/www/pages/about/how-it-works.md @@ -66,11 +66,7 @@ Caveats: #### SPA (expiremental) This mode will include all JavaScript, including a client side router provided by Greenwood (just like in development mode). This will pre-render your site _and_ also ship a full "SPA" experience for your users and try and hydrate from that, but at the cost of shipping a little more JavaScript, so choose wisely! -Here are some of the known issues we are tracking at this time as we refine support for this feature: -- [double rendering](https://github.com/ProjectEvergreen/greenwood/issues/348) -- [meta not changing on route change](https://github.com/ProjectEvergreen/greenwood/issues/306) -- [_cache.json_ hydration](https://github.com/ProjectEvergreen/greenwood/issues/349) - +> THere are a some [known issues](https://github.com/ProjectEvergreen/greenwood/labels/mode%3Aspa) we are tracking at this time as we refine support for this feature. ### Evergreen Build Greenwood promotes an "evergreen" build that ensures that the code delivered to users is as modern as the code all based on real browser usage and support statistics. Automatically! From b70ae8deccdd288641b59323d9d3c9d393ead1de Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 31 Jul 2020 19:56:51 -0400 Subject: [PATCH 10/26] make strict default and add unit tests --- greenwood.config.js | 2 +- packages/cli/src/data/queries/config.gql | 1 + packages/cli/src/data/schema/config.js | 1 + packages/cli/test/unit/data/schema/config.spec.js | 8 ++++++++ www/pages/docs/data.md | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/greenwood.config.js b/greenwood.config.js index 4c5f83452..a1484f3f9 100644 --- a/greenwood.config.js +++ b/greenwood.config.js @@ -6,7 +6,7 @@ const META_DESCRIPTION = 'A modern and performant static site generator supporti const FAVICON_HREF = '/assets/favicon.ico'; module.exports = { - mode: 'strict', + mode: 'spa', workspace: path.join(__dirname, 'www'), title: 'Greenwood', meta: [ diff --git a/packages/cli/src/data/queries/config.gql b/packages/cli/src/data/queries/config.gql index 371cc8867..27c08f0ff 100644 --- a/packages/cli/src/data/queries/config.gql +++ b/packages/cli/src/data/queries/config.gql @@ -12,6 +12,7 @@ query { value, href }, + mode, publicPath, title, workspace diff --git a/packages/cli/src/data/schema/config.js b/packages/cli/src/data/schema/config.js index a4adea9f2..99997b41b 100644 --- a/packages/cli/src/data/schema/config.js +++ b/packages/cli/src/data/schema/config.js @@ -23,6 +23,7 @@ const configTypeDefs = gql` type Config { devServer: DevServer, meta: [Meta], + mode: String, publicPath: String, title: String, workspace: String diff --git a/packages/cli/test/unit/data/schema/config.spec.js b/packages/cli/test/unit/data/schema/config.spec.js index 73897a66f..b315423e6 100644 --- a/packages/cli/test/unit/data/schema/config.spec.js +++ b/packages/cli/test/unit/data/schema/config.spec.js @@ -53,6 +53,14 @@ describe('Unit Test: Data', function() { }); }); + describe('Mode', function() { + + it('should have the default mode of strict', function() { + expect(config.mode).to.equal(MOCK_CONFIG.config.mode); + }); + + }); + describe('Public Path', function() { const { publicPath } = MOCK_CONFIG.config; diff --git a/www/pages/docs/data.md b/www/pages/docs/data.md index be9722f46..8bf00d941 100644 --- a/www/pages/docs/data.md +++ b/www/pages/docs/data.md @@ -219,6 +219,7 @@ query { value, href }, + mode, publicPath, title, workspace From 4010bcfc4e4b695cb5ed9f3819b0eeec3a800596 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 31 Jul 2020 20:09:30 -0400 Subject: [PATCH 11/26] fix mode check and set default to strict --- packages/cli/src/lifecycles/config.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/lifecycles/config.js b/packages/cli/src/lifecycles/config.js index 838ac1e40..78a6d81fb 100644 --- a/packages/cli/src/lifecycles/config.js +++ b/packages/cli/src/lifecycles/config.js @@ -3,9 +3,10 @@ const path = require('path'); const url = require('url'); const modes = ['strict', 'spa']; + let defaultConfig = { workspace: path.join(process.cwd(), 'src'), - mode: 'spa', + mode: 'strict', devServer: { port: 1984, host: 'localhost' @@ -45,12 +46,6 @@ module.exports = readAndMergeConfig = async() => { customConfig.workspace = workspace; } - if (typeof mode === 'string' && modes.indexOf(mode.toLowerCase()) >= 0) { - customConfig.mode = mode; - } else if (mode) { - reject(`Error: provided ${mode} not supported, plase use one of: ${modes.join()}`); - } - if (!await fs.exists(customConfig.workspace)) { reject('Error: greenwood.config.js workspace doesn\'t exist! \n' + 'common issues to check might be: \n' + @@ -80,6 +75,12 @@ module.exports = readAndMergeConfig = async() => { customConfig.meta = meta; } + if (typeof mode === 'string' && modes.indexOf(mode.toLowerCase()) >= 0) { + customConfig.mode = mode; + } else if (mode) { + reject(`Error: provided mode "${mode}" is not supported. Please use one of: ${modes.join(', ')}.`); + } + if (plugins && plugins.length > 0) { const types = ['index', 'webpack']; From e72ca70bf52197ebfc589f659647de7bc5c624e4 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 31 Jul 2020 20:11:14 -0400 Subject: [PATCH 12/26] add error test case for mode --- .../build.config.error-mode.spec.js | 44 +++++++++++++++++++ .../greenwood.config.js | 3 ++ 2 files changed, 47 insertions(+) create mode 100644 packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js create mode 100644 packages/cli/test/cases/build.config.error-mode/greenwood.config.js diff --git a/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js b/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js new file mode 100644 index 000000000..12234d392 --- /dev/null +++ b/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js @@ -0,0 +1,44 @@ +/* + * Use Case + * Run Greenwood build command with a bad value for mode in a custom config. + * + * User Result + * Should throw an error. + * + * User Command + * greenwood build + * + * User Config + * { + * mode: 'lorumipsum' + * } + * + * User Workspace + * Greenwood default + */ +const expect = require('chai').expect; +const TestBed = require('../../../../../test/test-bed'); + +describe.only('Build Greenwood With: ', function() { + let setup; + + before(async function() { + setup = new TestBed(true); + await setup.setupTestBed(__dirname); + }); + + describe('Custom Configuration with a bad value for Mode', function() { + it('should throw an error that provided mode is not valid', async function() { + try { + await setup.runGreenwoodCommand('build'); + } catch (err) { + expect(err).to.contain('Error: provided mode "loremipsum" is not supported. Please use one of: strict, spa.'); + } + }); + }); + + after(function() { + setup.teardownTestBed(); + }); + +}); \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.error-mode/greenwood.config.js b/packages/cli/test/cases/build.config.error-mode/greenwood.config.js new file mode 100644 index 000000000..92ceb3337 --- /dev/null +++ b/packages/cli/test/cases/build.config.error-mode/greenwood.config.js @@ -0,0 +1,3 @@ +module.exports = { + mode: 'loremipsum' +}; \ No newline at end of file From 5a4d24ad0effd90f35d83b91f1bd97fdf2edc647 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 31 Jul 2020 20:16:28 -0400 Subject: [PATCH 13/26] remove describe.only --- .../build.config.error-mode/build.config.error-mode.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js b/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js index 12234d392..cebc8be03 100644 --- a/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js +++ b/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js @@ -19,7 +19,7 @@ const expect = require('chai').expect; const TestBed = require('../../../../../test/test-bed'); -describe.only('Build Greenwood With: ', function() { +describe('Build Greenwood With: ', function() { let setup; before(async function() { From 58571f5d8a78ba13efc50b3237b38e943ccc4f12 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 31 Jul 2020 22:11:01 -0400 Subject: [PATCH 14/26] remove debugging --- .../build.config.error-mode/build.config.error-mode.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js b/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js index cebc8be03..36718fab6 100644 --- a/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js +++ b/packages/cli/test/cases/build.config.error-mode/build.config.error-mode.spec.js @@ -23,7 +23,7 @@ describe('Build Greenwood With: ', function() { let setup; before(async function() { - setup = new TestBed(true); + setup = new TestBed(); await setup.setupTestBed(__dirname); }); From 87d66a539a36a27d8e7694f0c088e59116d85829 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Fri, 31 Jul 2020 22:11:13 -0400 Subject: [PATCH 15/26] strict mode as default for test cases --- .../build.config.meta/build.config.meta.spec.js | 8 ++++---- .../build.config.title.spec.js | 9 +++++---- .../build.default.webpack.spec.js | 17 +++++------------ ...build.default.workspace-template-app.spec.js | 8 ++++---- .../build-plugins-webpack.spec.js | 17 +++++------------ test/smoke-test.js | 13 +------------ 6 files changed, 24 insertions(+), 48 deletions(-) diff --git a/packages/cli/test/cases/build.config.meta/build.config.meta.spec.js b/packages/cli/test/cases/build.config.meta/build.config.meta.spec.js index 7e844ba57..288206318 100644 --- a/packages/cli/test/cases/build.config.meta/build.config.meta.spec.js +++ b/packages/cli/test/cases/build.config.meta/build.config.meta.spec.js @@ -82,7 +82,7 @@ describe('Build Greenwood With: ', function() { expect(title).to.be.equal('My Custom Greenwood App'); }); - it('should have one ', ` - - `); + `; + + let html = content + .replace(polyfill, '') + .replace('', apolloScript); - if (mode === 'strict') { // no javascript + if (isStrictMode) { // no javascript html = html.replace(/', apolloScript); if (isStrictMode) { // no javascript - html = html.replace(/', apolloScript); - if (isStrictMode) { // no javascript + if (isStrictOptimization) { // no javascript html = html.replace(/ + + + + + +
+ +
+ +
+
`; }