Refactor Webpack build scripts (#4093)

* Refactor Webpack build scripts

* Add Gallery too

* Fix icons

* Update travis
pull/4098/head
Paulus Schoutsen 2019-10-21 15:02:54 -07:00 committed by GitHub
parent 0621218e16
commit 70d6c6b902
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 626 additions and 532 deletions

View File

@ -8,19 +8,20 @@ install: yarn install
script: script:
- npm run build - npm run build
- hassio/script/build_hassio - hassio/script/build_hassio
# Because else eslint fails because hassio has cleaned that build
- ./node_modules/.bin/gulp gen-icons-app
- npm run test - npm run test
# - xvfb-run wct --module-resolution=node --npm # - xvfb-run wct --module-resolution=node --npm
# - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct --module-resolution=node --npm --plugin sauce; fi' # - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct --module-resolution=node --npm --plugin sauce; fi'
services: services:
- docker - docker
before_deploy: before_deploy:
- 'docker pull lokalise/lokalise-cli@sha256:2198814ebddfda56ee041a4b427521757dd57f75415ea9693696a64c550cef21' - "docker pull lokalise/lokalise-cli@sha256:2198814ebddfda56ee041a4b427521757dd57f75415ea9693696a64c550cef21"
deploy: deploy:
provider: script provider: script
script: script/travis_deploy script: script/travis_deploy
'on': "on":
branch: master branch: master
dist: trusty dist: trusty
addons: addons:
sauce_connect: true sauce_connect: true

6
build-scripts/env.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
isProdBuild: process.env.NODE_ENV === "production",
isStatsBuild: process.env.STATS === "1",
isTravis: process.env.TRAVIS === "true",
isNetlify: process.env.NETLIFY === "true",
};

View File

@ -1,6 +1,8 @@
// Run HA develop mode // Run HA develop mode
const gulp = require("gulp"); const gulp = require("gulp");
const envVars = require("../env");
require("./clean.js"); require("./clean.js");
require("./translations.js"); require("./translations.js");
require("./gen-icons.js"); require("./gen-icons.js");
@ -19,7 +21,7 @@ gulp.task(
"clean", "clean",
gulp.parallel( gulp.parallel(
"gen-service-worker-dev", "gen-service-worker-dev",
"gen-icons", gulp.parallel("gen-icons-app", "gen-icons-mdi"),
"gen-pages-dev", "gen-pages-dev",
"gen-index-app-dev", "gen-index-app-dev",
gulp.series("create-test-translation", "build-translations") gulp.series("create-test-translation", "build-translations")
@ -36,10 +38,11 @@ gulp.task(
process.env.NODE_ENV = "production"; process.env.NODE_ENV = "production";
}, },
"clean", "clean",
gulp.parallel("gen-icons", "build-translations"), gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
"copy-static", "copy-static",
"webpack-prod-app", "webpack-prod-app",
...(process.env.CI === "true" ? [] : ["compress-app"]), ...// Don't compress running tests
(envVars.isTravis ? [] : ["compress-app"]),
gulp.parallel( gulp.parallel(
"gen-pages-prod", "gen-pages-prod",
"gen-index-app-prod", "gen-index-app-prod",

View File

@ -1,4 +1,3 @@
// Run cast develop mode
const gulp = require("gulp"); const gulp = require("gulp");
require("./clean.js"); require("./clean.js");
@ -16,7 +15,12 @@ gulp.task(
process.env.NODE_ENV = "development"; process.env.NODE_ENV = "development";
}, },
"clean-cast", "clean-cast",
gulp.parallel("gen-icons", "gen-index-cast-dev", "build-translations"), gulp.parallel(
"gen-icons-app",
"gen-icons-mdi",
"gen-index-cast-dev",
"build-translations"
),
"copy-static-cast", "copy-static-cast",
"webpack-dev-server-cast" "webpack-dev-server-cast"
) )
@ -29,7 +33,7 @@ gulp.task(
process.env.NODE_ENV = "production"; process.env.NODE_ENV = "production";
}, },
"clean-cast", "clean-cast",
gulp.parallel("gen-icons", "build-translations"), gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
"copy-static-cast", "copy-static-cast",
"webpack-prod-cast", "webpack-prod-cast",
"gen-index-cast-prod" "gen-index-cast-prod"

View File

@ -9,15 +9,31 @@ gulp.task(
return del([config.root, config.build_dir]); return del([config.root, config.build_dir]);
}) })
); );
gulp.task( gulp.task(
"clean-demo", "clean-demo",
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() { gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
return del([config.demo_root, config.build_dir]); return del([config.demo_root, config.build_dir]);
}) })
); );
gulp.task( gulp.task(
"clean-cast", "clean-cast",
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() { gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
return del([config.cast_root, config.build_dir]); return del([config.cast_root, config.build_dir]);
}) })
); );
gulp.task(
"clean-hassio",
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
return del([config.hassio_root, config.build_dir]);
})
);
gulp.task(
"clean-gallery",
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
return del([config.gallery_root, config.build_dir]);
})
);

View File

@ -29,3 +29,10 @@ gulp.task("compress-app", function compressApp() {
return merge(jsLatest, jsEs5, polyfills, translations); return merge(jsLatest, jsEs5, polyfills, translations);
}); });
gulp.task("compress-hassio", function compressApp() {
return gulp
.src(path.resolve(paths.hassio_root, "**/*.js"))
.pipe(zopfli())
.pipe(gulp.dest(paths.hassio_root));
});

View File

@ -17,7 +17,8 @@ gulp.task(
}, },
"clean-demo", "clean-demo",
gulp.parallel( gulp.parallel(
"gen-icons", "gen-icons-app",
"gen-icons-mdi",
"gen-icons-demo", "gen-icons-demo",
"gen-index-demo-dev", "gen-index-demo-dev",
"build-translations" "build-translations"
@ -34,7 +35,12 @@ gulp.task(
process.env.NODE_ENV = "production"; process.env.NODE_ENV = "production";
}, },
"clean-demo", "clean-demo",
gulp.parallel("gen-icons", "gen-icons-demo", "build-translations"), gulp.parallel(
"gen-icons-app",
"gen-icons-mdi",
"gen-icons-demo",
"build-translations"
),
"copy-static-demo", "copy-static-demo",
"webpack-prod-demo", "webpack-prod-demo",
"gen-index-demo-prod" "gen-index-demo-prod"

View File

@ -11,12 +11,6 @@ const config = require("../paths.js");
const templatePath = (tpl) => const templatePath = (tpl) =>
path.resolve(config.polymer_dir, "src/html/", `${tpl}.html.template`); path.resolve(config.polymer_dir, "src/html/", `${tpl}.html.template`);
const demoTemplatePath = (tpl) =>
path.resolve(config.demo_dir, "src/html/", `${tpl}.html.template`);
const castTemplatePath = (tpl) =>
path.resolve(config.cast_dir, "src/html/", `${tpl}.html.template`);
const readFile = (pth) => fs.readFileSync(pth).toString(); const readFile = (pth) => fs.readFileSync(pth).toString();
const renderTemplate = (pth, data = {}, pathFunc = templatePath) => { const renderTemplate = (pth, data = {}, pathFunc = templatePath) => {
@ -25,10 +19,19 @@ const renderTemplate = (pth, data = {}, pathFunc = templatePath) => {
}; };
const renderDemoTemplate = (pth, data = {}) => const renderDemoTemplate = (pth, data = {}) =>
renderTemplate(pth, data, demoTemplatePath); renderTemplate(pth, data, (tpl) =>
path.resolve(config.demo_dir, "src/html/", `${tpl}.html.template`)
);
const renderCastTemplate = (pth, data = {}) => const renderCastTemplate = (pth, data = {}) =>
renderTemplate(pth, data, castTemplatePath); renderTemplate(pth, data, (tpl) =>
path.resolve(config.cast_dir, "src/html/", `${tpl}.html.template`)
);
const renderGalleryTemplate = (pth, data = {}) =>
renderTemplate(pth, data, (tpl) =>
path.resolve(config.gallery_dir, "src/html/", `${tpl}.html.template`)
);
const minifyHtml = (content) => const minifyHtml = (content) =>
minify(content, { minify(content, {
@ -209,8 +212,33 @@ gulp.task("gen-index-demo-prod", (done) => {
es5Compatibility: es5Manifest["compatibility.js"], es5Compatibility: es5Manifest["compatibility.js"],
es5DemoJS: es5Manifest["main.js"], es5DemoJS: es5Manifest["main.js"],
}); });
const minified = minifyHtml(content).replace(/#THEMEC/g, "{{ theme_color }}"); const minified = minifyHtml(content);
fs.outputFileSync(path.resolve(config.demo_root, "index.html"), minified); fs.outputFileSync(path.resolve(config.demo_root, "index.html"), minified);
done(); done();
}); });
gulp.task("gen-index-gallery-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 = renderGalleryTemplate("index", {
latestGalleryJS: "./entrypoint.js",
});
fs.outputFileSync(path.resolve(config.gallery_root, "index.html"), content);
done();
});
gulp.task("gen-index-gallery-prod", (done) => {
const latestManifest = require(path.resolve(
config.gallery_output,
"manifest.json"
));
const content = renderGalleryTemplate("index", {
latestGalleryJS: latestManifest["entrypoint.js"],
});
const minified = minifyHtml(content);
fs.outputFileSync(path.resolve(config.gallery_root, "index.html"), minified);
done();
});

View File

@ -0,0 +1,38 @@
// Run demo 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-gallery",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-gallery",
gulp.parallel("gen-icons-app", "gen-icons-app", "build-translations"),
"copy-static-gallery",
"gen-index-gallery-dev",
"webpack-dev-server-gallery"
)
);
gulp.task(
"build-gallery",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-gallery",
gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
"copy-static-gallery",
"webpack-prod-gallery",
"gen-index-gallery-prod"
)
);

View File

@ -111,3 +111,15 @@ gulp.task("copy-static-cast", (done) => {
copyTranslations(paths.cast_static); copyTranslations(paths.cast_static);
done(); done();
}); });
gulp.task("copy-static-gallery", (done) => {
// Copy app static files
fs.copySync(polyPath("public/static"), paths.gallery_static);
// Copy gallery static files
fs.copySync(path.resolve(paths.gallery_dir, "public"), paths.gallery_root);
copyMapPanel(paths.gallery_static);
copyFonts(paths.gallery_static);
copyTranslations(paths.gallery_static);
done();
});

View File

@ -57,18 +57,6 @@ function generateIconset(iconsetName, iconNames) {
return `<ha-iconset-svg name="${iconsetName}" 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
function genMDIIcons() {
const meta = JSON.parse(
fs.readFileSync(path.resolve(ICON_PACKAGE_PATH, META_PATH), "UTF-8")
);
const iconNames = meta.map((iconInfo) => iconInfo.name);
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR);
}
fs.writeFileSync(MDI_OUTPUT_PATH, generateIconset("mdi", iconNames));
}
// Helper function to map recursively over files in a folder and it's subfolders // Helper function to map recursively over files in a folder and it's subfolders
function mapFiles(startPath, filter, mapFunc) { function mapFiles(startPath, filter, mapFunc) {
const files = fs.readdirSync(startPath); const files = fs.readdirSync(startPath);
@ -101,24 +89,27 @@ function findIcons(searchPath, iconsetName) {
return icons; return icons;
} }
function genHassIcons() { gulp.task("gen-icons-mdi", (done) => {
const meta = JSON.parse(
fs.readFileSync(path.resolve(ICON_PACKAGE_PATH, META_PATH), "UTF-8")
);
const iconNames = meta.map((iconInfo) => iconInfo.name);
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR);
}
fs.writeFileSync(MDI_OUTPUT_PATH, generateIconset("mdi", iconNames));
done();
});
gulp.task("gen-icons-app", (done) => {
const iconNames = findIcons("./src", "hass"); const iconNames = findIcons("./src", "hass");
BUILT_IN_PANEL_ICONS.forEach((name) => iconNames.add(name)); BUILT_IN_PANEL_ICONS.forEach((name) => iconNames.add(name));
if (!fs.existsSync(OUTPUT_DIR)) { if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR); fs.mkdirSync(OUTPUT_DIR);
} }
fs.writeFileSync(HASS_OUTPUT_PATH, generateIconset("hass", iconNames)); fs.writeFileSync(HASS_OUTPUT_PATH, generateIconset("hass", iconNames));
}
gulp.task("gen-icons-mdi", (done) => {
genMDIIcons();
done(); done();
}); });
gulp.task("gen-icons-hass", (done) => {
genHassIcons();
done();
});
gulp.task("gen-icons", gulp.series("gen-icons-hass", "gen-icons-mdi"));
gulp.task("gen-icons-demo", (done) => { gulp.task("gen-icons-demo", (done) => {
const iconNames = findIcons(path.resolve(paths.demo_dir, "./src"), "hademo"); const iconNames = findIcons(path.resolve(paths.demo_dir, "./src"), "hademo");
@ -129,8 +120,21 @@ gulp.task("gen-icons-demo", (done) => {
done(); done();
}); });
module.exports = { gulp.task("gen-icons-hassio", (done) => {
findIcons, const iconNames = findIcons(
generateIconset, path.resolve(paths.hassio_dir, "./src"),
genMDIIcons, "hassio"
}; );
// Find hassio icons inside HA main repo.
for (const item of findIcons(
path.resolve(paths.polymer_dir, "./src"),
"hassio"
)) {
iconNames.add(item);
}
fs.writeFileSync(
path.resolve(paths.hassio_dir, "hassio-icons.html"),
generateIconset("hassio", iconNames)
);
done();
});

View File

@ -0,0 +1,34 @@
const gulp = require("gulp");
const envVars = require("../env");
require("./clean.js");
require("./gen-icons.js");
require("./webpack.js");
require("./compress.js");
gulp.task(
"develop-hassio",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-hassio",
gulp.parallel("gen-icons-hassio", "gen-icons-mdi"),
"webpack-watch-hassio"
)
);
gulp.task(
"build-hassio",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-hassio",
gulp.parallel("gen-icons-hassio", "gen-icons-mdi"),
"webpack-prod-hassio",
...// Don't compress running tests
(envVars.isTravis ? [] : ["compress-hassio"])
)
);

View File

@ -1,6 +1,5 @@
// Tasks to run webpack. // Tasks to run webpack.
const gulp = require("gulp"); const gulp = require("gulp");
const path = require("path");
const webpack = require("webpack"); const webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server"); const WebpackDevServer = require("webpack-dev-server");
const log = require("fancy-log"); const log = require("fancy-log");
@ -9,8 +8,33 @@ const {
createAppConfig, createAppConfig,
createDemoConfig, createDemoConfig,
createCastConfig, createCastConfig,
createHassioConfig,
createGalleryConfig,
} = require("../webpack"); } = require("../webpack");
const bothBuilds = (createConfigFunc, params) => [
createConfigFunc({ ...params, latestBuild: true }),
createConfigFunc({ ...params, latestBuild: false }),
];
const runDevServer = ({
compiler,
contentBase,
port,
listenHost = "localhost",
}) =>
new WebpackDevServer(compiler, {
open: true,
watchContentBase: true,
contentBase,
}).listen(port, listenHost, function(err) {
if (err) {
throw err;
}
// Server listening
log("[webpack-dev-server]", `http://localhost:${port}`);
});
const handler = (done) => (err, stats) => { const handler = (done) => (err, stats) => {
if (err) { if (err) {
console.log(err.stack || err); console.log(err.stack || err);
@ -32,20 +56,11 @@ const handler = (done) => (err, stats) => {
}; };
gulp.task("webpack-watch-app", () => { gulp.task("webpack-watch-app", () => {
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 // we are not calling done, so this command will run forever
webpack([bothBuilds(createAppConfig, { isProdBuild: false })]).watch(
{},
handler()
);
}); });
gulp.task( gulp.task(
@ -53,47 +68,17 @@ gulp.task(
() => () =>
new Promise((resolve) => new Promise((resolve) =>
webpack( webpack(
[ bothBuilds(createAppConfig, { isProdBuild: true }),
createAppConfig({
isProdBuild: true,
latestBuild: true,
isStatsBuild: false,
}),
createAppConfig({
isProdBuild: true,
latestBuild: false,
isStatsBuild: false,
}),
],
handler(resolve) handler(resolve)
) )
) )
); );
gulp.task("webpack-dev-server-demo", () => { gulp.task("webpack-dev-server-demo", () => {
const compiler = webpack([ runDevServer({
createDemoConfig({ compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
isProdBuild: false, contentBase: paths.demo_root,
latestBuild: false, port: 8090,
isStatsBuild: false,
}),
createDemoConfig({
isProdBuild: false,
latestBuild: true,
isStatsBuild: false,
}),
]);
new WebpackDevServer(compiler, {
open: true,
watchContentBase: true,
contentBase: path.resolve(paths.demo_dir, "dist"),
}).listen(8090, "localhost", function(err) {
if (err) {
throw err;
}
// Server listening
log("[webpack-dev-server]", "http://localhost:8090");
}); });
}); });
@ -102,51 +87,22 @@ gulp.task(
() => () =>
new Promise((resolve) => new Promise((resolve) =>
webpack( webpack(
[ bothBuilds(createDemoConfig, {
createDemoConfig({
isProdBuild: true, isProdBuild: true,
latestBuild: false,
isStatsBuild: false,
}), }),
createDemoConfig({
isProdBuild: true,
latestBuild: true,
isStatsBuild: false,
}),
],
handler(resolve) handler(resolve)
) )
) )
); );
gulp.task("webpack-dev-server-cast", () => { gulp.task("webpack-dev-server-cast", () => {
const compiler = webpack([ runDevServer({
createCastConfig({ compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
isProdBuild: false, contentBase: paths.cast_root,
latestBuild: false, port: 8080,
}),
createCastConfig({
isProdBuild: false,
latestBuild: true,
}),
]);
new WebpackDevServer(compiler, {
open: true,
watchContentBase: true,
contentBase: path.resolve(paths.cast_dir, "dist"),
}).listen(
8080,
// Accessible from the network, because that's how Cast hits it. // Accessible from the network, because that's how Cast hits it.
"0.0.0.0", listenHost: "0.0.0.0",
function(err) { });
if (err) {
throw err;
}
// Server listening
log("[webpack-dev-server]", "http://localhost:8080");
}
);
}); });
gulp.task( gulp.task(
@ -154,16 +110,59 @@ gulp.task(
() => () =>
new Promise((resolve) => new Promise((resolve) =>
webpack( webpack(
[ bothBuilds(createCastConfig, {
createCastConfig({
isProdBuild: true, isProdBuild: true,
latestBuild: false,
}), }),
createCastConfig({
isProdBuild: true, handler(resolve)
latestBuild: true, )
}), )
], );
gulp.task("webpack-watch-hassio", () => {
// we are not calling done, so this command will run forever
webpack(
createHassioConfig({
isProdBuild: false,
latestBuild: false,
})
).watch({}, handler());
});
gulp.task(
"webpack-prod-hassio",
() =>
new Promise((resolve) =>
webpack(
createHassioConfig({
isProdBuild: true,
latestBuild: false,
}),
handler(resolve)
)
)
);
gulp.task("webpack-dev-server-gallery", () => {
runDevServer({
compiler: webpack(
createGalleryConfig({ latestBuild: true, isProdBuild: false })
),
contentBase: paths.gallery_root,
port: 8100,
});
});
gulp.task(
"webpack-prod-gallery",
() =>
new Promise((resolve) =>
webpack(
createGalleryConfig({
isProdBuild: true,
latestBuild: true,
}),
handler(resolve) handler(resolve)
) )
) )

View File

@ -20,4 +20,13 @@ module.exports = {
cast_static: path.resolve(__dirname, "../cast/dist/static"), cast_static: path.resolve(__dirname, "../cast/dist/static"),
cast_output: path.resolve(__dirname, "../cast/dist/frontend_latest"), cast_output: path.resolve(__dirname, "../cast/dist/frontend_latest"),
cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"), cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"),
gallery_dir: path.resolve(__dirname, "../gallery"),
gallery_root: path.resolve(__dirname, "../gallery/dist"),
gallery_output: path.resolve(__dirname, "../gallery/dist/frontend_latest"),
gallery_static: path.resolve(__dirname, "../gallery/dist/static"),
hassio_dir: path.resolve(__dirname, "../hassio"),
hassio_root: path.resolve(__dirname, "../hassio/build"),
hassio_publicPath: "/api/hassio/app",
}; };

View File

@ -15,35 +15,26 @@ if (!version) {
} }
version = version[0]; version = version[0];
const genMode = (isProdBuild) => (isProdBuild ? "production" : "development"); const createWebpackConfig = ({
const genDevTool = (isProdBuild) => entry,
isProdBuild ? "source-map" : "inline-cheap-module-source-map"; outputRoot,
const genFilename = (isProdBuild, dontHash = new Set()) => ({ chunk }) => { defineOverlay,
if (!isProdBuild || dontHash.has(chunk.name)) { isProdBuild,
return `${chunk.name}.js`; latestBuild,
} isStatsBuild,
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; }) => {
}; return {
const genChunkFilename = (isProdBuild, isStatsBuild) => mode: isProdBuild ? "production" : "development",
isProdBuild && !isStatsBuild ? "chunk.[chunkhash].js" : "[name].chunk.js"; devtool: isProdBuild ? "source-map" : "inline-cheap-module-source-map",
entry,
const resolve = { module: {
extensions: [".ts", ".js", ".json", ".tsx"], rules: [
alias: { babelLoaderConfig({ latestBuild }),
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 cssLoader = {
test: /\.css$/, test: /\.css$/,
use: "raw-loader", use: "raw-loader",
}; },
const htmlLoader = { {
test: /\.(html)$/, test: /\.(html)$/,
use: { use: {
loader: "html-loader", loader: "html-loader",
@ -51,9 +42,36 @@ const htmlLoader = {
exportAsEs6Default: true, exportAsEs6Default: true,
}, },
}, },
}; },
],
const plugins = [ },
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
extractComments: true,
sourceMap: true,
terserOptions: {
safari10: true,
ecma: latestBuild ? undefined : 5,
},
}),
],
},
plugins: [
new ManifestPlugin(),
new webpack.DefinePlugin({
__DEV__: !isProdBuild,
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__VERSION__: JSON.stringify(version),
__DEMO__: false,
__STATIC_PATH__: "/static/",
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development"
),
...defineOverlay,
}),
// Ignore moment.js locales // Ignore moment.js locales
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// Color.js is bloated, it contains all color definitions for all material color sets. // Color.js is bloated, it contains all color definitions for all material color sets.
@ -71,26 +89,60 @@ const plugins = [
/@material\/mwc-icon\/mwc-icon-font\.js$/, /@material\/mwc-icon\/mwc-icon-font\.js$/,
path.resolve(paths.polymer_dir, "src/util/empty.js") path.resolve(paths.polymer_dir, "src/util/empty.js")
), ),
]; ].filter(Boolean),
resolve: {
const optimization = (latestBuild) => ({ extensions: [".ts", ".js", ".json", ".tsx"],
minimizer: [ alias: {
new TerserPlugin({ react: "preact-compat",
cache: true, "react-dom": "preact-compat",
parallel: true, // Not necessary unless you consume a module using `createClass`
extractComments: true, "create-react-class": "preact-compat/lib/create-react-class",
sourceMap: true, // Not necessary unless you consume a module requiring `react-dom-factories`
terserOptions: { "react-dom-factories": "preact-compat/lib/react-dom-factories",
safari10: true,
ecma: latestBuild ? undefined : 5,
}, },
}), },
], output: {
}); filename: ({ chunk }) => {
const dontHash = new Set();
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(
outputRoot,
latestBuild ? "frontend_latest" : "frontend_es5"
),
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
// For workerize loader
globalObject: "self",
},
};
};
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
const isCI = process.env.CI === "true"; const config = createWebpackConfig({
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",
},
outputRoot: paths.root,
isProdBuild,
latestBuild,
isStatsBuild,
});
if (latestBuild) {
// Create an object mapping browser urls to their paths during build // Create an object mapping browser urls to their paths during build
const translationMetadata = require("../build-translations/translationMetadata.json"); const translationMetadata = require("../build-translations/translationMetadata.json");
const workBoxTranslationsTemplatedURLs = {}; const workBoxTranslationsTemplatedURLs = {};
@ -101,38 +153,7 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
] = `build-translations/output/${key}.json`; ] = `build-translations/output/${key}.json`;
}); });
const entry = { config.plugins.push(
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: genMode(isProdBuild),
devtool: genDevTool(isProdBuild),
entry,
module: {
rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader],
},
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,
latestBuild &&
new WorkboxPlugin.InjectManifest({ new WorkboxPlugin.InjectManifest({
swSrc: "./src/entrypoints/service-worker-hass.js", swSrc: "./src/entrypoints/service-worker-hass.js",
swDest: "service_worker.js", swDest: "service_worker.js",
@ -151,113 +172,89 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
"/static/fonts/roboto/Roboto-Bold.woff2": "/static/fonts/roboto/Roboto-Bold.woff2":
"node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff2", "node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff2",
}, },
}), })
].filter(Boolean), );
output: { }
filename: genFilename(isProdBuild),
chunkFilename: genChunkFilename(isProdBuild, isStatsBuild), return config;
path: latestBuild ? paths.output : paths.output_es5,
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
// For workerize loader
globalObject: "self",
},
resolve,
};
}; };
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
return { return createWebpackConfig({
mode: genMode(isProdBuild),
devtool: genDevTool(isProdBuild),
entry: { entry: {
main: "./demo/src/entrypoint.ts", main: path.resolve(paths.demo_dir, "src/entrypoint.ts"),
compatibility: "./src/entrypoints/compatibility.ts", compatibility: path.resolve(
paths.polymer_dir,
"src/entrypoints/compatibility.ts"
),
}, },
module: { outputRoot: paths.demo_root,
rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader], defineOverlay: {
},
optimization: optimization(latestBuild),
plugins: [
new ManifestPlugin(),
new webpack.DefinePlugin({
__DEV__: !isProdBuild,
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__VERSION__: JSON.stringify(`DEMO-${version}`), __VERSION__: JSON.stringify(`DEMO-${version}`),
__DEMO__: true, __DEMO__: true,
__STATIC_PATH__: "/static/",
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development"
),
}),
...plugins,
].filter(Boolean),
resolve,
output: {
filename: genFilename(isProdBuild),
chunkFilename: genChunkFilename(isProdBuild, isStatsBuild),
path: path.resolve(
paths.demo_root,
latestBuild ? "frontend_latest" : "frontend_es5"
),
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
// For workerize loader
globalObject: "self",
}, },
}; isProdBuild,
latestBuild,
isStatsBuild,
});
}; };
const createCastConfig = ({ isProdBuild, latestBuild }) => { const createCastConfig = ({ isProdBuild, latestBuild }) => {
const isStatsBuild = false;
const entry = { const entry = {
launcher: "./cast/src/launcher/entrypoint.ts", launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
}; };
if (latestBuild) { if (latestBuild) {
entry.receiver = "./cast/src/receiver/entrypoint.ts"; entry.receiver = path.resolve(paths.cast_dir, "src/receiver/entrypoint.ts");
} }
return { return createWebpackConfig({
mode: genMode(isProdBuild),
devtool: genDevTool(isProdBuild),
entry, entry,
module: { outputRoot: paths.cast_root,
rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader], isProdBuild,
}, latestBuild,
optimization: optimization(latestBuild), });
plugins: [
new ManifestPlugin(),
new webpack.DefinePlugin({
__DEV__: !isProdBuild,
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__VERSION__: JSON.stringify(version),
__DEMO__: false,
__STATIC_PATH__: "/static/",
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development"
),
}),
...plugins,
].filter(Boolean),
resolve,
output: {
filename: genFilename(isProdBuild),
chunkFilename: genChunkFilename(isProdBuild, isStatsBuild),
path: path.resolve(
paths.cast_root,
latestBuild ? "frontend_latest" : "frontend_es5"
),
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
// For workerize loader
globalObject: "self",
},
}; };
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
if (latestBuild) {
throw new Error("Hass.io does not support latest build!");
}
const config = createWebpackConfig({
entry: {
entrypoint: path.resolve(paths.hassio_dir, "src/entrypoint.js"),
},
outputRoot: "",
isProdBuild,
latestBuild,
});
config.output.path = paths.hassio_root;
config.output.publicPath = paths.hassio_publicPath;
return config;
};
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
if (!latestBuild) {
throw new Error("Gallery only supports latest build!");
}
const config = createWebpackConfig({
entry: {
entrypoint: path.resolve(paths.gallery_dir, "src/entrypoint.js"),
},
outputRoot: paths.gallery_root,
isProdBuild,
latestBuild,
});
return config;
}; };
module.exports = { module.exports = {
resolve,
plugins,
optimization,
createAppConfig, createAppConfig,
createDemoConfig, createDemoConfig,
createCastConfig, createCastConfig,
createHassioConfig,
createGalleryConfig,
}; };

11
cast/webpack.config.js Normal file
View File

@ -0,0 +1,11 @@
const { createCastConfig } = require("../build-scripts/webpack.js");
const { isProdBuild } = require("../build-scripts/env.js");
// File just used for stats builds
const latestBuild = true;
module.exports = createCastConfig({
isProdBuild,
latestBuild,
});

View File

@ -1,10 +1,9 @@
const { createDemoConfig } = require("../build-scripts/webpack.js"); const { createDemoConfig } = require("../build-scripts/webpack.js");
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
// This file exists because we haven't migrated the stats script yet // File just used for stats builds
const isProdBuild = process.env.NODE_ENV === "production"; const latestBuild = true;
const isStatsBuild = process.env.STATS === "1";
const latestBuild = false;
module.exports = createDemoConfig({ module.exports = createDemoConfig({
isProdBuild, isProdBuild,

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#2157BC">
<title>HAGallery</title>
<script src='./main.js' async></script>
<style>
body {
font-family: Roboto, Noto, sans-serif;
margin: 0;
padding: 0;
}
</style>
</head>
<body></body>
</html>

View File

@ -4,14 +4,6 @@
# Stop on errors # Stop on errors
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/../.."
OUTPUT_DIR=dist ./node_modules/.bin/gulp build-gallery
rm -rf $OUTPUT_DIR
cd ..
./node_modules/.bin/gulp build-translations gen-icons
cd gallery
NODE_ENV=production ../node_modules/.bin/webpack -p --config webpack.config.js

View File

@ -4,10 +4,6 @@
# Stop on errors # Stop on errors
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/../.."
cd .. ./node_modules/.bin/gulp develop-gallery
./node_modules/.bin/gulp build-translations gen-icons
cd gallery
../node_modules/.bin/webpack-dev-server

View File

@ -26,7 +26,9 @@ class DemoCards extends PolymerElement {
</style> </style>
<app-toolbar> <app-toolbar>
<div class="filters"> <div class="filters">
<ha-switch checked="{{_showConfig}}">Show config</ha-switch> <ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
Show config
</ha-switch>
</div> </div>
</app-toolbar> </app-toolbar>
<div class="cards"> <div class="cards">
@ -51,6 +53,10 @@ class DemoCards extends PolymerElement {
}, },
}; };
} }
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
} }
customElements.define("demo-cards", DemoCards); customElements.define("demo-cards", DemoCards);

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#2157BC" />
<title>HAGallery</title>
<script type="module" src="<%= latestGalleryJS %>"></script>
<style>
body {
font-family: Roboto, Noto, sans-serif;
margin: 0;
padding: 0;
}
</style>
</head>
<body></body>
</html>

View File

@ -1,6 +1,6 @@
const path = require("path"); const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin");
const webpackBase = require("../build-scripts/webpack.js"); const { createGalleryConfig } = require("../build-scripts/webpack.js");
const { babelLoaderConfig } = require("../build-scripts/babel.js"); const { babelLoaderConfig } = require("../build-scripts/babel.js");
const isProd = process.env.NODE_ENV === "production"; const isProd = process.env.NODE_ENV === "production";
@ -9,7 +9,12 @@ const buildPath = path.resolve(__dirname, "dist");
const publicPath = isProd ? "./" : "http://localhost:8080/"; const publicPath = isProd ? "./" : "http://localhost:8080/";
const latestBuild = true; const latestBuild = true;
module.exports = { module.exports = createGalleryConfig({
latestBuild: true,
});
const bla = () => {
const oldExports = {
mode: isProd ? "production" : "development", mode: isProd ? "production" : "development",
// Disabled in prod while we make Home Assistant able to serve the right files. // Disabled in prod while we make Home Assistant able to serve the right files.
// Was source-map // Was source-map
@ -64,3 +69,4 @@ module.exports = {
contentBase: "./public", contentBase: "./public",
}, },
}; };
};

View File

@ -4,11 +4,6 @@
# Stop on errors # Stop on errors
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/../.."
OUTPUT_DIR=build ./node_modules/.bin/gulp build-hassio
rm -rf $OUTPUT_DIR
node script/gen-icons.js
NODE_ENV=production CI=false ../node_modules/.bin/webpack -p --config webpack.config.js

View File

@ -4,11 +4,6 @@
# Stop on errors # Stop on errors
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/../.."
OUTPUT_DIR=build ./node_modules/.bin/gulp develop-hassio
rm -rf $OUTPUT_DIR
mkdir $OUTPUT_DIR
node script/gen-icons.js
../node_modules/.bin/webpack --watch --progress

View File

@ -1,20 +0,0 @@
#!/usr/bin/env node
const fs = require("fs");
const {
findIcons,
generateIconset,
genMDIIcons,
} = require("../../build-scripts/gulp/gen-icons.js");
function genHassioIcons() {
const iconNames = findIcons("./src", "hassio");
for (const item of findIcons("../src", "hassio")) {
iconNames.add(item);
}
fs.writeFileSync("./hassio-icons.html", generateIconset("hassio", iconNames));
}
genMDIIcons();
genHassioIcons();

View File

@ -1,63 +1,11 @@
const webpack = require("webpack"); const { createHassioConfig } = require("../build-scripts/webpack.js");
const CompressionPlugin = require("compression-webpack-plugin"); const { isProdBuild } = require("../build-scripts/env.js");
const zopfli = require("@gfx/zopfli");
const config = require("./config.js"); // File just used for stats builds
const webpackBase = require("../build-scripts/webpack.js");
const { babelLoaderConfig } = require("../build-scripts/babel.js");
const isProdBuild = process.env.NODE_ENV === "production";
const isCI = process.env.CI === "true";
const chunkFilename = isProdBuild ? "chunk.[chunkhash].js" : "[name].chunk.js";
const latestBuild = false; const latestBuild = false;
module.exports = { module.exports = createHassioConfig({
mode: isProdBuild ? "production" : "development", isProdBuild,
devtool: isProdBuild ? "source-map" : "inline-source-map", latestBuild,
entry: { });
entrypoint: "./src/entrypoint.js",
},
module: {
rules: [
babelLoaderConfig({ latestBuild }),
{
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"),
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development"
),
}),
isProdBuild &&
!isCI &&
new CompressionPlugin({
cache: true,
exclude: [/\.js\.map$/, /\.LICENSE$/, /\.py$/, /\.txt$/],
algorithm(input, compressionOptions, callback) {
return zopfli.gzip(input, compressionOptions, callback);
},
}),
].filter(Boolean),
resolve: {
extensions: [".ts", ".js", ".json"],
},
output: {
filename: "[name].js",
chunkFilename,
path: config.buildDir,
publicPath: `${config.publicPath}/`,
},
};

View File

@ -1,10 +1,8 @@
const { createAppConfig } = require("./build-scripts/webpack.js"); const { createAppConfig } = require("./build-scripts/webpack.js");
const { isProdBuild, isStatsBuild } = require("./build-scripts/env.js");
// This file exists because we haven't migrated the stats script yet // This file exists because we haven't migrated the stats script yet
const isProdBuild = process.env.NODE_ENV === "production";
const isStatsBuild = process.env.STATS === "1";
const configs = [ const configs = [
createAppConfig({ createAppConfig({
isProdBuild, isProdBuild,