diff --git a/.browserslistrc b/.browserslistrc index 86481f8e08..516fec9cec 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -10,6 +10,12 @@ supports es6-module-dynamic-import not Safari < 13 not iOS < 13 +# Exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data +# Babel ignores these automatically, but we need here for Webpack to output ESM with dynamic imports +not KaiOS > 0 +not QQAndroid > 0 +not UCAndroid > 0 + # Exclude unsupported browsers not dead diff --git a/build-scripts/bundle.cjs b/build-scripts/bundle.cjs index a6d14f18c8..f840f21762 100644 --- a/build-scripts/bundle.cjs +++ b/build-scripts/bundle.cjs @@ -77,6 +77,7 @@ module.exports.htmlMinifierOptions = { module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({ safari10: !latestBuild, ecma: latestBuild ? 2015 : 5, + module: latestBuild, format: { comments: false }, sourceMap: !isTestBuild, }); diff --git a/build-scripts/webpack.cjs b/build-scripts/webpack.cjs index 58cc184ca8..0ff76cc832 100644 --- a/build-scripts/webpack.cjs +++ b/build-scripts/webpack.cjs @@ -41,7 +41,7 @@ const createWebpackConfig = ({ return { name, mode: isProdBuild ? "production" : "development", - target: ["web", latestBuild ? "es2017" : "es5"], + target: `browserslist:${latestBuild ? "modern" : "legacy"}`, // For tests/CI, source maps are skipped to gain build speed // For production, generate source maps for accurate stack traces without source code // For development, generate "cheap" versions that can map to original line numbers @@ -84,6 +84,13 @@ const createWebpackConfig = ({ ], moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", + splitChunks: { + // Disable splitting for web workers with ESM output + // Imports of external chunks are broken + chunks: latestBuild + ? (chunk) => !chunk.canBeInitial() && !/^.+-worker$/.test(chunk.name) + : undefined, + }, }, plugins: [ !isStatsBuild && new WebpackBar({ fancy: !isProdBuild }), @@ -163,6 +170,7 @@ const createWebpackConfig = ({ }, }, output: { + module: latestBuild, filename: ({ chunk }) => !isProdBuild || isStatsBuild || dontHash.has(chunk.name) ? "[name].js" @@ -196,7 +204,7 @@ const createWebpackConfig = ({ : undefined, }, experiments: { - topLevelAwait: true, + outputModule: true, }, }; }; diff --git a/src/components/data-table/sort_filter_worker.ts b/src/components/data-table/sort-filter-worker.ts similarity index 100% rename from src/components/data-table/sort_filter_worker.ts rename to src/components/data-table/sort-filter-worker.ts diff --git a/src/components/data-table/sort-filter.ts b/src/components/data-table/sort-filter.ts index 67d7679397..e0eacf3305 100644 --- a/src/components/data-table/sort-filter.ts +++ b/src/components/data-table/sort-filter.ts @@ -1,5 +1,5 @@ import { Remote, wrap } from "comlink"; -import type { Api } from "./sort_filter_worker"; +import type { Api } from "./sort-filter-worker"; type FilterDataType = Api["filterData"]; type FilterDataParamTypes = Parameters; @@ -9,27 +9,28 @@ type SortDataParamTypes = Parameters; let worker: Remote | undefined; +const getWorker = () => { + if (!worker) { + worker = wrap( + new Worker( + /* webpackChunkName: "sort-filter-worker" */ + new URL("./sort-filter-worker", import.meta.url) + ) + ); + } + return worker; +}; + export const filterData = ( data: FilterDataParamTypes[0], columns: FilterDataParamTypes[1], filter: FilterDataParamTypes[2] -): Promise> => { - if (!worker) { - worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url))); - } - - return worker.filterData(data, columns, filter); -}; - +): Promise> => + getWorker().filterData(data, columns, filter); export const sortData = ( data: SortDataParamTypes[0], columns: SortDataParamTypes[1], direction: SortDataParamTypes[2], sortColumn: SortDataParamTypes[3] -): Promise> => { - if (!worker) { - worker = wrap(new Worker(new URL("./sort_filter_worker", import.meta.url))); - } - - return worker.sortData(data, columns, direction, sortColumn); -}; +): Promise> => + getWorker().sortData(data, columns, direction, sortColumn); diff --git a/src/resources/markdown_worker.ts b/src/resources/markdown-worker.ts similarity index 100% rename from src/resources/markdown_worker.ts rename to src/resources/markdown-worker.ts diff --git a/src/resources/render-markdown.ts b/src/resources/render-markdown.ts index fb9b8cf438..85c584c939 100644 --- a/src/resources/render-markdown.ts +++ b/src/resources/render-markdown.ts @@ -1,5 +1,5 @@ import { Remote, wrap } from "comlink"; -import type { Api } from "./markdown_worker"; +import type { Api } from "./markdown-worker"; type RenderMarkdownType = Api["renderMarkdown"]; type RenderMarkdownParamTypes = Parameters; @@ -12,8 +12,12 @@ export const renderMarkdown = async ( hassOptions?: RenderMarkdownParamTypes[2] ): Promise> => { if (!worker) { - worker = wrap(new Worker(new URL("./markdown_worker", import.meta.url))); + worker = wrap( + new Worker( + /* webpackChunkName: "markdown-worker" */ + new URL("./markdown-worker", import.meta.url) + ) + ); } - return worker.renderMarkdown(content, markedOptions, hassOptions); }; diff --git a/test/webpack.config.js b/test/webpack.config.js index 20e1e163e3..ad593b58aa 100644 --- a/test/webpack.config.js +++ b/test/webpack.config.js @@ -1,8 +1,13 @@ import webpack from "../build-scripts/webpack.cjs"; -export default webpack.createAppConfig({ +const config = webpack.createAppConfig({ isProdBuild: false, latestBuild: true, isStatsBuild: false, isTestBuild: true, }); + +// instant-mocha forces a CJS library, so cannot output ESM +config.output.module = false; + +export default config;