Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/issue 1197 Lit v3 upgrade and SSR fixes and enhancements #1201

Merged
4 changes: 3 additions & 1 deletion greenwood.config.js
Expand Up @@ -14,7 +14,9 @@ export default {
interpolateFrontmatter: true,
plugins: [
greenwoodPluginGraphQL(),
greenwoodPluginPolyfills(),
greenwoodPluginPolyfills({
lit: true
}),
greenwoodPluginPostCss(),
greenwoodPluginImportJson(),
greenwoodPluginImportCss(),
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -30,7 +30,7 @@
"lint": "ls-lint && yarn lint:js && yarn lint:ts && yarn lint:css"
},
"resolutions": {
"lit": "^2.1.1"
"lit": "^3.1.0"
},
"devDependencies": {
"@ls-lint/ls-lint": "^1.10.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Expand Up @@ -52,7 +52,7 @@
"remark-rehype": "^7.0.0",
"rollup": "^3.29.4",
"unified": "^9.2.0",
"wc-compiler": "~0.11.0"
"wc-compiler": "~0.12.0"
},
"devDependencies": {
"@babel/runtime": "^7.10.4",
Expand All @@ -62,7 +62,7 @@
"@material/mwc-button": "^0.25.2",
"@stencil/core": "^2.12.0",
"@types/trusted-types": "^2.0.2",
"lit": "^2.0.0",
"lit": "^3.1.0",
"lit-redux-router": "~0.20.0",
"lodash-es": "^4.17.20",
"postcss-nested": "^4.1.2",
Expand Down
36 changes: 11 additions & 25 deletions packages/cli/src/config/rollup.config.js
Expand Up @@ -6,30 +6,11 @@
import * as walk from 'acorn-walk';

// https://github.com/rollup/rollup/issues/2121
// would be nice to get rid of this
function cleanRollupId(id) {
return id.replace('\x00', '');
}

// specifically to handle escodegen and other node modules
// using require for package.json or other json files
// https://github.com/estools/escodegen/issues/455
function greenwoodJsonLoader() {
return {
name: 'greenwood-json-loader',
async load(id) {
const idUrl = new URL(`file://${cleanRollupId(id)}`);
const extension = idUrl.pathname.split('.').pop();

if (extension === 'json') {
const json = JSON.parse(await fs.promises.readFile(idUrl, 'utf-8'));
const contents = `export default ${JSON.stringify(json)}`;

return contents;
}
}
};
}

function greenwoodResourceLoader (compilation) {
const resourcePlugins = compilation.config.plugins.filter((plugin) => {
return plugin.type === 'resource';
Expand Down Expand Up @@ -271,7 +252,7 @@
};
}

// TODO could we use this instead?

Check warning on line 255 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected 'todo' comment: 'TODO could we use this instead?'

Check warning on line 255 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected ' TODO' comment: 'TODO could we use this instead?'

Check warning on line 255 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected 'todo' comment: 'TODO could we use this instead?'

Check warning on line 255 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected ' TODO' comment: 'TODO could we use this instead?'
// https://github.com/rollup/rollup/blob/v2.79.1/docs/05-plugin-development.md#resolveimportmeta
// https://github.com/ProjectEvergreen/greenwood/issues/1087
function greenwoodPatchSsrPagesEntryPointRuntimeImport() {
Expand Down Expand Up @@ -363,7 +344,7 @@
recursive: true
});

// TODO should routes and APIs have chunks?

Check warning on line 347 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected 'todo' comment: 'TODO should routes and APIs have chunks?'

Check warning on line 347 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected ' TODO' comment: 'TODO should routes and APIs have chunks?'

Check warning on line 347 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected 'todo' comment: 'TODO should routes and APIs have chunks?'

Check warning on line 347 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected ' TODO' comment: 'TODO should routes and APIs have chunks?'
// https://github.com/ProjectEvergreen/greenwood/issues/1118
return [{
input,
Expand All @@ -373,9 +354,14 @@
chunkFileNames: '[name].[hash].js'
},
plugins: [
greenwoodJsonLoader(),
greenwoodResourceLoader(compilation),
nodeResolve(),
// support node export conditions for API routes
// https://github.com/ProjectEvergreen/greenwood/issues/1118
// https://github.com/rollup/plugins/issues/362#issuecomment-873448461
nodeResolve({
exportConditions: ['node'],
preferBuiltins: true
}),
commonjs(),
greenwoodImportMetaUrl(compilation)
]
Expand All @@ -385,7 +371,7 @@
const getRollupConfigForSsr = async (compilation, input) => {
const { outputDir } = compilation.context;

// TODO should routes and APIs have chunks?

Check warning on line 374 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected 'todo' comment: 'TODO should routes and APIs have chunks?'

Check warning on line 374 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected ' TODO' comment: 'TODO should routes and APIs have chunks?'

Check warning on line 374 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected 'todo' comment: 'TODO should routes and APIs have chunks?'

Check warning on line 374 in packages/cli/src/config/rollup.config.js

View workflow job for this annotation

GitHub Actions / build (18)

Unexpected ' TODO' comment: 'TODO should routes and APIs have chunks?'
// https://github.com/ProjectEvergreen/greenwood/issues/1118
return [{
input,
Expand All @@ -395,12 +381,12 @@
chunkFileNames: '[name].[hash].js'
},
plugins: [
greenwoodJsonLoader(),
greenwoodResourceLoader(compilation),
// TODO let this through for lit to enable nodeResolve({ preferBuiltins: true })
// https://github.com/lit/lit/issues/449
// support node export conditions for SSR pages
// https://github.com/ProjectEvergreen/greenwood/issues/1118
// https://github.com/rollup/plugins/issues/362#issuecomment-873448461
nodeResolve({
exportConditions: ['node'],
preferBuiltins: true
}),
commonjs(),
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/lib/resource-utils.js
Expand Up @@ -124,7 +124,8 @@ async function trackResourcesForRoute(html, compilation, route) {
const scripts = await Promise.all(root.querySelectorAll('script')
.filter(script => (
isLocalLink(script.getAttribute('src')) || script.rawText)
&& script.rawAttrs.indexOf('importmap') < 0)
&& script.rawAttrs.indexOf('importmap') < 0
&& script.getAttribute('type') !== 'application/json')
.map(async(script) => {
const src = script.getAttribute('src');
const optimizationAttr = script.getAttribute('data-gwd-opt');
Expand Down
19 changes: 1 addition & 18 deletions packages/cli/src/lib/templating-utils.js
@@ -1,7 +1,6 @@
import fs from 'fs/promises';
import htmlparser from 'node-html-parser';
import { checkResourceExists } from './resource-utils.js';
import { getPackageJson } from './node-modules-utils.js';

async function getCustomPageTemplatesFromPlugins(contextPlugins, templateName) {
const customTemplateLocations = [];
Expand Down Expand Up @@ -177,7 +176,7 @@ async function getAppTemplate(pageTemplateContents, context, customImports = [],
}

async function getUserScripts (contents, compilation) {
const { context, config } = compilation;
const { config } = compilation;

contents = contents.replace('<head>', `
<head>
Expand All @@ -186,22 +185,6 @@ async function getUserScripts (contents, compilation) {
</script>
`);

// TODO get rid of lit polyfills in core
// https://github.com/ProjectEvergreen/greenwood/issues/728
// https://lit.dev/docs/tools/requirements/#polyfills
if (process.env.__GWD_COMMAND__ === 'build') { // eslint-disable-line no-underscore-dangle
const userPackageJson = await getPackageJson(context);
const dependencies = userPackageJson?.dependencies || {};
const litPolyfill = dependencies && dependencies.lit
? '<script src="/node_modules/lit/polyfill-support.js"></script>\n'
: '';

contents = contents.replace('<head>', `
<head>
${litPolyfill}
`);
}

return contents;
}

Expand Down
23 changes: 23 additions & 0 deletions packages/cli/src/lifecycles/bundle.js
Expand Up @@ -7,6 +7,28 @@ import { checkResourceExists, mergeResponse, normalizePathnameForWindows } from
import path from 'path';
import { rollup } from 'rollup';

async function interceptPage(url, request, plugins, body) {
let response = new Response(body, {
headers: new Headers({ 'Content-Type': 'text/html' })
});

for (const plugin of plugins) {
if (plugin.shouldIntercept && await plugin.shouldIntercept(url, request, response)) {
response = await plugin.intercept(url, request, response);
}
}

return response;
}

function getPluginInstances(compilation) {
return [...compilation.config.plugins]
.filter(plugin => plugin.type === 'resource' && plugin.name !== 'plugin-node-modules:resource')
.map((plugin) => {
return plugin.provider(compilation);
});
}

async function emitResources(compilation) {
const { outputDir } = compilation.context;
const { resources, graph } = compilation;
Expand Down Expand Up @@ -202,6 +224,7 @@ async function bundleSsrPages(compilation) {
staticHtml = data.template ? data.template : await getPageTemplate(staticHtml, compilation.context, template, []);
staticHtml = await getAppTemplate(staticHtml, compilation.context, imports, [], false, title);
staticHtml = await getUserScripts(staticHtml, compilation);
staticHtml = await (await interceptPage(new URL(`http://localhost:8080${route}`), new Request(new URL(`http://localhost:8080${route}`)), getPluginInstances(compilation), staticHtml)).text();
staticHtml = await (await htmlOptimizer.optimize(new URL(`http://localhost:8080${route}`), new Response(staticHtml))).text();
staticHtml = staticHtml.replace(/[`\\$]/g, '\\$&'); // https://stackoverflow.com/a/75688937/417806

Expand Down
10 changes: 7 additions & 3 deletions packages/cli/src/lifecycles/graph.js
Expand Up @@ -51,6 +51,7 @@ const generateGraph = async (compilation) => {
let filePath;
let prerender = true;
let isolation = false;
let hydration = false;

/*
* check if additional nested directories exist to correctly determine route (minus filename)
Expand Down Expand Up @@ -132,8 +133,9 @@ const generateGraph = async (compilation) => {
const request = await requestAsObject(new Request(filenameUrl));

worker.on('message', async (result) => {
prerender = result.prerender;
prerender = result.prerender ?? false;
isolation = result.isolation ?? isolation;
hydration = result.hydration ?? hydration;

if (result.frontmatter) {
result.frontmatter.imports = result.frontmatter.imports || [];
Expand Down Expand Up @@ -203,8 +205,9 @@ const generateGraph = async (compilation) => {
* template: page template to use as a base for a generated component
* title: a default value that can be used for <title></title>
* isSSR: if this is a server side route
* prerednder: if this should be statically exported
* prerender: if this should be statically exported
* isolation: if this should be run in isolated mode
* hydration: if this page needs hydration support
*/
pages.push({
data: customData || {},
Expand All @@ -225,7 +228,8 @@ const generateGraph = async (compilation) => {
title,
isSSR: !isStatic,
prerender,
isolation
isolation,
hydration
});
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/plugins/resource/plugin-standard-html.js
Expand Up @@ -115,6 +115,7 @@ class StandardHtmlResource extends ResourceInterface {
if (result.template) {
ssrTemplate = result.template;
}

if (result.body) {
ssrBody = result.body;
}
Expand Down
Expand Up @@ -217,9 +217,8 @@ describe('Build Greenwood With: ', function() {
expect(mainScriptTags.length).to.be.equal(1);
});

// TODO clean up lit-polyfill as part of https://github.com/ProjectEvergreen/greenwood/issues/728
it('should have the total expected number of .js file in the output directory', async function() {
expect(await glob.promise(path.join(this.context.publicDir, '*.js'))).to.have.lengthOf(4);
expect(await glob.promise(path.join(this.context.publicDir, '*.js'))).to.have.lengthOf(3);
});

it('should have the expected main.js file in the output directory', async function() {
Expand All @@ -242,7 +241,7 @@ describe('Build Greenwood With: ', function() {
const inlineScriptTag = Array.from(dom.window.document.querySelectorAll('head > script:not([src])')).filter(tag => !tag.getAttribute('data-gwd'))[0];

expect(inlineScriptTag.textContent.replace(/\n/g, '')).to
.equal('import"/116321042.262925e6.js";import"/lit-html.71ac31d8.js";//# sourceMappingURL=116321042.f667a8c7.js.map');
.equal('import"/116321042.4f3171e3.js";import"/lit-html.31ea57aa.js";//# sourceMappingURL=116321042.69f46fc1.js.map');
});
});

Expand Down
Expand Up @@ -2,7 +2,7 @@
"name": "test-import-node-modules",
"type": "module",
"dependencies": {
"lit": "^2.0.0",
"lit": "^3.1.0",
"lodash-es": "^4.17.20",
"prismjs": "^1.21.0",
"pwa-helpers": "^0.9.1",
Expand Down
Expand Up @@ -235,8 +235,7 @@ describe('Build Greenwood With: ', function() {
// one for the footer.js
// one for index.js
// one for lit element bundle
// TODO clean up lit-polyfill as part of https://github.com/ProjectEvergreen/greenwood/issues/728
expect(jsFiles.length).to.be.equal(6);
expect(jsFiles.length).to.be.equal(5);
});

it('should have custom <title> tag in the <head>', function() {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/cases/build.default.spa/package.json
@@ -1,7 +1,7 @@
{
"type": "module",
"dependencies": {
"lit": "^2.0.0",
"lit": "^3.1.0",
"lit-redux-router": "~0.20.0",
"pwa-helpers": "^0.9.1",
"redux": "^4.0.5",
Expand Down
Expand Up @@ -147,11 +147,10 @@ describe('Build Greenwood With: ', function() {
expect(styles.length).to.equal(1);
});

// TODO clean up lit-polyfill as part of https://github.com/ProjectEvergreen/greenwood/issues/728
it('should have four script tags', function() {
const scripts = Array.from(dom.window.document.querySelectorAll('head > script')).filter(tag => !tag.getAttribute('data-gwd'));

expect(scripts.length).to.equal(4);
expect(scripts.length).to.equal(3);
});

it('should have expected SSR content from the non module script tag', function() {
Expand Down
@@ -1,6 +1,6 @@
{
"type": "module",
"dependencies": {
"lit": "^2.0.0"
"lit": "^3.1.0"
}
}
Expand Up @@ -101,6 +101,10 @@ describe('Develop Greenwood With: ', function() {
`${process.cwd()}/node_modules/lit/package.json`,
`${outputPath}/node_modules/lit/`
);
const litSsrPackageJson = await getDependencyFiles(
`${process.cwd()}/node_modules/@lit-labs/ssr-dom-shim/package.json`,
`${outputPath}/node_modules/@lit-labs/ssr-dom-shim/`
);
const litElement = await getDependencyFiles(
`${process.cwd()}/node_modules/lit-element/*.js`,
`${outputPath}/node_modules/lit-element/`
Expand Down Expand Up @@ -379,6 +383,7 @@ describe('Develop Greenwood With: ', function() {
...getSetupFiles(outputPath),
...lit,
...litPackageJson,
...litSsrPackageJson,
...litDirectives,
...litDecorators,
...litElementPackageJson,
Expand Down
Expand Up @@ -235,10 +235,6 @@
"lit-html/directives/unsafe-svg.js": "/node_modules/lit-html/directives/unsafe-svg.js",
"lit/directives/unsafe-svg.js": "/node_modules/lit/directives/unsafe-svg.js",
"lit-html/directives/until.js": "/node_modules/lit-html/directives/until.js",
"lit-element/experimental-hydrate-support.js": "/node_modules/lit-element/experimental-hydrate-support.js",
"lit/experimental-hydrate-support.js": "/node_modules/lit/experimental-hydrate-support.js",
"lit-html/experimental-hydrate.js": "/node_modules/lit-html/experimental-hydrate.js",
"lit/experimental-hydrate.js": "/node_modules/lit/experimental-hydrate.js",
"lit/html.js": "/node_modules/lit/html.js",
"lit/polyfill-support.js": "/node_modules/lit/polyfill-support.js",
"lit-html/static.js": "/node_modules/lit-html/static.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/cases/develop.default/package.json
Expand Up @@ -8,6 +8,6 @@
"@material/mwc-button": "^0.25.2",
"@stencil/core": "^2.12.0",
"@types/trusted-types": "^2.0.2",
"lit": "^2.0.0"
"lit": "^3.1.0"
}
}
5 changes: 5 additions & 0 deletions packages/cli/test/cases/develop.ssr/develop.ssr.spec.js
Expand Up @@ -64,6 +64,10 @@ describe('Develop Greenwood With: ', function() {
`${process.cwd()}/node_modules/lit/package.json`,
`${outputPath}/node_modules/lit/`
);
const litSsrPackageJson = await getDependencyFiles(
`${process.cwd()}/node_modules/@lit-labs/ssr-dom-shim/package.json`,
`${outputPath}/node_modules/@lit-labs/ssr-dom-shim/`
);
const litElement = await getDependencyFiles(
`${process.cwd()}/node_modules/lit-element/*.js`,
`${outputPath}/node_modules/lit-element/`
Expand Down Expand Up @@ -111,6 +115,7 @@ describe('Develop Greenwood With: ', function() {
...getSetupFiles(outputPath),
...lit,
...litPackageJson,
...litSsrPackageJson,
...litDirectives,
...litDecorators,
...litElementPackageJson,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/cases/develop.ssr/package.json
@@ -1,6 +1,6 @@
{
"type": "module",
"dependencies": {
"lit": "^2.0.0"
"lit": "^3.1.0"
}
}
@@ -1,6 +1,6 @@
{
"type": "module",
"dependencies": {
"lit": "^2.0.0"
"lit": "^3.1.0"
}
}
Expand Up @@ -175,11 +175,10 @@ describe('Serve Greenwood With: ', function() {
expect(styles.length).to.equal(1);
});

// TODO clean up lit-polyfill as part of https://github.com/ProjectEvergreen/greenwood/issues/728
it('should have four script tags', function() {
const scripts = Array.from(dom.window.document.querySelectorAll('head script')).filter(tag => !tag.getAttribute('data-gwd'));

expect(scripts.length).to.equal(4);
expect(scripts.length).to.equal(3);
});

it('should have expected SSR content from the non module script tag', function() {
Expand Down