diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000000..88ed1c7316 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,21 @@ +[modern] +# Support for dynamic import is the main litmus test for serving modern builds. +# Although officially a ES2020 feature, browsers implemented it early, so this +# enables all of ES2017 and some features in ES2018. +supports es6-module-dynamic-import + +# Exclude Safari 11-12 because of a bug in tagged template literals +# https://bugs.webkit.org/show_bug.cgi?id=190756 +# Note: Dropping version 11 also enables several more ES2018 features +not Safari < 13 +not iOS < 13 + +# Exclude unsupported browsers +not dead + +[legacy] +# Legacy builds are transpiled to ES5 (strict mode) but also must support some features that cannot be polyfilled: +# - web sockets to communicate with backend +# - inline SVG used widely in buttons, widgets, etc. +# - custom events used for most user interactions +supports use-strict and supports websockets and supports svg-html5 and supports customevent diff --git a/.github/workflows/cast_deployment.yaml b/.github/workflows/cast_deployment.yaml index e9e41b8b4d..1d46f47cb9 100644 --- a/.github/workflows/cast_deployment.yaml +++ b/.github/workflows/cast_deployment.yaml @@ -22,7 +22,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 with: ref: dev @@ -58,7 +58,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 with: ref: master diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c2b8d29baa..fe16a8df7d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3.6.0 with: @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3.6.0 with: @@ -66,7 +66,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3.6.0 with: @@ -84,7 +84,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3.6.0 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 94b8ee085d..b71752c7f7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/demo_deployment.yaml b/.github/workflows/demo_deployment.yaml index 025c461ab2..07eb7c900d 100644 --- a/.github/workflows/demo_deployment.yaml +++ b/.github/workflows/demo_deployment.yaml @@ -23,7 +23,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 with: ref: dev @@ -59,7 +59,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 with: ref: master diff --git a/.github/workflows/design_deployment.yaml b/.github/workflows/design_deployment.yaml index 22f8673603..821b6ae9a7 100644 --- a/.github/workflows/design_deployment.yaml +++ b/.github/workflows/design_deployment.yaml @@ -17,7 +17,7 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3.6.0 diff --git a/.github/workflows/design_preview.yaml b/.github/workflows/design_preview.yaml index 335cbca79b..e8d39053c1 100644 --- a/.github/workflows/design_preview.yaml +++ b/.github/workflows/design_preview.yaml @@ -22,7 +22,7 @@ jobs: if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview') steps: - name: Check out files from GitHub - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3.6.0 diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 224b1b49e0..455d418443 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -21,7 +21,7 @@ jobs: contents: write steps: - name: Checkout the repository - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v4 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 686ca2ab02..4675b383ff 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -24,7 +24,7 @@ jobs: contents: write # Required to upload release assets steps: - name: Checkout the repository - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Verify version uses: home-assistant/actions/helpers/verify-version@master @@ -75,7 +75,7 @@ jobs: echo "home-assistant-frontend==$version" > ./requirements.txt - name: Build wheels - uses: home-assistant/wheels@2022.10.1 + uses: home-assistant/wheels@2023.04.0 with: abi: cp310 tag: musllinux_1_2 diff --git a/.github/workflows/translations.yaml b/.github/workflows/translations.yaml index a0eee41524..e7e7f789ca 100644 --- a/.github/workflows/translations.yaml +++ b/.github/workflows/translations.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v3.5.0 + uses: actions/checkout@v3.5.2 - name: Upload Translations run: | diff --git a/build-scripts/bundle.cjs b/build-scripts/bundle.cjs index a5077badad..1a8735eb03 100644 --- a/build-scripts/bundle.cjs +++ b/build-scripts/bundle.cjs @@ -84,17 +84,23 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({ babelrc: false, compact: false, + assumptions: { + privateFieldsAsProperties: true, + setPublicClassFields: true, + setSpreadProperties: true, + }, + browserslistEnv: latestBuild ? "modern" : "legacy", presets: [ - !latestBuild && [ + [ "@babel/preset-env", { - useBuiltIns: "entry", - corejs: { version: "3.30", proposals: true }, + useBuiltIns: latestBuild ? false : "entry", + corejs: latestBuild ? false : { version: "3.30", proposals: true }, bugfixes: true, }, ], "@babel/preset-typescript", - ].filter(Boolean), + ], plugins: [ [ path.resolve( @@ -106,22 +112,8 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({ ignoreModuleNotFound: true, }, ], - // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) - !latestBuild && [ - "@babel/plugin-proposal-object-rest-spread", - { loose: true, useBuiltIns: true }, - ], - // Only support the syntax, Webpack will handle it. - "@babel/plugin-syntax-import-meta", - "@babel/plugin-syntax-dynamic-import", - "@babel/plugin-syntax-top-level-await", - // Support various proposals - "@babel/plugin-proposal-optional-chaining", - "@babel/plugin-proposal-nullish-coalescing-operator", + // Support some proposals still in TC39 process ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }], - ["@babel/plugin-proposal-private-methods", { loose: true }], - ["@babel/plugin-proposal-private-property-in-object", { loose: true }], - ["@babel/plugin-proposal-class-properties", { loose: true }], // Minify template literals for production isProdBuild && [ "template-html-minifier", diff --git a/build-scripts/gulp/app.cjs b/build-scripts/gulp/app.cjs index 0b6c49354c..4e77e26326 100644 --- a/build-scripts/gulp/app.cjs +++ b/build-scripts/gulp/app.cjs @@ -24,8 +24,7 @@ gulp.task( gulp.parallel( "gen-service-worker-app-dev", "gen-icons-json", - "gen-pages-dev", - "gen-index-app-dev", + "gen-pages-app-dev", "build-translations", "build-locale-data" ), @@ -50,10 +49,6 @@ gulp.task( env.useRollup() ? "rollup-prod-app" : "webpack-prod-app", // Don't compress running tests ...(env.isTestBuild() ? [] : ["compress-app"]), - gulp.parallel( - "gen-pages-prod", - "gen-index-app-prod", - "gen-service-worker-app-prod" - ) + gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod") ) ); diff --git a/build-scripts/gulp/cast.cjs b/build-scripts/gulp/cast.cjs index 4dbc9b1eff..ae1a961139 100644 --- a/build-scripts/gulp/cast.cjs +++ b/build-scripts/gulp/cast.cjs @@ -19,7 +19,7 @@ gulp.task( "translations-enable-merge-backend", gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), "copy-static-cast", - "gen-index-cast-dev", + "gen-pages-cast-dev", env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast" ) ); @@ -35,6 +35,6 @@ gulp.task( gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), "copy-static-cast", env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast", - "gen-index-cast-prod" + "gen-pages-cast-prod" ) ); diff --git a/build-scripts/gulp/demo.cjs b/build-scripts/gulp/demo.cjs index 65d8035c66..657dd35634 100644 --- a/build-scripts/gulp/demo.cjs +++ b/build-scripts/gulp/demo.cjs @@ -21,7 +21,7 @@ gulp.task( "translations-enable-merge-backend", gulp.parallel( "gen-icons-json", - "gen-index-demo-dev", + "gen-pages-demo-dev", "build-translations", "build-locale-data" ), @@ -42,6 +42,6 @@ gulp.task( gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), "copy-static-demo", env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo", - "gen-index-demo-prod" + "gen-pages-demo-prod" ) ); diff --git a/build-scripts/gulp/entry-html.cjs b/build-scripts/gulp/entry-html.cjs index ffb569f7ed..53d7b24f1f 100644 --- a/build-scripts/gulp/entry-html.cjs +++ b/build-scripts/gulp/entry-html.cjs @@ -8,344 +8,223 @@ const paths = require("../paths.cjs"); const env = require("../env.cjs"); const { htmlMinifierOptions, terserOptions } = require("../bundle.cjs"); -const templatePath = (tpl) => - path.resolve(paths.polymer_dir, "src/html/", `${tpl}.html.template`); - -const readFile = (pth) => fs.readFileSync(pth).toString(); - -const renderTemplate = (pth, data = {}, pathFunc = templatePath) => { - const compiled = template(readFile(pathFunc(pth))); +const renderTemplate = (templateFile, data = {}) => { + const compiled = template( + fs.readFileSync(templateFile, { encoding: "utf-8" }) + ); return compiled({ ...data, useRollup: env.useRollup(), useWDS: env.useWDS(), - renderTemplate, + // Resolve any child/nested templates relative to the parent and pass the same data + renderTemplate: (childTemplate) => + renderTemplate( + path.resolve(path.dirname(templateFile), childTemplate), + data + ), }); }; -const renderDemoTemplate = (pth, data = {}) => - renderTemplate(pth, data, (tpl) => - path.resolve(paths.demo_dir, "src/html/", `${tpl}.html.template`) - ); +const WRAP_TAGS = { ".js": "script", ".css": "style" }; -const renderCastTemplate = (pth, data = {}) => - renderTemplate(pth, data, (tpl) => - path.resolve(paths.cast_dir, "src/html/", `${tpl}.html.template`) - ); - -const renderGalleryTemplate = (pth, data = {}) => - renderTemplate(pth, data, (tpl) => - path.resolve(paths.gallery_dir, "src/html/", `${tpl}.html.template`) - ); - -const minifyHtml = (content) => - minify(content, { +const minifyHtml = (content, ext) => { + const wrapTag = WRAP_TAGS[ext] || ""; + const begTag = wrapTag && `<${wrapTag}>`; + const endTag = wrapTag && ``; + return minify(begTag + content + endTag, { ...htmlMinifierOptions, conservativeCollapse: false, minifyJS: terserOptions({ latestBuild: false, // Shared scripts should be ES5 isTestBuild: true, // Don't need source maps }), - }); + }).then((wrapped) => + wrapTag ? wrapped.slice(begTag.length, -endTag.length) : wrapped + ); +}; -const PAGES = ["onboarding", "authorize"]; +// Function to generate a dev task for each project's configuration +// Note Currently WDS paths are hard-coded to only work for app +const genPagesDevTask = + ( + pageEntries, + inputRoot, + outputRoot, + useWDS = false, + inputSub = "src/html", + publicRoot = "" + ) => + async () => { + for (const [page, entries] of Object.entries(pageEntries)) { + const content = renderTemplate( + path.resolve(inputRoot, inputSub, `${page}.template`), + { + latestEntryJS: entries.map((entry) => + useWDS + ? `http://localhost:8000/src/entrypoints/${entry}.ts` + : `${publicRoot}/frontend_latest/${entry}.js` + ), + es5EntryJS: entries.map( + (entry) => `${publicRoot}/frontend_es5/${entry}.js` + ), + latestCustomPanelJS: useWDS + ? "http://localhost:8000/src/entrypoints/custom-panel.ts" + : `${publicRoot}/frontend_latest/custom-panel.js`, + es5CustomPanelJS: `${publicRoot}/frontend_es5/custom-panel.js`, + } + ); + fs.outputFileSync(path.resolve(outputRoot, page), content); + } + }; -gulp.task("gen-pages-dev", (done) => { - for (const page of PAGES) { - const content = renderTemplate(page, { - latestPageJS: `/frontend_latest/${page}.js`, - - es5PageJS: `/frontend_es5/${page}.js`, - }); - - fs.outputFileSync( - path.resolve(paths.app_output_root, `${page}.html`), - content - ); - } - done(); -}); - -gulp.task("gen-pages-prod", async () => { - const latestManifest = require(path.resolve( - paths.app_output_latest, - "manifest.json" - )); - const es5Manifest = require(path.resolve( - paths.app_output_es5, - "manifest.json" - )); - - const minifiedHTML = []; - for (const page of PAGES) { - const content = renderTemplate(page, { - latestPageJS: latestManifest[`${page}.js`], - es5PageJS: es5Manifest[`${page}.js`], - }); - - minifiedHTML.push( - minifyHtml(content).then((minified) => - fs.outputFileSync( - path.resolve(paths.app_output_root, `${page}.html`), - minified +// Same as previous but for production builds +// (includes minification and hashed file names from manifest) +const genPagesProdTask = + ( + pageEntries, + inputRoot, + outputRoot, + outputLatest, + outputES5, + inputSub = "src/html" + ) => + async () => { + const latestManifest = require(path.resolve(outputLatest, "manifest.json")); + const es5Manifest = outputES5 + ? require(path.resolve(outputES5, "manifest.json")) + : {}; + const minifiedHTML = []; + for (const [page, entries] of Object.entries(pageEntries)) { + const content = renderTemplate( + path.resolve(inputRoot, inputSub, `${page}.template`), + { + latestEntryJS: entries.map((entry) => latestManifest[`${entry}.js`]), + es5EntryJS: entries.map((entry) => es5Manifest[`${entry}.js`]), + latestCustomPanelJS: latestManifest["custom-panel.js"], + es5CustomPanelJS: es5Manifest["custom-panel.js"], + } + ); + minifiedHTML.push( + minifyHtml(content, path.extname(page)).then((minified) => + fs.outputFileSync(path.resolve(outputRoot, page), minified) ) - ) - ); - } - await Promise.all(minifiedHTML); -}); + ); + } + await Promise.all(minifiedHTML); + }; -gulp.task("gen-index-app-dev", (done) => { - let latestAppJS; - let latestCoreJS; - let latestCustomPanelJS; +// Map HTML pages to their required entrypoints +const APP_PAGE_ENTRIES = { + "authorize.html": ["authorize"], + "onboarding.html": ["onboarding"], + "index.html": ["core", "app"], +}; - if (env.useWDS()) { - latestAppJS = "http://localhost:8000/src/entrypoints/app.ts"; - latestCoreJS = "http://localhost:8000/src/entrypoints/core.ts"; - latestCustomPanelJS = - "http://localhost:8000/src/entrypoints/custom-panel.ts"; - } else { - latestAppJS = "/frontend_latest/app.js"; - latestCoreJS = "/frontend_latest/core.js"; - latestCustomPanelJS = "/frontend_latest/custom-panel.js"; - } +gulp.task( + "gen-pages-app-dev", + genPagesDevTask( + APP_PAGE_ENTRIES, + paths.polymer_dir, + paths.app_output_root, + env.useWDS() + ) +); - const content = renderTemplate("index", { - latestAppJS, - latestCoreJS, - latestCustomPanelJS, - - es5AppJS: "/frontend_es5/app.js", - es5CoreJS: "/frontend_es5/core.js", - es5CustomPanelJS: "/frontend_es5/custom-panel.js", - }).replace(/#THEMEC/g, "{{ theme_color }}"); - - fs.outputFileSync(path.resolve(paths.app_output_root, "index.html"), content); - done(); -}); - -gulp.task("gen-index-app-prod", async () => { - const latestManifest = require(path.resolve( +gulp.task( + "gen-pages-app-prod", + genPagesProdTask( + APP_PAGE_ENTRIES, + paths.polymer_dir, + paths.app_output_root, paths.app_output_latest, - "manifest.json" - )); - const es5Manifest = require(path.resolve( - paths.app_output_es5, - "manifest.json" - )); - const content = renderTemplate("index", { - latestAppJS: latestManifest["app.js"], - latestCoreJS: latestManifest["core.js"], - latestCustomPanelJS: latestManifest["custom-panel.js"], + paths.app_output_es5 + ) +); - es5AppJS: es5Manifest["app.js"], - es5CoreJS: es5Manifest["core.js"], - es5CustomPanelJS: es5Manifest["custom-panel.js"], - }); - const minified = (await minifyHtml(content)).replace( - /#THEMEC/g, - "{{ theme_color }}" - ); +const CAST_PAGE_ENTRIES = { + "faq.html": ["launcher"], + "index.html": ["launcher"], + "media.html": ["media"], + "receiver.html": ["receiver"], +}; - fs.outputFileSync( - path.resolve(paths.app_output_root, "index.html"), - minified - ); -}); +gulp.task( + "gen-pages-cast-dev", + genPagesDevTask(CAST_PAGE_ENTRIES, paths.cast_dir, paths.cast_output_root) +); -gulp.task("gen-index-cast-dev", (done) => { - const contentReceiver = renderCastTemplate("receiver", { - latestReceiverJS: "/frontend_latest/receiver.js", - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "receiver.html"), - contentReceiver - ); - - const contentMedia = renderCastTemplate("media", { - latestMediaJS: "/frontend_latest/media.js", - es5MediaJS: "/frontend_es5/media.js", - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "media.html"), - contentMedia - ); - - const contentFAQ = renderCastTemplate("launcher-faq", { - latestLauncherJS: "/frontend_latest/launcher.js", - es5LauncherJS: "/frontend_es5/launcher.js", - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "faq.html"), - contentFAQ - ); - - const contentLauncher = renderCastTemplate("launcher", { - latestLauncherJS: "/frontend_latest/launcher.js", - es5LauncherJS: "/frontend_es5/launcher.js", - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "index.html"), - contentLauncher - ); - done(); -}); - -gulp.task("gen-index-cast-prod", (done) => { - const latestManifest = require(path.resolve( +gulp.task( + "gen-pages-cast-prod", + genPagesProdTask( + CAST_PAGE_ENTRIES, + paths.cast_dir, + paths.cast_output_root, paths.cast_output_latest, - "manifest.json" - )); - const es5Manifest = require(path.resolve( - paths.cast_output_es5, - "manifest.json" - )); + paths.cast_output_es5 + ) +); - const contentReceiver = renderCastTemplate("receiver", { - latestReceiverJS: latestManifest["receiver.js"], - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "receiver.html"), - contentReceiver - ); +const DEMO_PAGE_ENTRIES = { "index.html": ["main"] }; - const contentMedia = renderCastTemplate("media", { - latestMediaJS: latestManifest["media.js"], - es5MediaJS: es5Manifest["media.js"], - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "media.html"), - contentMedia - ); +gulp.task( + "gen-pages-demo-dev", + genPagesDevTask(DEMO_PAGE_ENTRIES, paths.demo_dir, paths.demo_output_root) +); - const contentFAQ = renderCastTemplate("launcher-faq", { - latestLauncherJS: latestManifest["launcher.js"], - es5LauncherJS: es5Manifest["launcher.js"], - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "faq.html"), - contentFAQ - ); - - const contentLauncher = renderCastTemplate("launcher", { - latestLauncherJS: latestManifest["launcher.js"], - es5LauncherJS: es5Manifest["launcher.js"], - }); - fs.outputFileSync( - path.resolve(paths.cast_output_root, "index.html"), - contentLauncher - ); - done(); -}); - -gulp.task("gen-index-demo-dev", (done) => { - const content = renderDemoTemplate("index", { - latestDemoJS: "/frontend_latest/main.js", - - es5DemoJS: "/frontend_es5/main.js", - }); - - fs.outputFileSync( - path.resolve(paths.demo_output_root, "index.html"), - content - ); - done(); -}); - -gulp.task("gen-index-demo-prod", async () => { - const latestManifest = require(path.resolve( +gulp.task( + "gen-pages-demo-prod", + genPagesProdTask( + DEMO_PAGE_ENTRIES, + paths.demo_dir, + paths.demo_output_root, paths.demo_output_latest, - "manifest.json" - )); - const es5Manifest = require(path.resolve( - paths.demo_output_es5, - "manifest.json" - )); - const content = renderDemoTemplate("index", { - latestDemoJS: latestManifest["main.js"], + paths.demo_output_es5 + ) +); - es5DemoJS: es5Manifest["main.js"], - }); - const minified = await minifyHtml(content); +const GALLERY_PAGE_ENTRIES = { "index.html": ["entrypoint"] }; - fs.outputFileSync( - path.resolve(paths.demo_output_root, "index.html"), - minified - ); -}); +gulp.task( + "gen-pages-gallery-dev", + genPagesDevTask( + GALLERY_PAGE_ENTRIES, + paths.gallery_dir, + paths.gallery_output_root + ) +); -gulp.task("gen-index-gallery-dev", (done) => { - const content = renderGalleryTemplate("index", { - latestGalleryJS: "./frontend_latest/entrypoint.js", - }); +gulp.task( + "gen-pages-gallery-prod", + genPagesProdTask( + GALLERY_PAGE_ENTRIES, + paths.gallery_dir, + paths.gallery_output_root, + paths.gallery_output_latest + ) +); - fs.outputFileSync( - path.resolve(paths.gallery_output_root, "index.html"), - content - ); - done(); -}); +const HASSIO_PAGE_ENTRIES = { "entrypoint.js": ["entrypoint"] }; -gulp.task("gen-index-gallery-prod", async () => { - const latestManifest = require(path.resolve( - paths.gallery_output_latest, - "manifest.json" - )); - const content = renderGalleryTemplate("index", { - latestGalleryJS: latestManifest["entrypoint.js"], - }); - const minified = await minifyHtml(content); +gulp.task( + "gen-pages-hassio-dev", + genPagesDevTask( + HASSIO_PAGE_ENTRIES, + paths.hassio_dir, + paths.hassio_output_root, + undefined, + "src", + paths.hassio_publicPath + ) +); - fs.outputFileSync( - path.resolve(paths.gallery_output_root, "index.html"), - minified - ); -}); - -gulp.task("gen-index-hassio-dev", async () => { - writeHassioEntrypoint( - `${paths.hassio_publicPath}/frontend_latest/entrypoint.js`, - `${paths.hassio_publicPath}/frontend_es5/entrypoint.js` - ); -}); - -gulp.task("gen-index-hassio-prod", async () => { - const latestManifest = require(path.resolve( +gulp.task( + "gen-pages-hassio-prod", + genPagesProdTask( + HASSIO_PAGE_ENTRIES, + paths.hassio_dir, + paths.hassio_output_root, paths.hassio_output_latest, - "manifest.json" - )); - const es5Manifest = require(path.resolve( paths.hassio_output_es5, - "manifest.json" - )); - writeHassioEntrypoint( - latestManifest["entrypoint.js"], - es5Manifest["entrypoint.js"] - ); -}); - -function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) { - fs.mkdirSync(paths.hassio_output_root, { recursive: true }); - // Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5 - fs.writeFileSync( - path.resolve(paths.hassio_output_root, "entrypoint.js"), - ` -function loadES5() { - var el = document.createElement('script'); - el.src = '${es5Entrypoint}'; - document.body.appendChild(el); -} -if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) { - loadES5(); -} else { - try { - new Function("import('${latestEntrypoint}')")(); - } catch (err) { - loadES5(); - } -} - `, - { encoding: "utf-8" } - ); -} + "src" + ) +); diff --git a/build-scripts/gulp/gallery.cjs b/build-scripts/gulp/gallery.cjs index 25b53332ce..3670eb18e3 100644 --- a/build-scripts/gulp/gallery.cjs +++ b/build-scripts/gulp/gallery.cjs @@ -3,7 +3,7 @@ const gulp = require("gulp"); const fs = require("fs"); const path = require("path"); const { marked } = require("marked"); -const glob = require("glob"); +const { glob } = require("glob"); const yaml = require("js-yaml"); const env = require("../env.cjs"); @@ -159,7 +159,7 @@ gulp.task( "gather-gallery-pages" ), "copy-static-gallery", - "gen-index-gallery-dev", + "gen-pages-gallery-dev", gulp.parallel( env.useRollup() ? "rollup-dev-server-gallery" @@ -193,6 +193,6 @@ gulp.task( ), "copy-static-gallery", env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", - "gen-index-gallery-prod" + "gen-pages-gallery-prod" ) ); diff --git a/build-scripts/gulp/hassio.cjs b/build-scripts/gulp/hassio.cjs index 562ed3b646..a958cb0365 100644 --- a/build-scripts/gulp/hassio.cjs +++ b/build-scripts/gulp/hassio.cjs @@ -17,7 +17,7 @@ gulp.task( }, "clean-hassio", "gen-dummy-icons-json", - "gen-index-hassio-dev", + "gen-pages-hassio-dev", "build-supervisor-translations", "copy-translations-supervisor", "build-locale-data", @@ -39,7 +39,7 @@ gulp.task( "build-locale-data", "copy-locale-data-supervisor", env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", - "gen-index-hassio-prod", + "gen-pages-hassio-prod", ...// Don't compress running tests (env.isTestBuild() ? [] : ["compress-hassio"]) ) diff --git a/build-scripts/gulp/locale-data.cjs b/build-scripts/gulp/locale-data.cjs index 00bf705f43..4dee68abe2 100755 --- a/build-scripts/gulp/locale-data.cjs +++ b/build-scripts/gulp/locale-data.cjs @@ -19,6 +19,7 @@ const modules = { "intl-relativetimeformat": "RelativeTimeFormat", "intl-datetimeformat": "DateTimeFormat", "intl-numberformat": "NumberFormat", + "intl-displaynames": "DisplayNames", }; gulp.task("create-locale-data", (done) => { diff --git a/build-scripts/list-preset-env-plugins.js b/build-scripts/list-preset-env-plugins.js new file mode 100755 index 0000000000..5e869a4ef1 --- /dev/null +++ b/build-scripts/list-preset-env-plugins.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node +// Script to print Babel plugins that will be used by browserslist environments + +import { version as babelVersion } from "@babel/core"; +import presetEnv from "@babel/preset-env"; +import { babelOptions } from "./bundle.cjs"; + +const dummyAPI = { + version: babelVersion, + assertVersion: () => {}, + caller: (callback) => + callback({ + name: "Dummy Bundler", + supportsStaticESM: true, + supportsDynamicImport: true, + supportsTopLevelAwait: true, + supportsExportNamespaceFrom: true, + }), + targets: () => ({}), +}; + +for (const browserslistEnv of ["modern", "legacy"]) { + console.log("\nBrowsersList Environment = %s\n", browserslistEnv); + presetEnv.default(dummyAPI, { + ...babelOptions({ latestBuild: browserslistEnv === "modern" }) + .presets[0][1], + browserslistEnv, + debug: true, + }); +} diff --git a/cast/src/html/_social_meta.html.template b/cast/src/html/_social_meta.html.template new file mode 100644 index 0000000000..d3ee681ed3 --- /dev/null +++ b/cast/src/html/_social_meta.html.template @@ -0,0 +1,24 @@ + + + + + + + + + + + + diff --git a/cast/src/html/launcher-faq.html.template b/cast/src/html/faq.html.template similarity index 95% rename from cast/src/html/launcher-faq.html.template rename to cast/src/html/faq.html.template index 7ba706c16c..c67a5b477a 100644 --- a/cast/src/html/launcher-faq.html.template +++ b/cast/src/html/faq.html.template @@ -3,7 +3,7 @@ Home Assistant Cast - FAQ - <%= renderTemplate('_style_base') %> + <%= renderTemplate("../../../src/html/_style_base.html.template") %> + <%= renderTemplate("_social_meta.html.template") %> + + + <%= renderTemplate("../../../src/html/_js_base.html.template") %> + + + <%= renderTemplate("../../../src/html/_script_load_es5.html.template") %> + + + diff --git a/cast/src/html/launcher.html.template b/cast/src/html/launcher.html.template deleted file mode 100644 index d6108c96b8..0000000000 --- a/cast/src/html/launcher.html.template +++ /dev/null @@ -1,57 +0,0 @@ - - - - Home Assistant Cast - - - <%= renderTemplate('_style_base') %> - - - - - - - - - - - - - - - - <%= renderTemplate('_js_base') %> - - - - - - - - - diff --git a/cast/src/html/media.html.template b/cast/src/html/media.html.template index 5da7267646..e91fbb881c 100644 --- a/cast/src/html/media.html.template +++ b/cast/src/html/media.html.template @@ -22,25 +22,14 @@ - <%= renderTemplate('_js_base') %> - + <%= renderTemplate("../../../src/html/_js_base.html.template") %> - - - + <%= renderTemplate("../../../src/html/_script_load_es5.html.template") %> diff --git a/cast/src/html/receiver.html.template b/cast/src/html/receiver.html.template index 3e1da1fa12..582e9ca865 100644 --- a/cast/src/html/receiver.html.template +++ b/cast/src/html/receiver.html.template @@ -1,8 +1,10 @@ - - <%= renderTemplate('_style_base') %> + <% for (const entry of latestEntryJS) { %> + + <% } %> + <%= renderTemplate("../../../src/html/_style_base.html.template") %>