mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-11-03 16:09:54 +00:00 
			
		
		
		
	* Made it easier to test the frontend against an existing core instance. * Ensured that script works regardless of current working dir * Use consistent quote style * Also allow using variables in hassUrl override * Improved the default behavior of the script * more consistent variable naming * don't install a global dependency * documented caching wierdness where if you switch core endpoints the old one remains in use * Simplified some code * improved documentation --------- Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
		
			
				
	
	
		
			348 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const path = require("path");
 | 
						|
const env = require("./env.cjs");
 | 
						|
const paths = require("./paths.cjs");
 | 
						|
const { dependencies } = require("../package.json");
 | 
						|
 | 
						|
const BABEL_PLUGINS = path.join(__dirname, "babel-plugins");
 | 
						|
 | 
						|
// GitHub base URL to use for production source maps
 | 
						|
// Nightly builds use the commit SHA, otherwise assumes there is a tag that matches the version
 | 
						|
module.exports.sourceMapURL = () => {
 | 
						|
  const ref = env.version().endsWith("dev")
 | 
						|
    ? process.env.GITHUB_SHA || "dev"
 | 
						|
    : env.version();
 | 
						|
  return `https://raw.githubusercontent.com/home-assistant/frontend/${ref}/`;
 | 
						|
};
 | 
						|
 | 
						|
// Files from NPM Packages that should not be imported
 | 
						|
module.exports.ignorePackages = () => [];
 | 
						|
 | 
						|
// Files from NPM packages that we should replace with empty file
 | 
						|
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
 | 
						|
  [
 | 
						|
    // Contains all color definitions for all material color sets.
 | 
						|
    // We don't use it
 | 
						|
    require.resolve("@polymer/paper-styles/color.js"),
 | 
						|
    require.resolve("@polymer/paper-styles/default-theme.js"),
 | 
						|
    // Loads stuff from a CDN
 | 
						|
    require.resolve("@polymer/font-roboto/roboto.js"),
 | 
						|
    require.resolve("@vaadin/vaadin-material-styles/typography.js"),
 | 
						|
    require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
 | 
						|
    // Compatibility not needed for latest builds
 | 
						|
    latestBuild &&
 | 
						|
      // wrapped in require.resolve so it blows up if file no longer exists
 | 
						|
      require.resolve(
 | 
						|
        path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
 | 
						|
      ),
 | 
						|
    // Icons in supervisor conflict with icons in HA so we don't load.
 | 
						|
    isHassioBuild &&
 | 
						|
      require.resolve(
 | 
						|
        path.resolve(paths.polymer_dir, "src/components/ha-icon.ts")
 | 
						|
      ),
 | 
						|
    isHassioBuild &&
 | 
						|
      require.resolve(
 | 
						|
        path.resolve(paths.polymer_dir, "src/components/ha-icon-picker.ts")
 | 
						|
      ),
 | 
						|
  ].filter(Boolean);
 | 
						|
 | 
						|
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
 | 
						|
  __DEV__: !isProdBuild,
 | 
						|
  __BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"),
 | 
						|
  __VERSION__: JSON.stringify(env.version()),
 | 
						|
  __DEMO__: false,
 | 
						|
  __SUPERVISOR__: false,
 | 
						|
  __BACKWARDS_COMPAT__: false,
 | 
						|
  __STATIC_PATH__: "/static/",
 | 
						|
  __HASS_URL__: `\`${
 | 
						|
    "HASS_URL" in process.env
 | 
						|
      ? process.env["HASS_URL"]
 | 
						|
      : "${location.protocol}//${location.host}"
 | 
						|
  }\``,
 | 
						|
  "process.env.NODE_ENV": JSON.stringify(
 | 
						|
    isProdBuild ? "production" : "development"
 | 
						|
  ),
 | 
						|
  ...defineOverlay,
 | 
						|
});
 | 
						|
 | 
						|
module.exports.htmlMinifierOptions = {
 | 
						|
  caseSensitive: true,
 | 
						|
  collapseWhitespace: true,
 | 
						|
  conservativeCollapse: true,
 | 
						|
  decodeEntities: true,
 | 
						|
  removeComments: true,
 | 
						|
  removeRedundantAttributes: true,
 | 
						|
  minifyCSS: {
 | 
						|
    compatibility: "*,-properties.zeroUnits",
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
 | 
						|
  safari10: !latestBuild,
 | 
						|
  ecma: latestBuild ? 2015 : 5,
 | 
						|
  module: latestBuild,
 | 
						|
  format: { comments: false },
 | 
						|
  sourceMap: !isTestBuild,
 | 
						|
});
 | 
						|
 | 
						|
module.exports.babelOptions = ({
 | 
						|
  latestBuild,
 | 
						|
  isProdBuild,
 | 
						|
  isTestBuild,
 | 
						|
  sw,
 | 
						|
}) => ({
 | 
						|
  babelrc: false,
 | 
						|
  compact: false,
 | 
						|
  assumptions: {
 | 
						|
    privateFieldsAsProperties: true,
 | 
						|
    setPublicClassFields: true,
 | 
						|
    setSpreadProperties: true,
 | 
						|
  },
 | 
						|
  browserslistEnv: latestBuild ? "modern" : `legacy${sw ? "-sw" : ""}`,
 | 
						|
  presets: [
 | 
						|
    [
 | 
						|
      "@babel/preset-env",
 | 
						|
      {
 | 
						|
        useBuiltIns: "usage",
 | 
						|
        corejs: dependencies["core-js"],
 | 
						|
        bugfixes: true,
 | 
						|
        shippedProposals: true,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    "@babel/preset-typescript",
 | 
						|
  ],
 | 
						|
  plugins: [
 | 
						|
    [
 | 
						|
      path.join(BABEL_PLUGINS, "inline-constants-plugin.cjs"),
 | 
						|
      {
 | 
						|
        modules: ["@mdi/js"],
 | 
						|
        ignoreModuleNotFound: true,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    // Minify template literals for production
 | 
						|
    isProdBuild && [
 | 
						|
      "template-html-minifier",
 | 
						|
      {
 | 
						|
        modules: {
 | 
						|
          ...Object.fromEntries(
 | 
						|
            ["lit", "lit-element", "lit-html"].map((m) => [
 | 
						|
              m,
 | 
						|
              [
 | 
						|
                "html",
 | 
						|
                { name: "svg", encapsulation: "svg" },
 | 
						|
                { name: "css", encapsulation: "style" },
 | 
						|
              ],
 | 
						|
            ])
 | 
						|
          ),
 | 
						|
          "@polymer/polymer/lib/utils/html-tag.js": ["html"],
 | 
						|
        },
 | 
						|
        strictCSS: true,
 | 
						|
        htmlMinifier: module.exports.htmlMinifierOptions,
 | 
						|
        failOnError: false, // we can turn this off in case of false positives
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    // Import helpers and regenerator from runtime package
 | 
						|
    [
 | 
						|
      "@babel/plugin-transform-runtime",
 | 
						|
      { version: dependencies["@babel/runtime"] },
 | 
						|
    ],
 | 
						|
    // Transpile decorators (still in TC39 process)
 | 
						|
    // Modern browsers support class fields and private methods, but transform is required with the older decorator version dictated by Lit
 | 
						|
    [
 | 
						|
      "@babel/plugin-proposal-decorators",
 | 
						|
      { version: "2018-09", decoratorsBeforeExport: true },
 | 
						|
    ],
 | 
						|
    "@babel/plugin-transform-class-properties",
 | 
						|
    "@babel/plugin-transform-private-methods",
 | 
						|
  ].filter(Boolean),
 | 
						|
  exclude: [
 | 
						|
    // \\ for Windows, / for Mac OS and Linux
 | 
						|
    /node_modules[\\/]core-js/,
 | 
						|
  ],
 | 
						|
  sourceMaps: !isTestBuild,
 | 
						|
  overrides: [
 | 
						|
    {
 | 
						|
      // Add plugin to inject various polyfills, excluding the polyfills
 | 
						|
      // themselves to prevent self-injection.
 | 
						|
      plugins: [
 | 
						|
        [
 | 
						|
          path.join(BABEL_PLUGINS, "custom-polyfill-plugin.js"),
 | 
						|
          { method: "usage-global" },
 | 
						|
        ],
 | 
						|
      ],
 | 
						|
      exclude: [
 | 
						|
        path.join(paths.polymer_dir, "src/resources/polyfills"),
 | 
						|
        ...[
 | 
						|
          "@formatjs/(?:ecma402-abstract|intl-\\w+)",
 | 
						|
          "@lit-labs/virtualizer/polyfills",
 | 
						|
          "@webcomponents/scoped-custom-element-registry",
 | 
						|
          "element-internals-polyfill",
 | 
						|
          "proxy-polyfill",
 | 
						|
          "unfetch",
 | 
						|
        ].map((p) => new RegExp(`/node_modules/${p}/`)),
 | 
						|
      ],
 | 
						|
    },
 | 
						|
    {
 | 
						|
      // Use unambiguous for dependencies so that require() is correctly injected into CommonJS files
 | 
						|
      // Exclusions are needed in some cases where ES modules have no static imports or exports, such as polyfills
 | 
						|
      sourceType: "unambiguous",
 | 
						|
      include: /\/node_modules\//,
 | 
						|
      exclude: [
 | 
						|
        "element-internals-polyfill",
 | 
						|
        "@?lit(?:-labs|-element|-html)?",
 | 
						|
      ].map((p) => new RegExp(`/node_modules/${p}/`)),
 | 
						|
    },
 | 
						|
  ],
 | 
						|
});
 | 
						|
 | 
						|
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");
 | 
						|
 | 
						|
const outputPath = (outputRoot, latestBuild) =>
 | 
						|
  path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
 | 
						|
 | 
						|
const publicPath = (latestBuild, root = "") =>
 | 
						|
  latestBuild ? `${root}/frontend_latest/` : `${root}/frontend_es5/`;
 | 
						|
 | 
						|
/*
 | 
						|
  BundleConfig {
 | 
						|
    // Object with entrypoints that need to be bundled
 | 
						|
    entry: { [name: string]: pathToFile },
 | 
						|
    // Folder where bundled files need to be written
 | 
						|
    outputPath: string,
 | 
						|
    // absolute url-path where bundled files can be found
 | 
						|
    publicPath: string,
 | 
						|
    // extra definitions that we need to replace in source
 | 
						|
    defineOverlay: {[name: string]: value },
 | 
						|
    // if this is a production build
 | 
						|
    isProdBuild: boolean,
 | 
						|
    // If we're targeting latest browsers
 | 
						|
    latestBuild: boolean,
 | 
						|
    // If we're doing a stats build (create nice chunk names)
 | 
						|
    isStatsBuild: boolean,
 | 
						|
    // If it's just a test build in CI, skip time on source map generation
 | 
						|
    isTestBuild: boolean,
 | 
						|
    // Names of entrypoints that should not be hashed
 | 
						|
    dontHash: Set<string>
 | 
						|
  }
 | 
						|
  */
 | 
						|
 | 
						|
module.exports.config = {
 | 
						|
  app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
 | 
						|
    return {
 | 
						|
      name: "frontend" + nameSuffix(latestBuild),
 | 
						|
      entry: {
 | 
						|
        "service-worker": !latestBuild
 | 
						|
          ? {
 | 
						|
              import: "./src/entrypoints/service-worker.ts",
 | 
						|
              layer: "sw",
 | 
						|
            }
 | 
						|
          : "./src/entrypoints/service-worker.ts",
 | 
						|
        app: "./src/entrypoints/app.ts",
 | 
						|
        authorize: "./src/entrypoints/authorize.ts",
 | 
						|
        onboarding: "./src/entrypoints/onboarding.ts",
 | 
						|
        core: "./src/entrypoints/core.ts",
 | 
						|
        "custom-panel": "./src/entrypoints/custom-panel.ts",
 | 
						|
      },
 | 
						|
      outputPath: outputPath(paths.app_output_root, latestBuild),
 | 
						|
      publicPath: publicPath(latestBuild),
 | 
						|
      isProdBuild,
 | 
						|
      latestBuild,
 | 
						|
      isStatsBuild,
 | 
						|
      isTestBuild,
 | 
						|
      isWDS,
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  demo({ isProdBuild, latestBuild, isStatsBuild }) {
 | 
						|
    return {
 | 
						|
      name: "demo" + nameSuffix(latestBuild),
 | 
						|
      entry: {
 | 
						|
        main: path.resolve(paths.demo_dir, "src/entrypoint.ts"),
 | 
						|
      },
 | 
						|
      outputPath: outputPath(paths.demo_output_root, latestBuild),
 | 
						|
      publicPath: publicPath(latestBuild),
 | 
						|
      defineOverlay: {
 | 
						|
        __VERSION__: JSON.stringify(`DEMO-${env.version()}`),
 | 
						|
        __DEMO__: true,
 | 
						|
      },
 | 
						|
      isProdBuild,
 | 
						|
      latestBuild,
 | 
						|
      isStatsBuild,
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  cast({ isProdBuild, latestBuild }) {
 | 
						|
    const entry = {
 | 
						|
      launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
 | 
						|
      media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
 | 
						|
    };
 | 
						|
 | 
						|
    if (latestBuild) {
 | 
						|
      entry.receiver = path.resolve(
 | 
						|
        paths.cast_dir,
 | 
						|
        "src/receiver/entrypoint.ts"
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    return {
 | 
						|
      name: "cast" + nameSuffix(latestBuild),
 | 
						|
      entry,
 | 
						|
      outputPath: outputPath(paths.cast_output_root, latestBuild),
 | 
						|
      publicPath: publicPath(latestBuild),
 | 
						|
      isProdBuild,
 | 
						|
      latestBuild,
 | 
						|
      defineOverlay: {
 | 
						|
        __BACKWARDS_COMPAT__: true,
 | 
						|
      },
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  hassio({ isProdBuild, latestBuild, isStatsBuild, isTestBuild }) {
 | 
						|
    return {
 | 
						|
      name: "supervisor" + nameSuffix(latestBuild),
 | 
						|
      entry: {
 | 
						|
        entrypoint: path.resolve(paths.hassio_dir, "src/entrypoint.ts"),
 | 
						|
      },
 | 
						|
      outputPath: outputPath(paths.hassio_output_root, latestBuild),
 | 
						|
      publicPath: publicPath(latestBuild, paths.hassio_publicPath),
 | 
						|
      isProdBuild,
 | 
						|
      latestBuild,
 | 
						|
      isStatsBuild,
 | 
						|
      isTestBuild,
 | 
						|
      isHassioBuild: true,
 | 
						|
      defineOverlay: {
 | 
						|
        __SUPERVISOR__: true,
 | 
						|
        __STATIC_PATH__: `"${paths.hassio_publicPath}/static/"`,
 | 
						|
      },
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  gallery({ isProdBuild, latestBuild }) {
 | 
						|
    return {
 | 
						|
      name: "gallery" + nameSuffix(latestBuild),
 | 
						|
      entry: {
 | 
						|
        entrypoint: path.resolve(paths.gallery_dir, "src/entrypoint.js"),
 | 
						|
      },
 | 
						|
      outputPath: outputPath(paths.gallery_output_root, latestBuild),
 | 
						|
      publicPath: publicPath(latestBuild),
 | 
						|
      isProdBuild,
 | 
						|
      latestBuild,
 | 
						|
      defineOverlay: {
 | 
						|
        __DEMO__: true,
 | 
						|
      },
 | 
						|
    };
 | 
						|
  },
 | 
						|
 | 
						|
  landingPage({ isProdBuild, latestBuild }) {
 | 
						|
    return {
 | 
						|
      name: "landing-page" + nameSuffix(latestBuild),
 | 
						|
      entry: {
 | 
						|
        entrypoint: path.resolve(paths.landingPage_dir, "src/entrypoint.js"),
 | 
						|
      },
 | 
						|
      outputPath: outputPath(paths.landingPage_output_root, latestBuild),
 | 
						|
      publicPath: publicPath(latestBuild),
 | 
						|
      isProdBuild,
 | 
						|
      latestBuild,
 | 
						|
    };
 | 
						|
  },
 | 
						|
};
 |