From 953286b307171df9ec53e4f94fd297264b5e1562 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Wed, 6 Oct 2021 10:41:25 -0400 Subject: [PATCH] upgrade website to `lit@2` (#611) * init commit with website components working use lit 2 * update import node modules spec per new lit packaging * disable SPA mode spec * all specs passing with new lit * update cli devDep * everything working and rebased * restore eve components * add trusted types scaffolding for lit-html * upgrade to latest version of lit-redux-router with lit 2 compat * update specs * update develop specs for lit@2 * upgrade lit rc4 * handling puppeteer serialization issues with lit 2 and Shady DOM (#719) * WIP * revert www changes * apply lit polyfill support * conditially check for lit before using polyfill * upgrade to final stable version * rebased from master but broken??? * bump deps and set lit resolututions * ship lit polyfill for lit and plugin polyfills users --- package.json | 3 + packages/cli/package.json | 10 +- .../plugins/resource/plugin-standard-html.js | 20 +- .../build.config.mode-spa.spec.js | 63 +++- .../cases/build.config.mode-spa/package.json | 4 +- .../src/components/footer.js | 2 +- .../cases/build.config.mode-spa/src/index.js | 6 +- .../build.config.mode-spa/src/routes/about.js | 4 +- .../build.config.mode-spa/src/routes/home.js | 4 +- .../build.default.import-node-modules.spec.js | 91 ++++-- .../package.json | 2 +- .../src/pages/index.html | 5 +- .../src/scripts/main.js | 14 +- .../develop.default/develop.default.spec.js | 79 +++-- .../develop.default/import-map.snapshot.json | 274 ++++++++++++++++++ .../test/cases/develop.default/package.json | 6 +- .../test/cases/query-children/package.json | 2 +- .../query-children/query-children.spec.js | 66 ++++- .../src/components/posts-list.js | 2 +- .../query-custom-frontmatter/package.json | 2 +- .../query-custom-frontmatter.spec.js | 75 ++++- .../src/components/posts-list.js | 2 +- .../test/cases/query-graph/package.json | 2 +- .../cases/query-graph/query-graph.spec.js | 66 ++++- .../src/components/debug-output.js | 2 +- .../test/cases/query-menu/package.json | 2 +- .../test/cases/query-menu/query-menu.spec.js | 66 ++++- .../cases/query-menu/src/components/header.js | 2 +- packages/plugin-polyfills/README.md | 2 +- packages/plugin-polyfills/package.json | 2 +- packages/plugin-polyfills/src/index.js | 19 +- .../test/cases/lit/greenwood.config.js | 7 + .../test/cases/lit/lit.spec.js | 182 ++++++++++++ .../test/cases/lit/package.json | 6 + www/components/banner/banner.js | 2 +- www/components/card/card.js | 2 +- www/components/footer/footer.js | 2 +- www/components/header/header.js | 2 +- www/components/icons/chevron-down.js | 2 +- www/components/icons/chevron-right.js | 2 +- www/components/icons/github-icon.js | 2 +- www/components/icons/slack-icon.js | 2 +- www/components/icons/twitter-icon.js | 2 +- www/components/scroll/scroll.js | 2 +- www/components/shelf/shelf.js | 2 +- www/components/social-icons/social-icons.js | 2 +- www/package.json | 6 +- www/pages/docs/component-model.md | 4 +- www/pages/docs/css-and-images.md | 2 +- www/pages/docs/data.md | 2 +- www/pages/docs/menus.md | 2 +- yarn.lock | 142 +++++---- 52 files changed, 1051 insertions(+), 225 deletions(-) create mode 100644 packages/cli/test/cases/develop.default/import-map.snapshot.json create mode 100644 packages/plugin-polyfills/test/cases/lit/greenwood.config.js create mode 100644 packages/plugin-polyfills/test/cases/lit/lit.spec.js create mode 100644 packages/plugin-polyfills/test/cases/lit/package.json diff --git a/package.json b/package.json index 11293720e..b093718bb 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,9 @@ "lint:css": "stylelint \"./www/**/*.js\", \"./www/**/*.css\"", "lint": "ls-lint && yarn lint:js && yarn lint:ts && yarn lint:css" }, + "resolutions": { + "lit": "^2.0.0" + }, "devDependencies": { "@ls-lint/ls-lint": "^1.9.2", "@typescript-eslint/eslint-plugin": "^4.28.2", diff --git a/packages/cli/package.json b/packages/cli/package.json index 5f1a18f16..243208442 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -25,7 +25,7 @@ "dependencies": { "@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-replace": "^2.3.4", - "@webcomponents/webcomponentsjs": "^2.4.4", + "@webcomponents/webcomponentsjs": "^2.6.0", "acorn": "^8.0.1", "acorn-walk": "^8.0.0", "commander": "^2.20.0", @@ -50,12 +50,12 @@ }, "devDependencies": { "@babel/runtime": "^7.10.4", - "@lion/button": "~0.12.0", - "@lion/calendar": "~0.15.0", + "@lion/button": "^0.14.3", + "@lion/calendar": "^0.16.5", "@mapbox/rehype-prism": "^0.5.0", "@types/trusted-types": "^2.0.2", - "lit-element": "^2.4.0", - "lit-redux-router": "^0.17.1", + "lit": "^2.0.0", + "lit-redux-router": "~0.20.0", "lodash-es": "^4.17.20", "postcss-nested": "^4.1.2", "pwa-helpers": "^0.9.1", diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js index 234c8ea25..cc0734219 100644 --- a/packages/cli/src/plugins/resource/plugin-standard-html.js +++ b/packages/cli/src/plugins/resource/plugin-standard-html.js @@ -197,11 +197,24 @@ const getAppTemplate = (contents, templatesDir, customImports = [], contextPlugi return appTemplateContents; }; -const getUserScripts = (contents) => { - // polyfill chromium for WCs support +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; + const dependencies = fs.existsSync(path.join(userWorkspace, 'package.json')) // handle monorepos first + ? JSON.parse(fs.readFileSync(path.join(userWorkspace, 'package.json'), 'utf-8')).dependencies + : fs.existsSync(path.join(projectDirectory, 'package.json')) + ? JSON.parse(fs.readFileSync(path.join(projectDirectory, 'package.json'), 'utf-8')).dependencies + : {}; + + const litPolyfill = dependencies && dependencies.lit + ? '\n' + : ''; + contents = contents.replace('', ` + ${litPolyfill} `); } @@ -353,7 +366,7 @@ class StandardHtmlResource extends ResourceInterface { } body = getAppTemplate(body, userTemplatesDir, customImports, contextPlugins); - body = getUserScripts(body, customImports); + body = getUserScripts(body, this.compilation.context); body = getMetaContent(normalizedUrl.replace(/\\/g, '/'), config, body); if (processedMarkdown) { @@ -404,6 +417,7 @@ class StandardHtmlResource extends ResourceInterface { if (hasHead && hasHead.length > 0) { let contents = hasHead[0]; + contents = contents.replace(/ @@ -21,6 +21,7 @@ + \ No newline at end of file diff --git a/packages/cli/test/cases/build.default.import-node-modules/src/scripts/main.js b/packages/cli/test/cases/build.default.import-node-modules/src/scripts/main.js index dd58e67f4..8276a7960 100644 --- a/packages/cli/test/cases/build.default.import-node-modules/src/scripts/main.js +++ b/packages/cli/test/cases/build.default.import-node-modules/src/scripts/main.js @@ -1,9 +1,13 @@ -import { LitElement } from 'lit-element'; +import { LitElement } from 'lit'; import { defaults } from 'lodash-es'; import { lazyReducerEnhancer } from 'pwa-helpers'; import { createStore } from 'redux'; -document.getElementsByClassName('output-lit')[0].innerHTML = `import from lit-element ${btoa(LitElement).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)}`; \ No newline at end of file +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/cli/test/cases/develop.default/develop.default.spec.js b/packages/cli/test/cases/develop.default/develop.default.spec.js index f7f6642b3..6cbf6b790 100644 --- a/packages/cli/test/cases/develop.default/develop.default.spec.js +++ b/packages/cli/test/cases/develop.default/develop.default.spec.js @@ -86,29 +86,57 @@ describe('Develop Greenwood With: ', function() { describe(LABEL, function() { before(async function() { - const litElementLibs = await getDependencyFiles( - `${process.cwd()}/node_modules/lit-element/lib/*.js`, - `${outputPath}/node_modules/lit-element/lib/` + const lit = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/*.js`, + `${outputPath}/node_modules/lit/` + ); + const litDecorators = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/decorators/*.js`, + `${outputPath}/node_modules/lit/decorators/` + ); + const litDirectives = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/directives/*.js`, + `${outputPath}/node_modules/lit/directives/` + ); + const litPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/package.json`, + `${outputPath}/node_modules/lit/` ); const litElement = await getDependencyFiles( - `${process.cwd()}/node_modules/lit-element/lit-element.js`, + `${process.cwd()}/node_modules/lit-element/*.js`, `${outputPath}/node_modules/lit-element/` ); const litElementPackageJson = await getDependencyFiles( `${process.cwd()}/node_modules/lit-element/package.json`, `${outputPath}/node_modules/lit-element/` ); + const litElementDecorators = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-element/decorators/*.js`, + `${outputPath}/node_modules/lit-element/decorators/` + ); const litHtml = await getDependencyFiles( - `${process.cwd()}/node_modules/lit-html/lit-html.js`, + `${process.cwd()}/node_modules/lit-html/*.js`, `${outputPath}/node_modules/lit-html/` ); const litHtmlPackageJson = await getDependencyFiles( `${process.cwd()}/node_modules/lit-html/package.json`, `${outputPath}/node_modules/lit-html/` ); - const litHtmlLibs = await getDependencyFiles( - `${process.cwd()}/node_modules/lit-html/lib/*.js`, - `${outputPath}/node_modules/lit-html/lib/` + const litHtmlDirectives = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-html/directives/*.js`, + `${outputPath}/node_modules/lit-html/directives/` + ); + const litReactiveElement = await getDependencyFiles( + `${process.cwd()}/node_modules/@lit/reactive-element/*.js`, + `${outputPath}/node_modules/@lit/reactive-element/` + ); + const litReactiveElementDecorators = await getDependencyFiles( + `${process.cwd()}/node_modules/@lit/reactive-element/decorators/*.js`, + `${outputPath}/node_modules/@lit/reactive-element/decorators/` + ); + const litReactiveElementPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/@lit/reactive-element/package.json`, + `${outputPath}/node_modules/@lit/reactive-element/` ); const litHtmlSourceMap = await getDependencyFiles( `${process.cwd()}/node_modules/lit-html/lit-html.js.map`, @@ -202,6 +230,14 @@ describe('Develop Greenwood With: ', function() { `${process.cwd()}/node_modules/@types/trusted-types/package.json`, `${outputPath}/node_modules/@types/trusted-types/` ); + const scopedCustomElementRegistryPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/@webcomponents/scoped-custom-element-registry/package.json`, + `${outputPath}/node_modules/@webcomponents/scoped-custom-element-registry/` + ); + const scopedCustomElementRegistryLibs = await getDependencyFiles( + `${process.cwd()}/node_modules/@webcomponents/scoped-custom-element-registry/*.js`, + `${outputPath}/node_modules/@webcomponents/scoped-custom-element-registry/` + ); // manually copy all these @babel/runtime files recursively since there are too many of them to do it individually const babelRuntimeLibs = await rreaddir(`${process.cwd()}/node_modules/@babel/runtime`); @@ -234,12 +270,19 @@ describe('Develop Greenwood With: ', function() { await runner.setup(outputPath, [ ...getSetupFiles(outputPath), + ...lit, + ...litPackageJson, + ...litDirectives, + ...litDecorators, ...litElementPackageJson, ...litElement, - ...litElementLibs, + ...litElementDecorators, ...litHtmlPackageJson, ...litHtml, - ...litHtmlLibs, + ...litHtmlDirectives, + ...litReactiveElement, + ...litReactiveElementDecorators, + ...litReactiveElementPackageJson, ...litHtmlSourceMap, ...simpleCss, ...simpleCssPackageJson, @@ -264,7 +307,9 @@ describe('Develop Greenwood With: ', function() { ...singletonManagerLibs, ...trustedTypesPackageJson, ...regeneratorRuntimeLibs, - ...regeneratorRuntimeLibsPackageJson + ...regeneratorRuntimeLibsPackageJson, + ...scopedCustomElementRegistryPackageJson, + ...scopedCustomElementRegistryLibs ]); return new Promise(async (resolve) => { @@ -316,16 +361,16 @@ describe('Develop Greenwood With: ', function() { it('should return an import map shim \n' + : ''; const polyfillFiles = [ 'webcomponents-loader.js', ...fs.readdirSync(path.join(polyfillNodeModulesLocation, 'bundles')).map(file => { @@ -40,8 +49,16 @@ class PolyfillsResource extends ResourceInterface { : Promise.resolve(); })); + if (litPolyfill !== '' && !fs.existsSync(path.join(outputDir, 'polyfill-support.js'))) { + await fs.promises.copyFile( + path.join(litNodeModulesLocation, 'polyfill-support.js'), + path.join(outputDir, 'polyfill-support.js') + ); + } + const newHtml = body.replace('', ` + ${litPolyfill} `); diff --git a/packages/plugin-polyfills/test/cases/lit/greenwood.config.js b/packages/plugin-polyfills/test/cases/lit/greenwood.config.js new file mode 100644 index 000000000..df0a2b27d --- /dev/null +++ b/packages/plugin-polyfills/test/cases/lit/greenwood.config.js @@ -0,0 +1,7 @@ +const polyfillsPlugin = require('../../../src/index'); + +module.exports = { + plugins: [ + polyfillsPlugin() + ] +}; \ No newline at end of file diff --git a/packages/plugin-polyfills/test/cases/lit/lit.spec.js b/packages/plugin-polyfills/test/cases/lit/lit.spec.js new file mode 100644 index 000000000..2aff11034 --- /dev/null +++ b/packages/plugin-polyfills/test/cases/lit/lit.spec.js @@ -0,0 +1,182 @@ +/* + * Use Case + * Run Greenwood with Polyfills composite plugin with default options and using Lit. + * + * Uaer Result + * Should generate a bare bones Greenwood build with polyfills injected into index.html. + * + * User Command + * greenwood build + * + * User Config + * const polyfillsPlugin = require('@greenwod/plugin-polyfills'); + * + * { + * plugins: [ + * polyfillsPlugin() + * ] + * + * } + * + * User Workspace + * Greenwood default + */ +const expect = require('chai').expect; +const fs = require('fs'); +const { JSDOM } = require('jsdom'); +const path = require('path'); +const runSmokeTest = require('../../../../../test/smoke-test'); +const { getSetupFiles, getDependencyFiles, getOutputTeardownFiles } = require('../../../../../test/utils'); +const Runner = require('gallinago').Runner; + +const expectedLitPolyfillFiles = [ + 'polyfill-support.js' +]; + +const expectedPolyfillFiles = [ + 'webcomponents-loader.js', + 'webcomponents-ce.js', + 'webcomponents-ce.js.map', + 'webcomponents-sd-ce-pf.js', + 'webcomponents-sd-ce-pf.js.map', + 'webcomponents-sd-ce.js', + 'webcomponents-sd-ce.js.map', + 'webcomponents-sd.js', + 'webcomponents-sd.js.map' +]; + +describe('Build Greenwood With: ', function() { + const LABEL = 'Lit Polyfill Plugin with default options and Default Workspace'; + const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); + const outputPath = __dirname; + let runner; + + before(async function() { + this.context = { + publicDir: path.join(outputPath, 'public') + }; + runner = new Runner(); + }); + + describe(LABEL, function() { + before(async function() { + const lit = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/*.js`, + `${outputPath}/node_modules/lit/` + ); + const litDecorators = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/decorators/*.js`, + `${outputPath}/node_modules/lit/decorators/` + ); + const litDirectives = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/directives/*.js`, + `${outputPath}/node_modules/lit/directives/` + ); + const litPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/lit/package.json`, + `${outputPath}/node_modules/lit/` + ); + const litElement = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-element/*.js`, + `${outputPath}/node_modules/lit-element/` + ); + const litElementPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-element/package.json`, + `${outputPath}/node_modules/lit-element/` + ); + const litElementDecorators = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-element/decorators/*.js`, + `${outputPath}/node_modules/lit-element/decorators/` + ); + const litHtml = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-html/*.js`, + `${outputPath}/node_modules/lit-html/` + ); + const litHtmlPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-html/package.json`, + `${outputPath}/node_modules/lit-html/` + ); + const litHtmlDirectives = await getDependencyFiles( + `${process.cwd()}/node_modules/lit-html/directives/*.js`, + `${outputPath}/node_modules/lit-html/directives/` + ); + // lit-html has a dependency on this + // https://github.com/lit/lit/blob/main/packages/lit-html/package.json#L82 + const trustedTypes = await getDependencyFiles( + `${process.cwd()}/node_modules/@types/trusted-types/package.json`, + `${outputPath}/node_modules/@types/trusted-types/` + ); + const litReactiveElement = await getDependencyFiles( + `${process.cwd()}/node_modules/@lit/reactive-element/*.js`, + `${outputPath}/node_modules/@lit/reactive-element/` + ); + const litReactiveElementDecorators = await getDependencyFiles( + `${process.cwd()}/node_modules/@lit/reactive-element/decorators/*.js`, + `${outputPath}/node_modules/@lit/reactive-element/decorators/` + ); + const litReactiveElementPackageJson = await getDependencyFiles( + `${process.cwd()}/node_modules/@lit/reactive-element/package.json`, + `${outputPath}/node_modules/@lit/reactive-element/` + ); + + await runner.setup(outputPath, [ + ...getSetupFiles(outputPath), + ...expectedPolyfillFiles.map((file) => { + const dir = file === 'webcomponents-loader.js' + ? 'node_modules/@webcomponents/webcomponentsjs' + : 'node_modules/@webcomponents/webcomponentsjs/bundles'; + + return { + source: `${process.cwd()}/${dir}/${file}`, + destination: `${outputPath}/${dir}/${file}` + }; + }), + ...lit, // includes polyfill-support.js + ...litPackageJson, + ...litDirectives, + ...litDecorators, + ...litElementPackageJson, + ...litElement, + ...litElementDecorators, + ...litHtmlPackageJson, + ...litHtml, + ...litHtmlDirectives, + ...trustedTypes, + ...litReactiveElement, + ...litReactiveElementDecorators, + ...litReactiveElementPackageJson + ]); + await runner.runCommand(cliPath, 'build'); + }); + + runSmokeTest(['public', 'index'], LABEL); + + describe('Script tag in the tag', function() { + let dom; + + before(async function() { + dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, 'index.html')); + }); + + it('should have one