Gulpify build pipeline (#3145)
* Gulpify build pipeline * Update build frontend script * Fixes * Limit service worker to latest build * Use shorthand * Fix hassio build
|
@ -4,7 +4,6 @@ node_modules/*
|
|||
npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
hass_frontend_es5/*
|
||||
.reify-cache
|
||||
demo/hademo-icons.html
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
const del = require("del");
|
||||
const gulp = require("gulp");
|
||||
const config = require("../paths");
|
||||
|
||||
gulp.task("clean", () => del([config.root, config.build_dir]));
|
|
@ -0,0 +1,29 @@
|
|||
// Run HA develop mode
|
||||
const gulp = require("gulp");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gather-static.js");
|
||||
require("./webpack.js");
|
||||
require("./service-worker.js");
|
||||
require("./entry-html.js");
|
||||
|
||||
gulp.task(
|
||||
"develop",
|
||||
gulp.series(
|
||||
async function setEnv() {
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean",
|
||||
gulp.parallel(
|
||||
"copy-static",
|
||||
"gen-service-worker-dev",
|
||||
"gen-icons",
|
||||
"gen-pages-dev",
|
||||
"gen-index-html-dev",
|
||||
gulp.series("build-translations", "copy-translations")
|
||||
),
|
||||
"webpack-watch"
|
||||
)
|
||||
);
|
|
@ -0,0 +1,108 @@
|
|||
// Tasks to generate entry HTML
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable global-require */
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const template = require("lodash.template");
|
||||
const minify = require("html-minifier").minify;
|
||||
const config = require("../paths.js");
|
||||
|
||||
const templatePath = (tpl) =>
|
||||
path.resolve(config.polymer_dir, "src/html/", `${tpl}.html.template`);
|
||||
|
||||
const readFile = (pth) => fs.readFileSync(pth).toString();
|
||||
|
||||
const renderTemplate = (pth, data = {}) => {
|
||||
const compiled = template(readFile(templatePath(pth)));
|
||||
return compiled({ ...data, renderTemplate });
|
||||
};
|
||||
|
||||
const minifyHtml = (content) =>
|
||||
minify(content, {
|
||||
collapseWhitespace: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
removeComments: true,
|
||||
});
|
||||
|
||||
const PAGES = ["onboarding", "authorize"];
|
||||
|
||||
gulp.task("gen-pages-dev", (done) => {
|
||||
for (const page of PAGES) {
|
||||
const content = renderTemplate(page, {
|
||||
latestPageJS: `/frontend_latest/${page}.js`,
|
||||
latestHassIconsJS: "/frontend_latest/hass-icons.js",
|
||||
|
||||
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||
es5PageJS: `/frontend_es5/${page}.js`,
|
||||
es5HassIconsJS: "/frontend_es5/hass-icons.js",
|
||||
});
|
||||
|
||||
fs.outputFileSync(path.resolve(config.root, `${page}.html`), content);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-pages-prod", (done) => {
|
||||
const latestManifest = require(path.resolve(config.output, "manifest.json"));
|
||||
const es5Manifest = require(path.resolve(config.output_es5, "manifest.json"));
|
||||
|
||||
for (const page of PAGES) {
|
||||
const content = renderTemplate(page, {
|
||||
latestPageJS: latestManifest[`${page}.js`],
|
||||
latestHassIconsJS: latestManifest["hass-icons.js"],
|
||||
|
||||
es5Compatibility: es5Manifest["compatibility.js"],
|
||||
es5PageJS: es5Manifest[`${page}.js`],
|
||||
es5HassIconsJS: es5Manifest["hass-icons.js"],
|
||||
});
|
||||
|
||||
fs.outputFileSync(
|
||||
path.resolve(config.root, `${page}.html`),
|
||||
minifyHtml(content)
|
||||
);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-index-html-dev", (done) => {
|
||||
// In dev mode we don't mangle names, so we hardcode urls. That way we can
|
||||
// run webpack as last in watch mode, which blocks output.
|
||||
const content = renderTemplate("index", {
|
||||
latestAppJS: "/frontend_latest/app.js",
|
||||
latestCoreJS: "/frontend_latest/core.js",
|
||||
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
|
||||
latestHassIconsJS: "/frontend_latest/hass-icons.js",
|
||||
|
||||
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||
es5AppJS: "/frontend_es5/app.js",
|
||||
es5CoreJS: "/frontend_es5/core.js",
|
||||
es5CustomPanelJS: "/frontend_es5/custom-panel.js",
|
||||
es5HassIconsJS: "/frontend_es5/hass-icons.js",
|
||||
});
|
||||
|
||||
fs.outputFileSync(path.resolve(config.root, "index.html"), content);
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-index-html-prod", (done) => {
|
||||
const latestManifest = require(path.resolve(config.output, "manifest.json"));
|
||||
const es5Manifest = require(path.resolve(config.output_es5, "manifest.json"));
|
||||
const content = renderTemplate("index", {
|
||||
latestAppJS: latestManifest["app.js"],
|
||||
latestCoreJS: latestManifest["core.js"],
|
||||
latestCustomPanelJS: latestManifest["custom-panel.js"],
|
||||
latestHassIconsJS: latestManifest["hass-icons.js"],
|
||||
|
||||
es5Compatibility: es5Manifest["compatibility.js"],
|
||||
es5AppJS: es5Manifest["app.js"],
|
||||
es5CoreJS: es5Manifest["core.js"],
|
||||
es5CustomPanelJS: es5Manifest["custom-panel.js"],
|
||||
es5HassIconsJS: es5Manifest["hass-icons.js"],
|
||||
});
|
||||
const minified = minifyHtml(content).replace(/#THEMEC/g, "{{ theme_color }}");
|
||||
|
||||
fs.outputFileSync(path.resolve(config.root, "index.html"), minified);
|
||||
done();
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
// Gulp task to gather all static files.
|
||||
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const zopfli = require("gulp-zopfli-green");
|
||||
const merge = require("merge-stream");
|
||||
const config = require("../paths");
|
||||
|
||||
const npmPath = (...parts) =>
|
||||
path.resolve(config.polymer_dir, "node_modules", ...parts);
|
||||
const polyPath = (...parts) => path.resolve(config.polymer_dir, ...parts);
|
||||
const staticPath = (...parts) => path.resolve(config.root, "static", ...parts);
|
||||
|
||||
const copyFileDir = (fromFile, toDir) =>
|
||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||
|
||||
function copyTranslations() {
|
||||
// Translation output
|
||||
fs.copySync(
|
||||
polyPath("build-translations/output"),
|
||||
staticPath("translations")
|
||||
);
|
||||
}
|
||||
|
||||
function copyStatic() {
|
||||
// Basic static files
|
||||
fs.copySync(polyPath("public"), config.root);
|
||||
|
||||
// Web Component polyfills and adapters
|
||||
copyFileDir(
|
||||
npmPath("@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"),
|
||||
staticPath("polyfills/")
|
||||
);
|
||||
copyFileDir(
|
||||
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js"),
|
||||
staticPath("polyfills/")
|
||||
);
|
||||
copyFileDir(
|
||||
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"),
|
||||
staticPath("polyfills/")
|
||||
);
|
||||
|
||||
// Local fonts
|
||||
fs.copySync(npmPath("@polymer/font-roboto-local/fonts"), staticPath("fonts"));
|
||||
|
||||
// External dependency assets
|
||||
copyFileDir(
|
||||
npmPath("react-big-calendar/lib/css/react-big-calendar.css"),
|
||||
staticPath("panels/calendar/")
|
||||
);
|
||||
copyFileDir(
|
||||
npmPath("leaflet/dist/leaflet.css"),
|
||||
staticPath("images/leaflet/")
|
||||
);
|
||||
fs.copySync(
|
||||
npmPath("leaflet/dist/images"),
|
||||
staticPath("images/leaflet/images/")
|
||||
);
|
||||
}
|
||||
|
||||
gulp.task("copy-static", (done) => {
|
||||
copyStatic();
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("compress-static", () => {
|
||||
const fonts = gulp
|
||||
.src(staticPath("fonts/**/*.ttf"))
|
||||
.pipe(zopfli())
|
||||
.pipe(gulp.dest(staticPath("fonts")));
|
||||
const polyfills = gulp
|
||||
.src(staticPath("polyfills/*.js"))
|
||||
.pipe(zopfli())
|
||||
.pipe(gulp.dest(staticPath("polyfills")));
|
||||
const translations = gulp
|
||||
.src(staticPath("translations/*.json"))
|
||||
.pipe(zopfli())
|
||||
.pipe(gulp.dest(staticPath("translations")));
|
||||
|
||||
return merge(fonts, polyfills, translations);
|
||||
});
|
||||
|
||||
gulp.task("copy-translations", (done) => {
|
||||
copyTranslations();
|
||||
done();
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const config = require("../config");
|
||||
|
||||
const ICON_PACKAGE_PATH = path.resolve(
|
||||
__dirname,
|
||||
|
@ -38,12 +37,12 @@ function loadIcon(name) {
|
|||
function transformXMLtoPolymer(name, xml) {
|
||||
const start = xml.indexOf("><path") + 1;
|
||||
const end = xml.length - start - 6;
|
||||
const path = xml.substr(start, end);
|
||||
return `<g id="${name}">${path}</g>`;
|
||||
const pth = xml.substr(start, end);
|
||||
return `<g id="${name}">${pth}</g>`;
|
||||
}
|
||||
|
||||
// Given an iconset name and icon names, generate a polymer iconset
|
||||
function generateIconset(name, iconNames) {
|
||||
function generateIconset(iconsetName, iconNames) {
|
||||
const iconDefs = Array.from(iconNames)
|
||||
.map((name) => {
|
||||
const iconDef = loadIcon(name);
|
||||
|
@ -53,7 +52,7 @@ function generateIconset(name, iconNames) {
|
|||
return transformXMLtoPolymer(name, iconDef);
|
||||
})
|
||||
.join("");
|
||||
return `<ha-iconset-svg name="${name}" size="24"><svg><defs>${iconDefs}</defs></svg></ha-iconset-svg>`;
|
||||
return `<ha-iconset-svg name="${iconsetName}" size="24"><svg><defs>${iconDefs}</defs></svg></ha-iconset-svg>`;
|
||||
}
|
||||
|
||||
// Generate the full MDI iconset
|
||||
|
@ -62,7 +61,9 @@ function genMDIIcons() {
|
|||
fs.readFileSync(path.resolve(ICON_PACKAGE_PATH, META_PATH), "UTF-8")
|
||||
);
|
||||
const iconNames = meta.map((iconInfo) => iconInfo.name);
|
||||
fs.existsSync(OUTPUT_DIR) || fs.mkdirSync(OUTPUT_DIR);
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR);
|
||||
}
|
||||
fs.writeFileSync(MDI_OUTPUT_PATH, generateIconset("mdi", iconNames));
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ function mapFiles(startPath, filter, mapFunc) {
|
|||
}
|
||||
|
||||
// Find all icons used by the project.
|
||||
function findIcons(path, iconsetName) {
|
||||
function findIcons(searchPath, iconsetName) {
|
||||
const iconRegex = new RegExp(`${iconsetName}:[\\w-]+`, "g");
|
||||
const icons = new Set();
|
||||
function processFile(filename) {
|
||||
|
@ -93,8 +94,8 @@ function findIcons(path, iconsetName) {
|
|||
icons.add(match[0].substr(iconsetName.length + 1));
|
||||
}
|
||||
}
|
||||
mapFiles(path, ".js", processFile);
|
||||
mapFiles(path, ".ts", processFile);
|
||||
mapFiles(searchPath, ".js", processFile);
|
||||
mapFiles(searchPath, ".ts", processFile);
|
||||
return icons;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Run HA develop mode
|
||||
const gulp = require("gulp");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gather-static.js");
|
||||
require("./webpack.js");
|
||||
require("./service-worker.js");
|
||||
require("./entry-html.js");
|
||||
|
||||
gulp.task(
|
||||
"build-release",
|
||||
gulp.series(
|
||||
async function setEnv() {
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean",
|
||||
gulp.parallel(
|
||||
"copy-static",
|
||||
"gen-icons",
|
||||
gulp.series("build-translations", "copy-translations")
|
||||
),
|
||||
gulp.parallel("webpack-prod", "compress-static"),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-html-prod",
|
||||
"gen-service-worker-prod"
|
||||
)
|
||||
)
|
||||
);
|
|
@ -0,0 +1,29 @@
|
|||
// Generate service worker.
|
||||
// Based on manifest, create a file with the content as service_worker.js
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable global-require */
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const config = require("../paths.js");
|
||||
|
||||
const swPath = path.resolve(config.root, "service_worker.js");
|
||||
|
||||
const writeSW = (content) => fs.outputFileSync(swPath, content.trim() + "\n");
|
||||
|
||||
gulp.task("gen-service-worker-dev", (done) => {
|
||||
writeSW(
|
||||
`
|
||||
console.debug('Service worker disabled in development');
|
||||
`
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-service-worker-prod", (done) => {
|
||||
fs.copySync(
|
||||
path.resolve(config.output, "service_worker.js"),
|
||||
path.resolve(config.root, "service_worker.js")
|
||||
);
|
||||
done();
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
// Tasks to run webpack.
|
||||
const gulp = require("gulp");
|
||||
const webpack = require("webpack");
|
||||
const { createAppConfig } = require("../webpack");
|
||||
|
||||
const handler = (done) => (err, stats) => {
|
||||
if (err) {
|
||||
console.log(err.stack || err);
|
||||
if (err.details) {
|
||||
console.log(err.details);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Build done @ ${new Date().toLocaleTimeString()}`);
|
||||
|
||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||
console.log(stats.toString("minimal"));
|
||||
}
|
||||
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
gulp.task("webpack-watch", () => {
|
||||
const compiler = webpack([
|
||||
createAppConfig({
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
isStatsBuild: false,
|
||||
}),
|
||||
createAppConfig({
|
||||
isProdBuild: false,
|
||||
latestBuild: false,
|
||||
isStatsBuild: false,
|
||||
}),
|
||||
]);
|
||||
compiler.watch({}, handler());
|
||||
// we are not calling done, so this command will run forever
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"webpack-prod",
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
[
|
||||
createAppConfig({
|
||||
isProdBuild: true,
|
||||
latestBuild: true,
|
||||
isStatsBuild: false,
|
||||
}),
|
||||
createAppConfig({
|
||||
isProdBuild: true,
|
||||
latestBuild: false,
|
||||
isStatsBuild: false,
|
||||
}),
|
||||
],
|
||||
handler(resolve)
|
||||
)
|
||||
)
|
||||
);
|
|
@ -0,0 +1,10 @@
|
|||
var path = require("path");
|
||||
|
||||
module.exports = {
|
||||
polymer_dir: path.resolve(__dirname, ".."),
|
||||
build_dir: path.resolve(__dirname, "../build"),
|
||||
root: path.resolve(__dirname, "../hass_frontend"),
|
||||
static: path.resolve(__dirname, "../hass_frontend/static"),
|
||||
output: path.resolve(__dirname, "../hass_frontend/frontend_latest"),
|
||||
output_es5: path.resolve(__dirname, "../hass_frontend/frontend_es5"),
|
||||
};
|
|
@ -0,0 +1,184 @@
|
|||
const webpack = require("webpack");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const WorkboxPlugin = require("workbox-webpack-plugin");
|
||||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
const zopfli = require("@gfx/zopfli");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const { babelLoaderConfig } = require("./babel.js");
|
||||
|
||||
let version = fs
|
||||
.readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8")
|
||||
.match(/\d{8}\.\d+/);
|
||||
if (!version) {
|
||||
throw Error("Version not found");
|
||||
}
|
||||
version = version[0];
|
||||
|
||||
const resolve = {
|
||||
extensions: [".ts", ".js", ".json", ".tsx"],
|
||||
alias: {
|
||||
react: "preact-compat",
|
||||
"react-dom": "preact-compat",
|
||||
// Not necessary unless you consume a module using `createClass`
|
||||
"create-react-class": "preact-compat/lib/create-react-class",
|
||||
// Not necessary unless you consume a module requiring `react-dom-factories`
|
||||
"react-dom-factories": "preact-compat/lib/react-dom-factories",
|
||||
},
|
||||
};
|
||||
|
||||
const plugins = [
|
||||
// Ignore moment.js locales
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Color.js is bloated, it contains all color definitions for all material color sets.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@polymer\/paper-styles\/color\.js$/,
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// Ignore roboto pointing at CDN. We use local font-roboto-local.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@polymer\/font-roboto\/roboto\.js$/,
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// Ignore mwc icons pointing at CDN.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@material\/mwc-icon\/mwc-icon-font\.js$/,
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
];
|
||||
|
||||
const optimization = (latestBuild) => ({
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
extractComments: true,
|
||||
terserOptions: {
|
||||
ecma: latestBuild ? undefined : 5,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
const isCI = process.env.CI === "true";
|
||||
|
||||
// Create an object mapping browser urls to their paths during build
|
||||
const translationMetadata = require("../build-translations/translationMetadata.json");
|
||||
const workBoxTranslationsTemplatedURLs = {};
|
||||
const englishFP = translationMetadata["translations"]["en"]["fingerprints"];
|
||||
Object.keys(englishFP).forEach((key) => {
|
||||
workBoxTranslationsTemplatedURLs[
|
||||
`/static/translations/${englishFP[key]}`
|
||||
] = `build-translations/output/${key}.json`;
|
||||
});
|
||||
|
||||
const publicPath = latestBuild ? "/frontend_latest/" : "/frontend_es5/";
|
||||
|
||||
const entry = {
|
||||
app: "./src/entrypoints/app.ts",
|
||||
authorize: "./src/entrypoints/authorize.ts",
|
||||
onboarding: "./src/entrypoints/onboarding.ts",
|
||||
core: "./src/entrypoints/core.ts",
|
||||
compatibility: "./src/entrypoints/compatibility.ts",
|
||||
"custom-panel": "./src/entrypoints/custom-panel.ts",
|
||||
"hass-icons": "./src/entrypoints/hass-icons.ts",
|
||||
};
|
||||
|
||||
return {
|
||||
mode: isProdBuild ? "production" : "development",
|
||||
devtool: isProdBuild
|
||||
? "cheap-source-map "
|
||||
: "inline-cheap-module-source-map",
|
||||
entry,
|
||||
module: {
|
||||
rules: [
|
||||
babelLoaderConfig({ latestBuild }),
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: "raw-loader",
|
||||
},
|
||||
{
|
||||
test: /\.(html)$/,
|
||||
use: {
|
||||
loader: "html-loader",
|
||||
options: {
|
||||
exportAsEs6Default: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: optimization(latestBuild),
|
||||
plugins: [
|
||||
new ManifestPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
__DEV__: JSON.stringify(!isProdBuild),
|
||||
__DEMO__: false,
|
||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||
__VERSION__: JSON.stringify(version),
|
||||
__STATIC_PATH__: "/static/",
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProdBuild ? "production" : "development"
|
||||
),
|
||||
}),
|
||||
...plugins,
|
||||
isProdBuild &&
|
||||
!isCI &&
|
||||
!isStatsBuild &&
|
||||
new CompressionPlugin({
|
||||
cache: true,
|
||||
exclude: [/\.js\.map$/, /\.LICENSE$/, /\.py$/, /\.txt$/],
|
||||
algorithm(input, compressionOptions, callback) {
|
||||
return zopfli.gzip(input, compressionOptions, callback);
|
||||
},
|
||||
}),
|
||||
latestBuild &&
|
||||
new WorkboxPlugin.InjectManifest({
|
||||
swSrc: "./src/entrypoints/service-worker-hass.js",
|
||||
swDest: "service_worker.js",
|
||||
importWorkboxFrom: "local",
|
||||
include: [/\.js$/],
|
||||
templatedURLs: {
|
||||
...workBoxTranslationsTemplatedURLs,
|
||||
"/static/icons/favicon-192x192.png":
|
||||
"public/icons/favicon-192x192.png",
|
||||
"/static/fonts/roboto/Roboto-Light.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Light.ttf",
|
||||
"/static/fonts/roboto/Roboto-Medium.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Medium.ttf",
|
||||
"/static/fonts/roboto/Roboto-Regular.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Regular.ttf",
|
||||
"/static/fonts/roboto/Roboto-Bold.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Bold.ttf",
|
||||
},
|
||||
}),
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
const dontHash = new Set([
|
||||
// Files who'se names should not be hashed.
|
||||
// We currently have none.
|
||||
]);
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) return `${chunk.name}.js`;
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
path: latestBuild ? paths.output : paths.output_es5,
|
||||
publicPath,
|
||||
},
|
||||
resolve,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
resolve,
|
||||
plugins,
|
||||
optimization,
|
||||
createAppConfig,
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
|
||||
module.exports.resolve = {
|
||||
extensions: [".ts", ".js", ".json", ".tsx"],
|
||||
alias: {
|
||||
react: "preact-compat",
|
||||
"react-dom": "preact-compat",
|
||||
// Not necessary unless you consume a module using `createClass`
|
||||
"create-react-class": "preact-compat/lib/create-react-class",
|
||||
// Not necessary unless you consume a module requiring `react-dom-factories`
|
||||
"react-dom-factories": "preact-compat/lib/react-dom-factories",
|
||||
},
|
||||
};
|
||||
|
||||
module.exports.plugins = [
|
||||
// Ignore moment.js locales
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Color.js is bloated, it contains all color definitions for all material color sets.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@polymer\/paper-styles\/color\.js$/,
|
||||
path.resolve(__dirname, "../src/util/empty.js")
|
||||
),
|
||||
// Ignore roboto pointing at CDN. We use local font-roboto-local.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@polymer\/font-roboto\/roboto\.js$/,
|
||||
path.resolve(__dirname, "../src/util/empty.js")
|
||||
),
|
||||
// Ignore mwc icons pointing at CDN.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@material\/mwc-icon\/mwc-icon-font\.js$/,
|
||||
path.resolve(__dirname, "../src/util/empty.js")
|
||||
),
|
||||
];
|
||||
|
||||
module.exports.optimization = (latestBuild) => ({
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
extractComments: true,
|
||||
terserOptions: {
|
||||
ecma: latestBuild ? undefined : 5,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
|
@ -4,7 +4,7 @@ const {
|
|||
findIcons,
|
||||
generateIconset,
|
||||
genMDIIcons,
|
||||
} = require("../../gulp/tasks/gen-icons.js");
|
||||
} = require("../../build-scripts/gulp/gen-icons.js");
|
||||
|
||||
function genHademoIcons() {
|
||||
const iconNames = findIcons("./src", "hademo");
|
||||
|
|
|
@ -2,8 +2,8 @@ const path = require("path");
|
|||
const webpack = require("webpack");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const WorkboxPlugin = require("workbox-webpack-plugin");
|
||||
const { babelLoaderConfig } = require("../config/babel.js");
|
||||
const webpackBase = require("../config/webpack.js");
|
||||
const { babelLoaderConfig } = require("../build-scripts/babel.js");
|
||||
const webpackBase = require("../build-scripts/webpack.js");
|
||||
|
||||
const isProd = process.env.NODE_ENV === "production";
|
||||
const isStatsBuild = process.env.STATS === "1";
|
||||
|
@ -72,7 +72,7 @@ module.exports = {
|
|||
...webpackBase.plugins,
|
||||
isProd &&
|
||||
new WorkboxPlugin.GenerateSW({
|
||||
swDest: "service_worker_es5.js",
|
||||
swDest: "service_worker.js",
|
||||
importWorkboxFrom: "local",
|
||||
include: [],
|
||||
}),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const path = require("path");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const { babelLoaderConfig } = require("../config/babel.js");
|
||||
const webpackBase = require("../config/webpack.js");
|
||||
const { babelLoaderConfig } = require("../build-scripts/babel.js");
|
||||
const webpackBase = require("../build-scripts/webpack.js");
|
||||
|
||||
const isProd = process.env.NODE_ENV === "production";
|
||||
const chunkFilename = isProd ? "chunk.[chunkhash].js" : "[name].chunk.js";
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
var path = require("path");
|
||||
|
||||
module.exports = {
|
||||
polymer_dir: path.resolve(__dirname, ".."),
|
||||
build_dir: path.resolve(__dirname, "../build"),
|
||||
output: path.resolve(__dirname, "../hass_frontend"),
|
||||
output_es5: path.resolve(__dirname, "../hass_frontend_es5"),
|
||||
};
|
|
@ -1,3 +1,3 @@
|
|||
var requireDir = require('require-dir');
|
||||
var requireDir = require("require-dir");
|
||||
|
||||
requireDir('./gulp/tasks/');
|
||||
requireDir("./build-scripts/gulp/");
|
||||
|
|
|
@ -4,7 +4,7 @@ const {
|
|||
findIcons,
|
||||
generateIconset,
|
||||
genMDIIcons,
|
||||
} = require("../../gulp/tasks/gen-icons.js");
|
||||
} = require("../../build-scripts/gulp/gen-icons.js");
|
||||
|
||||
function genHassioIcons() {
|
||||
const iconNames = findIcons("./src", "hassio");
|
||||
|
|
|
@ -3,8 +3,8 @@ const CompressionPlugin = require("compression-webpack-plugin");
|
|||
const zopfli = require("@gfx/zopfli");
|
||||
|
||||
const config = require("./config.js");
|
||||
const { babelLoaderConfig } = require("../config/babel.js");
|
||||
const webpackBase = require("../config/webpack.js");
|
||||
const { babelLoaderConfig } = require("../build-scripts/babel.js");
|
||||
const webpackBase = require("../build-scripts/webpack.js");
|
||||
|
||||
const isProdBuild = process.env.NODE_ENV === "production";
|
||||
const isCI = process.env.CI === "true";
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-prettier": "^3.0.1",
|
||||
"eslint-plugin-react": "^7.12.4",
|
||||
"fs-extra": "^7.0.1",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-foreach": "^0.1.0",
|
||||
"gulp-hash": "^4.2.2",
|
||||
|
@ -134,10 +135,12 @@
|
|||
"gulp-jsonminify": "^1.1.0",
|
||||
"gulp-merge-json": "^1.3.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-zopfli-green": "^3.0.1",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^1.3.1",
|
||||
"lint-staged": "^8.1.5",
|
||||
"lodash.template": "^4.4.0",
|
||||
"merge-stream": "^1.0.1",
|
||||
"mocha": "^6.0.2",
|
||||
"parse5": "^5.1.0",
|
||||
|
@ -160,6 +163,7 @@
|
|||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.3.0",
|
||||
"webpack-dev-server": "^3.2.1",
|
||||
"webpack-manifest-plugin": "^2.0.4",
|
||||
"workbox-webpack-plugin": "^4.1.1"
|
||||
},
|
||||
"resolutions": {
|
||||
|
|
|
@ -1,30 +1,7 @@
|
|||
"""Frontend for Home Assistant."""
|
||||
import os
|
||||
from user_agents import parse
|
||||
|
||||
FAMILY_MIN_VERSION = {
|
||||
'Chrome': 55, # Async/await
|
||||
'Chrome Mobile': 55,
|
||||
'Firefox': 52, # Async/await
|
||||
'Firefox Mobile': 52,
|
||||
'Opera': 42, # Async/await
|
||||
'Edge': 15, # Async/await
|
||||
'Safari': 10.1, # Async/await
|
||||
}
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def where():
|
||||
"""Return path to the frontend."""
|
||||
return os.path.dirname(__file__)
|
||||
|
||||
|
||||
def version(useragent):
|
||||
"""Get the version for given user agent."""
|
||||
useragent = parse(useragent)
|
||||
|
||||
# on iOS every browser uses the Safari engine
|
||||
if useragent.os.family == 'iOS':
|
||||
return useragent.os.version[0] >= FAMILY_MIN_VERSION['Safari']
|
||||
|
||||
version = FAMILY_MIN_VERSION.get(useragent.browser.family)
|
||||
return version and useragent.browser.version[0] >= version
|
||||
return Path(__file__).parent
|
||||
|
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 824 B After Width: | Height: | Size: 824 B |
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 424 B After Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 734 B After Width: | Height: | Size: 734 B |
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 1016 B After Width: | Height: | Size: 1016 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 768 B |
Before Width: | Height: | Size: 1022 B After Width: | Height: | Size: 1022 B |
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 803 B |
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 571 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 947 B |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 263 B |
|
@ -6,19 +6,4 @@ set -e
|
|||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
BUILD_DIR=build
|
||||
BUILD_TRANSLATIONS_DIR=build-translations
|
||||
OUTPUT_DIR=hass_frontend
|
||||
OUTPUT_DIR_ES5=hass_frontend_es5
|
||||
|
||||
rm -rf $OUTPUT_DIR $OUTPUT_DIR_ES5 $BUILD_DIR $BUILD_TRANSLATIONS_DIR
|
||||
|
||||
# Build frontend
|
||||
./node_modules/.bin/gulp build-translations gen-icons
|
||||
NODE_ENV=production ./node_modules/.bin/webpack
|
||||
|
||||
# Generate the __init__ file
|
||||
echo "VERSION = '`git rev-parse HEAD`'" >> $OUTPUT_DIR/__init__.py
|
||||
echo "CREATED_AT = `date +%s`" >> $OUTPUT_DIR/__init__.py
|
||||
echo "VERSION = '`git rev-parse HEAD`'" >> $OUTPUT_DIR_ES5/__init__.py
|
||||
echo "CREATED_AT = `date +%s`" >> $OUTPUT_DIR_ES5/__init__.py
|
||||
./node_modules/.bin/gulp build-release
|
||||
|
|
|
@ -6,14 +6,4 @@ set -e
|
|||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
BUILD_DIR=build
|
||||
OUTPUT_DIR=hass_frontend
|
||||
OUTPUT_DIR_ES5=hass_frontend_es5
|
||||
|
||||
rm -rf $OUTPUT_DIR $OUTPUT_DIR_ES5 $BUILD_DIR
|
||||
mkdir $OUTPUT_DIR $OUTPUT_DIR_ES5
|
||||
# Needed in case frontend repo installed with pip3 install -e
|
||||
cp -r public/__init__.py $OUTPUT_DIR_ES5/
|
||||
|
||||
./node_modules/.bin/gulp build-translations gen-icons
|
||||
./node_modules/.bin/webpack --watch --progress
|
||||
./node_modules/.bin/gulp develop
|
||||
|
|
|
@ -7,5 +7,5 @@ set -e
|
|||
cd "$(dirname "$0")/.."
|
||||
|
||||
STATS=1 NODE_ENV=production webpack --profile --json > compilation-stats.json
|
||||
npx webpack-bundle-analyzer compilation-stats.json hass_frontend
|
||||
npx webpack-bundle-analyzer compilation-stats.json hass_frontend/frontend_latest
|
||||
rm compilation-stats.json
|
||||
|
|
10
setup.py
|
@ -8,15 +8,7 @@ setup(
|
|||
author="The Home Assistant Authors",
|
||||
author_email="hello@home-assistant.io",
|
||||
license="Apache License 2.0",
|
||||
packages=find_packages(
|
||||
include=[
|
||||
"hass_frontend",
|
||||
"hass_frontend_es5",
|
||||
"hass_frontend.*",
|
||||
"hass_frontend_es5.*",
|
||||
]
|
||||
),
|
||||
install_requires=["user-agents==2.0.0"],
|
||||
packages=find_packages(include=["hass_frontend", "hass_frontend.*"]),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
css,
|
||||
} from "lit-element";
|
||||
import "./ha-auth-flow";
|
||||
import { AuthProvider } from "../data/auth";
|
||||
import { AuthProvider, fetchAuthProviders } from "../data/auth";
|
||||
import { registerServiceWorker } from "../util/register-service-worker";
|
||||
|
||||
import(/* webpackChunkName: "pick-auth-provider" */ "../auth/ha-pick-auth-provider");
|
||||
|
@ -135,7 +135,9 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||
private async _fetchAuthProviders() {
|
||||
// Fetch auth providers
|
||||
try {
|
||||
const response = await (window as any).providersPromise;
|
||||
// We prefetch this data on page load in authorize.html.template for modern builds
|
||||
const response = await ((window as any).providersPromise ||
|
||||
fetchAuthProviders());
|
||||
const authProviders = await response.json();
|
||||
|
||||
// Forward to main screen which will redirect to right onboarding page.
|
||||
|
|
|
@ -18,3 +18,8 @@ export const getSignedPath = (
|
|||
hass: HomeAssistant,
|
||||
path: string
|
||||
): Promise<SignedPath> => hass.callWS({ type: "auth/sign_path", path });
|
||||
|
||||
export const fetchAuthProviders = () =>
|
||||
fetch("/auth/providers", {
|
||||
credentials: "same-origin",
|
||||
});
|
||||
|
|
|
@ -9,7 +9,10 @@ interface UserStepResponse {
|
|||
auth_code: string;
|
||||
}
|
||||
|
||||
export const onboardUserStep = async (params: {
|
||||
export const fetchOnboardingOverview = () =>
|
||||
fetch("/api/onboarding", { credentials: "same-origin" });
|
||||
|
||||
export const onboardUserStep = (params: {
|
||||
client_id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
/* global importScripts */
|
||||
importScripts("/static/service-worker-hass.js");
|
|
@ -1,3 +1,7 @@
|
|||
/*
|
||||
This file is not run through webpack, but instead is directly manipulated
|
||||
by Workbox Webpack plugin. So we cannot use __DEV__ or other constants.
|
||||
*/
|
||||
/* global workbox clients */
|
||||
|
||||
function initRouting() {
|
||||
|
@ -17,9 +21,7 @@ function initRouting() {
|
|||
|
||||
// Get manifest and service worker from network.
|
||||
workbox.routing.registerRoute(
|
||||
new RegExp(
|
||||
`${location.host}/(service_worker.js|service_worker_es5.js|manifest.json)`
|
||||
),
|
||||
new RegExp(`${location.host}/(service_worker.js|manifest.json)`),
|
||||
new workbox.strategies.NetworkOnly()
|
||||
);
|
||||
|
||||
|
@ -161,11 +163,8 @@ self.addEventListener("message", (message) => {
|
|||
});
|
||||
|
||||
workbox.setConfig({
|
||||
debug: __DEV__,
|
||||
debug: false,
|
||||
});
|
||||
|
||||
if (!__DEV__) {
|
||||
initRouting();
|
||||
}
|
||||
|
||||
initRouting();
|
||||
initPushNotifications();
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<script>
|
||||
function _ls(src) {
|
||||
var doc = document.documentElement;
|
||||
var script = doc.insertBefore(
|
||||
document.createElement("script"),
|
||||
doc.lastChild
|
||||
);
|
||||
script.type = "text/javascript";
|
||||
script.src = src;
|
||||
}
|
||||
window.Polymer = {
|
||||
lazyRegister: true,
|
||||
useNativeCSSProperties: true,
|
||||
dom: "shadow",
|
||||
suppressTemplateNotifications: true,
|
||||
suppressBindingNotifications: true,
|
||||
};
|
||||
var webComponentsSupported =
|
||||
"customElements" in window &&
|
||||
"content" in document.createElement("template");
|
||||
if (!webComponentsSupported) {
|
||||
_ls("/static/polyfills/webcomponents-bundle.js");
|
||||
}
|
||||
var isS101 = /\s+Version\/10\.1(?:\.\d+)?\s+Safari\//.test(navigator.userAgent);
|
||||
</script>
|
|
@ -1,10 +1,21 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Home Assistant</title>
|
||||
<link rel='preload' href='/static/fonts/roboto/Roboto-Light.ttf' as='font' crossorigin />
|
||||
<link rel='preload' href='/static/fonts/roboto/Roboto-Regular.ttf' as='font' crossorigin />
|
||||
<%= require('raw-loader!./_header.html.template').default %>
|
||||
<link rel="preload" href="<%= latestPageJS %>" as="script" crossorigin />
|
||||
<link
|
||||
rel="preload"
|
||||
href="/static/fonts/roboto/Roboto-Light.ttf"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/static/fonts/roboto/Roboto-Regular.ttf"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<%= renderTemplate('_header') %>
|
||||
<style>
|
||||
.content {
|
||||
padding: 20px 16px;
|
||||
|
@ -27,29 +38,33 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class='header'>
|
||||
<img src="/static/icons/favicon-192x192.png" height="52">
|
||||
<div class="header">
|
||||
<img src="/static/icons/favicon-192x192.png" height="52" />
|
||||
Home Assistant
|
||||
</div>
|
||||
<ha-authorize><p>Initializing</p></ha-authorize>
|
||||
</div>
|
||||
<% if (!latestBuild) { %>
|
||||
<script src="/static/custom-elements-es5-adapter.js"></script>
|
||||
<script src="<%= compatibility %>"></script>
|
||||
<% } %>
|
||||
<script>
|
||||
window.providersPromise = fetch('/auth/providers', { credentials: 'same-origin' });
|
||||
|
||||
var webComponentsSupported = (
|
||||
'customElements' in window &&
|
||||
'content' in document.createElement('template'));
|
||||
if (!webComponentsSupported) {
|
||||
var e = document.createElement('script');
|
||||
e.src = '/static/webcomponents-bundle.js';
|
||||
document.write(e.outerHTML);
|
||||
}
|
||||
<%= renderTemplate('_js_base') %>
|
||||
|
||||
<script type="module">
|
||||
import "<%= latestPageJS %>";
|
||||
import "<%= latestHassIconsJS %>";
|
||||
window.providersPromise = fetch("/auth/providers", {
|
||||
credentials: "same-origin",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script nomodule>
|
||||
(function() {
|
||||
// Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
_ls("<%= es5Compatibility %>");
|
||||
_ls("<%= es5PageJS %>");
|
||||
_ls("<%= es5HassIconsJS %>");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="<%= entrypoint %>"></script>
|
||||
<script src='<%= hassIconsJS %>' async></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,70 +1,87 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel='preload' href='<%= coreJS %>' as='script'/>
|
||||
<link rel='preload' href='/static/fonts/roboto/Roboto-Regular.ttf' as='font' crossorigin />
|
||||
<link rel='preload' href='/static/fonts/roboto/Roboto-Medium.ttf' as='font' crossorigin />
|
||||
<%= require('raw-loader!./_header.html.template').default %>
|
||||
<link rel="preload" href="<%= latestCoreJS %>" as="script" crossorigin />
|
||||
<link
|
||||
rel="preload"
|
||||
href="/static/fonts/roboto/Roboto-Regular.ttf"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/static/fonts/roboto/Roboto-Medium.ttf"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<%= renderTemplate('_header') %>
|
||||
<title>Home Assistant</title>
|
||||
<link rel='apple-touch-icon' sizes='180x180'
|
||||
href='/static/icons/favicon-apple-180x180.png'>
|
||||
<link rel="mask-icon" href="/static/icons/mask-icon.svg" color="#03a9f4">
|
||||
<meta name="apple-itunes-app" content="app-id=1099568401">
|
||||
<meta name='apple-mobile-web-app-capable' content='yes'>
|
||||
<meta name="msapplication-square70x70logo" content="/static/icons/tile-win-70x70.png"/>
|
||||
<meta name="msapplication-square150x150logo" content="/static/icons/tile-win-150x150.png"/>
|
||||
<meta name="msapplication-wide310x150logo" content="/static/icons/tile-win-310x150.png"/>
|
||||
<meta name="msapplication-square310x310logo" content="/static/icons/tile-win-310x310.png"/>
|
||||
<meta name="msapplication-TileColor" content="#03a9f4ff"/>
|
||||
<meta name='mobile-web-app-capable' content='yes'>
|
||||
<meta name='referrer' content='same-origin'>
|
||||
<meta name='theme-color' content='{{ theme_color }}'>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/static/icons/favicon-apple-180x180.png"
|
||||
/>
|
||||
<link rel="mask-icon" href="/static/icons/mask-icon.svg" color="#03a9f4" />
|
||||
<meta name="apple-itunes-app" content="app-id=1099568401" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta
|
||||
name="msapplication-square70x70logo"
|
||||
content="/static/icons/tile-win-70x70.png"
|
||||
/>
|
||||
<meta
|
||||
name="msapplication-square150x150logo"
|
||||
content="/static/icons/tile-win-150x150.png"
|
||||
/>
|
||||
<meta
|
||||
name="msapplication-wide310x150logo"
|
||||
content="/static/icons/tile-win-310x150.png"
|
||||
/>
|
||||
<meta
|
||||
name="msapplication-square310x310logo"
|
||||
content="/static/icons/tile-win-310x310.png"
|
||||
/>
|
||||
<meta name="msapplication-TileColor" content="#03a9f4ff" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="referrer" content="same-origin" />
|
||||
<meta name="theme-color" content="#THEMEC" />
|
||||
<style>
|
||||
#ha-init-skeleton::before {
|
||||
display: block;
|
||||
content: "";
|
||||
height: 112px;
|
||||
background-color: {{ theme_color }};
|
||||
background-color: #THEMEC;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.customPanelJS = '<%= customPanelJS %>';
|
||||
window.noAuth = '{{ no_auth }}';
|
||||
window.useOAuth = '{{ use_oauth }}'
|
||||
window.Polymer = {
|
||||
lazyRegister: true,
|
||||
useNativeCSSProperties: true,
|
||||
dom: 'shadow',
|
||||
suppressTemplateNotifications: true,
|
||||
suppressBindingNotifications: true,
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='ha-init-skeleton'></div>
|
||||
<home-assistant>
|
||||
</home-assistant>
|
||||
<% if (!latestBuild) { %>
|
||||
<script src="/static/custom-elements-es5-adapter.js"></script>
|
||||
<script src="<%= compatibility %>"></script>
|
||||
<% } %>
|
||||
<script>
|
||||
var webComponentsSupported = (
|
||||
'customElements' in window &&
|
||||
'content' in document.createElement('template'));
|
||||
if (!webComponentsSupported) {
|
||||
(function() {
|
||||
var e = document.createElement('script');
|
||||
e.src = '/static/webcomponents-bundle.js';
|
||||
document.write(e.outerHTML);
|
||||
}());
|
||||
}
|
||||
<div id="ha-init-skeleton"></div>
|
||||
<home-assistant> </home-assistant>
|
||||
|
||||
<%= renderTemplate('_js_base') %>
|
||||
|
||||
<script type="module">
|
||||
import "<%= latestCoreJS %>";
|
||||
import "<%= latestAppJS %>";
|
||||
import "<%= latestHassIconsJS %>";
|
||||
window.customPanelJS = "<%= latestCustomPanelJS %>";
|
||||
</script>
|
||||
<script src='<%= coreJS %>'></script>
|
||||
<script src='<%= appJS %>'></script>
|
||||
<script src='<%= hassIconsJS %>' async></script>
|
||||
|
||||
<script nomodule>
|
||||
(function() {
|
||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
window.customPanelJS = "<%= es5CustomPanelJS %>";
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
_ls("<%= es5Compatibility %>");
|
||||
_ls("<%= es5CoreJS %>");
|
||||
_ls("<%= es5AppJS %>");
|
||||
_ls("<%= es5HassIconsJS %>");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
{% for extra_url in extra_urls -%}
|
||||
<link rel='import' href='{{ extra_url }}' async>
|
||||
<link rel="import" href="{{ extra_url }}" async />
|
||||
{% endfor -%}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Home Assistant</title>
|
||||
<link rel='preload' href='/static/fonts/roboto/Roboto-Light.ttf' as='font' crossorigin />
|
||||
<link rel='preload' href='/static/fonts/roboto/Roboto-Regular.ttf' as='font' crossorigin />
|
||||
<%= require('raw-loader!./_header.html.template').default %>
|
||||
<link rel="preload" href="<%= latestPageJS %>" as="script" crossorigin />
|
||||
<link
|
||||
rel="preload"
|
||||
href="/static/fonts/roboto/Roboto-Light.ttf"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/static/fonts/roboto/Roboto-Regular.ttf"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<%= renderTemplate('_header') %>
|
||||
<style>
|
||||
.content {
|
||||
padding: 20px 16px;
|
||||
|
@ -28,30 +39,34 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class='header'>
|
||||
<img src="/static/icons/favicon-192x192.png" height="52">
|
||||
<div class="header">
|
||||
<img src="/static/icons/favicon-192x192.png" height="52" />
|
||||
Home Assistant
|
||||
</div>
|
||||
|
||||
<ha-onboarding>Initializing</ha-onboarding>
|
||||
</div>
|
||||
<% if (!latestBuild) { %>
|
||||
<script src="/static/custom-elements-es5-adapter.js"></script>
|
||||
<script src="<%= compatibility %>"></script>
|
||||
<% } %>
|
||||
<script>
|
||||
window.stepsPromise = fetch('/api/onboarding', { credentials: 'same-origin' });
|
||||
|
||||
var webComponentsSupported = (
|
||||
'customElements' in window &&
|
||||
'content' in document.createElement('template'));
|
||||
if (!webComponentsSupported) {
|
||||
var e = document.createElement('script');
|
||||
e.src = '/static/webcomponents-bundle.js';
|
||||
document.write(e.outerHTML);
|
||||
}
|
||||
<%= renderTemplate('_js_base') %>
|
||||
|
||||
<script type="module">
|
||||
import "<%= latestPageJS %>";
|
||||
import "<%= latestHassIconsJS %>";
|
||||
window.stepsPromise = fetch("/api/onboarding", {
|
||||
credentials: "same-origin",
|
||||
});
|
||||
</script>
|
||||
|
||||
<script nomodule>
|
||||
(function() {
|
||||
// Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
_ls("<%= es5Compatibility %>");
|
||||
_ls("<%= es5PageJS %>");
|
||||
_ls("<%= es5HassIconsJS %>");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="<%= entrypoint %>"></script>
|
||||
<script src='<%= hassIconsJS %>' async></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
const serviceWorkerUrl =
|
||||
__BUILD__ === "latest" ? "/service_worker.js" : "/service_worker_es5.js";
|
||||
|
||||
export const registerServiceWorker = (notifyUpdate = true) => {
|
||||
if (
|
||||
!("serviceWorker" in navigator) ||
|
||||
|
@ -9,7 +6,7 @@ export const registerServiceWorker = (notifyUpdate = true) => {
|
|||
return;
|
||||
}
|
||||
|
||||
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
|
||||
navigator.serviceWorker.register("/service_worker.js").then((reg) => {
|
||||
reg.addEventListener("updatefound", () => {
|
||||
const installingWorker = reg.installing;
|
||||
if (!installingWorker || !notifyUpdate) {
|
||||
|
|
|
@ -1,207 +1,24 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const WorkboxPlugin = require("workbox-webpack-plugin");
|
||||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const zopfli = require("@gfx/zopfli");
|
||||
const translationMetadata = require("./build-translations/translationMetadata.json");
|
||||
const { babelLoaderConfig } = require("./config/babel.js");
|
||||
const webpackBase = require("./config/webpack.js");
|
||||
|
||||
const version = fs.readFileSync("setup.py", "utf8").match(/\d{8}\.\d+/);
|
||||
if (!version) {
|
||||
throw Error("Version not found");
|
||||
}
|
||||
const VERSION = version[0];
|
||||
const isCI = process.env.CI === "true";
|
||||
const isStatsBuild = process.env.STATS === "1";
|
||||
|
||||
const generateJSPage = (entrypoint, latestBuild) => {
|
||||
return new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
template: `./src/html/${entrypoint}.html.template`,
|
||||
// Default templateParameterGenerator code
|
||||
// https://github.com/jantimon/html-webpack-plugin/blob/master/index.js#L719
|
||||
templateParameters: (compilation, assets, option) => ({
|
||||
latestBuild,
|
||||
compatibility: assets.chunks.compatibility.entry,
|
||||
entrypoint: assets.chunks[entrypoint].entry,
|
||||
hassIconsJS: assets.chunks["hass-icons"].entry,
|
||||
}),
|
||||
filename: `${entrypoint}.html`,
|
||||
});
|
||||
};
|
||||
|
||||
// Create an object mapping browser urls to their paths during build
|
||||
const workBoxTranslationsTemplatedURLs = {};
|
||||
const englishFP = translationMetadata["translations"]["en"]["fingerprints"];
|
||||
Object.keys(englishFP).forEach((key) => {
|
||||
workBoxTranslationsTemplatedURLs[
|
||||
`/static/translations/${englishFP[key]}`
|
||||
] = `build-translations/output/${key}.json`;
|
||||
});
|
||||
|
||||
function createConfig(isProdBuild, latestBuild) {
|
||||
const buildPath = latestBuild ? "hass_frontend/" : "hass_frontend_es5/";
|
||||
const publicPath = latestBuild ? "/frontend_latest/" : "/frontend_es5/";
|
||||
|
||||
const entry = {
|
||||
app: "./src/entrypoints/app.ts",
|
||||
authorize: "./src/entrypoints/authorize.ts",
|
||||
onboarding: "./src/entrypoints/onboarding.ts",
|
||||
core: "./src/entrypoints/core.ts",
|
||||
compatibility: "./src/entrypoints/compatibility.ts",
|
||||
"custom-panel": "./src/entrypoints/custom-panel.ts",
|
||||
"hass-icons": "./src/entrypoints/hass-icons.ts",
|
||||
};
|
||||
|
||||
if (latestBuild) {
|
||||
entry["service-worker-hass"] = "./src/entrypoints/service-worker-hass.js";
|
||||
}
|
||||
|
||||
return {
|
||||
mode: isProdBuild ? "production" : "development",
|
||||
devtool: isProdBuild
|
||||
? "cheap-source-map "
|
||||
: "inline-cheap-module-source-map",
|
||||
entry,
|
||||
module: {
|
||||
rules: [
|
||||
babelLoaderConfig({ latestBuild }),
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: "raw-loader",
|
||||
},
|
||||
{
|
||||
test: /\.(html)$/,
|
||||
use: {
|
||||
loader: "html-loader",
|
||||
options: {
|
||||
exportAsEs6Default: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: webpackBase.optimization(latestBuild),
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
__DEV__: JSON.stringify(!isProdBuild),
|
||||
__DEMO__: false,
|
||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||
__VERSION__: JSON.stringify(VERSION),
|
||||
__STATIC_PATH__: "/static/",
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProdBuild ? "production" : "development"
|
||||
),
|
||||
}),
|
||||
new CopyWebpackPlugin(
|
||||
[
|
||||
latestBuild &&
|
||||
"node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js",
|
||||
latestBuild && { from: "public", to: "." },
|
||||
latestBuild && {
|
||||
from: "build-translations/output",
|
||||
to: `translations`,
|
||||
},
|
||||
latestBuild && {
|
||||
from: "node_modules/@polymer/font-roboto-local/fonts",
|
||||
to: "fonts",
|
||||
},
|
||||
latestBuild &&
|
||||
"node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js",
|
||||
latestBuild &&
|
||||
"node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js.map",
|
||||
latestBuild && {
|
||||
from:
|
||||
"node_modules/react-big-calendar/lib/css/react-big-calendar.css",
|
||||
to: "panels/calendar/",
|
||||
},
|
||||
latestBuild && {
|
||||
from: "node_modules/leaflet/dist/leaflet.css",
|
||||
to: `images/leaflet/`,
|
||||
},
|
||||
latestBuild && {
|
||||
from: "node_modules/leaflet/dist/images",
|
||||
to: `images/leaflet/`,
|
||||
},
|
||||
!latestBuild && "public/__init__.py",
|
||||
].filter(Boolean)
|
||||
),
|
||||
...webpackBase.plugins,
|
||||
isProdBuild &&
|
||||
!isCI &&
|
||||
!isStatsBuild &&
|
||||
new CompressionPlugin({
|
||||
cache: true,
|
||||
exclude: [/\.js\.map$/, /\.LICENSE$/, /\.py$/, /\.txt$/],
|
||||
algorithm(input, compressionOptions, callback) {
|
||||
return zopfli.gzip(input, compressionOptions, callback);
|
||||
},
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
template: "./src/html/index.html.template",
|
||||
// Default templateParameterGenerator code
|
||||
// https://github.com/jantimon/html-webpack-plugin/blob/master/index.js#L719
|
||||
templateParameters: (compilation, assets, option) => ({
|
||||
latestBuild,
|
||||
compatibility: assets.chunks.compatibility.entry,
|
||||
appJS: assets.chunks.app.entry,
|
||||
coreJS: assets.chunks.core.entry,
|
||||
customPanelJS: assets.chunks["custom-panel"].entry,
|
||||
hassIconsJS: assets.chunks["hass-icons"].entry,
|
||||
}),
|
||||
filename: `index.html`,
|
||||
}),
|
||||
generateJSPage("onboarding", latestBuild),
|
||||
generateJSPage("authorize", latestBuild),
|
||||
new WorkboxPlugin.InjectManifest({
|
||||
swSrc: "./src/entrypoints/service-worker-bootstrap.js",
|
||||
swDest: "service_worker.js",
|
||||
importWorkboxFrom: "local",
|
||||
include: [/\.js$/],
|
||||
templatedURLs: {
|
||||
...workBoxTranslationsTemplatedURLs,
|
||||
"/static/icons/favicon-192x192.png":
|
||||
"public/icons/favicon-192x192.png",
|
||||
"/static/fonts/roboto/Roboto-Light.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Light.ttf",
|
||||
"/static/fonts/roboto/Roboto-Medium.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Medium.ttf",
|
||||
"/static/fonts/roboto/Roboto-Regular.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Regular.ttf",
|
||||
"/static/fonts/roboto/Roboto-Bold.ttf":
|
||||
"node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Bold.ttf",
|
||||
},
|
||||
}),
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
const dontHash = new Set([
|
||||
// This is loaded from service-worker-bootstrap.js
|
||||
// which is processed by Workbox, not Webpack
|
||||
"service-worker-hass",
|
||||
]);
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) return `${chunk.name}.js`;
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
path: path.resolve(__dirname, buildPath),
|
||||
publicPath,
|
||||
},
|
||||
resolve: webpackBase.resolve,
|
||||
};
|
||||
}
|
||||
const { createAppConfig } = require("./build-scripts/webpack.js");
|
||||
|
||||
const isProdBuild = process.env.NODE_ENV === "production";
|
||||
const configs = [createConfig(isProdBuild, /* latestBuild */ true)];
|
||||
const isStatsBuild = process.env.STATS === "1";
|
||||
|
||||
const configs = [
|
||||
createAppConfig({
|
||||
isProdBuild,
|
||||
isStatsBuild,
|
||||
latestBuild: true,
|
||||
}),
|
||||
];
|
||||
// const configs = [createConfig(isProdBuild, /* latestBuild */ true)];
|
||||
if (isProdBuild && !isStatsBuild) {
|
||||
configs.push(createConfig(isProdBuild, /* latestBuild */ false));
|
||||
configs.push(
|
||||
createAppConfig({
|
||||
isProdBuild,
|
||||
isStatsBuild,
|
||||
latestBuild: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = configs;
|
||||
|
|
69
yarn.lock
|
@ -719,7 +719,7 @@
|
|||
lodash "^4.17.11"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@gfx/zopfli@^1.0.11":
|
||||
"@gfx/zopfli@^1.0.11", "@gfx/zopfli@^1.0.9":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@gfx/zopfli/-/zopfli-1.0.11.tgz#6ced06b4566a5feb0036fe6a1e0262ce6cb1d6c5"
|
||||
integrity sha512-wW+hi+bqdYCpBIvL8g7RYub9YXf5crhZK9SNk/VZmbF177Em1VwFv488qyh8iBpCo6GptcP1Zam0bJfY3VmMbg==
|
||||
|
@ -2599,7 +2599,7 @@ any-observable@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
|
||||
integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
|
||||
|
||||
any-promise@^1.0.0:
|
||||
any-promise@^1.0.0, any-promise@^1.1.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
|
||||
|
@ -3749,6 +3749,11 @@ bytes@3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
|
||||
|
||||
bytes@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
|
||||
|
||||
cacache@^11.0.2, cacache@^11.2.0, cacache@^11.3.1:
|
||||
version "11.3.2"
|
||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa"
|
||||
|
@ -4157,7 +4162,7 @@ clone-stats@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680"
|
||||
integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=
|
||||
|
||||
clone@^1.0.0:
|
||||
clone@^1.0.0, clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
|
||||
|
@ -4901,6 +4906,13 @@ default-resolution@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684"
|
||||
integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=
|
||||
|
||||
defaults@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
|
||||
integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
|
||||
dependencies:
|
||||
clone "^1.0.2"
|
||||
|
||||
define-properties@^1.1.2, define-properties@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
|
||||
|
@ -6393,7 +6405,7 @@ fresh@0.5.2:
|
|||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
|
||||
from2@^2.1.0:
|
||||
from2@^2.1.0, from2@^2.1.1:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
|
||||
integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
|
||||
|
@ -6420,6 +6432,15 @@ fs-extra@^4.0.2:
|
|||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^7.0.0, fs-extra@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-minipass@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
|
||||
|
@ -7001,6 +7022,20 @@ gulp-util@~2.2.14:
|
|||
through2 "^0.5.0"
|
||||
vinyl "^0.2.1"
|
||||
|
||||
gulp-zopfli-green@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gulp-zopfli-green/-/gulp-zopfli-green-3.0.1.tgz#5a7dc7b2eb5e33eacaf7fb5a0e5434f15e893d9b"
|
||||
integrity sha512-/ZLtK7VXe8sRpMiVMpu1VtGK/tUEA/B7QPUC0/6leMgsijfMaNelcmcerXTex0+F9SozVPUt+kjK/hU88QtvXA==
|
||||
dependencies:
|
||||
"@gfx/zopfli" "^1.0.9"
|
||||
bytes "^3.0.0"
|
||||
defaults "^1.0.2"
|
||||
fancy-log "^1.3.2"
|
||||
into-stream "^4.0.0"
|
||||
plugin-error "^1.0.1"
|
||||
stream-to-array "^2.0.2"
|
||||
through2 "^3.0.0"
|
||||
|
||||
gulp@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.0.tgz#95766c601dade4a77ed3e7b2b6dc03881b596366"
|
||||
|
@ -7566,6 +7601,14 @@ intl-messageformat@^2.2.0:
|
|||
dependencies:
|
||||
intl-messageformat-parser "1.4.0"
|
||||
|
||||
into-stream@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-4.0.0.tgz#ef10ee2ffb6f78af34c93194bbdc36c35f7d8a9d"
|
||||
integrity sha512-i29KNyE5r0Y/UQzcQ0IbZO1MYJ53Jn0EcFRZPj5FzWKYH17kDFEOwuA+3jroymOI06SW1dEDnly9A1CAreC5dg==
|
||||
dependencies:
|
||||
from2 "^2.1.1"
|
||||
p-is-promise "^2.0.0"
|
||||
|
||||
invariant@^2.2.0, invariant@^2.2.2, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
|
@ -8885,7 +8928,7 @@ lodash.values@~2.4.1:
|
|||
dependencies:
|
||||
lodash.keys "~2.4.1"
|
||||
|
||||
lodash@4.17.11, lodash@^4.11.1, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0:
|
||||
lodash@4.17.11, "lodash@>=3.5 <5", lodash@^4.11.1, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
@ -12389,6 +12432,13 @@ stream-shift@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
||||
integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
|
||||
|
||||
stream-to-array@^2.0.2:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353"
|
||||
integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=
|
||||
dependencies:
|
||||
any-promise "^1.1.0"
|
||||
|
||||
stream@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef"
|
||||
|
@ -13908,6 +13958,15 @@ webpack-log@^2.0.0:
|
|||
ansi-colors "^3.0.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
webpack-manifest-plugin@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.0.4.tgz#e4ca2999b09557716b8ba4475fb79fab5986f0cd"
|
||||
integrity sha512-nejhOHexXDBKQOj/5v5IZSfCeTO3x1Dt1RZEcGfBSul891X/eLIcIVH31gwxPDdsi2Z8LKKFGpM4w9+oTBOSCg==
|
||||
dependencies:
|
||||
fs-extra "^7.0.0"
|
||||
lodash ">=3.5 <5"
|
||||
tapable "^1.0.0"
|
||||
|
||||
webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
|
||||
|
|