diff --git a/build-scripts/gulp/release.js b/build-scripts/gulp/app.js similarity index 54% rename from build-scripts/gulp/release.js rename to build-scripts/gulp/app.js index f7e26b45ca..0563fc0e9e 100644 --- a/build-scripts/gulp/release.js +++ b/build-scripts/gulp/app.js @@ -10,18 +10,34 @@ require("./service-worker.js"); require("./entry-html.js"); gulp.task( - "build-release", + "develop-app", + gulp.series( + async function setEnv() { + process.env.NODE_ENV = "development"; + }, + "clean", + gulp.parallel( + "gen-service-worker-dev", + "gen-icons", + "gen-pages-dev", + "gen-index-html-dev", + "build-translations" + ), + "copy-static", + "webpack-watch-app" + ) +); + +gulp.task( + "build-app", 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-icons", "build-translations"), + "copy-static", + gulp.parallel("webpack-prod-app", "compress-static"), gulp.parallel( "gen-pages-prod", "gen-index-html-prod", diff --git a/build-scripts/gulp/clean.js b/build-scripts/gulp/clean.js index d3eaed526a..d1226c198e 100644 --- a/build-scripts/gulp/clean.js +++ b/build-scripts/gulp/clean.js @@ -3,3 +3,4 @@ const gulp = require("gulp"); const config = require("../paths"); gulp.task("clean", () => del([config.root, config.build_dir])); +gulp.task("clean-demo", () => del([config.demo_root, config.build_dir])); diff --git a/build-scripts/gulp/demo.js b/build-scripts/gulp/demo.js new file mode 100644 index 0000000000..cbe23e326c --- /dev/null +++ b/build-scripts/gulp/demo.js @@ -0,0 +1,36 @@ +// 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-demo", + gulp.series( + async function setEnv() { + process.env.NODE_ENV = "development"; + }, + "clean-demo", + gulp.parallel("gen-icons", "gen-icons-demo", "build-translations"), + "copy-static-demo", + "webpack-dev-server-demo" + ) +); + +gulp.task( + "build-demo", + gulp.series( + async function setEnv() { + process.env.NODE_ENV = "production"; + }, + "clean-demo", + gulp.parallel("gen-icons", "gen-icons-demo", "build-translations"), + "copy-static-demo", + "webpack-prod-demo" + ) +); diff --git a/build-scripts/gulp/develop.js b/build-scripts/gulp/develop.js deleted file mode 100644 index 2dbcbaac45..0000000000 --- a/build-scripts/gulp/develop.js +++ /dev/null @@ -1,29 +0,0 @@ -// 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" - ) -); diff --git a/build-scripts/gulp/gather-static.js b/build-scripts/gulp/gather-static.js index 733c0e876c..8eb98bef36 100644 --- a/build-scripts/gulp/gather-static.js +++ b/build-scripts/gulp/gather-static.js @@ -5,17 +5,21 @@ 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 paths = 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); + path.resolve(paths.polymer_dir, "node_modules", ...parts); +const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts); const copyFileDir = (fromFile, toDir) => fs.copySync(fromFile, path.join(toDir, path.basename(fromFile))); -function copyTranslations() { +const genStaticPath = (staticDir) => (...parts) => + path.resolve(staticDir, ...parts); + +function copyTranslations(staticDir) { + const staticPath = genStaticPath(staticDir); + // Translation output fs.copySync( polyPath("build-translations/output"), @@ -23,9 +27,8 @@ function copyTranslations() { ); } -function copyStatic() { - // Basic static files - fs.copySync(polyPath("public"), config.root); +function copyPolyfills(staticDir) { + const staticPath = genStaticPath(staticDir); // Web Component polyfills and adapters copyFileDir( @@ -40,31 +43,16 @@ function copyStatic() { 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(); -}); +function copyFonts(staticDir) { + const staticPath = genStaticPath(staticDir); + // Local fonts + fs.copySync(npmPath("@polymer/font-roboto-local/fonts"), staticPath("fonts")); +} -gulp.task("compress-static", () => { +function compressStatic(staticDir) { + const staticPath = genStaticPath(staticDir); const fonts = gulp .src(staticPath("fonts/**/*.ttf")) .pipe(zopfli()) @@ -79,9 +67,44 @@ gulp.task("compress-static", () => { .pipe(gulp.dest(staticPath("translations"))); return merge(fonts, polyfills, translations); -}); +} -gulp.task("copy-translations", (done) => { - copyTranslations(); +gulp.task("copy-static", (done) => { + const staticDir = paths.static; + const staticPath = genStaticPath(paths.static); + // Basic static files + fs.copySync(polyPath("public"), paths.root); + + copyPolyfills(staticDir); + copyFonts(staticDir); + copyTranslations(staticDir); + + // Panel 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/") + ); + done(); +}); + +gulp.task("compress-static", () => compressStatic(paths.root)); + +gulp.task("copy-static-demo", (done) => { + // Copy app static files + fs.copySync(polyPath("public"), paths.demo_root); + // Copy demo static files + fs.copySync(path.resolve(paths.demo_dir, "public"), paths.demo_root); + + copyPolyfills(paths.demo_static); + copyFonts(paths.demo_static); + copyTranslations(paths.demo_static); done(); }); diff --git a/build-scripts/gulp/gen-icons.js b/build-scripts/gulp/gen-icons.js index 6563b9a8f1..bfa7d7cc77 100644 --- a/build-scripts/gulp/gen-icons.js +++ b/build-scripts/gulp/gen-icons.js @@ -1,6 +1,7 @@ const gulp = require("gulp"); const path = require("path"); const fs = require("fs"); +const paths = require("../paths"); const ICON_PACKAGE_PATH = path.resolve( __dirname, @@ -118,6 +119,15 @@ gulp.task("gen-icons-hass", (done) => { }); gulp.task("gen-icons", gulp.series("gen-icons-hass", "gen-icons-mdi")); +gulp.task("gen-icons-demo", (done) => { + const iconNames = findIcons(path.resolve(paths.demo_dir, "./src"), "hademo"); + fs.writeFileSync( + path.resolve(paths.demo_dir, "hademo-icons.html"), + generateIconset("hademo", iconNames) + ); + done(); +}); + module.exports = { findIcons, generateIconset, diff --git a/build-scripts/gulp/webpack.js b/build-scripts/gulp/webpack.js index 6ba6fa1e4a..2a21a14011 100644 --- a/build-scripts/gulp/webpack.js +++ b/build-scripts/gulp/webpack.js @@ -1,7 +1,11 @@ // Tasks to run webpack. const gulp = require("gulp"); +const path = require("path"); const webpack = require("webpack"); -const { createAppConfig } = require("../webpack"); +const WebpackDevServer = require("webpack-dev-server"); +const log = require("fancy-log"); +const paths = require("../paths"); +const { createAppConfig, createDemoConfig } = require("../webpack"); const handler = (done) => (err, stats) => { if (err) { @@ -12,7 +16,7 @@ const handler = (done) => (err, stats) => { return; } - console.log(`Build done @ ${new Date().toLocaleTimeString()}`); + log(`Build done @ ${new Date().toLocaleTimeString()}`); if (stats.hasErrors() || stats.hasWarnings()) { console.log(stats.toString("minimal")); @@ -23,7 +27,7 @@ const handler = (done) => (err, stats) => { } }; -gulp.task("webpack-watch", () => { +gulp.task("webpack-watch-app", () => { const compiler = webpack([ createAppConfig({ isProdBuild: false, @@ -41,7 +45,7 @@ gulp.task("webpack-watch", () => { }); gulp.task( - "webpack-prod", + "webpack-prod-app", () => new Promise((resolve) => webpack( @@ -61,3 +65,52 @@ gulp.task( ) ) ); + +gulp.task("webpack-dev-server-demo", () => { + const compiler = webpack([ + createDemoConfig({ + isProdBuild: false, + latestBuild: false, + isStatsBuild: false, + }), + createDemoConfig({ + isProdBuild: false, + latestBuild: true, + isStatsBuild: false, + }), + ]); + + new WebpackDevServer(compiler, { + open: true, + watchContentBase: true, + contentBase: path.resolve(paths.demo_dir, "dist"), + }).listen(8080, "localhost", function(err) { + if (err) { + throw err; + } + // Server listening + log("[webpack-dev-server]", "http://localhost:8080"); + }); +}); + +gulp.task( + "webpack-prod-demo", + () => + new Promise((resolve) => + webpack( + [ + createDemoConfig({ + isProdBuild: true, + latestBuild: false, + isStatsBuild: false, + }), + createDemoConfig({ + isProdBuild: true, + latestBuild: true, + isStatsBuild: false, + }), + ], + handler(resolve) + ) + ) +); diff --git a/build-scripts/paths.js b/build-scripts/paths.js index 28d553717b..f078c80da9 100644 --- a/build-scripts/paths.js +++ b/build-scripts/paths.js @@ -2,9 +2,16 @@ 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"), + + demo_dir: path.resolve(__dirname, "../demo"), + demo_root: path.resolve(__dirname, "../demo/dist"), + demo_static: path.resolve(__dirname, "../demo/dist/static"), + demo_output: path.resolve(__dirname, "../demo/dist/frontend_latest"), + demo_output_es5: path.resolve(__dirname, "../demo/frontend_es5"), }; diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index 1a48454661..a29e6ec85a 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -17,6 +17,12 @@ if (!version) { } version = version[0]; +const genMode = (isProdBuild) => (isProdBuild ? "production" : "development"); +const genDevTool = (isProdBuild) => + isProdBuild ? "cheap-source-map" : "inline-cheap-module-source-map"; +const genChunkFilename = (isProdBuild, isStatsBuild) => + isProdBuild && !isStatsBuild ? "chunk.[chunkhash].js" : "[name].chunk.js"; + const resolve = { extensions: [".ts", ".js", ".json", ".tsx"], alias: { @@ -29,6 +35,20 @@ const resolve = { }, }; +const cssLoader = { + test: /\.css$/, + use: "raw-loader", +}; +const htmlLoader = { + test: /\.(html)$/, + use: { + loader: "html-loader", + options: { + exportAsEs6Default: true, + }, + }, +}; + const plugins = [ // Ignore moment.js locales new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), @@ -75,8 +95,6 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { ] = `build-translations/output/${key}.json`; }); - const publicPath = latestBuild ? "/frontend_latest/" : "/frontend_es5/"; - const entry = { app: "./src/entrypoints/app.ts", authorize: "./src/entrypoints/authorize.ts", @@ -88,28 +106,11 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { }; return { - mode: isProdBuild ? "production" : "development", - devtool: isProdBuild - ? "cheap-source-map " - : "inline-cheap-module-source-map", + mode: genMode(isProdBuild), + devtool: genDevTool(isProdBuild), entry, module: { - rules: [ - babelLoaderConfig({ latestBuild }), - { - test: /\.css$/, - use: "raw-loader", - }, - { - test: /\.(html)$/, - use: { - loader: "html-loader", - options: { - exportAsEs6Default: true, - }, - }, - }, - ], + rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader], }, optimization: optimization(latestBuild), plugins: [ @@ -165,20 +166,56 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { 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", + chunkFilename: genChunkFilename(isProdBuild, isStatsBuild), path: latestBuild ? paths.output : paths.output_es5, - publicPath, + publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/", }, resolve, }; }; +const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { + return { + mode: genMode(isProdBuild), + devtool: genDevTool(isProdBuild), + entry: { + main: "./demo/src/entrypoint.ts", + compatibility: "./src/entrypoints/compatibility.ts", + }, + module: { + rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader], + }, + optimization: optimization(latestBuild), + plugins: [ + new webpack.DefinePlugin({ + __DEV__: !isProdBuild, + __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"), + __VERSION__: JSON.stringify("DEMO"), + __DEMO__: true, + __STATIC_PATH__: "/static/", + "process.env.NODE_ENV": JSON.stringify( + isProdBuild ? "production" : "development" + ), + }), + ...plugins, + ].filter(Boolean), + resolve, + output: { + filename: "[name].js", + chunkFilename: genChunkFilename(isProdBuild, isStatsBuild), + path: path.resolve( + paths.demo_root, + latestBuild ? "frontend_latest" : "frontend_es5" + ), + publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/", + }, + }; +}; + module.exports = { resolve, plugins, optimization, createAppConfig, + createDemoConfig, }; diff --git a/demo/public/index.html b/demo/public/index.html index 5a9e3e49a7..4878fe8d43 100644 --- a/demo/public/index.html +++ b/demo/public/index.html @@ -74,9 +74,6 @@ content="https://www.home-assistant.io/images/default-social.png" /> Home Assistant Demo - - -