mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-10-31 14:39:38 +00:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			triggers-d
			...
			gulp-ts
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | efece17f50 | ||
|   | 79e5c59fdf | ||
|   | 0aa34a14dd | ||
|   | 1ced9959fa | ||
|   | 1b67a6f358 | ||
|   | 62f2b286ae | ||
|   | 8f7760f88f | ||
|   | ff3b65605e | 
							
								
								
									
										6
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/copilot-instructions.md
									
									
									
									
										vendored
									
									
								
							| @@ -310,11 +310,7 @@ export class DialogMyFeature | |||||||
|         .heading=${createCloseHeading(this.hass, this._params.title)} |         .heading=${createCloseHeading(this.hass, this._params.title)} | ||||||
|       > |       > | ||||||
|         <!-- Dialog content --> |         <!-- Dialog content --> | ||||||
|         <ha-button |         <ha-button @click=${this.closeDialog} slot="secondaryAction"> | ||||||
|           appearance="plain" |  | ||||||
|           @click=${this.closeDialog} |  | ||||||
|           slot="secondaryAction" |  | ||||||
|         > |  | ||||||
|           ${this.hass.localize("ui.common.cancel")} |           ${this.hass.localize("ui.common.cancel")} | ||||||
|         </ha-button> |         </ha-button> | ||||||
|         <ha-button @click=${this._submit} slot="primaryAction"> |         <ha-button @click=${this._submit} slot="primaryAction"> | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								.github/workflows/cast_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/cast_deployment.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -21,12 +21,12 @@ jobs: | |||||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} |       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|         with: |         with: | ||||||
|           ref: dev |           ref: dev | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -35,14 +35,14 @@ jobs: | |||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|  |  | ||||||
|       - name: Build Cast |       - name: Build Cast | ||||||
|         run: ./node_modules/.bin/gulp build-cast |         run: yarn run-task build-cast | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|       - name: Deploy to Netlify |       - name: Deploy to Netlify | ||||||
|         id: deploy |         id: deploy | ||||||
|         run: | |         run: | | ||||||
|           npx -y netlify-cli@23.7.3 deploy --dir=cast/dist --alias dev |           npx -y netlify-cli deploy --dir=cast/dist --alias dev | ||||||
|         env: |         env: | ||||||
|           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} |           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | ||||||
|           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }} |           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }} | ||||||
| @@ -56,12 +56,12 @@ jobs: | |||||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} |       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|         with: |         with: | ||||||
|           ref: master |           ref: master | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -70,14 +70,14 @@ jobs: | |||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|  |  | ||||||
|       - name: Build Cast |       - name: Build Cast | ||||||
|         run: ./node_modules/.bin/gulp build-cast |         run: yarn run-task build-cast | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|       - name: Deploy to Netlify |       - name: Deploy to Netlify | ||||||
|         id: deploy |         id: deploy | ||||||
|         run: | |         run: | | ||||||
|           npx -y netlify-cli@23.7.3 deploy --dir=cast/dist --prod |           npx -y netlify-cli deploy --dir=cast/dist --prod | ||||||
|         env: |         env: | ||||||
|           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} |           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | ||||||
|           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }} |           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }} | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -24,9 +24,9 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -35,9 +35,9 @@ jobs: | |||||||
|       - name: Check for duplicate dependencies |       - name: Check for duplicate dependencies | ||||||
|         run: yarn dedupe --check |         run: yarn dedupe --check | ||||||
|       - name: Build resources |       - name: Build resources | ||||||
|         run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages |         run: yarn run-task gen-icons-json build-translations build-locale-data gather-gallery-pages | ||||||
|       - name: Setup lint cache |       - name: Setup lint cache | ||||||
|         uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 |         uses: actions/cache@v4.2.3 | ||||||
|         with: |         with: | ||||||
|           path: | |           path: | | ||||||
|             node_modules/.cache/prettier |             node_modules/.cache/prettier | ||||||
| @@ -58,16 +58,16 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|       - name: Build resources |       - name: Build resources | ||||||
|         run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data |         run: yarn run-task gen-icons-json build-translations build-locale-data | ||||||
|       - name: Run Tests |       - name: Run Tests | ||||||
|         run: yarn run test |         run: yarn run test | ||||||
|   build: |   build: | ||||||
| @@ -76,20 +76,20 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|       - name: Build Application |       - name: Build Application | ||||||
|         run: ./node_modules/.bin/gulp build-app |         run: yarn run-task build-app | ||||||
|         env: |         env: | ||||||
|           IS_TEST: "true" |           IS_TEST: "true" | ||||||
|       - name: Upload bundle stats |       - name: Upload bundle stats | ||||||
|         uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |         uses: actions/upload-artifact@v4.6.2 | ||||||
|         with: |         with: | ||||||
|           name: frontend-bundle-stats |           name: frontend-bundle-stats | ||||||
|           path: build/stats/*.json |           path: build/stats/*.json | ||||||
| @@ -100,20 +100,20 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|       - name: Build Application |       - name: Build Application | ||||||
|         run: ./node_modules/.bin/gulp build-hassio |         run: yarn run-task build-hassio | ||||||
|         env: |         env: | ||||||
|           IS_TEST: "true" |           IS_TEST: "true" | ||||||
|       - name: Upload bundle stats |       - name: Upload bundle stats | ||||||
|         uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |         uses: actions/upload-artifact@v4.6.2 | ||||||
|         with: |         with: | ||||||
|           name: supervisor-bundle-stats |           name: supervisor-bundle-stats | ||||||
|           path: build/stats/*.json |           path: build/stats/*.json | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,7 +23,7 @@ jobs: | |||||||
|  |  | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout repository |       - name: Checkout repository | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|         with: |         with: | ||||||
|           # We must fetch at least the immediate parents so that if this is |           # We must fetch at least the immediate parents so that if this is | ||||||
|           # a pull request then we can checkout the head. |           # a pull request then we can checkout the head. | ||||||
| @@ -36,14 +36,14 @@ jobs: | |||||||
|  |  | ||||||
|       # Initializes the CodeQL tools for scanning. |       # Initializes the CodeQL tools for scanning. | ||||||
|       - name: Initialize CodeQL |       - name: Initialize CodeQL | ||||||
|         uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 |         uses: github/codeql-action/init@v3 | ||||||
|         with: |         with: | ||||||
|           languages: ${{ matrix.language }} |           languages: ${{ matrix.language }} | ||||||
|  |  | ||||||
|       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java). |       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java). | ||||||
|       # If this step fails, then you should remove it and run the build manually (see below) |       # If this step fails, then you should remove it and run the build manually (see below) | ||||||
|       - name: Autobuild |       - name: Autobuild | ||||||
|         uses: github/codeql-action/autobuild@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 |         uses: github/codeql-action/autobuild@v3 | ||||||
|  |  | ||||||
|       # ℹ️ Command-line programs to run using the OS shell. |       # ℹ️ Command-line programs to run using the OS shell. | ||||||
|       # 📚 https://git.io/JvXDl |       # 📚 https://git.io/JvXDl | ||||||
| @@ -57,4 +57,4 @@ jobs: | |||||||
|       #   make release |       #   make release | ||||||
|  |  | ||||||
|       - name: Perform CodeQL Analysis |       - name: Perform CodeQL Analysis | ||||||
|         uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 |         uses: github/codeql-action/analyze@v3 | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								.github/workflows/demo_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/demo_deployment.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -22,12 +22,12 @@ jobs: | |||||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} |       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|         with: |         with: | ||||||
|           ref: dev |           ref: dev | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -36,14 +36,14 @@ jobs: | |||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|  |  | ||||||
|       - name: Build Demo |       - name: Build Demo | ||||||
|         run: ./node_modules/.bin/gulp build-demo |         run: yarn run-task build-demo | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|       - name: Deploy to Netlify |       - name: Deploy to Netlify | ||||||
|         id: deploy |         id: deploy | ||||||
|         run: | |         run: | | ||||||
|           npx -y netlify-cli@23.7.3 deploy --dir=demo/dist --prod |           npx -y netlify-cli deploy --dir=demo/dist --prod | ||||||
|         env: |         env: | ||||||
|           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} |           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | ||||||
|           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }} |           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }} | ||||||
| @@ -57,12 +57,12 @@ jobs: | |||||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} |       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|         with: |         with: | ||||||
|           ref: master |           ref: master | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -71,14 +71,14 @@ jobs: | |||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|  |  | ||||||
|       - name: Build Demo |       - name: Build Demo | ||||||
|         run: ./node_modules/.bin/gulp build-demo |         run: yarn run-task build-demo | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|       - name: Deploy to Netlify |       - name: Deploy to Netlify | ||||||
|         id: deploy |         id: deploy | ||||||
|         run: | |         run: | | ||||||
|           npx -y netlify-cli@23.7.3 deploy --dir=demo/dist --prod |           npx -y netlify-cli deploy --dir=demo/dist --prod | ||||||
|         env: |         env: | ||||||
|           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} |           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | ||||||
|           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }} |           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }} | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.github/workflows/design_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/design_deployment.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -16,10 +16,10 @@ jobs: | |||||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} |       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -28,14 +28,14 @@ jobs: | |||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|  |  | ||||||
|       - name: Build Gallery |       - name: Build Gallery | ||||||
|         run: ./node_modules/.bin/gulp build-gallery |         run: yarn run-task build-gallery | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|       - name: Deploy to Netlify |       - name: Deploy to Netlify | ||||||
|         id: deploy |         id: deploy | ||||||
|         run: | |         run: | | ||||||
|           npx -y netlify-cli@23.7.3 deploy --dir=gallery/dist --prod |           npx -y netlify-cli deploy --dir=gallery/dist --prod | ||||||
|         env: |         env: | ||||||
|           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} |           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | ||||||
|           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }} |           NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }} | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.github/workflows/design_preview.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/design_preview.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -21,10 +21,10 @@ jobs: | |||||||
|     if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview') |     if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview') | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -33,14 +33,14 @@ jobs: | |||||||
|         run: yarn install --immutable |         run: yarn install --immutable | ||||||
|  |  | ||||||
|       - name: Build Gallery |       - name: Build Gallery | ||||||
|         run: ./node_modules/.bin/gulp build-gallery |         run: yarn run-task build-gallery | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |  | ||||||
|       - name: Deploy preview to Netlify |       - name: Deploy preview to Netlify | ||||||
|         id: deploy |         id: deploy | ||||||
|         run: | |         run: | | ||||||
|           npx -y netlify-cli@23.7.3 deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}" \ |           npx -y netlify-cli deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}" \ | ||||||
|             --json > deploy_output.json |             --json > deploy_output.json | ||||||
|         env: |         env: | ||||||
|           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} |           NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/labeler.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/labeler.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -10,6 +10,6 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Apply labels |       - name: Apply labels | ||||||
|         uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 |         uses: actions/labeler@v5.0.0 | ||||||
|         with: |         with: | ||||||
|           sync-labels: true |           sync-labels: true | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							| @@ -9,7 +9,7 @@ jobs: | |||||||
|   lock: |   lock: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 |       - uses: dessant/lock-threads@v5.0.1 | ||||||
|         with: |         with: | ||||||
|           github-token: ${{ github.token }} |           github-token: ${{ github.token }} | ||||||
|           process-only: "issues, prs" |           process-only: "issues, prs" | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								.github/workflows/nightly.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/nightly.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -20,15 +20,15 @@ jobs: | |||||||
|       contents: write |       contents: write | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout the repository |       - name: Checkout the repository | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|  |  | ||||||
|       - name: Set up Python ${{ env.PYTHON_VERSION }} |       - name: Set up Python ${{ env.PYTHON_VERSION }} | ||||||
|         uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 |         uses: actions/setup-python@v5 | ||||||
|         with: |         with: | ||||||
|           python-version: ${{ env.PYTHON_VERSION }} |           python-version: ${{ env.PYTHON_VERSION }} | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -57,14 +57,14 @@ jobs: | |||||||
|         run: tar -czvf translations.tar.gz translations |         run: tar -czvf translations.tar.gz translations | ||||||
|  |  | ||||||
|       - name: Upload build artifacts |       - name: Upload build artifacts | ||||||
|         uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |         uses: actions/upload-artifact@v4.6.2 | ||||||
|         with: |         with: | ||||||
|           name: wheels |           name: wheels | ||||||
|           path: dist/home_assistant_frontend*.whl |           path: dist/home_assistant_frontend*.whl | ||||||
|           if-no-files-found: error |           if-no-files-found: error | ||||||
|  |  | ||||||
|       - name: Upload translations |       - name: Upload translations | ||||||
|         uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |         uses: actions/upload-artifact@v4.6.2 | ||||||
|         with: |         with: | ||||||
|           name: translations |           name: translations | ||||||
|           path: translations.tar.gz |           path: translations.tar.gz | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/relative-ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/relative-ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -17,7 +17,7 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Send bundle stats and build information to RelativeCI |       - name: Send bundle stats and build information to RelativeCI | ||||||
|         uses: relative-ci/agent-action@1707825cbfcc7452b2913d273414705415ae64d4 # v3.0.1 |         uses: relative-ci/agent-action@v3.0.0 | ||||||
|         with: |         with: | ||||||
|           key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} |           key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} | ||||||
|           token: ${{ github.token }} |           token: ${{ github.token }} | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/release-drafter.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/release-drafter.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -18,6 +18,6 @@ jobs: | |||||||
|       pull-requests: read |       pull-requests: read | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 |       - uses: release-drafter/release-drafter@v6.1.0 | ||||||
|         env: |         env: | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -23,10 +23,10 @@ jobs: | |||||||
|       contents: write # Required to upload release assets |       contents: write # Required to upload release assets | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout the repository |       - name: Checkout the repository | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|  |  | ||||||
|       - name: Set up Python ${{ env.PYTHON_VERSION }} |       - name: Set up Python ${{ env.PYTHON_VERSION }} | ||||||
|         uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 |         uses: actions/setup-python@v5 | ||||||
|         with: |         with: | ||||||
|           python-version: ${{ env.PYTHON_VERSION }} |           python-version: ${{ env.PYTHON_VERSION }} | ||||||
|  |  | ||||||
| @@ -34,7 +34,7 @@ jobs: | |||||||
|         uses: home-assistant/actions/helpers/verify-version@master |         uses: home-assistant/actions/helpers/verify-version@master | ||||||
|  |  | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -55,7 +55,7 @@ jobs: | |||||||
|           script/release |           script/release | ||||||
|  |  | ||||||
|       - name: Upload release assets |       - name: Upload release assets | ||||||
|         uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 |         uses: softprops/action-gh-release@v2.3.2 | ||||||
|         with: |         with: | ||||||
|           files: | |           files: | | ||||||
|             dist/*.whl |             dist/*.whl | ||||||
| @@ -73,9 +73,8 @@ jobs: | |||||||
|           version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' ) |           version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' ) | ||||||
|           echo "home-assistant-frontend==$version" > ./requirements.txt |           echo "home-assistant-frontend==$version" > ./requirements.txt | ||||||
|  |  | ||||||
|       # home-assistant/wheels doesn't support SHA pinning |  | ||||||
|       - name: Build wheels |       - name: Build wheels | ||||||
|         uses: home-assistant/wheels@2025.09.1 |         uses: home-assistant/wheels@2025.03.0 | ||||||
|         with: |         with: | ||||||
|           abi: cp313 |           abi: cp313 | ||||||
|           tag: musllinux_1_2 |           tag: musllinux_1_2 | ||||||
| @@ -91,9 +90,9 @@ jobs: | |||||||
|       contents: write # Required to upload release assets |       contents: write # Required to upload release assets | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout the repository |       - name: Checkout the repository | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -108,7 +107,7 @@ jobs: | |||||||
|       - name: Tar folder |       - name: Tar folder | ||||||
|         run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist . |         run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist . | ||||||
|       - name: Upload release asset |       - name: Upload release asset | ||||||
|         uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 |         uses: softprops/action-gh-release@v2.3.2 | ||||||
|         with: |         with: | ||||||
|           files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz |           files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz | ||||||
|  |  | ||||||
| @@ -120,9 +119,9 @@ jobs: | |||||||
|       contents: write # Required to upload release assets |       contents: write # Required to upload release assets | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout the repository |       - name: Checkout the repository | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 |         uses: actions/setup-node@v4.4.0 | ||||||
|         with: |         with: | ||||||
|           node-version-file: ".nvmrc" |           node-version-file: ".nvmrc" | ||||||
|           cache: yarn |           cache: yarn | ||||||
| @@ -137,6 +136,6 @@ jobs: | |||||||
|       - name: Tar folder |       - name: Tar folder | ||||||
|         run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build . |         run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build . | ||||||
|       - name: Upload release asset |       - name: Upload release asset | ||||||
|         uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 |         uses: softprops/action-gh-release@v2.3.2 | ||||||
|         with: |         with: | ||||||
|           files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz |           files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.github/workflows/restrict-task-creation.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/restrict-task-creation.yml
									
									
									
									
										vendored
									
									
								
							| @@ -9,10 +9,10 @@ jobs: | |||||||
|   check-authorization: |   check-authorization: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     # Only run if this is a Task issue type (from the issue form) |     # Only run if this is a Task issue type (from the issue form) | ||||||
|     if: github.event.issue.type.name == 'Task' |     if: github.event.issue.issue_type == 'Task' | ||||||
|     steps: |     steps: | ||||||
|       - name: Check if user is authorized |       - name: Check if user is authorized | ||||||
|         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 |         uses: actions/github-script@v7 | ||||||
|         with: |         with: | ||||||
|           script: | |           script: | | ||||||
|             const issueAuthor = context.payload.issue.user.login; |             const issueAuthor = context.payload.issue.user.login; | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -10,7 +10,7 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: 90 days stale policy |       - name: 90 days stale policy | ||||||
|         uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 |         uses: actions/stale@v9.1.0 | ||||||
|         with: |         with: | ||||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} |           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|           days-before-stale: 90 |           days-before-stale: 90 | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -14,7 +14,7 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout the repository |       - name: Checkout the repository | ||||||
|         uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 |         uses: actions/checkout@v4.2.2 | ||||||
|  |  | ||||||
|       - name: Upload Translations |       - name: Upload Translations | ||||||
|         run: | |         run: | | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -6,4 +6,4 @@ enableGlobalCache: false | |||||||
|  |  | ||||||
| nodeLinker: node-modules | nodeLinker: node-modules | ||||||
|  |  | ||||||
| yarnPath: .yarn/releases/yarn-4.10.3.cjs | yarnPath: .yarn/releases/yarn-4.9.2.cjs | ||||||
|   | |||||||
| @@ -1,8 +0,0 @@ | |||||||
| # People marked here will be automatically requested for a review |  | ||||||
| # when the code that they own is touched. |  | ||||||
| # https://github.com/blog/2392-introducing-code-owners |  | ||||||
| # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners |  | ||||||
|  |  | ||||||
| # Part of the frontend that mobile developper should review |  | ||||||
| src/external_app/ @bgoncal @TimoPtr |  | ||||||
| test/external_app/ @bgoncal @TimoPtr |  | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| import defineProvider from "@babel/helper-define-polyfill-provider"; | import defineProvider from "@babel/helper-define-polyfill-provider"; | ||||||
| import { join } from "node:path"; | import { join } from "node:path"; | ||||||
| import paths from "../paths.cjs"; | import paths from "../paths"; | ||||||
| 
 | 
 | ||||||
| const POLYFILL_DIR = join(paths.root_dir, "src/resources/polyfills"); | const POLYFILL_DIR = join(paths.root_dir, "src/resources/polyfills"); | ||||||
| 
 | 
 | ||||||
| @@ -1,42 +1,41 @@ | |||||||
| const path = require("path"); | import path from "node:path"; | ||||||
| const env = require("./env.cjs"); | import packageJson from "../package.json" assert { type: "json" }; | ||||||
| const paths = require("./paths.cjs"); | import { version } from "./env.ts"; | ||||||
| const { dependencies } = require("../package.json"); | import paths, { dirname } from "./paths.ts"; | ||||||
| 
 | 
 | ||||||
| const BABEL_PLUGINS = path.join(__dirname, "babel-plugins"); | const dependencies = packageJson.dependencies; | ||||||
|  | 
 | ||||||
|  | const BABEL_PLUGINS = path.join(dirname, "babel-plugins"); | ||||||
| 
 | 
 | ||||||
| // GitHub base URL to use for production source maps
 | // 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
 | // Nightly builds use the commit SHA, otherwise assumes there is a tag that matches the version
 | ||||||
| module.exports.sourceMapURL = () => { | export const sourceMapURL = () => { | ||||||
|   const ref = env.version().endsWith("dev") |   const ref = version().endsWith("dev") | ||||||
|     ? process.env.GITHUB_SHA || "dev" |     ? process.env.GITHUB_SHA || "dev" | ||||||
|     : env.version(); |     : version(); | ||||||
|   return `https://raw.githubusercontent.com/home-assistant/frontend/${ref}/`; |   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
 | // Files from NPM packages that we should replace with empty file
 | ||||||
| module.exports.emptyPackages = ({ isHassioBuild }) => | export const emptyPackages = ({ isHassioBuild }) => | ||||||
|   [ |   [ | ||||||
|     require.resolve("@vaadin/vaadin-material-styles/typography.js"), |     import.meta.resolve("@vaadin/vaadin-material-styles/typography.js"), | ||||||
|     require.resolve("@vaadin/vaadin-material-styles/font-icons.js"), |     import.meta.resolve("@vaadin/vaadin-material-styles/font-icons.js"), | ||||||
|     // Icons in supervisor conflict with icons in HA so we don't load.
 |     // Icons in supervisor conflict with icons in HA so we don't load.
 | ||||||
|     isHassioBuild && |     isHassioBuild && | ||||||
|       require.resolve( |       import.meta.resolve( | ||||||
|         path.resolve(paths.root_dir, "src/components/ha-icon.ts") |         path.resolve(paths.root_dir, "src/components/ha-icon.ts") | ||||||
|       ), |       ), | ||||||
|     isHassioBuild && |     isHassioBuild && | ||||||
|       require.resolve( |       import.meta.resolve( | ||||||
|         path.resolve(paths.root_dir, "src/components/ha-icon-picker.ts") |         path.resolve(paths.root_dir, "src/components/ha-icon-picker.ts") | ||||||
|       ), |       ), | ||||||
|   ].filter(Boolean); |   ].filter(Boolean); | ||||||
| 
 | 
 | ||||||
| module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | export const definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | ||||||
|   __DEV__: !isProdBuild, |   __DEV__: !isProdBuild, | ||||||
|   __BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"), |   __BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"), | ||||||
|   __VERSION__: JSON.stringify(env.version()), |   __VERSION__: JSON.stringify(version()), | ||||||
|   __DEMO__: false, |   __DEMO__: false, | ||||||
|   __SUPERVISOR__: false, |   __SUPERVISOR__: false, | ||||||
|   __BACKWARDS_COMPAT__: false, |   __BACKWARDS_COMPAT__: false, | ||||||
| @@ -53,7 +52,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | |||||||
|   ...defineOverlay, |   ...defineOverlay, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| module.exports.htmlMinifierOptions = { | export const htmlMinifierOptions = { | ||||||
|   caseSensitive: true, |   caseSensitive: true, | ||||||
|   collapseWhitespace: true, |   collapseWhitespace: true, | ||||||
|   conservativeCollapse: true, |   conservativeCollapse: true, | ||||||
| @@ -65,16 +64,16 @@ module.exports.htmlMinifierOptions = { | |||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({ | export const terserOptions = ({ latestBuild, isTestBuild }) => ({ | ||||||
|   safari10: !latestBuild, |   safari10: !latestBuild, | ||||||
|   ecma: latestBuild ? 2015 : 5, |   ecma: latestBuild ? (2015 as const) : (5 as const), | ||||||
|   module: latestBuild, |   module: latestBuild, | ||||||
|   format: { comments: false }, |   format: { comments: false }, | ||||||
|   sourceMap: !isTestBuild, |   sourceMap: !isTestBuild, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** @type {import('@rspack/core').SwcLoaderOptions} */ | /** @type {import('@rspack/core').SwcLoaderOptions} */ | ||||||
| module.exports.swcOptions = () => ({ | export const swcOptions = () => ({ | ||||||
|   jsc: { |   jsc: { | ||||||
|     loose: true, |     loose: true, | ||||||
|     externalHelpers: true, |     externalHelpers: true, | ||||||
| @@ -86,11 +85,16 @@ module.exports.swcOptions = () => ({ | |||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| module.exports.babelOptions = ({ | export const babelOptions = ({ | ||||||
|   latestBuild, |   latestBuild, | ||||||
|   isProdBuild, |   isProdBuild, | ||||||
|   isTestBuild, |   isTestBuild, | ||||||
|   sw, |   sw, | ||||||
|  | }: { | ||||||
|  |   latestBuild?: boolean; | ||||||
|  |   isProdBuild?: boolean; | ||||||
|  |   isTestBuild?: boolean; | ||||||
|  |   sw?: boolean; | ||||||
| }) => ({ | }) => ({ | ||||||
|   babelrc: false, |   babelrc: false, | ||||||
|   compact: false, |   compact: false, | ||||||
| @@ -137,7 +141,7 @@ module.exports.babelOptions = ({ | |||||||
|           "@polymer/polymer/lib/utils/html-tag.js": ["html"], |           "@polymer/polymer/lib/utils/html-tag.js": ["html"], | ||||||
|         }, |         }, | ||||||
|         strictCSS: true, |         strictCSS: true, | ||||||
|         htmlMinifier: module.exports.htmlMinifierOptions, |         htmlMinifier: htmlMinifierOptions, | ||||||
|         failOnError: false, // we can turn this off in case of false positives
 |         failOnError: false, // we can turn this off in case of false positives
 | ||||||
|       }, |       }, | ||||||
|     ], |     ], | ||||||
| @@ -160,7 +164,7 @@ module.exports.babelOptions = ({ | |||||||
|       // themselves to prevent self-injection.
 |       // themselves to prevent self-injection.
 | ||||||
|       plugins: [ |       plugins: [ | ||||||
|         [ |         [ | ||||||
|           path.join(BABEL_PLUGINS, "custom-polyfill-plugin.js"), |           path.join(BABEL_PLUGINS, "custom-polyfill-plugin.ts"), | ||||||
|           { method: "usage-global" }, |           { method: "usage-global" }, | ||||||
|         ], |         ], | ||||||
|       ], |       ], | ||||||
| @@ -183,6 +187,7 @@ module.exports.babelOptions = ({ | |||||||
|       include: /\/node_modules\//, |       include: /\/node_modules\//, | ||||||
|       exclude: [ |       exclude: [ | ||||||
|         "element-internals-polyfill", |         "element-internals-polyfill", | ||||||
|  |         "@shoelace-style", | ||||||
|         "@?lit(?:-labs|-element|-html)?", |         "@?lit(?:-labs|-element|-html)?", | ||||||
|       ].map((p) => new RegExp(`/node_modules/${p}/`)), |       ].map((p) => new RegExp(`/node_modules/${p}/`)), | ||||||
|     }, |     }, | ||||||
| @@ -220,8 +225,20 @@ const publicPath = (latestBuild, root = "") => | |||||||
|   } |   } | ||||||
|   */ |   */ | ||||||
| 
 | 
 | ||||||
| module.exports.config = { | export const config = { | ||||||
|   app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) { |   app({ | ||||||
|  |     isProdBuild, | ||||||
|  |     latestBuild, | ||||||
|  |     isStatsBuild, | ||||||
|  |     isTestBuild, | ||||||
|  |     isWDS, | ||||||
|  |   }: { | ||||||
|  |     isProdBuild?: boolean; | ||||||
|  |     latestBuild?: boolean; | ||||||
|  |     isStatsBuild?: boolean; | ||||||
|  |     isTestBuild?: boolean; | ||||||
|  |     isWDS?: boolean; | ||||||
|  |   }) { | ||||||
|     return { |     return { | ||||||
|       name: "frontend" + nameSuffix(latestBuild), |       name: "frontend" + nameSuffix(latestBuild), | ||||||
|       entry: { |       entry: { | ||||||
| @@ -256,7 +273,7 @@ module.exports.config = { | |||||||
|       outputPath: outputPath(paths.demo_output_root, latestBuild), |       outputPath: outputPath(paths.demo_output_root, latestBuild), | ||||||
|       publicPath: publicPath(latestBuild), |       publicPath: publicPath(latestBuild), | ||||||
|       defineOverlay: { |       defineOverlay: { | ||||||
|         __VERSION__: JSON.stringify(`DEMO-${env.version()}`), |         __VERSION__: JSON.stringify(`DEMO-${version()}`), | ||||||
|         __DEMO__: true, |         __DEMO__: true, | ||||||
|       }, |       }, | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
| @@ -266,7 +283,7 @@ module.exports.config = { | |||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   cast({ isProdBuild, latestBuild }) { |   cast({ isProdBuild, latestBuild }) { | ||||||
|     const entry = { |     const entry: Record<string, string> = { | ||||||
|       launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"), |       launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"), | ||||||
|       media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"), |       media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"), | ||||||
|     }; |     }; | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| const fs = require("fs"); |  | ||||||
| const path = require("path"); |  | ||||||
| const paths = require("./paths.cjs"); |  | ||||||
|  |  | ||||||
| const isTrue = (value) => value === "1" || value?.toLowerCase() === "true"; |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|   isProdBuild() { |  | ||||||
|     return ( |  | ||||||
|       process.env.NODE_ENV === "production" || module.exports.isStatsBuild() |  | ||||||
|     ); |  | ||||||
|   }, |  | ||||||
|   isStatsBuild() { |  | ||||||
|     return isTrue(process.env.STATS); |  | ||||||
|   }, |  | ||||||
|   isTestBuild() { |  | ||||||
|     return isTrue(process.env.IS_TEST); |  | ||||||
|   }, |  | ||||||
|   isNetlify() { |  | ||||||
|     return isTrue(process.env.NETLIFY); |  | ||||||
|   }, |  | ||||||
|   version() { |  | ||||||
|     const version = fs |  | ||||||
|       .readFileSync(path.resolve(paths.root_dir, "pyproject.toml"), "utf8") |  | ||||||
|       .match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/); |  | ||||||
|     if (!version) { |  | ||||||
|       throw Error("Version not found"); |  | ||||||
|     } |  | ||||||
|     return version[1]; |  | ||||||
|   }, |  | ||||||
|   isDevContainer() { |  | ||||||
|     return isTrue(process.env.DEV_CONTAINER); |  | ||||||
|   }, |  | ||||||
| }; |  | ||||||
							
								
								
									
										21
									
								
								build-scripts/env.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								build-scripts/env.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import fs from "node:fs"; | ||||||
|  | import path from "node:path"; | ||||||
|  | import paths from "./paths.ts"; | ||||||
|  |  | ||||||
|  | const isTrue = (value) => value === "1" || value?.toLowerCase() === "true"; | ||||||
|  |  | ||||||
|  | export const isProdBuild = () => | ||||||
|  |   process.env.NODE_ENV === "production" || isStatsBuild(); | ||||||
|  | export const isStatsBuild = () => isTrue(process.env.STATS); | ||||||
|  | export const isTestBuild = () => isTrue(process.env.IS_TEST); | ||||||
|  | export const isNetlify = () => isTrue(process.env.NETLIFY); | ||||||
|  | export const version = () => { | ||||||
|  |   const pyProjectVersion = fs | ||||||
|  |     .readFileSync(path.resolve(paths.root_dir, "pyproject.toml"), "utf8") | ||||||
|  |     .match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/); | ||||||
|  |   if (!pyProjectVersion) { | ||||||
|  |     throw Error("Version not found"); | ||||||
|  |   } | ||||||
|  |   return pyProjectVersion[1]; | ||||||
|  | }; | ||||||
|  | export const isDevContainer = () => isTrue(process.env.DEV_CONTAINER); | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| import gulp from "gulp"; |  | ||||||
| import env from "../env.cjs"; |  | ||||||
| import "./clean.js"; |  | ||||||
| import "./compress.js"; |  | ||||||
| import "./entry-html.js"; |  | ||||||
| import "./gather-static.js"; |  | ||||||
| import "./gen-icons-json.js"; |  | ||||||
| import "./locale-data.js"; |  | ||||||
| import "./service-worker.js"; |  | ||||||
| import "./translations.js"; |  | ||||||
| import "./rspack.js"; |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "develop-app", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "development"; |  | ||||||
|     }, |  | ||||||
|     "clean", |  | ||||||
|     gulp.parallel( |  | ||||||
|       "gen-service-worker-app-dev", |  | ||||||
|       "gen-icons-json", |  | ||||||
|       "gen-pages-app-dev", |  | ||||||
|       "build-translations", |  | ||||||
|       "build-locale-data" |  | ||||||
|     ), |  | ||||||
|     "copy-static-app", |  | ||||||
|     "rspack-watch-app" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-app", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "production"; |  | ||||||
|     }, |  | ||||||
|     "clean", |  | ||||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), |  | ||||||
|     "copy-static-app", |  | ||||||
|     "rspack-prod-app", |  | ||||||
|     gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod"), |  | ||||||
|     // Don't compress running tests |  | ||||||
|     ...(env.isTestBuild() || env.isStatsBuild() ? [] : ["compress-app"]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "analyze-app", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.STATS = "1"; |  | ||||||
|     }, |  | ||||||
|     "clean", |  | ||||||
|     "rspack-prod-app" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
							
								
								
									
										54
									
								
								build-scripts/gulp/app.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								build-scripts/gulp/app.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | import { parallel, series } from "gulp"; | ||||||
|  | import { isStatsBuild, isTestBuild } from "../env.ts"; | ||||||
|  | import { clean } from "./clean.ts"; | ||||||
|  | import { compressApp } from "./compress.ts"; | ||||||
|  | import { genPagesAppDev, genPagesAppProd } from "./entry-html.ts"; | ||||||
|  | import { copyStaticApp } from "./gather-static.ts"; | ||||||
|  | import { genIconsJson } from "./gen-icons-json.ts"; | ||||||
|  | import { buildLocaleData } from "./locale-data.ts"; | ||||||
|  | import { rspackProdApp, rspackWatchApp } from "./rspack.ts"; | ||||||
|  | import { | ||||||
|  |   genServiceWorkerAppDev, | ||||||
|  |   genServiceWorkerAppProd, | ||||||
|  | } from "./service-worker.ts"; | ||||||
|  | import { buildTranslations } from "./translations.ts"; | ||||||
|  |  | ||||||
|  | // develop-app | ||||||
|  | export const developApp = series( | ||||||
|  |   async () => { | ||||||
|  |     process.env.NODE_ENV = "development"; | ||||||
|  |   }, | ||||||
|  |   clean, | ||||||
|  |   parallel( | ||||||
|  |     genServiceWorkerAppDev, | ||||||
|  |     genIconsJson, | ||||||
|  |     genPagesAppDev, | ||||||
|  |     buildTranslations, | ||||||
|  |     buildLocaleData | ||||||
|  |   ), | ||||||
|  |   copyStaticApp, | ||||||
|  |   rspackWatchApp | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // build-app | ||||||
|  | export const buildApp = series( | ||||||
|  |   async () => { | ||||||
|  |     process.env.NODE_ENV = "production"; | ||||||
|  |   }, | ||||||
|  |   clean, | ||||||
|  |   parallel(genIconsJson, buildTranslations, buildLocaleData), | ||||||
|  |   copyStaticApp, | ||||||
|  |   rspackProdApp, | ||||||
|  |   parallel(genPagesAppProd, genServiceWorkerAppProd), | ||||||
|  |   // Don't compress running tests | ||||||
|  |   ...(isTestBuild() || isStatsBuild() ? [] : [compressApp]) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // analyze-app | ||||||
|  | export const analyzeApp = series( | ||||||
|  |   async () => { | ||||||
|  |     process.env.STATS = "1"; | ||||||
|  |   }, | ||||||
|  |   clean, | ||||||
|  |   rspackProdApp | ||||||
|  | ); | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| import gulp from "gulp"; |  | ||||||
| import "./clean.js"; |  | ||||||
| import "./entry-html.js"; |  | ||||||
| import "./gather-static.js"; |  | ||||||
| import "./service-worker.js"; |  | ||||||
| import "./translations.js"; |  | ||||||
| import "./rspack.js"; |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "develop-cast", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "development"; |  | ||||||
|     }, |  | ||||||
|     "clean-cast", |  | ||||||
|     "translations-enable-merge-backend", |  | ||||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), |  | ||||||
|     "copy-static-cast", |  | ||||||
|     "gen-pages-cast-dev", |  | ||||||
|     "rspack-dev-server-cast" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-cast", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "production"; |  | ||||||
|     }, |  | ||||||
|     "clean-cast", |  | ||||||
|     "translations-enable-merge-backend", |  | ||||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), |  | ||||||
|     "copy-static-cast", |  | ||||||
|     "rspack-prod-cast", |  | ||||||
|     "gen-pages-cast-prod" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
							
								
								
									
										38
									
								
								build-scripts/gulp/cast.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								build-scripts/gulp/cast.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | import { parallel, series } from "gulp"; | ||||||
|  | import { cleanCast } from "./clean.ts"; | ||||||
|  | import { genPagesCastDev, genPagesCastProd } from "./entry-html.ts"; | ||||||
|  | import { copyStaticCast } from "./gather-static.ts"; | ||||||
|  | import { genIconsJson } from "./gen-icons-json.ts"; | ||||||
|  | import { buildLocaleData } from "./locale-data.ts"; | ||||||
|  | import { rspackDevServerCast, rspackProdCast } from "./rspack.ts"; | ||||||
|  | import "./service-worker.ts"; | ||||||
|  | import { | ||||||
|  |   buildTranslations, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  | } from "./translations.ts"; | ||||||
|  |  | ||||||
|  | // develop-cast | ||||||
|  | export const developCast = series( | ||||||
|  |   async () => { | ||||||
|  |     process.env.NODE_ENV = "development"; | ||||||
|  |   }, | ||||||
|  |   cleanCast, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  |   parallel(genIconsJson, buildTranslations, buildLocaleData), | ||||||
|  |   copyStaticCast, | ||||||
|  |   genPagesCastDev, | ||||||
|  |   rspackDevServerCast | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // build-cast | ||||||
|  | export const buildCast = series( | ||||||
|  |   async () => { | ||||||
|  |     process.env.NODE_ENV = "production"; | ||||||
|  |   }, | ||||||
|  |   cleanCast, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  |   parallel(genIconsJson, buildTranslations, buildLocaleData), | ||||||
|  |   copyStaticCast, | ||||||
|  |   rspackProdCast, | ||||||
|  |   genPagesCastProd | ||||||
|  | ); | ||||||
| @@ -1,51 +0,0 @@ | |||||||
| import { deleteSync } from "del"; |  | ||||||
| import gulp from "gulp"; |  | ||||||
| import paths from "../paths.cjs"; |  | ||||||
| import "./translations.js"; |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "clean", |  | ||||||
|   gulp.parallel("clean-translations", async () => |  | ||||||
|     deleteSync([paths.app_output_root, paths.build_dir]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "clean-demo", |  | ||||||
|   gulp.parallel("clean-translations", async () => |  | ||||||
|     deleteSync([paths.demo_output_root, paths.build_dir]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "clean-cast", |  | ||||||
|   gulp.parallel("clean-translations", async () => |  | ||||||
|     deleteSync([paths.cast_output_root, paths.build_dir]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task("clean-hassio", async () => |  | ||||||
|   deleteSync([paths.hassio_output_root, paths.build_dir]) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "clean-gallery", |  | ||||||
|   gulp.parallel("clean-translations", async () => |  | ||||||
|     deleteSync([ |  | ||||||
|       paths.gallery_output_root, |  | ||||||
|       paths.gallery_build, |  | ||||||
|       paths.build_dir, |  | ||||||
|     ]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "clean-landing-page", |  | ||||||
|   gulp.parallel("clean-translations", async () => |  | ||||||
|     deleteSync([ |  | ||||||
|       paths.landingPage_output_root, |  | ||||||
|       paths.landingPage_build, |  | ||||||
|       paths.build_dir, |  | ||||||
|     ]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
							
								
								
									
										31
									
								
								build-scripts/gulp/clean.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								build-scripts/gulp/clean.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import { deleteSync } from "del"; | ||||||
|  | import { parallel } from "gulp"; | ||||||
|  | import paths from "../paths.ts"; | ||||||
|  | import { cleanTranslations } from "./translations.ts"; | ||||||
|  |  | ||||||
|  | export const clean = parallel(cleanTranslations, async () => | ||||||
|  |   deleteSync([paths.app_output_root, paths.build_dir]) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | export const cleanDemo = parallel(cleanTranslations, async () => | ||||||
|  |   deleteSync([paths.demo_output_root, paths.build_dir]) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | export const cleanCast = parallel(cleanTranslations, async () => | ||||||
|  |   deleteSync([paths.cast_output_root, paths.build_dir]) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | export const cleanHassio = async () => | ||||||
|  |   deleteSync([paths.hassio_output_root, paths.build_dir]); | ||||||
|  |  | ||||||
|  | export const cleanGallery = parallel(cleanTranslations, async () => | ||||||
|  |   deleteSync([paths.gallery_output_root, paths.gallery_build, paths.build_dir]) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | export const cleanLandingPage = parallel(cleanTranslations, async () => | ||||||
|  |   deleteSync([ | ||||||
|  |     paths.landingPage_output_root, | ||||||
|  |     paths.landingPage_build, | ||||||
|  |     paths.build_dir, | ||||||
|  |   ]) | ||||||
|  | ); | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| // Tasks to compress
 | // Tasks to compress
 | ||||||
| 
 | 
 | ||||||
| import { constants } from "node:zlib"; | import { dest, parallel, src } from "gulp"; | ||||||
| import gulp from "gulp"; |  | ||||||
| import brotli from "gulp-brotli"; | import brotli from "gulp-brotli"; | ||||||
| import zopfli from "gulp-zopfli-green"; | import zopfli from "gulp-zopfli-green"; | ||||||
| import paths from "../paths.cjs"; | import { constants } from "node:zlib"; | ||||||
|  | import paths from "../paths.ts"; | ||||||
| 
 | 
 | ||||||
| const filesGlob = "*.{js,json,css,svg,xml}"; | const filesGlob = "*.{js,json,css,svg,xml}"; | ||||||
| const brotliOptions = { | const brotliOptions = { | ||||||
| @@ -16,17 +16,15 @@ const brotliOptions = { | |||||||
| const zopfliOptions = { threshold: 150 }; | const zopfliOptions = { threshold: 150 }; | ||||||
| 
 | 
 | ||||||
| const compressModern = (rootDir, modernDir, compress) => | const compressModern = (rootDir, modernDir, compress) => | ||||||
|   gulp |   src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], { | ||||||
|     .src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], { |  | ||||||
|     base: rootDir, |     base: rootDir, | ||||||
|     allowEmpty: true, |     allowEmpty: true, | ||||||
|   }) |   }) | ||||||
|     .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) |     .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) | ||||||
|     .pipe(gulp.dest(rootDir)); |     .pipe(dest(rootDir)); | ||||||
| 
 | 
 | ||||||
| const compressOther = (rootDir, modernDir, compress) => | const compressOther = (rootDir, modernDir, compress) => | ||||||
|   gulp |   src( | ||||||
|     .src( |  | ||||||
|     [ |     [ | ||||||
|       `${rootDir}/**/${filesGlob}`, |       `${rootDir}/**/${filesGlob}`, | ||||||
|       `!${modernDir}/**/${filesGlob}`, |       `!${modernDir}/**/${filesGlob}`, | ||||||
| @@ -36,7 +34,7 @@ const compressOther = (rootDir, modernDir, compress) => | |||||||
|     { base: rootDir, allowEmpty: true } |     { base: rootDir, allowEmpty: true } | ||||||
|   ) |   ) | ||||||
|     .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) |     .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) | ||||||
|     .pipe(gulp.dest(rootDir)); |     .pipe(dest(rootDir)); | ||||||
| 
 | 
 | ||||||
| const compressAppModernBrotli = () => | const compressAppModernBrotli = () => | ||||||
|   compressModern(paths.app_output_root, paths.app_output_latest, "brotli"); |   compressModern(paths.app_output_root, paths.app_output_latest, "brotli"); | ||||||
| @@ -66,21 +64,16 @@ const compressHassioOtherBrotli = () => | |||||||
| const compressHassioOtherZopfli = () => | const compressHassioOtherZopfli = () => | ||||||
|   compressOther(paths.hassio_output_root, paths.hassio_output_latest, "zopfli"); |   compressOther(paths.hassio_output_root, paths.hassio_output_latest, "zopfli"); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const compressApp = parallel( | ||||||
|   "compress-app", |  | ||||||
|   gulp.parallel( |  | ||||||
|   compressAppModernBrotli, |   compressAppModernBrotli, | ||||||
|   compressAppOtherBrotli, |   compressAppOtherBrotli, | ||||||
|   compressAppModernZopfli, |   compressAppModernZopfli, | ||||||
|   compressAppOtherZopfli |   compressAppOtherZopfli | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| gulp.task( | 
 | ||||||
|   "compress-hassio", | export const compressHassio = parallel( | ||||||
|   gulp.parallel( |  | ||||||
|   compressHassioModernBrotli, |   compressHassioModernBrotli, | ||||||
|   compressHassioOtherBrotli, |   compressHassioOtherBrotli, | ||||||
|   compressHassioModernZopfli, |   compressHassioModernZopfli, | ||||||
|   compressHassioOtherZopfli |   compressHassioOtherZopfli | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| import gulp from "gulp"; |  | ||||||
| import "./clean.js"; |  | ||||||
| import "./entry-html.js"; |  | ||||||
| import "./gather-static.js"; |  | ||||||
| import "./gen-icons-json.js"; |  | ||||||
| import "./service-worker.js"; |  | ||||||
| import "./translations.js"; |  | ||||||
| import "./rspack.js"; |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "develop-demo", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "development"; |  | ||||||
|     }, |  | ||||||
|     "clean-demo", |  | ||||||
|     "translations-enable-merge-backend", |  | ||||||
|     gulp.parallel( |  | ||||||
|       "gen-icons-json", |  | ||||||
|       "gen-pages-demo-dev", |  | ||||||
|       "build-translations", |  | ||||||
|       "build-locale-data" |  | ||||||
|     ), |  | ||||||
|     "copy-static-demo", |  | ||||||
|     "rspack-dev-server-demo" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-demo", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "production"; |  | ||||||
|     }, |  | ||||||
|     "clean-demo", |  | ||||||
|     // Cast needs to be backwards compatible and older HA has no translations |  | ||||||
|     "translations-enable-merge-backend", |  | ||||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), |  | ||||||
|     "copy-static-demo", |  | ||||||
|     "rspack-prod-demo", |  | ||||||
|     "gen-pages-demo-prod" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "analyze-demo", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.STATS = "1"; |  | ||||||
|     }, |  | ||||||
|     "clean", |  | ||||||
|     "rspack-prod-demo" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
							
								
								
									
										47
									
								
								build-scripts/gulp/demo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								build-scripts/gulp/demo.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | import { parallel, series } from "gulp"; | ||||||
|  | import { clean, cleanDemo } from "./clean.ts"; | ||||||
|  | import { genPagesDemoDev, genPagesDemoProd } from "./entry-html.ts"; | ||||||
|  | import { copyStaticDemo } from "./gather-static.ts"; | ||||||
|  | import { genIconsJson } from "./gen-icons-json.ts"; | ||||||
|  | import { buildLocaleData } from "./locale-data.ts"; | ||||||
|  | import { rspackDevServerDemo, rspackProdDemo } from "./rspack.ts"; | ||||||
|  | import "./service-worker.ts"; | ||||||
|  | import { | ||||||
|  |   buildTranslations, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  | } from "./translations.ts"; | ||||||
|  |  | ||||||
|  | // develop-demo | ||||||
|  | export const developDemo = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.NODE_ENV = "development"; | ||||||
|  |   }, | ||||||
|  |   cleanDemo, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  |   parallel(genIconsJson, genPagesDemoDev, buildTranslations, buildLocaleData), | ||||||
|  |   copyStaticDemo, | ||||||
|  |   rspackDevServerDemo | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // build-demo | ||||||
|  | export const buildDemo = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.NODE_ENV = "production"; | ||||||
|  |   }, | ||||||
|  |   cleanDemo, | ||||||
|  |   // Cast needs to be backwards compatible and older HA has no translations | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  |   parallel(genIconsJson, buildTranslations, buildLocaleData), | ||||||
|  |   copyStaticDemo, | ||||||
|  |   rspackProdDemo, | ||||||
|  |   genPagesDemoProd | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // analyze-demo | ||||||
|  | export const analyzeDemo = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.STATS = "1"; | ||||||
|  |   }, | ||||||
|  |   clean, | ||||||
|  |   rspackProdDemo | ||||||
|  | ); | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| import fs from "fs/promises"; |  | ||||||
| import gulp from "gulp"; |  | ||||||
| import path from "path"; |  | ||||||
| import mapStream from "map-stream"; |  | ||||||
| import transform from "gulp-json-transform"; |  | ||||||
| import { LokaliseApi } from "@lokalise/node-api"; | import { LokaliseApi } from "@lokalise/node-api"; | ||||||
|  | import { dest, series, src } from "gulp"; | ||||||
|  | import transform from "gulp-json-transform"; | ||||||
| import JSZip from "jszip"; | import JSZip from "jszip"; | ||||||
|  | import mapStream from "map-stream"; | ||||||
|  | import fs from "node:fs/promises"; | ||||||
|  | import path from "node:path"; | ||||||
| 
 | 
 | ||||||
| const inDir = "translations"; | const inDir = "translations"; | ||||||
| const inDirFrontend = `${inDir}/frontend`; | const inDirFrontend = `${inDir}/frontend`; | ||||||
| @@ -12,11 +12,14 @@ const inDirBackend = `${inDir}/backend`; | |||||||
| const srcMeta = "src/translations/translationMetadata.json"; | const srcMeta = "src/translations/translationMetadata.json"; | ||||||
| const encoding = "utf8"; | const encoding = "utf8"; | ||||||
| 
 | 
 | ||||||
| function hasHtml(data) { | const hasHtml = (data) => /<\S*>/i.test(data); | ||||||
|   return /<\S*>/i.test(data); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| function recursiveCheckHasHtml(file, data, errors, recKey) { | const recursiveCheckHasHtml = ( | ||||||
|  |   file, | ||||||
|  |   data, | ||||||
|  |   errors: string[], | ||||||
|  |   recKey?: string | ||||||
|  | ) => { | ||||||
|   Object.keys(data).forEach(function (key) { |   Object.keys(data).forEach(function (key) { | ||||||
|     if (typeof data[key] === "object") { |     if (typeof data[key] === "object") { | ||||||
|       const nextRecKey = recKey ? `${recKey}.${key}` : key; |       const nextRecKey = recKey ? `${recKey}.${key}` : key; | ||||||
| @@ -25,9 +28,9 @@ function recursiveCheckHasHtml(file, data, errors, recKey) { | |||||||
|       errors.push(`HTML found in ${file.path} at key ${recKey}.${key}`); |       errors.push(`HTML found in ${file.path} at key ${recKey}.${key}`); | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function checkHtml() { | const checkHtml = () => { | ||||||
|   const errors = []; |   const errors = []; | ||||||
| 
 | 
 | ||||||
|   return mapStream(function (file, cb) { |   return mapStream(function (file, cb) { | ||||||
| @@ -44,9 +47,9 @@ function checkHtml() { | |||||||
|     } |     } | ||||||
|     cb(error, file); |     cb(error, file); | ||||||
|   }); |   }); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function convertBackendTranslations(data, _file) { | const convertBackendTranslationsTransform = (data, _file) => { | ||||||
|   const output = { component: {} }; |   const output = { component: {} }; | ||||||
|   if (!data.component) { |   if (!data.component) { | ||||||
|     return output; |     return output; | ||||||
| @@ -62,25 +65,22 @@ function convertBackendTranslations(data, _file) { | |||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|   return output; |   return output; | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("convert-backend-translations", function () { | const convertBackendTranslations = () => | ||||||
|   return gulp |   src([`${inDirBackend}/*.json`]) | ||||||
|     .src([`${inDirBackend}/*.json`]) |     .pipe( | ||||||
|     .pipe(transform((data, file) => convertBackendTranslations(data, file))) |       transform((data, file) => convertBackendTranslationsTransform(data, file)) | ||||||
|     .pipe(gulp.dest(inDirBackend)); |     ) | ||||||
| }); |     .pipe(dest(inDirBackend)); | ||||||
| 
 | 
 | ||||||
| gulp.task("check-translations-html", function () { | const checkTranslationsHtml = () => | ||||||
|   return gulp |   src([`${inDirFrontend}/*.json`, `${inDirBackend}/*.json`]).pipe(checkHtml()); | ||||||
|     .src([`${inDirFrontend}/*.json`, `${inDirBackend}/*.json`]) |  | ||||||
|     .pipe(checkHtml()); |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| gulp.task("check-all-files-exist", async function () { | const checkAllFilesExist = async () => { | ||||||
|   const file = await fs.readFile(srcMeta, { encoding }); |   const file = await fs.readFile(srcMeta, { encoding }); | ||||||
|   const meta = JSON.parse(file); |   const meta = JSON.parse(file); | ||||||
|   const writings = []; |   const writings: Promise<void>[] = []; | ||||||
|   Object.keys(meta).forEach((lang) => { |   Object.keys(meta).forEach((lang) => { | ||||||
|     writings.push( |     writings.push( | ||||||
|       fs.writeFile(`${inDirFrontend}/${lang}.json`, JSON.stringify({}), { |       fs.writeFile(`${inDirFrontend}/${lang}.json`, JSON.stringify({}), { | ||||||
| @@ -92,14 +92,14 @@ gulp.task("check-all-files-exist", async function () { | |||||||
|     ); |     ); | ||||||
|   }); |   }); | ||||||
|   await Promise.allSettled(writings); |   await Promise.allSettled(writings); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| const lokaliseProjects = { | const lokaliseProjects = { | ||||||
|   backend: "130246255a974bd3b5e8a1.51616605", |   backend: "130246255a974bd3b5e8a1.51616605", | ||||||
|   frontend: "3420425759f6d6d241f598.13594006", |   frontend: "3420425759f6d6d241f598.13594006", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("fetch-lokalise", async function () { | const fetchLokalise = async () => { | ||||||
|   let apiKey; |   let apiKey; | ||||||
|   try { |   try { | ||||||
|     apiKey = |     apiKey = | ||||||
| @@ -168,14 +168,11 @@ gulp.task("fetch-lokalise", async function () { | |||||||
|         }) |         }) | ||||||
|     ) |     ) | ||||||
|   ); |   ); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const downloadTranslations = series( | ||||||
|   "download-translations", |   fetchLokalise, | ||||||
|   gulp.series( |   convertBackendTranslations, | ||||||
|     "fetch-lokalise", |   checkTranslationsHtml, | ||||||
|     "convert-backend-translations", |   checkAllFilesExist | ||||||
|     "check-translations-html", |  | ||||||
|     "check-all-files-exist" |  | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| @@ -6,12 +6,11 @@ import { | |||||||
|   getPreUserAgentRegexes, |   getPreUserAgentRegexes, | ||||||
| } from "browserslist-useragent-regexp"; | } from "browserslist-useragent-regexp"; | ||||||
| import fs from "fs-extra"; | import fs from "fs-extra"; | ||||||
| import gulp from "gulp"; |  | ||||||
| import { minify } from "html-minifier-terser"; | import { minify } from "html-minifier-terser"; | ||||||
| import template from "lodash.template"; | import template from "lodash.template"; | ||||||
| import { dirname, extname, resolve } from "node:path"; | import { dirname, extname, resolve } from "node:path"; | ||||||
| import { htmlMinifierOptions, terserOptions } from "../bundle.cjs"; | import { htmlMinifierOptions, terserOptions } from "../bundle.ts"; | ||||||
| import paths from "../paths.cjs"; | import paths from "../paths.ts"; | ||||||
| 
 | 
 | ||||||
| // macOS companion app has no way to obtain the Safari version used by WKWebView,
 | // macOS companion app has no way to obtain the Safari version used by WKWebView,
 | ||||||
| // and it is not in the default user agent string. So we add an additional regex
 | // and it is not in the default user agent string. So we add an additional regex
 | ||||||
| @@ -34,9 +33,9 @@ const getCommonTemplateVars = () => { | |||||||
|     mobileToDesktop: true, |     mobileToDesktop: true, | ||||||
|     throwOnMissing: true, |     throwOnMissing: true, | ||||||
|   }); |   }); | ||||||
|   const minSafariVersion = browserRegexes.find( |   const minSafariVersion = | ||||||
|     (regex) => regex.family === "safari" |     browserRegexes.find((regex) => regex.family === "safari") | ||||||
|   )?.matchedVersions[0][0]; |       ?.matchedVersions[0][0] ?? 18; | ||||||
|   const minMacOSVersion = SAFARI_TO_MACOS[minSafariVersion]; |   const minMacOSVersion = SAFARI_TO_MACOS[minSafariVersion]; | ||||||
|   if (!minMacOSVersion) { |   if (!minMacOSVersion) { | ||||||
|     throw Error( |     throw Error( | ||||||
| @@ -106,10 +105,10 @@ const genPagesDevTask = | |||||||
|         resolve(inputRoot, inputSub, `${page}.template`), |         resolve(inputRoot, inputSub, `${page}.template`), | ||||||
|         { |         { | ||||||
|           ...commonVars, |           ...commonVars, | ||||||
|           latestEntryJS: entries.map( |           latestEntryJS: (entries as string[]).map( | ||||||
|             (entry) => `${publicRoot}/frontend_latest/${entry}.js` |             (entry) => `${publicRoot}/frontend_latest/${entry}.js` | ||||||
|           ), |           ), | ||||||
|           es5EntryJS: entries.map( |           es5EntryJS: (entries as string[]).map( | ||||||
|             (entry) => `${publicRoot}/frontend_es5/${entry}.js` |             (entry) => `${publicRoot}/frontend_es5/${entry}.js` | ||||||
|           ), |           ), | ||||||
|           latestCustomPanelJS: `${publicRoot}/frontend_latest/custom-panel.js`, |           latestCustomPanelJS: `${publicRoot}/frontend_latest/custom-panel.js`, | ||||||
| @@ -128,7 +127,7 @@ const genPagesProdTask = | |||||||
|     inputRoot, |     inputRoot, | ||||||
|     outputRoot, |     outputRoot, | ||||||
|     outputLatest, |     outputLatest, | ||||||
|     outputES5, |     outputES5?: string, | ||||||
|     inputSub = "src/html" |     inputSub = "src/html" | ||||||
|   ) => |   ) => | ||||||
|   async () => { |   async () => { | ||||||
| @@ -139,14 +138,18 @@ const genPagesProdTask = | |||||||
|       ? fs.readJsonSync(resolve(outputES5, "manifest.json")) |       ? fs.readJsonSync(resolve(outputES5, "manifest.json")) | ||||||
|       : {}; |       : {}; | ||||||
|     const commonVars = getCommonTemplateVars(); |     const commonVars = getCommonTemplateVars(); | ||||||
|     const minifiedHTML = []; |     const minifiedHTML: Promise<void>[] = []; | ||||||
|     for (const [page, entries] of Object.entries(pageEntries)) { |     for (const [page, entries] of Object.entries(pageEntries)) { | ||||||
|       const content = renderTemplate( |       const content = renderTemplate( | ||||||
|         resolve(inputRoot, inputSub, `${page}.template`), |         resolve(inputRoot, inputSub, `${page}.template`), | ||||||
|         { |         { | ||||||
|           ...commonVars, |           ...commonVars, | ||||||
|           latestEntryJS: entries.map((entry) => latestManifest[`${entry}.js`]), |           latestEntryJS: (entries as string[]).map( | ||||||
|           es5EntryJS: entries.map((entry) => es5Manifest[`${entry}.js`]), |             (entry) => latestManifest[`${entry}.js`] | ||||||
|  |           ), | ||||||
|  |           es5EntryJS: (entries as string[]).map( | ||||||
|  |             (entry) => es5Manifest[`${entry}.js`] | ||||||
|  |           ), | ||||||
|           latestCustomPanelJS: latestManifest["custom-panel.js"], |           latestCustomPanelJS: latestManifest["custom-panel.js"], | ||||||
|           es5CustomPanelJS: es5Manifest["custom-panel.js"], |           es5CustomPanelJS: es5Manifest["custom-panel.js"], | ||||||
|         } |         } | ||||||
| @@ -167,20 +170,18 @@ const APP_PAGE_ENTRIES = { | |||||||
|   "index.html": ["core", "app"], |   "index.html": ["core", "app"], | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesAppDev = genPagesDevTask( | ||||||
|   "gen-pages-app-dev", |   APP_PAGE_ENTRIES, | ||||||
|   genPagesDevTask(APP_PAGE_ENTRIES, paths.root_dir, paths.app_output_root) |   paths.root_dir, | ||||||
|  |   paths.app_output_root | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesAppProd = genPagesProdTask( | ||||||
|   "gen-pages-app-prod", |  | ||||||
|   genPagesProdTask( |  | ||||||
|   APP_PAGE_ENTRIES, |   APP_PAGE_ENTRIES, | ||||||
|   paths.root_dir, |   paths.root_dir, | ||||||
|   paths.app_output_root, |   paths.app_output_root, | ||||||
|   paths.app_output_latest, |   paths.app_output_latest, | ||||||
|   paths.app_output_es5 |   paths.app_output_es5 | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const CAST_PAGE_ENTRIES = { | const CAST_PAGE_ENTRIES = { | ||||||
| @@ -190,104 +191,82 @@ const CAST_PAGE_ENTRIES = { | |||||||
|   "receiver.html": ["receiver"], |   "receiver.html": ["receiver"], | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesCastDev = genPagesDevTask( | ||||||
|   "gen-pages-cast-dev", |   CAST_PAGE_ENTRIES, | ||||||
|   genPagesDevTask(CAST_PAGE_ENTRIES, paths.cast_dir, paths.cast_output_root) |   paths.cast_dir, | ||||||
|  |   paths.cast_output_root | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesCastProd = genPagesProdTask( | ||||||
|   "gen-pages-cast-prod", |  | ||||||
|   genPagesProdTask( |  | ||||||
|   CAST_PAGE_ENTRIES, |   CAST_PAGE_ENTRIES, | ||||||
|   paths.cast_dir, |   paths.cast_dir, | ||||||
|   paths.cast_output_root, |   paths.cast_output_root, | ||||||
|   paths.cast_output_latest, |   paths.cast_output_latest, | ||||||
|   paths.cast_output_es5 |   paths.cast_output_es5 | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const DEMO_PAGE_ENTRIES = { "index.html": ["main"] }; | const DEMO_PAGE_ENTRIES = { "index.html": ["main"] }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesDemoDev = genPagesDevTask( | ||||||
|   "gen-pages-demo-dev", |   DEMO_PAGE_ENTRIES, | ||||||
|   genPagesDevTask(DEMO_PAGE_ENTRIES, paths.demo_dir, paths.demo_output_root) |   paths.demo_dir, | ||||||
|  |   paths.demo_output_root | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesDemoProd = genPagesProdTask( | ||||||
|   "gen-pages-demo-prod", |  | ||||||
|   genPagesProdTask( |  | ||||||
|   DEMO_PAGE_ENTRIES, |   DEMO_PAGE_ENTRIES, | ||||||
|   paths.demo_dir, |   paths.demo_dir, | ||||||
|   paths.demo_output_root, |   paths.demo_output_root, | ||||||
|   paths.demo_output_latest, |   paths.demo_output_latest, | ||||||
|   paths.demo_output_es5 |   paths.demo_output_es5 | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const GALLERY_PAGE_ENTRIES = { "index.html": ["entrypoint"] }; | const GALLERY_PAGE_ENTRIES = { "index.html": ["entrypoint"] }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesGalleryDev = genPagesDevTask( | ||||||
|   "gen-pages-gallery-dev", |  | ||||||
|   genPagesDevTask( |  | ||||||
|   GALLERY_PAGE_ENTRIES, |   GALLERY_PAGE_ENTRIES, | ||||||
|   paths.gallery_dir, |   paths.gallery_dir, | ||||||
|   paths.gallery_output_root |   paths.gallery_output_root | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesGalleryProd = genPagesProdTask( | ||||||
|   "gen-pages-gallery-prod", |  | ||||||
|   genPagesProdTask( |  | ||||||
|   GALLERY_PAGE_ENTRIES, |   GALLERY_PAGE_ENTRIES, | ||||||
|   paths.gallery_dir, |   paths.gallery_dir, | ||||||
|   paths.gallery_output_root, |   paths.gallery_output_root, | ||||||
|   paths.gallery_output_latest |   paths.gallery_output_latest | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const LANDING_PAGE_PAGE_ENTRIES = { "index.html": ["entrypoint"] }; | const LANDING_PAGE_PAGE_ENTRIES = { "index.html": ["entrypoint"] }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesLandingPageDev = genPagesDevTask( | ||||||
|   "gen-pages-landing-page-dev", |  | ||||||
|   genPagesDevTask( |  | ||||||
|   LANDING_PAGE_PAGE_ENTRIES, |   LANDING_PAGE_PAGE_ENTRIES, | ||||||
|   paths.landingPage_dir, |   paths.landingPage_dir, | ||||||
|   paths.landingPage_output_root |   paths.landingPage_output_root | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesLandingPageProd = genPagesProdTask( | ||||||
|   "gen-pages-landing-page-prod", |  | ||||||
|   genPagesProdTask( |  | ||||||
|   LANDING_PAGE_PAGE_ENTRIES, |   LANDING_PAGE_PAGE_ENTRIES, | ||||||
|   paths.landingPage_dir, |   paths.landingPage_dir, | ||||||
|   paths.landingPage_output_root, |   paths.landingPage_output_root, | ||||||
|   paths.landingPage_output_latest, |   paths.landingPage_output_latest, | ||||||
|   paths.landingPage_output_es5 |   paths.landingPage_output_es5 | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| const HASSIO_PAGE_ENTRIES = { "entrypoint.js": ["entrypoint"] }; | const HASSIO_PAGE_ENTRIES = { "entrypoint.js": ["entrypoint"] }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesHassioDev = genPagesDevTask( | ||||||
|   "gen-pages-hassio-dev", |  | ||||||
|   genPagesDevTask( |  | ||||||
|   HASSIO_PAGE_ENTRIES, |   HASSIO_PAGE_ENTRIES, | ||||||
|   paths.hassio_dir, |   paths.hassio_dir, | ||||||
|   paths.hassio_output_root, |   paths.hassio_output_root, | ||||||
|   "src", |   "src", | ||||||
|   paths.hassio_publicPath |   paths.hassio_publicPath | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const genPagesHassioProd = genPagesProdTask( | ||||||
|   "gen-pages-hassio-prod", |  | ||||||
|   genPagesProdTask( |  | ||||||
|   HASSIO_PAGE_ENTRIES, |   HASSIO_PAGE_ENTRIES, | ||||||
|   paths.hassio_dir, |   paths.hassio_dir, | ||||||
|   paths.hassio_output_root, |   paths.hassio_output_root, | ||||||
|   paths.hassio_output_latest, |   paths.hassio_output_latest, | ||||||
|   paths.hassio_output_es5, |   paths.hassio_output_es5, | ||||||
|   "src" |   "src" | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| @@ -1,14 +1,14 @@ | |||||||
| // Task to download the latest Lokalise translations from the nightly workflow artifacts
 | // Task to download the latest 00Lokalise translations from the nightly workflow artifacts
 | ||||||
| 
 | 
 | ||||||
| import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device"; | import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device"; | ||||||
| import { retry } from "@octokit/plugin-retry"; | import { retry } from "@octokit/plugin-retry"; | ||||||
| import { Octokit } from "@octokit/rest"; | import { Octokit } from "@octokit/rest"; | ||||||
| import { deleteAsync } from "del"; | import { deleteAsync } from "del"; | ||||||
| import { mkdir, readFile, writeFile } from "fs/promises"; | import { series } from "gulp"; | ||||||
| import gulp from "gulp"; |  | ||||||
| import jszip from "jszip"; | import jszip from "jszip"; | ||||||
| import path from "path"; | import { mkdir, readFile, writeFile } from "node:fs/promises"; | ||||||
| import process from "process"; | import path from "node:path"; | ||||||
|  | import process from "node:process"; | ||||||
| import { extract } from "tar"; | import { extract } from "tar"; | ||||||
| 
 | 
 | ||||||
| const MAX_AGE = 24; // hours
 | const MAX_AGE = 24; // hours
 | ||||||
| @@ -22,12 +22,13 @@ const TOKEN_FILE = path.posix.join(EXTRACT_DIR, "token.json"); | |||||||
| const ARTIFACT_FILE = path.posix.join(EXTRACT_DIR, "artifact.json"); | const ARTIFACT_FILE = path.posix.join(EXTRACT_DIR, "artifact.json"); | ||||||
| 
 | 
 | ||||||
| let allowTokenSetup = false; | let allowTokenSetup = false; | ||||||
| gulp.task("allow-setup-fetch-nightly-translations", (done) => { | 
 | ||||||
|  | export const allowSetupFetchNightlyTranslations = (done) => { | ||||||
|   allowTokenSetup = true; |   allowTokenSetup = true; | ||||||
|   done(); |   done(); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("fetch-nightly-translations", async function () { | export const fetchNightlyTranslations = async () => { | ||||||
|   // Skip all when environment flag is set (assumes translations are already in place)
 |   // Skip all when environment flag is set (assumes translations are already in place)
 | ||||||
|   if (process.env?.SKIP_FETCH_NIGHTLY_TRANSLATIONS) { |   if (process.env?.SKIP_FETCH_NIGHTLY_TRANSLATIONS) { | ||||||
|     console.log("Skipping fetch due to environment signal"); |     console.log("Skipping fetch due to environment signal"); | ||||||
| @@ -54,7 +55,7 @@ gulp.task("fetch-nightly-translations", async function () { | |||||||
| 
 | 
 | ||||||
|   // To store file writing promises
 |   // To store file writing promises
 | ||||||
|   const createExtractDir = mkdir(EXTRACT_DIR, { recursive: true }); |   const createExtractDir = mkdir(EXTRACT_DIR, { recursive: true }); | ||||||
|   const writings = []; |   const writings: Promise<void>[] = []; | ||||||
| 
 | 
 | ||||||
|   // Authenticate to GitHub using GitHub action token if it exists,
 |   // Authenticate to GitHub using GitHub action token if it exists,
 | ||||||
|   // otherwise look for a saved user token or generate a new one if none
 |   // otherwise look for a saved user token or generate a new one if none
 | ||||||
| @@ -87,7 +88,7 @@ gulp.task("fetch-nightly-translations", async function () { | |||||||
|       }); |       }); | ||||||
|       tokenAuth = await auth({ type: "oauth" }); |       tokenAuth = await auth({ type: "oauth" }); | ||||||
|       writings.push( |       writings.push( | ||||||
|         createExtractDir.then( |         createExtractDir.then(() => | ||||||
|           writeFile(TOKEN_FILE, JSON.stringify(tokenAuth, null, 2)) |           writeFile(TOKEN_FILE, JSON.stringify(tokenAuth, null, 2)) | ||||||
|         ) |         ) | ||||||
|       ); |       ); | ||||||
| @@ -131,13 +132,13 @@ gulp.task("fetch-nightly-translations", async function () { | |||||||
|     throw Error("Latest nightly workflow run has no translations artifact"); |     throw Error("Latest nightly workflow run has no translations artifact"); | ||||||
|   } |   } | ||||||
|   writings.push( |   writings.push( | ||||||
|     createExtractDir.then( |     createExtractDir.then(() => | ||||||
|       writeFile(ARTIFACT_FILE, JSON.stringify(latestArtifact, null, 2)) |       writeFile(ARTIFACT_FILE, JSON.stringify(latestArtifact, null, 2)) | ||||||
|     ) |     ) | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   // Remove the current translations
 |   // Remove the current translations
 | ||||||
|   const deleteCurrent = Promise.all(writings).then( |   const deleteCurrent = Promise.all(writings).then(() => | ||||||
|     deleteAsync([`${EXTRACT_DIR}/*`, `!${ARTIFACT_FILE}`, `!${TOKEN_FILE}`]) |     deleteAsync([`${EXTRACT_DIR}/*`, `!${ARTIFACT_FILE}`, `!${TOKEN_FILE}`]) | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| @@ -148,24 +149,22 @@ gulp.task("fetch-nightly-translations", async function () { | |||||||
|     artifact_id: latestArtifact.id, |     artifact_id: latestArtifact.id, | ||||||
|     archive_format: "zip", |     archive_format: "zip", | ||||||
|   }); |   }); | ||||||
|  |   // @ts-ignore OctokitResponse<unknown, 302> doesn't allow to check for 200
 | ||||||
|   if (downloadResponse.status !== 200) { |   if (downloadResponse.status !== 200) { | ||||||
|     throw Error("Failure downloading translations artifact"); |     throw Error("Failure downloading translations artifact"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Artifact is a tarball, but GitHub adds it to a zip file
 |   // Artifact is a tarball, but GitHub adds it to a zip file
 | ||||||
|   console.log("Unpacking downloaded translations..."); |   console.log("Unpacking downloaded translations..."); | ||||||
|   const zip = await jszip.loadAsync(downloadResponse.data); |   const zip = await jszip.loadAsync(downloadResponse.data as any); | ||||||
|   await deleteCurrent; |   await deleteCurrent; | ||||||
|   const extractStream = zip.file(/.*/)[0].nodeStream().pipe(extract()); |   const extractStream = zip.file(/.*/)[0].nodeStream().pipe(extract()); | ||||||
|   await new Promise((resolve, reject) => { |   await new Promise((resolve, reject) => { | ||||||
|     extractStream.on("close", resolve).on("error", reject); |     extractStream.on("close", resolve).on("error", reject); | ||||||
|   }); |   }); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const setupAndFetchNightlyTranslations = series( | ||||||
|   "setup-and-fetch-nightly-translations", |   allowSetupFetchNightlyTranslations, | ||||||
|   gulp.series( |   fetchNightlyTranslations | ||||||
|     "allow-setup-fetch-nightly-translations", |  | ||||||
|     "fetch-nightly-translations" |  | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| @@ -1,19 +1,23 @@ | |||||||
| import fs from "fs"; |  | ||||||
| import { glob } from "glob"; | import { glob } from "glob"; | ||||||
| import gulp from "gulp"; | import { parallel, series, watch } from "gulp"; | ||||||
| import yaml from "js-yaml"; | import yaml from "js-yaml"; | ||||||
| import { marked } from "marked"; | import { marked } from "marked"; | ||||||
| import path from "path"; | import fs from "node:fs"; | ||||||
| import paths from "../paths.cjs"; | import path from "node:path"; | ||||||
| import "./clean.js"; | import paths from "../paths.ts"; | ||||||
| import "./entry-html.js"; | import { cleanGallery } from "./clean.ts"; | ||||||
| import "./gather-static.js"; | import { genPagesGalleryDev, genPagesGalleryProd } from "./entry-html.ts"; | ||||||
| import "./gen-icons-json.js"; | import { copyStaticGallery } from "./gather-static.ts"; | ||||||
| import "./service-worker.js"; | import { genIconsJson } from "./gen-icons-json.ts"; | ||||||
| import "./translations.js"; | import { buildLocaleData } from "./locale-data.ts"; | ||||||
| import "./rspack.js"; | import { rspackDevServerGallery, rspackProdGallery } from "./rspack.ts"; | ||||||
|  | import { | ||||||
|  |   buildTranslations, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  | } from "./translations.ts"; | ||||||
| 
 | 
 | ||||||
| gulp.task("gather-gallery-pages", async function gatherPages() { | // gather-gallery-pages
 | ||||||
|  | export const gatherGalleryPages = async function gatherPages() { | ||||||
|   const pageDir = path.resolve(paths.gallery_dir, "src/pages"); |   const pageDir = path.resolve(paths.gallery_dir, "src/pages"); | ||||||
|   const files = await glob(path.resolve(pageDir, "**/*")); |   const files = await glob(path.resolve(pageDir, "**/*")); | ||||||
| 
 | 
 | ||||||
| @@ -22,7 +26,7 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
| 
 | 
 | ||||||
|   let content = "export const PAGES = {\n"; |   let content = "export const PAGES = {\n"; | ||||||
| 
 | 
 | ||||||
|   const processed = new Set(); |   const processed = new Set<string>(); | ||||||
| 
 | 
 | ||||||
|   for (const file of files) { |   for (const file of files) { | ||||||
|     if (fs.lstatSync(file).isDirectory()) { |     if (fs.lstatSync(file).isDirectory()) { | ||||||
| @@ -47,7 +51,9 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
| 
 | 
 | ||||||
|       if (descriptionContent.startsWith("---")) { |       if (descriptionContent.startsWith("---")) { | ||||||
|         const metadataEnd = descriptionContent.indexOf("---", 3); |         const metadataEnd = descriptionContent.indexOf("---", 3); | ||||||
|         metadata = yaml.load(descriptionContent.substring(3, metadataEnd)); |         metadata = yaml.load( | ||||||
|  |           descriptionContent.substring(3, metadataEnd) | ||||||
|  |         ) as any; | ||||||
|         descriptionContent = descriptionContent |         descriptionContent = descriptionContent | ||||||
|           .substring(metadataEnd + 3) |           .substring(metadataEnd + 3) | ||||||
|           .trim(); |           .trim(); | ||||||
| @@ -57,7 +63,9 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
|       if (descriptionContent === "") { |       if (descriptionContent === "") { | ||||||
|         hasDescription = false; |         hasDescription = false; | ||||||
|       } else { |       } else { | ||||||
|         descriptionContent = marked(descriptionContent).replace(/`/g, "\\`"); |         // eslint-disable-next-line no-await-in-loop
 | ||||||
|  |         descriptionContent = await marked(descriptionContent); | ||||||
|  |         descriptionContent = descriptionContent.replace(/`/g, "\\`"); | ||||||
|         fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true }); |         fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true }); | ||||||
|         fs.writeFileSync( |         fs.writeFileSync( | ||||||
|           path.resolve(galleryBuild, `${pageId}-description.ts`), |           path.resolve(galleryBuild, `${pageId}-description.ts`), | ||||||
| @@ -95,7 +103,10 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
|     pagesToProcess[category].add(page); |     pagesToProcess[category].add(page); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   for (const group of Object.values(sidebar)) { |   for (const group of Object.values(sidebar) as { | ||||||
|  |     category: string; | ||||||
|  |     pages?: string[]; | ||||||
|  |   }[]) { | ||||||
|     const toProcess = pagesToProcess[group.category]; |     const toProcess = pagesToProcess[group.category]; | ||||||
|     delete pagesToProcess[group.category]; |     delete pagesToProcess[group.category]; | ||||||
| 
 | 
 | ||||||
| @@ -118,7 +129,7 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
|       group.pages = []; |       group.pages = []; | ||||||
|     } |     } | ||||||
|     for (const page of Array.from(toProcess).sort()) { |     for (const page of Array.from(toProcess).sort()) { | ||||||
|       group.pages.push(page); |       group.pages.push(page as string); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -126,7 +137,7 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
|     sidebar.push({ |     sidebar.push({ | ||||||
|       category, |       category, | ||||||
|       header: category, |       header: category, | ||||||
|       pages: Array.from(pages).sort(), |       pages: Array.from(pages as Set<string>).sort(), | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -137,55 +148,48 @@ gulp.task("gather-gallery-pages", async function gatherPages() { | |||||||
|     content, |     content, | ||||||
|     "utf-8" |     "utf-8" | ||||||
|   ); |   ); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | // develop-gallery
 | ||||||
|   "develop-gallery", | export const developGallery = series( | ||||||
|   gulp.series( |  | ||||||
|   async function setEnv() { |   async function setEnv() { | ||||||
|     process.env.NODE_ENV = "development"; |     process.env.NODE_ENV = "development"; | ||||||
|   }, |   }, | ||||||
|     "clean-gallery", |   cleanGallery, | ||||||
|     "translations-enable-merge-backend", |   translationsEnableMergeBackend, | ||||||
|     gulp.parallel( |   parallel( | ||||||
|       "gen-icons-json", |     genIconsJson, | ||||||
|       "build-translations", |     buildTranslations, | ||||||
|       "build-locale-data", |     buildLocaleData, | ||||||
|       "gather-gallery-pages" |     gatherGalleryPages | ||||||
|   ), |   ), | ||||||
|     "copy-static-gallery", |   copyStaticGallery, | ||||||
|     "gen-pages-gallery-dev", |   genPagesGalleryDev, | ||||||
|     gulp.parallel( |   parallel(rspackDevServerGallery, async function watchMarkdownFiles() { | ||||||
|       "rspack-dev-server-gallery", |     watch( | ||||||
|       async function watchMarkdownFiles() { |  | ||||||
|         gulp.watch( |  | ||||||
|       [ |       [ | ||||||
|         path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"), |         path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"), | ||||||
|         path.resolve(paths.gallery_dir, "sidebar.js"), |         path.resolve(paths.gallery_dir, "sidebar.js"), | ||||||
|       ], |       ], | ||||||
|           gulp.series("gather-gallery-pages") |       series(gatherGalleryPages) | ||||||
|     ); |     ); | ||||||
|       } |   }) | ||||||
|     ) |  | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | // build-gallery
 | ||||||
|   "build-gallery", | export const buildGallery = series( | ||||||
|   gulp.series( |  | ||||||
|   async function setEnv() { |   async function setEnv() { | ||||||
|     process.env.NODE_ENV = "production"; |     process.env.NODE_ENV = "production"; | ||||||
|   }, |   }, | ||||||
|     "clean-gallery", |   cleanGallery, | ||||||
|     "translations-enable-merge-backend", |   translationsEnableMergeBackend, | ||||||
|     gulp.parallel( |   parallel( | ||||||
|       "gen-icons-json", |     genIconsJson, | ||||||
|       "build-translations", |     buildTranslations, | ||||||
|       "build-locale-data", |     buildLocaleData, | ||||||
|       "gather-gallery-pages" |     gatherGalleryPages | ||||||
|   ), |   ), | ||||||
|     "copy-static-gallery", |   copyStaticGallery, | ||||||
|     "rspack-prod-gallery", |   rspackProdGallery, | ||||||
|     "gen-pages-gallery-prod" |   genPagesGalleryProd | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| @@ -1,9 +1,8 @@ | |||||||
| // Gulp task to gather all static files.
 | // Gulp task to gather all static files.
 | ||||||
| 
 | 
 | ||||||
| import fs from "fs-extra"; | import fs from "fs-extra"; | ||||||
| import gulp from "gulp"; | import path from "node:path"; | ||||||
| import path from "path"; | import paths from "../paths.ts"; | ||||||
| import paths from "../paths.cjs"; |  | ||||||
| 
 | 
 | ||||||
| const npmPath = (...parts) => | const npmPath = (...parts) => | ||||||
|   path.resolve(paths.root_dir, "node_modules", ...parts); |   path.resolve(paths.root_dir, "node_modules", ...parts); | ||||||
| @@ -17,7 +16,7 @@ const genStaticPath = | |||||||
|   (...parts) => |   (...parts) => | ||||||
|     path.resolve(staticDir, ...parts); |     path.resolve(staticDir, ...parts); | ||||||
| 
 | 
 | ||||||
| function copyTranslations(staticDir) { | const copyTranslations = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
| 
 | 
 | ||||||
|   // Translation output
 |   // Translation output
 | ||||||
| @@ -25,23 +24,23 @@ function copyTranslations(staticDir) { | |||||||
|     polyPath("build/translations/output"), |     polyPath("build/translations/output"), | ||||||
|     staticPath("translations") |     staticPath("translations") | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyLocaleData(staticDir) { | const copyLocaleData = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
| 
 | 
 | ||||||
|   // Locale data output
 |   // Locale data output
 | ||||||
|   fs.copySync(polyPath("build/locale-data"), staticPath("locale-data")); |   fs.copySync(polyPath("build/locale-data"), staticPath("locale-data")); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyMdiIcons(staticDir) { | const copyMdiIcons = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
| 
 | 
 | ||||||
|   // MDI icons output
 |   // MDI icons output
 | ||||||
|   fs.copySync(polyPath("build/mdi"), staticPath("mdi")); |   fs.copySync(polyPath("build/mdi"), staticPath("mdi")); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyPolyfills(staticDir) { | const copyPolyfills = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
| 
 | 
 | ||||||
|   // For custom panels using ES5 builds that don't use Babel 7+
 |   // For custom panels using ES5 builds that don't use Babel 7+
 | ||||||
| @@ -70,9 +69,9 @@ function copyPolyfills(staticDir) { | |||||||
|     npmPath("dialog-polyfill/dialog-polyfill.css"), |     npmPath("dialog-polyfill/dialog-polyfill.css"), | ||||||
|     staticPath("polyfills/") |     staticPath("polyfills/") | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyFonts(staticDir) { | const copyFonts = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|   // Local fonts
 |   // Local fonts
 | ||||||
|   fs.copySync( |   fs.copySync( | ||||||
| @@ -82,14 +81,14 @@ function copyFonts(staticDir) { | |||||||
|       filter: (src) => !src.includes(".") || src.endsWith(".woff2"), |       filter: (src) => !src.includes(".") || src.endsWith(".woff2"), | ||||||
|     } |     } | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyQrScannerWorker(staticDir) { | const copyQrScannerWorker = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|   copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js")); |   copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js")); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyMapPanel(staticDir) { | const copyMapPanel = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|   copyFileDir( |   copyFileDir( | ||||||
|     npmPath("leaflet/dist/leaflet.css"), |     npmPath("leaflet/dist/leaflet.css"), | ||||||
| @@ -103,43 +102,38 @@ function copyMapPanel(staticDir) { | |||||||
|     npmPath("leaflet/dist/images"), |     npmPath("leaflet/dist/images"), | ||||||
|     staticPath("images/leaflet/images/") |     staticPath("images/leaflet/images/") | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| function copyZXingWasm(staticDir) { | const copyZXingWasm = (staticDir) => { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|   copyFileDir( |   copyFileDir( | ||||||
|     npmPath("zxing-wasm/dist/reader/zxing_reader.wasm"), |     npmPath("zxing-wasm/dist/reader/zxing_reader.wasm"), | ||||||
|     staticPath("js") |     staticPath("js") | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-locale-data", async () => { | export const copyTranslationsApp = async () => { | ||||||
|   const staticDir = paths.app_output_static; |  | ||||||
|   copyLocaleData(staticDir); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| gulp.task("copy-translations-app", async () => { |  | ||||||
|   const staticDir = paths.app_output_static; |   const staticDir = paths.app_output_static; | ||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-translations-supervisor", async () => { | export const copyTranslationsSupervisor = async () => { | ||||||
|   const staticDir = paths.hassio_output_static; |   const staticDir = paths.hassio_output_static; | ||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-translations-landing-page", async () => { | export const copyTranslationsLandingPage = async () => { | ||||||
|   const staticDir = paths.landingPage_output_static; |   const staticDir = paths.landingPage_output_static; | ||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-static-supervisor", async () => { | export const copyStaticSupervisor = async () => { | ||||||
|   const staticDir = paths.hassio_output_static; |   const staticDir = paths.hassio_output_static; | ||||||
|   copyLocaleData(staticDir); |   copyLocaleData(staticDir); | ||||||
|   copyFonts(staticDir); |   copyFonts(staticDir); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-static-app", async () => { | export const copyStaticApp = async () => { | ||||||
|   const staticDir = paths.app_output_static; |   const staticDir = paths.app_output_static; | ||||||
|   // Basic static files
 |   // Basic static files
 | ||||||
|   fs.copySync(polyPath("public"), paths.app_output_root); |   fs.copySync(polyPath("public"), paths.app_output_root); | ||||||
| @@ -155,9 +149,9 @@ gulp.task("copy-static-app", async () => { | |||||||
|   // Qr Scanner assets
 |   // Qr Scanner assets
 | ||||||
|   copyZXingWasm(staticDir); |   copyZXingWasm(staticDir); | ||||||
|   copyQrScannerWorker(staticDir); |   copyQrScannerWorker(staticDir); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-static-demo", async () => { | export const copyStaticDemo = async () => { | ||||||
|   // Copy app static files
 |   // Copy app static files
 | ||||||
|   fs.copySync( |   fs.copySync( | ||||||
|     polyPath("public/static"), |     polyPath("public/static"), | ||||||
| @@ -171,9 +165,9 @@ gulp.task("copy-static-demo", async () => { | |||||||
|   copyTranslations(paths.demo_output_static); |   copyTranslations(paths.demo_output_static); | ||||||
|   copyLocaleData(paths.demo_output_static); |   copyLocaleData(paths.demo_output_static); | ||||||
|   copyMdiIcons(paths.demo_output_static); |   copyMdiIcons(paths.demo_output_static); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-static-cast", async () => { | export const copyStaticCast = async () => { | ||||||
|   // Copy app static files
 |   // Copy app static files
 | ||||||
|   fs.copySync(polyPath("public/static"), paths.cast_output_static); |   fs.copySync(polyPath("public/static"), paths.cast_output_static); | ||||||
|   // Copy cast static files
 |   // Copy cast static files
 | ||||||
| @@ -184,9 +178,9 @@ gulp.task("copy-static-cast", async () => { | |||||||
|   copyTranslations(paths.cast_output_static); |   copyTranslations(paths.cast_output_static); | ||||||
|   copyLocaleData(paths.cast_output_static); |   copyLocaleData(paths.cast_output_static); | ||||||
|   copyMdiIcons(paths.cast_output_static); |   copyMdiIcons(paths.cast_output_static); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-static-gallery", async () => { | export const copyStaticGallery = async () => { | ||||||
|   // Copy app static files
 |   // Copy app static files
 | ||||||
|   fs.copySync(polyPath("public/static"), paths.gallery_output_static); |   fs.copySync(polyPath("public/static"), paths.gallery_output_static); | ||||||
|   // Copy gallery static files
 |   // Copy gallery static files
 | ||||||
| @@ -200,9 +194,9 @@ gulp.task("copy-static-gallery", async () => { | |||||||
|   copyTranslations(paths.gallery_output_static); |   copyTranslations(paths.gallery_output_static); | ||||||
|   copyLocaleData(paths.gallery_output_static); |   copyLocaleData(paths.gallery_output_static); | ||||||
|   copyMdiIcons(paths.gallery_output_static); |   copyMdiIcons(paths.gallery_output_static); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("copy-static-landing-page", async () => { | export const copyStaticLandingPage = async () => { | ||||||
|   // Copy landing-page static files
 |   // Copy landing-page static files
 | ||||||
|   fs.copySync( |   fs.copySync( | ||||||
|     path.resolve(paths.landingPage_dir, "public"), |     path.resolve(paths.landingPage_dir, "public"), | ||||||
| @@ -211,4 +205,4 @@ gulp.task("copy-static-landing-page", async () => { | |||||||
| 
 | 
 | ||||||
|   copyFonts(paths.landingPage_output_static); |   copyFonts(paths.landingPage_output_static); | ||||||
|   copyTranslations(paths.landingPage_output_static); |   copyTranslations(paths.landingPage_output_static); | ||||||
| }); | }; | ||||||
| @@ -1,8 +1,7 @@ | |||||||
| import fs from "fs"; | import fs from "node:fs"; | ||||||
| import gulp from "gulp"; | import path from "node:path"; | ||||||
| import hash from "object-hash"; | import hash from "object-hash"; | ||||||
| import path from "path"; | import paths from "../paths.ts"; | ||||||
| import paths from "../paths.cjs"; |  | ||||||
| 
 | 
 | ||||||
| const ICON_PACKAGE_PATH = path.resolve("node_modules/@mdi/svg/"); | const ICON_PACKAGE_PATH = path.resolve("node_modules/@mdi/svg/"); | ||||||
| const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json"); | const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json"); | ||||||
| @@ -21,7 +20,7 @@ const getMeta = () => { | |||||||
|       encoding, |       encoding, | ||||||
|     }); |     }); | ||||||
|     return { |     return { | ||||||
|       path: svg.match(/ d="([^"]+)"/)[1], |       path: svg.match(/ d="([^"]+)"/)?.[1], | ||||||
|       name: icon.name, |       name: icon.name, | ||||||
|       tags: icon.tags, |       tags: icon.tags, | ||||||
|       aliases: icon.aliases, |       aliases: icon.aliases, | ||||||
| @@ -55,14 +54,14 @@ const orderMeta = (meta) => { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const splitBySize = (meta) => { | const splitBySize = (meta) => { | ||||||
|   const chunks = []; |   const chunks: any[] = []; | ||||||
|   const CHUNK_SIZE = 50000; |   const CHUNK_SIZE = 50000; | ||||||
| 
 | 
 | ||||||
|   let curSize = 0; |   let curSize = 0; | ||||||
|   let startKey; |   let startKey; | ||||||
|   let icons = []; |   let icons: any[] = []; | ||||||
| 
 | 
 | ||||||
|   Object.values(meta).forEach((icon) => { |   Object.values(meta).forEach((icon: any) => { | ||||||
|     if (startKey === undefined) { |     if (startKey === undefined) { | ||||||
|       startKey = icon.name; |       startKey = icon.name; | ||||||
|     } |     } | ||||||
| @@ -94,10 +93,10 @@ const findDifferentiator = (curString, prevString) => { | |||||||
|       return curString.substring(0, i + 1); |       return curString.substring(0, i + 1); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   throw new Error("Cannot find differentiator", curString, prevString); |   throw new Error(`Cannot find differentiator; ${curString}; ${prevString}`); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("gen-icons-json", (done) => { | export const genIconsJson = (done) => { | ||||||
|   const meta = getMeta(); |   const meta = getMeta(); | ||||||
| 
 | 
 | ||||||
|   const metaAndRemoved = addRemovedMeta(meta); |   const metaAndRemoved = addRemovedMeta(meta); | ||||||
| @@ -106,7 +105,7 @@ gulp.task("gen-icons-json", (done) => { | |||||||
|   if (!fs.existsSync(OUTPUT_DIR)) { |   if (!fs.existsSync(OUTPUT_DIR)) { | ||||||
|     fs.mkdirSync(OUTPUT_DIR, { recursive: true }); |     fs.mkdirSync(OUTPUT_DIR, { recursive: true }); | ||||||
|   } |   } | ||||||
|   const parts = []; |   const parts: any[] = []; | ||||||
| 
 | 
 | ||||||
|   let lastEnd; |   let lastEnd; | ||||||
|   split.forEach((chunk) => { |   split.forEach((chunk) => { | ||||||
| @@ -153,13 +152,13 @@ gulp.task("gen-icons-json", (done) => { | |||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   done(); |   done(); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("gen-dummy-icons-json", (done) => { | export const genDummyIconsJson = (done) => { | ||||||
|   if (!fs.existsSync(OUTPUT_DIR)) { |   if (!fs.existsSync(OUTPUT_DIR)) { | ||||||
|     fs.mkdirSync(OUTPUT_DIR, { recursive: true }); |     fs.mkdirSync(OUTPUT_DIR, { recursive: true }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fs.writeFileSync(path.resolve(OUTPUT_DIR, "iconList.json"), "[]"); |   fs.writeFileSync(path.resolve(OUTPUT_DIR, "iconList.json"), "[]"); | ||||||
|   done(); |   done(); | ||||||
| }); | }; | ||||||
| @@ -1,45 +0,0 @@ | |||||||
| import gulp from "gulp"; |  | ||||||
| import env from "../env.cjs"; |  | ||||||
| import "./clean.js"; |  | ||||||
| import "./compress.js"; |  | ||||||
| import "./entry-html.js"; |  | ||||||
| import "./gather-static.js"; |  | ||||||
| import "./gen-icons-json.js"; |  | ||||||
| import "./translations.js"; |  | ||||||
| import "./rspack.js"; |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "develop-hassio", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "development"; |  | ||||||
|     }, |  | ||||||
|     "clean-hassio", |  | ||||||
|     "gen-dummy-icons-json", |  | ||||||
|     "gen-pages-hassio-dev", |  | ||||||
|     "build-supervisor-translations", |  | ||||||
|     "copy-translations-supervisor", |  | ||||||
|     "build-locale-data", |  | ||||||
|     "copy-static-supervisor", |  | ||||||
|     "rspack-watch-hassio" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-hassio", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "production"; |  | ||||||
|     }, |  | ||||||
|     "clean-hassio", |  | ||||||
|     "gen-dummy-icons-json", |  | ||||||
|     "build-supervisor-translations", |  | ||||||
|     "copy-translations-supervisor", |  | ||||||
|     "build-locale-data", |  | ||||||
|     "copy-static-supervisor", |  | ||||||
|     "rspack-prod-hassio", |  | ||||||
|     "gen-pages-hassio-prod", |  | ||||||
|     ...// Don't compress running tests |  | ||||||
|     (env.isTestBuild() ? [] : ["compress-hassio"]) |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
							
								
								
									
										45
									
								
								build-scripts/gulp/hassio.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								build-scripts/gulp/hassio.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | import { series } from "gulp"; | ||||||
|  | import { isTestBuild } from "../env.ts"; | ||||||
|  | import { cleanHassio } from "./clean.ts"; | ||||||
|  | import { compressHassio } from "./compress.ts"; | ||||||
|  | import { genPagesHassioDev, genPagesHassioProd } from "./entry-html.ts"; | ||||||
|  | import { | ||||||
|  |   copyStaticSupervisor, | ||||||
|  |   copyTranslationsSupervisor, | ||||||
|  | } from "./gather-static.ts"; | ||||||
|  | import { genDummyIconsJson } from "./gen-icons-json.ts"; | ||||||
|  | import { buildLocaleData } from "./locale-data.ts"; | ||||||
|  | import { rspackProdHassio, rspackWatchHassio } from "./rspack.ts"; | ||||||
|  | import { buildSupervisorTranslations } from "./translations.ts"; | ||||||
|  |  | ||||||
|  | // develop-hassio | ||||||
|  | export const developHassio = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.NODE_ENV = "development"; | ||||||
|  |   }, | ||||||
|  |   cleanHassio, | ||||||
|  |   genDummyIconsJson, | ||||||
|  |   genPagesHassioDev, | ||||||
|  |   buildSupervisorTranslations, | ||||||
|  |   copyTranslationsSupervisor, | ||||||
|  |   buildLocaleData, | ||||||
|  |   copyStaticSupervisor, | ||||||
|  |   rspackWatchHassio | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // build-hassio | ||||||
|  | export const buildHassio = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.NODE_ENV = "production"; | ||||||
|  |   }, | ||||||
|  |   cleanHassio, | ||||||
|  |   genDummyIconsJson, | ||||||
|  |   buildSupervisorTranslations, | ||||||
|  |   copyTranslationsSupervisor, | ||||||
|  |   buildLocaleData, | ||||||
|  |   copyStaticSupervisor, | ||||||
|  |   rspackProdHassio, | ||||||
|  |   genPagesHassioProd, | ||||||
|  |   ...// Don't compress running tests | ||||||
|  |   (isTestBuild() ? [] : [compressHassio]) | ||||||
|  | ); | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| import "./app.js"; |  | ||||||
| import "./cast.js"; |  | ||||||
| import "./clean.js"; |  | ||||||
| import "./compress.js"; |  | ||||||
| import "./demo.js"; |  | ||||||
| import "./download-translations.js"; |  | ||||||
| import "./entry-html.js"; |  | ||||||
| import "./fetch-nightly-translations.js"; |  | ||||||
| import "./gallery.js"; |  | ||||||
| import "./gather-static.js"; |  | ||||||
| import "./gen-icons-json.js"; |  | ||||||
| import "./hassio.js"; |  | ||||||
| import "./landing-page.js"; |  | ||||||
| import "./locale-data.js"; |  | ||||||
| import "./rspack.js"; |  | ||||||
| import "./service-worker.js"; |  | ||||||
| import "./translations.js"; |  | ||||||
							
								
								
									
										42
									
								
								build-scripts/gulp/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								build-scripts/gulp/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | import { analyzeApp, buildApp, developApp } from "./app"; | ||||||
|  | import { buildCast, developCast } from "./cast"; | ||||||
|  | import { analyzeDemo, buildDemo, developDemo } from "./demo"; | ||||||
|  | import { downloadTranslations } from "./download-translations"; | ||||||
|  | import { setupAndFetchNightlyTranslations } from "./fetch-nightly-translations"; | ||||||
|  | import { buildGallery, developGallery, gatherGalleryPages } from "./gallery"; | ||||||
|  | import { genIconsJson } from "./gen-icons-json"; | ||||||
|  | import { buildHassio, developHassio } from "./hassio"; | ||||||
|  | import { buildLandingPage, developLandingPage } from "./landing-page"; | ||||||
|  | import { buildLocaleData } from "./locale-data"; | ||||||
|  | import { buildTranslations } from "./translations"; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   "develop-app": developApp, | ||||||
|  |   "build-app": buildApp, | ||||||
|  |   "analyze-app": analyzeApp, | ||||||
|  |  | ||||||
|  |   "develop-cast": developCast, | ||||||
|  |   "build-cast": buildCast, | ||||||
|  |  | ||||||
|  |   "develop-demo": developDemo, | ||||||
|  |   "build-demo": buildDemo, | ||||||
|  |   "analyze-demo": analyzeDemo, | ||||||
|  |  | ||||||
|  |   "develop-gallery": developGallery, | ||||||
|  |   "build-gallery": buildGallery, | ||||||
|  |   "gather-gallery-pages": gatherGalleryPages, | ||||||
|  |  | ||||||
|  |   "develop-hassio": developHassio, | ||||||
|  |   "build-hassio": buildHassio, | ||||||
|  |  | ||||||
|  |   "develop-landing-page": developLandingPage, | ||||||
|  |   "build-landing-page": buildLandingPage, | ||||||
|  |  | ||||||
|  |   "setup-and-fetch-nightly-translations": setupAndFetchNightlyTranslations, | ||||||
|  |   "download-translations": downloadTranslations, | ||||||
|  |   "build-translations": buildTranslations, | ||||||
|  |  | ||||||
|  |   "gen-icons-json": genIconsJson, | ||||||
|  |  | ||||||
|  |   "build-locale-data": buildLocaleData, | ||||||
|  | }; | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| import gulp from "gulp"; |  | ||||||
| import "./clean.js"; |  | ||||||
| import "./compress.js"; |  | ||||||
| import "./entry-html.js"; |  | ||||||
| import "./gather-static.js"; |  | ||||||
| import "./gen-icons-json.js"; |  | ||||||
| import "./translations.js"; |  | ||||||
| import "./rspack.js"; |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "develop-landing-page", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "development"; |  | ||||||
|     }, |  | ||||||
|     "clean-landing-page", |  | ||||||
|     "translations-enable-merge-backend", |  | ||||||
|     "build-landing-page-translations", |  | ||||||
|     "copy-translations-landing-page", |  | ||||||
|     "build-locale-data", |  | ||||||
|     "copy-static-landing-page", |  | ||||||
|     "gen-pages-landing-page-dev", |  | ||||||
|     "rspack-watch-landing-page" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-landing-page", |  | ||||||
|   gulp.series( |  | ||||||
|     async function setEnv() { |  | ||||||
|       process.env.NODE_ENV = "production"; |  | ||||||
|     }, |  | ||||||
|     "clean-landing-page", |  | ||||||
|     "build-landing-page-translations", |  | ||||||
|     "copy-translations-landing-page", |  | ||||||
|     "build-locale-data", |  | ||||||
|     "copy-static-landing-page", |  | ||||||
|     "rspack-prod-landing-page", |  | ||||||
|     "gen-pages-landing-page-prod" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
							
								
								
									
										46
									
								
								build-scripts/gulp/landing-page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								build-scripts/gulp/landing-page.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | import { series } from "gulp"; | ||||||
|  | import { cleanLandingPage } from "./clean.ts"; | ||||||
|  | import "./compress.ts"; | ||||||
|  | import { | ||||||
|  |   genPagesLandingPageDev, | ||||||
|  |   genPagesLandingPageProd, | ||||||
|  | } from "./entry-html.ts"; | ||||||
|  | import { | ||||||
|  |   copyStaticLandingPage, | ||||||
|  |   copyTranslationsLandingPage, | ||||||
|  | } from "./gather-static.ts"; | ||||||
|  | import { buildLocaleData } from "./locale-data.ts"; | ||||||
|  | import { rspackProdLandingPage, rspackWatchLandingPage } from "./rspack.ts"; | ||||||
|  | import { | ||||||
|  |   buildLandingPageTranslations, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  | } from "./translations.ts"; | ||||||
|  |  | ||||||
|  | // develop-landing-page | ||||||
|  | export const developLandingPage = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.NODE_ENV = "development"; | ||||||
|  |   }, | ||||||
|  |   cleanLandingPage, | ||||||
|  |   translationsEnableMergeBackend, | ||||||
|  |   buildLandingPageTranslations, | ||||||
|  |   copyTranslationsLandingPage, | ||||||
|  |   buildLocaleData, | ||||||
|  |   copyStaticLandingPage, | ||||||
|  |   genPagesLandingPageDev, | ||||||
|  |   rspackWatchLandingPage | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | // build-landing-page | ||||||
|  | export const buildLandingPage = series( | ||||||
|  |   async function setEnv() { | ||||||
|  |     process.env.NODE_ENV = "production"; | ||||||
|  |   }, | ||||||
|  |   cleanLandingPage, | ||||||
|  |   buildLandingPageTranslations, | ||||||
|  |   copyTranslationsLandingPage, | ||||||
|  |   buildLocaleData, | ||||||
|  |   copyStaticLandingPage, | ||||||
|  |   rspackProdLandingPage, | ||||||
|  |   genPagesLandingPageProd | ||||||
|  | ); | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| import { deleteSync } from "del"; | import { deleteSync } from "del"; | ||||||
| import { mkdir, readFile, writeFile } from "fs/promises"; | import { series } from "gulp"; | ||||||
| import gulp from "gulp"; | import { mkdir, readFile, writeFile } from "node:fs/promises"; | ||||||
| import { join, resolve } from "node:path"; | import { join, resolve } from "node:path"; | ||||||
| import paths from "../paths.cjs"; | import paths from "../paths.ts"; | ||||||
| 
 | 
 | ||||||
| const formatjsDir = join(paths.root_dir, "node_modules", "@formatjs"); | const formatjsDir = join(paths.root_dir, "node_modules", "@formatjs"); | ||||||
| const outDir = join(paths.build_dir, "locale-data"); | const outDir = join(paths.build_dir, "locale-data"); | ||||||
| @@ -31,7 +31,7 @@ const convertToJSON = async ( | |||||||
|       join(formatjsDir, pkg, subDir, `${language}.js`), |       join(formatjsDir, pkg, subDir, `${language}.js`), | ||||||
|       "utf-8" |       "utf-8" | ||||||
|     ); |     ); | ||||||
|   } catch (e) { |   } catch (e: any) { | ||||||
|     // Ignore if language is missing (i.e. not supported by @formatjs)
 |     // Ignore if language is missing (i.e. not supported by @formatjs)
 | ||||||
|     if (e.code === "ENOENT" && skipMissing) { |     if (e.code === "ENOENT" && skipMissing) { | ||||||
|       console.warn(`Skipped missing data for language ${lang} from ${pkg}`); |       console.warn(`Skipped missing data for language ${lang} from ${pkg}`); | ||||||
| @@ -54,16 +54,16 @@ const convertToJSON = async ( | |||||||
|   await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData); |   await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("clean-locale-data", async () => deleteSync([outDir])); | const cleanLocaleData = async () => deleteSync([outDir]); | ||||||
| 
 | 
 | ||||||
| gulp.task("create-locale-data", async () => { | const createLocaleData = async () => { | ||||||
|   const translationMeta = JSON.parse( |   const translationMeta = JSON.parse( | ||||||
|     await readFile( |     await readFile( | ||||||
|       resolve(paths.translations_src, "translationMetadata.json"), |       resolve(paths.translations_src, "translationMetadata.json"), | ||||||
|       "utf-8" |       "utf-8" | ||||||
|     ) |     ) | ||||||
|   ); |   ); | ||||||
|   const conversions = []; |   const conversions: any[] = []; | ||||||
|   for (const pkg of Object.keys(INTL_POLYFILLS)) { |   for (const pkg of Object.keys(INTL_POLYFILLS)) { | ||||||
|     // eslint-disable-next-line no-await-in-loop
 |     // eslint-disable-next-line no-await-in-loop
 | ||||||
|     await mkdir(join(outDir, pkg), { recursive: true }); |     await mkdir(join(outDir, pkg), { recursive: true }); | ||||||
| @@ -81,9 +81,6 @@ gulp.task("create-locale-data", async () => { | |||||||
|     ) |     ) | ||||||
|   ); |   ); | ||||||
|   await Promise.all(conversions); |   await Promise.all(conversions); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const buildLocaleData = series(cleanLocaleData, createLocaleData); | ||||||
|   "build-locale-data", |  | ||||||
|   gulp.series("clean-locale-data", "create-locale-data") |  | ||||||
| ); |  | ||||||
| @@ -1,13 +1,13 @@ | |||||||
| // Tasks to run rspack.
 | // Tasks to run rspack.
 | ||||||
| 
 | 
 | ||||||
| import fs from "fs"; |  | ||||||
| import path from "path"; |  | ||||||
| import log from "fancy-log"; |  | ||||||
| import gulp from "gulp"; |  | ||||||
| import rspack from "@rspack/core"; | import rspack from "@rspack/core"; | ||||||
| import { RspackDevServer } from "@rspack/dev-server"; | import { RspackDevServer } from "@rspack/dev-server"; | ||||||
| import env from "../env.cjs"; | import log from "fancy-log"; | ||||||
| import paths from "../paths.cjs"; | import { series, watch } from "gulp"; | ||||||
|  | import fs from "node:fs"; | ||||||
|  | import path from "node:path"; | ||||||
|  | import { isDevContainer, isStatsBuild, isTestBuild } from "../env.ts"; | ||||||
|  | import paths from "../paths.ts"; | ||||||
| import { | import { | ||||||
|   createAppConfig, |   createAppConfig, | ||||||
|   createCastConfig, |   createCastConfig, | ||||||
| @@ -15,7 +15,17 @@ import { | |||||||
|   createGalleryConfig, |   createGalleryConfig, | ||||||
|   createHassioConfig, |   createHassioConfig, | ||||||
|   createLandingPageConfig, |   createLandingPageConfig, | ||||||
| } from "../rspack.cjs"; | } from "../rspack.ts"; | ||||||
|  | import { | ||||||
|  |   copyTranslationsApp, | ||||||
|  |   copyTranslationsLandingPage, | ||||||
|  |   copyTranslationsSupervisor, | ||||||
|  | } from "./gather-static.ts"; | ||||||
|  | import { | ||||||
|  |   buildLandingPageTranslations, | ||||||
|  |   buildSupervisorTranslations, | ||||||
|  |   buildTranslations, | ||||||
|  | } from "./translations.ts"; | ||||||
| 
 | 
 | ||||||
| const bothBuilds = (createConfigFunc, params) => [ | const bothBuilds = (createConfigFunc, params) => [ | ||||||
|   createConfigFunc({ ...params, latestBuild: true }), |   createConfigFunc({ ...params, latestBuild: true }), | ||||||
| @@ -29,6 +39,14 @@ const isWsl = | |||||||
|     .toLocaleLowerCase() |     .toLocaleLowerCase() | ||||||
|     .includes("microsoft"); |     .includes("microsoft"); | ||||||
| 
 | 
 | ||||||
|  | interface RunDevServer { | ||||||
|  |   compiler: any; | ||||||
|  |   contentBase: string; | ||||||
|  |   port: number; | ||||||
|  |   listenHost?: string; | ||||||
|  |   proxy?: any; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * @param {{ |  * @param {{ | ||||||
|  *   compiler: import("@rspack/core").Compiler, |  *   compiler: import("@rspack/core").Compiler, | ||||||
| @@ -41,12 +59,12 @@ const runDevServer = async ({ | |||||||
|   compiler, |   compiler, | ||||||
|   contentBase, |   contentBase, | ||||||
|   port, |   port, | ||||||
|   listenHost = undefined, |   listenHost, | ||||||
|   proxy = undefined, |   proxy, | ||||||
| }) => { | }: RunDevServer) => { | ||||||
|   if (listenHost === undefined) { |   if (listenHost === undefined) { | ||||||
|     // For dev container, we need to listen on all hosts
 |     // For dev container, we need to listen on all hosts
 | ||||||
|     listenHost = env.isDevContainer() ? "0.0.0.0" : "localhost"; |     listenHost = isDevContainer() ? "0.0.0.0" : "localhost"; | ||||||
|   } |   } | ||||||
|   const server = new RspackDevServer( |   const server = new RspackDevServer( | ||||||
|     { |     { | ||||||
| @@ -68,7 +86,7 @@ const runDevServer = async ({ | |||||||
|   log("[rspack-dev-server]", `Project is running at http://localhost:${port}`); |   log("[rspack-dev-server]", `Project is running at http://localhost:${port}`); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const doneHandler = (done) => (err, stats) => { | const doneHandler = (done?: (value?: unknown) => void) => (err, stats) => { | ||||||
|   if (err) { |   if (err) { | ||||||
|     log.error(err.stack || err); |     log.error(err.stack || err); | ||||||
|     if (err.details) { |     if (err.details) { | ||||||
| @@ -97,49 +115,46 @@ const prodBuild = (conf) => | |||||||
|     ); |     ); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-watch-app", () => { | export const rspackWatchApp = () => { | ||||||
|   // This command will run forever because we don't close compiler
 |   // This command will run forever because we don't close compiler
 | ||||||
|   rspack( |   rspack( | ||||||
|     process.env.ES5 |     process.env.ES5 | ||||||
|       ? bothBuilds(createAppConfig, { isProdBuild: false }) |       ? bothBuilds(createAppConfig, { isProdBuild: false }) | ||||||
|       : createAppConfig({ isProdBuild: false, latestBuild: true }) |       : createAppConfig({ isProdBuild: false, latestBuild: true }) | ||||||
|   ).watch({ poll: isWsl }, doneHandler()); |   ).watch({ poll: isWsl }, doneHandler()); | ||||||
|   gulp.watch( |   watch( | ||||||
|     path.join(paths.translations_src, "en.json"), |     path.join(paths.translations_src, "en.json"), | ||||||
|     gulp.series("build-translations", "copy-translations-app") |     series(buildTranslations, copyTranslationsApp) | ||||||
|   ); |   ); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-prod-app", () => | export const rspackProdApp = () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|     bothBuilds(createAppConfig, { |     bothBuilds(createAppConfig, { | ||||||
|       isProdBuild: true, |       isProdBuild: true, | ||||||
|       isStatsBuild: env.isStatsBuild(), |       isStatsBuild: isStatsBuild(), | ||||||
|       isTestBuild: env.isTestBuild(), |       isTestBuild: isTestBuild(), | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-dev-server-demo", () => | export const rspackDevServerDemo = () => | ||||||
|   runDevServer({ |   runDevServer({ | ||||||
|     compiler: rspack( |     compiler: rspack( | ||||||
|       createDemoConfig({ isProdBuild: false, latestBuild: true }) |       createDemoConfig({ isProdBuild: false, latestBuild: true }) | ||||||
|     ), |     ), | ||||||
|     contentBase: paths.demo_output_root, |     contentBase: paths.demo_output_root, | ||||||
|     port: 8090, |     port: 8090, | ||||||
|   }) |   }); | ||||||
| ); |  | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-prod-demo", () => | export const rspackProdDemo = () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|     bothBuilds(createDemoConfig, { |     bothBuilds(createDemoConfig, { | ||||||
|       isProdBuild: true, |       isProdBuild: true, | ||||||
|       isStatsBuild: env.isStatsBuild(), |       isStatsBuild: isStatsBuild(), | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-dev-server-cast", () => | export const rspackDevServerCast = () => | ||||||
|   runDevServer({ |   runDevServer({ | ||||||
|     compiler: rspack( |     compiler: rspack( | ||||||
|       createCastConfig({ isProdBuild: false, latestBuild: true }) |       createCastConfig({ isProdBuild: false, latestBuild: true }) | ||||||
| @@ -148,18 +163,16 @@ gulp.task("rspack-dev-server-cast", () => | |||||||
|     port: 8080, |     port: 8080, | ||||||
|     // Accessible from the network, because that's how Cast hits it.
 |     // Accessible from the network, because that's how Cast hits it.
 | ||||||
|     listenHost: "0.0.0.0", |     listenHost: "0.0.0.0", | ||||||
|   }) |   }); | ||||||
| ); |  | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-prod-cast", () => | export const rspackProdCast = () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|     bothBuilds(createCastConfig, { |     bothBuilds(createCastConfig, { | ||||||
|       isProdBuild: true, |       isProdBuild: true, | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-watch-hassio", () => { | export const rspackWatchHassio = () => { | ||||||
|   // This command will run forever because we don't close compiler
 |   // This command will run forever because we don't close compiler
 | ||||||
|   rspack( |   rspack( | ||||||
|     createHassioConfig({ |     createHassioConfig({ | ||||||
| @@ -168,23 +181,22 @@ gulp.task("rspack-watch-hassio", () => { | |||||||
|     }) |     }) | ||||||
|   ).watch({ ignored: /build/, poll: isWsl }, doneHandler()); |   ).watch({ ignored: /build/, poll: isWsl }, doneHandler()); | ||||||
| 
 | 
 | ||||||
|   gulp.watch( |   watch( | ||||||
|     path.join(paths.translations_src, "en.json"), |     path.join(paths.translations_src, "en.json"), | ||||||
|     gulp.series("build-supervisor-translations", "copy-translations-supervisor") |     series(buildSupervisorTranslations, copyTranslationsSupervisor) | ||||||
|   ); |   ); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-prod-hassio", () => | export const rspackProdHassio = () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|     bothBuilds(createHassioConfig, { |     bothBuilds(createHassioConfig, { | ||||||
|       isProdBuild: true, |       isProdBuild: true, | ||||||
|       isStatsBuild: env.isStatsBuild(), |       isStatsBuild: isStatsBuild(), | ||||||
|       isTestBuild: env.isTestBuild(), |       isTestBuild: isTestBuild(), | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-dev-server-gallery", () => | export const rspackDevServerGallery = () => | ||||||
|   runDevServer({ |   runDevServer({ | ||||||
|     compiler: rspack( |     compiler: rspack( | ||||||
|       createGalleryConfig({ isProdBuild: false, latestBuild: true }) |       createGalleryConfig({ isProdBuild: false, latestBuild: true }) | ||||||
| @@ -192,19 +204,17 @@ gulp.task("rspack-dev-server-gallery", () => | |||||||
|     contentBase: paths.gallery_output_root, |     contentBase: paths.gallery_output_root, | ||||||
|     port: 8100, |     port: 8100, | ||||||
|     listenHost: "0.0.0.0", |     listenHost: "0.0.0.0", | ||||||
|   }) |   }); | ||||||
| ); |  | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-prod-gallery", () => | export const rspackProdGallery = () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|     createGalleryConfig({ |     createGalleryConfig({ | ||||||
|       isProdBuild: true, |       isProdBuild: true, | ||||||
|       latestBuild: true, |       latestBuild: true, | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-watch-landing-page", () => { | export const rspackWatchLandingPage = () => { | ||||||
|   // This command will run forever because we don't close compiler
 |   // This command will run forever because we don't close compiler
 | ||||||
|   rspack( |   rspack( | ||||||
|     process.env.ES5 |     process.env.ES5 | ||||||
| @@ -212,21 +222,17 @@ gulp.task("rspack-watch-landing-page", () => { | |||||||
|       : createLandingPageConfig({ isProdBuild: false, latestBuild: true }) |       : createLandingPageConfig({ isProdBuild: false, latestBuild: true }) | ||||||
|   ).watch({ poll: isWsl }, doneHandler()); |   ).watch({ poll: isWsl }, doneHandler()); | ||||||
| 
 | 
 | ||||||
|   gulp.watch( |   watch( | ||||||
|     path.join(paths.translations_src, "en.json"), |     path.join(paths.translations_src, "en.json"), | ||||||
|     gulp.series( |     series(buildLandingPageTranslations, copyTranslationsLandingPage) | ||||||
|       "build-landing-page-translations", |  | ||||||
|       "copy-translations-landing-page" |  | ||||||
|     ) |  | ||||||
|   ); |   ); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("rspack-prod-landing-page", () => | export const rspackProdLandingPage = () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|     bothBuilds(createLandingPageConfig, { |     bothBuilds(createLandingPageConfig, { | ||||||
|       isProdBuild: true, |       isProdBuild: true, | ||||||
|       isStatsBuild: env.isStatsBuild(), |       isStatsBuild: isStatsBuild(), | ||||||
|       isTestBuild: env.isTestBuild(), |       isTestBuild: isTestBuild(), | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| @@ -1,11 +1,10 @@ | |||||||
| // Generate service workers
 | // Generate service workers
 | ||||||
| 
 | 
 | ||||||
| import { deleteAsync } from "del"; | import { deleteAsync } from "del"; | ||||||
| import gulp from "gulp"; |  | ||||||
| import { mkdir, readFile, symlink, writeFile } from "node:fs/promises"; | import { mkdir, readFile, symlink, writeFile } from "node:fs/promises"; | ||||||
| import { basename, join, relative } from "node:path"; | import { basename, join, relative } from "node:path"; | ||||||
| import { injectManifest } from "workbox-build"; | import { injectManifest } from "workbox-build"; | ||||||
| import paths from "../paths.cjs"; | import paths from "../paths.ts"; | ||||||
| 
 | 
 | ||||||
| const SW_MAP = { | const SW_MAP = { | ||||||
|   [paths.app_output_latest]: "modern", |   [paths.app_output_latest]: "modern", | ||||||
| @@ -23,7 +22,7 @@ self.addEventListener('install', (event) => { | |||||||
| }); | }); | ||||||
|   `.trim() + "\n";
 |   `.trim() + "\n";
 | ||||||
| 
 | 
 | ||||||
| gulp.task("gen-service-worker-app-dev", async () => { | export const genServiceWorkerAppDev = async () => { | ||||||
|   await mkdir(paths.app_output_root, { recursive: true }); |   await mkdir(paths.app_output_root, { recursive: true }); | ||||||
|   await Promise.all( |   await Promise.all( | ||||||
|     Object.values(SW_MAP).map((build) => |     Object.values(SW_MAP).map((build) => | ||||||
| @@ -32,9 +31,9 @@ gulp.task("gen-service-worker-app-dev", async () => { | |||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|   ); |   ); | ||||||
| }); | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("gen-service-worker-app-prod", () => | export const genServiceWorkerAppProd = () => | ||||||
|   Promise.all( |   Promise.all( | ||||||
|     Object.entries(SW_MAP).map(async ([outPath, build]) => { |     Object.entries(SW_MAP).map(async ([outPath, build]) => { | ||||||
|       const manifest = JSON.parse( |       const manifest = JSON.parse( | ||||||
| @@ -83,5 +82,4 @@ gulp.task("gen-service-worker-app-prod", () => | |||||||
|         await symlink(basename(swDest), swOld); |         await symlink(basename(swDest), swOld); | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|   ) |  | ||||||
|   ); |   ); | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| import { deleteAsync } from "del"; | import { deleteAsync } from "del"; | ||||||
| import { glob } from "glob"; | import { glob } from "glob"; | ||||||
| import gulp from "gulp"; | import { src as glupSrc, dest as gulpDest, parallel, series } from "gulp"; | ||||||
| import rename from "gulp-rename"; | import rename from "gulp-rename"; | ||||||
| import merge from "lodash.merge"; | import merge from "lodash.merge"; | ||||||
| import { createHash } from "node:crypto"; | import { createHash } from "node:crypto"; | ||||||
| @@ -10,9 +10,12 @@ import { mkdir, readFile } from "node:fs/promises"; | |||||||
| import { basename, join } from "node:path"; | import { basename, join } from "node:path"; | ||||||
| import { PassThrough, Transform } from "node:stream"; | import { PassThrough, Transform } from "node:stream"; | ||||||
| import { finished } from "node:stream/promises"; | import { finished } from "node:stream/promises"; | ||||||
| import env from "../env.cjs"; | import { isProdBuild } from "../env.ts"; | ||||||
| import paths from "../paths.cjs"; | import paths from "../paths.ts"; | ||||||
| import "./fetch-nightly-translations.js"; | import { | ||||||
|  |   allowSetupFetchNightlyTranslations, | ||||||
|  |   fetchNightlyTranslations, | ||||||
|  | } from "./fetch-nightly-translations.ts"; | ||||||
| 
 | 
 | ||||||
| const inFrontendDir = "translations/frontend"; | const inFrontendDir = "translations/frontend"; | ||||||
| const inBackendDir = "translations/backend"; | const inBackendDir = "translations/backend"; | ||||||
| @@ -23,18 +26,20 @@ const TEST_LOCALE = "en-x-test"; | |||||||
| 
 | 
 | ||||||
| let mergeBackend = false; | let mergeBackend = false; | ||||||
| 
 | 
 | ||||||
| gulp.task( | // translations-enable-merge-backend
 | ||||||
|   "translations-enable-merge-backend", | export const translationsEnableMergeBackend = parallel(async () => { | ||||||
|   gulp.parallel(async () => { |  | ||||||
|   mergeBackend = true; |   mergeBackend = true; | ||||||
|   }, "allow-setup-fetch-nightly-translations") | }, allowSetupFetchNightlyTranslations); | ||||||
| ); |  | ||||||
| 
 | 
 | ||||||
| // Transform stream to apply a function on Vinyl JSON files (buffer mode only).
 | // Transform stream to apply a function on Vinyl JSON files (buffer mode only).
 | ||||||
| // The provided function can either return a new object, or an array of
 | // The provided function can either return a new object, or an array of
 | ||||||
| // [object, subdirectory] pairs for fragmentizing the JSON.
 | // [object, subdirectory] pairs for fragmentizing the JSON.
 | ||||||
| class CustomJSON extends Transform { | class CustomJSON extends Transform { | ||||||
|   constructor(func, reviver = null) { |   _func: any; | ||||||
|  | 
 | ||||||
|  |   _reviver: any; | ||||||
|  | 
 | ||||||
|  |   constructor(func, reviver: any = null) { | ||||||
|     super({ objectMode: true }); |     super({ objectMode: true }); | ||||||
|     this._func = func; |     this._func = func; | ||||||
|     this._reviver = reviver; |     this._reviver = reviver; | ||||||
| @@ -56,9 +61,17 @@ class CustomJSON extends Transform { | |||||||
| 
 | 
 | ||||||
| // Transform stream to merge Vinyl JSON files (buffer mode only).
 | // Transform stream to merge Vinyl JSON files (buffer mode only).
 | ||||||
| class MergeJSON extends Transform { | class MergeJSON extends Transform { | ||||||
|   _objects = []; |   _objects: any[] = []; | ||||||
| 
 | 
 | ||||||
|   constructor(stem, startObj = {}, reviver = null) { |   _stem: any; | ||||||
|  | 
 | ||||||
|  |   _startObj: any; | ||||||
|  | 
 | ||||||
|  |   _reviver: any; | ||||||
|  | 
 | ||||||
|  |   _outFile: any; | ||||||
|  | 
 | ||||||
|  |   constructor(stem, startObj = {}, reviver: any = null) { | ||||||
|     super({ objectMode: true, allowHalfOpen: false }); |     super({ objectMode: true, allowHalfOpen: false }); | ||||||
|     this._stem = stem; |     this._stem = stem; | ||||||
|     this._startObj = structuredClone(startObj); |     this._startObj = structuredClone(startObj); | ||||||
| @@ -111,11 +124,12 @@ const testReviver = (_key, value) => | |||||||
| const KEY_REFERENCE = /\[%key:([^%]+)%\]/; | const KEY_REFERENCE = /\[%key:([^%]+)%\]/; | ||||||
| const lokaliseTransform = (data, path, original = data) => { | const lokaliseTransform = (data, path, original = data) => { | ||||||
|   const output = {}; |   const output = {}; | ||||||
|   for (const [key, value] of Object.entries(data)) { |   for (const entry of Object.entries(data)) { | ||||||
|  |     const [key, value] = entry as [string, string]; | ||||||
|     if (typeof value === "object") { |     if (typeof value === "object") { | ||||||
|       output[key] = lokaliseTransform(value, path, original); |       output[key] = lokaliseTransform(value, path, original); | ||||||
|     } else { |     } else { | ||||||
|       output[key] = value.replace(KEY_REFERENCE, (_match, lokalise_key) => { |       output[key] = value?.replace(KEY_REFERENCE, (_match, lokalise_key) => { | ||||||
|         const replace = lokalise_key.split("::").reduce((tr, k) => { |         const replace = lokalise_key.split("::").reduce((tr, k) => { | ||||||
|           if (!tr) { |           if (!tr) { | ||||||
|             throw Error(`Invalid key placeholder ${lokalise_key} in ${path}`); |             throw Error(`Invalid key placeholder ${lokalise_key} in ${path}`); | ||||||
| @@ -132,18 +146,17 @@ const lokaliseTransform = (data, path, original = data) => { | |||||||
|   return output; |   return output; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| gulp.task("clean-translations", () => deleteAsync([workDir])); | export const cleanTranslations = () => deleteAsync([workDir]); | ||||||
| 
 | 
 | ||||||
| const makeWorkDir = () => mkdir(workDir, { recursive: true }); | const makeWorkDir = () => mkdir(workDir, { recursive: true }); | ||||||
| 
 | 
 | ||||||
| const createTestTranslation = () => | const createTestTranslation = () => | ||||||
|   env.isProdBuild() |   isProdBuild() | ||||||
|     ? Promise.resolve() |     ? Promise.resolve() | ||||||
|     : gulp |     : glupSrc(EN_SRC) | ||||||
|         .src(EN_SRC) |  | ||||||
|         .pipe(new CustomJSON(null, testReviver)) |         .pipe(new CustomJSON(null, testReviver)) | ||||||
|         .pipe(rename(`${TEST_LOCALE}.json`)) |         .pipe(rename(`${TEST_LOCALE}.json`)) | ||||||
|         .pipe(gulp.dest(workDir)); |         .pipe(gulpDest(workDir)); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * This task will build a master translation file, to be used as the base for |  * This task will build a master translation file, to be used as the base for | ||||||
| @@ -155,11 +168,10 @@ const createTestTranslation = () => | |||||||
|  * the Lokalise update to translations/en.json will not happen immediately. |  * the Lokalise update to translations/en.json will not happen immediately. | ||||||
|  */ |  */ | ||||||
| const createMasterTranslation = () => | const createMasterTranslation = () => | ||||||
|   gulp |   glupSrc([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])]) | ||||||
|     .src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])]) |  | ||||||
|     .pipe(new CustomJSON(lokaliseTransform)) |     .pipe(new CustomJSON(lokaliseTransform)) | ||||||
|     .pipe(new MergeJSON("en")) |     .pipe(new MergeJSON("en")) | ||||||
|     .pipe(gulp.dest(workDir)); |     .pipe(gulpDest(workDir)); | ||||||
| 
 | 
 | ||||||
| const FRAGMENTS = ["base"]; | const FRAGMENTS = ["base"]; | ||||||
| 
 | 
 | ||||||
| @@ -186,12 +198,12 @@ const createTranslations = async () => { | |||||||
|   // each locale, then fragmentizes and flattens the data for final output.
 |   // each locale, then fragmentizes and flattens the data for final output.
 | ||||||
|   const translationFiles = await glob([ |   const translationFiles = await glob([ | ||||||
|     `${inFrontendDir}/!(en).json`, |     `${inFrontendDir}/!(en).json`, | ||||||
|     ...(env.isProdBuild() ? [] : [`${workDir}/${TEST_LOCALE}.json`]), |     ...(isProdBuild() ? [] : [`${workDir}/${TEST_LOCALE}.json`]), | ||||||
|   ]); |   ]); | ||||||
|   const hashStream = new Transform({ |   const hashStream = new Transform({ | ||||||
|     objectMode: true, |     objectMode: true, | ||||||
|     transform: async (file, _, callback) => { |     transform: async (file, _, callback) => { | ||||||
|       const hash = env.isProdBuild() |       const hash = isProdBuild() | ||||||
|         ? createHash("md5").update(file.contents).digest("hex") |         ? createHash("md5").update(file.contents).digest("hex") | ||||||
|         : "dev"; |         : "dev"; | ||||||
|       HASHES.set(file.stem, hash); |       HASHES.set(file.stem, hash); | ||||||
| @@ -230,7 +242,7 @@ const createTranslations = async () => { | |||||||
|         }) |         }) | ||||||
|       ) |       ) | ||||||
|     ) |     ) | ||||||
|     .pipe(gulp.dest(outDir)); |     .pipe(gulpDest(outDir)); | ||||||
| 
 | 
 | ||||||
|   // Send the English master downstream first, then for each other locale
 |   // Send the English master downstream first, then for each other locale
 | ||||||
|   // generate merged JSON data to continue piping. It begins with the master
 |   // generate merged JSON data to continue piping. It begins with the master
 | ||||||
| @@ -240,15 +252,15 @@ const createTranslations = async () => { | |||||||
|   // TODO: This is a naive interpretation of BCP47 that should be improved.
 |   // TODO: This is a naive interpretation of BCP47 that should be improved.
 | ||||||
|   //       Will be OK for now as long as we don't have anything more complicated
 |   //       Will be OK for now as long as we don't have anything more complicated
 | ||||||
|   // than a base translation + region.
 |   // than a base translation + region.
 | ||||||
|   const masterStream = gulp |   const masterStream = glupSrc(`${workDir}/en.json`).pipe( | ||||||
|     .src(`${workDir}/en.json`) |     new PassThrough({ objectMode: true }) | ||||||
|     .pipe(new PassThrough({ objectMode: true })); |   ); | ||||||
|   masterStream.pipe(hashStream, { end: false }); |   masterStream.pipe(hashStream, { end: false }); | ||||||
|   const mergesFinished = [finished(masterStream)]; |   const mergesFinished = [finished(masterStream)]; | ||||||
|   for (const translationFile of translationFiles) { |   for (const translationFile of translationFiles) { | ||||||
|     const locale = basename(translationFile, ".json"); |     const locale = basename(translationFile, ".json"); | ||||||
|     const subtags = locale.split("-"); |     const subtags = locale.split("-"); | ||||||
|     const mergeFiles = []; |     const mergeFiles: string[] = []; | ||||||
|     for (let i = 1; i <= subtags.length; i++) { |     for (let i = 1; i <= subtags.length; i++) { | ||||||
|       const lang = subtags.slice(0, i).join("-"); |       const lang = subtags.slice(0, i).join("-"); | ||||||
|       if (lang === TEST_LOCALE) { |       if (lang === TEST_LOCALE) { | ||||||
| @@ -260,9 +272,9 @@ const createTranslations = async () => { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     const mergeStream = gulp |     const mergeStream = glupSrc(mergeFiles, { allowEmpty: true }).pipe( | ||||||
|       .src(mergeFiles, { allowEmpty: true }) |       new MergeJSON(locale, enMaster, emptyReviver) | ||||||
|       .pipe(new MergeJSON(locale, enMaster, emptyReviver)); |     ); | ||||||
|     mergesFinished.push(finished(mergeStream)); |     mergesFinished.push(finished(mergeStream)); | ||||||
|     mergeStream.pipe(hashStream, { end: false }); |     mergeStream.pipe(hashStream, { end: false }); | ||||||
|   } |   } | ||||||
| @@ -275,12 +287,11 @@ const createTranslations = async () => { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const writeTranslationMetaData = () => | const writeTranslationMetaData = () => | ||||||
|   gulp |   glupSrc([`${paths.translations_src}/translationMetadata.json`]) | ||||||
|     .src([`${paths.translations_src}/translationMetadata.json`]) |  | ||||||
|     .pipe( |     .pipe( | ||||||
|       new CustomJSON((meta) => { |       new CustomJSON((meta) => { | ||||||
|         // Add the test translation in development.
 |         // Add the test translation in development.
 | ||||||
|         if (!env.isProdBuild()) { |         if (!isProdBuild()) { | ||||||
|           meta[TEST_LOCALE] = { nativeName: "Translation Test" }; |           meta[TEST_LOCALE] = { nativeName: "Translation Test" }; | ||||||
|         } |         } | ||||||
|         // Filter out locales without a native name, and add the hashes.
 |         // Filter out locales without a native name, and add the hashes.
 | ||||||
| @@ -300,28 +311,22 @@ const writeTranslationMetaData = () => | |||||||
|         }; |         }; | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|     .pipe(gulp.dest(workDir)); |     .pipe(gulpDest(workDir)); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const buildTranslations = series( | ||||||
|   "build-translations", |   parallel(fetchNightlyTranslations, series(cleanTranslations, makeWorkDir)), | ||||||
|   gulp.series( |  | ||||||
|     gulp.parallel( |  | ||||||
|       "fetch-nightly-translations", |  | ||||||
|       gulp.series("clean-translations", makeWorkDir) |  | ||||||
|     ), |  | ||||||
|   createTestTranslation, |   createTestTranslation, | ||||||
|   createMasterTranslation, |   createMasterTranslation, | ||||||
|   createTranslations, |   createTranslations, | ||||||
|   writeTranslationMetaData |   writeTranslationMetaData | ||||||
|   ) |  | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const buildSupervisorTranslations = series( | ||||||
|   "build-supervisor-translations", |   setFragment("supervisor"), | ||||||
|   gulp.series(setFragment("supervisor"), "build-translations") |   buildTranslations | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| gulp.task( | export const buildLandingPageTranslations = series( | ||||||
|   "build-landing-page-translations", |   setFragment("landing-page"), | ||||||
|   gulp.series(setFragment("landing-page"), "build-translations") |   buildTranslations | ||||||
| ); | ); | ||||||
| @@ -5,10 +5,11 @@ import { version as babelVersion } from "@babel/core"; | |||||||
| import presetEnv from "@babel/preset-env"; | import presetEnv from "@babel/preset-env"; | ||||||
| import compilationTargets from "@babel/helper-compilation-targets"; | import compilationTargets from "@babel/helper-compilation-targets"; | ||||||
| import coreJSCompat from "core-js-compat"; | import coreJSCompat from "core-js-compat"; | ||||||
|  | 
 | ||||||
| import { logPlugin } from "@babel/preset-env/lib/debug.js"; | import { logPlugin } from "@babel/preset-env/lib/debug.js"; | ||||||
| // eslint-disable-next-line import/no-relative-packages
 | // eslint-disable-next-line import/no-relative-packages
 | ||||||
| import shippedPolyfills from "../node_modules/babel-plugin-polyfill-corejs3/lib/shipped-proposals.js"; | import shippedPolyfills from "../node_modules/babel-plugin-polyfill-corejs3/lib/shipped-proposals.js"; | ||||||
| import { babelOptions } from "./bundle.cjs"; | import { babelOptions } from "./bundle.ts"; | ||||||
| 
 | 
 | ||||||
| const detailsOpen = (heading) => | const detailsOpen = (heading) => | ||||||
|   `<details>\n<summary><h4>${heading}</h4></summary>\n`; |   `<details>\n<summary><h4>${heading}</h4></summary>\n`; | ||||||
| @@ -50,6 +51,12 @@ for (const buildType of ["Modern", "Legacy"]) { | |||||||
|   const babelOpts = babelOptions({ latestBuild: browserslistEnv === "modern" }); |   const babelOpts = babelOptions({ latestBuild: browserslistEnv === "modern" }); | ||||||
|   const presetEnvOpts = babelOpts.presets[0][1]; |   const presetEnvOpts = babelOpts.presets[0][1]; | ||||||
| 
 | 
 | ||||||
|  |   if (typeof presetEnvOpts !== "object") { | ||||||
|  |     throw new Error( | ||||||
|  |       "The first preset in babelOptions is not an object. This is unexpected." | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // Invoking preset-env in debug mode will log the included plugins
 |   // Invoking preset-env in debug mode will log the included plugins
 | ||||||
|   console.log(detailsOpen(`${buildType} Build Babel Plugins`)); |   console.log(detailsOpen(`${buildType} Build Babel Plugins`)); | ||||||
|   presetEnv.default(dummyAPI, { |   presetEnv.default(dummyAPI, { | ||||||
| @@ -1,63 +0,0 @@ | |||||||
| const path = require("path"); |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|   root_dir: path.resolve(__dirname, ".."), |  | ||||||
|  |  | ||||||
|   build_dir: path.resolve(__dirname, "../build"), |  | ||||||
|   app_output_root: path.resolve(__dirname, "../hass_frontend"), |  | ||||||
|   app_output_static: path.resolve(__dirname, "../hass_frontend/static"), |  | ||||||
|   app_output_latest: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../hass_frontend/frontend_latest" |  | ||||||
|   ), |  | ||||||
|   app_output_es5: path.resolve(__dirname, "../hass_frontend/frontend_es5"), |  | ||||||
|  |  | ||||||
|   demo_dir: path.resolve(__dirname, "../demo"), |  | ||||||
|   demo_output_root: path.resolve(__dirname, "../demo/dist"), |  | ||||||
|   demo_output_static: path.resolve(__dirname, "../demo/dist/static"), |  | ||||||
|   demo_output_latest: path.resolve(__dirname, "../demo/dist/frontend_latest"), |  | ||||||
|   demo_output_es5: path.resolve(__dirname, "../demo/dist/frontend_es5"), |  | ||||||
|  |  | ||||||
|   cast_dir: path.resolve(__dirname, "../cast"), |  | ||||||
|   cast_output_root: path.resolve(__dirname, "../cast/dist"), |  | ||||||
|   cast_output_static: path.resolve(__dirname, "../cast/dist/static"), |  | ||||||
|   cast_output_latest: path.resolve(__dirname, "../cast/dist/frontend_latest"), |  | ||||||
|   cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"), |  | ||||||
|  |  | ||||||
|   gallery_dir: path.resolve(__dirname, "../gallery"), |  | ||||||
|   gallery_build: path.resolve(__dirname, "../gallery/build"), |  | ||||||
|   gallery_output_root: path.resolve(__dirname, "../gallery/dist"), |  | ||||||
|   gallery_output_latest: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../gallery/dist/frontend_latest" |  | ||||||
|   ), |  | ||||||
|   gallery_output_static: path.resolve(__dirname, "../gallery/dist/static"), |  | ||||||
|  |  | ||||||
|   landingPage_dir: path.resolve(__dirname, "../landing-page"), |  | ||||||
|   landingPage_build: path.resolve(__dirname, "../landing-page/build"), |  | ||||||
|   landingPage_output_root: path.resolve(__dirname, "../landing-page/dist"), |  | ||||||
|   landingPage_output_latest: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../landing-page/dist/frontend_latest" |  | ||||||
|   ), |  | ||||||
|   landingPage_output_es5: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../landing-page/dist/frontend_es5" |  | ||||||
|   ), |  | ||||||
|   landingPage_output_static: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../landing-page/dist/static" |  | ||||||
|   ), |  | ||||||
|  |  | ||||||
|   hassio_dir: path.resolve(__dirname, "../hassio"), |  | ||||||
|   hassio_output_root: path.resolve(__dirname, "../hassio/build"), |  | ||||||
|   hassio_output_static: path.resolve(__dirname, "../hassio/build/static"), |  | ||||||
|   hassio_output_latest: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../hassio/build/frontend_latest" |  | ||||||
|   ), |  | ||||||
|   hassio_output_es5: path.resolve(__dirname, "../hassio/build/frontend_es5"), |  | ||||||
|   hassio_publicPath: "/api/hassio/app", |  | ||||||
|  |  | ||||||
|   translations_src: path.resolve(__dirname, "../src/translations"), |  | ||||||
| }; |  | ||||||
							
								
								
									
										63
									
								
								build-scripts/paths.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								build-scripts/paths.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | import path, { dirname as pathDirname } from "node:path"; | ||||||
|  | import { fileURLToPath } from "node:url"; | ||||||
|  |  | ||||||
|  | export const dirname = pathDirname(fileURLToPath(import.meta.url)); | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   root_dir: path.resolve(dirname, ".."), | ||||||
|  |  | ||||||
|  |   build_dir: path.resolve(dirname, "../build"), | ||||||
|  |   app_output_root: path.resolve(dirname, "../hass_frontend"), | ||||||
|  |   app_output_static: path.resolve(dirname, "../hass_frontend/static"), | ||||||
|  |   app_output_latest: path.resolve(dirname, "../hass_frontend/frontend_latest"), | ||||||
|  |   app_output_es5: path.resolve(dirname, "../hass_frontend/frontend_es5"), | ||||||
|  |  | ||||||
|  |   demo_dir: path.resolve(dirname, "../demo"), | ||||||
|  |   demo_output_root: path.resolve(dirname, "../demo/dist"), | ||||||
|  |   demo_output_static: path.resolve(dirname, "../demo/dist/static"), | ||||||
|  |   demo_output_latest: path.resolve(dirname, "../demo/dist/frontend_latest"), | ||||||
|  |   demo_output_es5: path.resolve(dirname, "../demo/dist/frontend_es5"), | ||||||
|  |  | ||||||
|  |   cast_dir: path.resolve(dirname, "../cast"), | ||||||
|  |   cast_output_root: path.resolve(dirname, "../cast/dist"), | ||||||
|  |   cast_output_static: path.resolve(dirname, "../cast/dist/static"), | ||||||
|  |   cast_output_latest: path.resolve(dirname, "../cast/dist/frontend_latest"), | ||||||
|  |   cast_output_es5: path.resolve(dirname, "../cast/dist/frontend_es5"), | ||||||
|  |  | ||||||
|  |   gallery_dir: path.resolve(dirname, "../gallery"), | ||||||
|  |   gallery_build: path.resolve(dirname, "../gallery/build"), | ||||||
|  |   gallery_output_root: path.resolve(dirname, "../gallery/dist"), | ||||||
|  |   gallery_output_latest: path.resolve( | ||||||
|  |     dirname, | ||||||
|  |     "../gallery/dist/frontend_latest" | ||||||
|  |   ), | ||||||
|  |   gallery_output_static: path.resolve(dirname, "../gallery/dist/static"), | ||||||
|  |  | ||||||
|  |   landingPage_dir: path.resolve(dirname, "../landing-page"), | ||||||
|  |   landingPage_build: path.resolve(dirname, "../landing-page/build"), | ||||||
|  |   landingPage_output_root: path.resolve(dirname, "../landing-page/dist"), | ||||||
|  |   landingPage_output_latest: path.resolve( | ||||||
|  |     dirname, | ||||||
|  |     "../landing-page/dist/frontend_latest" | ||||||
|  |   ), | ||||||
|  |   landingPage_output_es5: path.resolve( | ||||||
|  |     dirname, | ||||||
|  |     "../landing-page/dist/frontend_es5" | ||||||
|  |   ), | ||||||
|  |   landingPage_output_static: path.resolve( | ||||||
|  |     dirname, | ||||||
|  |     "../landing-page/dist/static" | ||||||
|  |   ), | ||||||
|  |  | ||||||
|  |   hassio_dir: path.resolve(dirname, "../hassio"), | ||||||
|  |   hassio_output_root: path.resolve(dirname, "../hassio/build"), | ||||||
|  |   hassio_output_static: path.resolve(dirname, "../hassio/build/static"), | ||||||
|  |   hassio_output_latest: path.resolve( | ||||||
|  |     dirname, | ||||||
|  |     "../hassio/build/frontend_latest" | ||||||
|  |   ), | ||||||
|  |   hassio_output_es5: path.resolve(dirname, "../hassio/build/frontend_es5"), | ||||||
|  |   hassio_publicPath: "/api/hassio/app", | ||||||
|  |  | ||||||
|  |   translations_src: path.resolve(dirname, "../src/translations"), | ||||||
|  | }; | ||||||
| @@ -1,20 +1,25 @@ | |||||||
| const { existsSync } = require("fs"); | import filterStats from "@bundle-stats/plugin-webpack-filter"; | ||||||
| const path = require("path"); | import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin"; | ||||||
| const rspack = require("@rspack/core"); | import { DefinePlugin, NormalModuleReplacementPlugin } from "@rspack/core"; | ||||||
| // eslint-disable-next-line @typescript-eslint/naming-convention
 | import { defineConfig } from "@rspack/cli"; | ||||||
| const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); | import log from "fancy-log"; | ||||||
| // eslint-disable-next-line @typescript-eslint/naming-convention
 | import { existsSync } from "node:fs"; | ||||||
| const { StatsWriterPlugin } = require("webpack-stats-plugin"); | import path from "node:path"; | ||||||
| const filterStats = require("@bundle-stats/plugin-webpack-filter"); | import { WebpackManifestPlugin } from "rspack-manifest-plugin"; | ||||||
| // eslint-disable-next-line @typescript-eslint/naming-convention
 | import TerserPlugin from "terser-webpack-plugin"; | ||||||
| const TerserPlugin = require("terser-webpack-plugin"); | import { StatsWriterPlugin } from "webpack-stats-plugin"; | ||||||
| // eslint-disable-next-line @typescript-eslint/naming-convention
 | // @ts-ignore
 | ||||||
| const { WebpackManifestPlugin } = require("rspack-manifest-plugin"); | import WebpackBar from "webpackbar/rspack"; | ||||||
| const log = require("fancy-log"); | import { | ||||||
| // eslint-disable-next-line @typescript-eslint/naming-convention
 |   babelOptions, | ||||||
| const WebpackBar = require("webpackbar/rspack"); |   config, | ||||||
| const paths = require("./paths.cjs"); |   definedVars, | ||||||
| const bundle = require("./bundle.cjs"); |   emptyPackages, | ||||||
|  |   sourceMapURL, | ||||||
|  |   swcOptions, | ||||||
|  |   terserOptions, | ||||||
|  | } from "./bundle.ts"; | ||||||
|  | import paths from "./paths.ts"; | ||||||
| 
 | 
 | ||||||
| class LogStartCompilePlugin { | class LogStartCompilePlugin { | ||||||
|   ignoredFirst = false; |   ignoredFirst = false; | ||||||
| @@ -30,7 +35,7 @@ class LogStartCompilePlugin { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const createRspackConfig = ({ | export const createRspackConfig = ({ | ||||||
|   name, |   name, | ||||||
|   entry, |   entry, | ||||||
|   outputPath, |   outputPath, | ||||||
| @@ -42,12 +47,23 @@ const createRspackConfig = ({ | |||||||
|   isTestBuild, |   isTestBuild, | ||||||
|   isHassioBuild, |   isHassioBuild, | ||||||
|   dontHash, |   dontHash, | ||||||
|  | }: { | ||||||
|  |   name: string; | ||||||
|  |   entry: any; | ||||||
|  |   outputPath: string; | ||||||
|  |   publicPath: string; | ||||||
|  |   defineOverlay?: Record<string, any>; | ||||||
|  |   isProdBuild?: boolean; | ||||||
|  |   latestBuild?: boolean; | ||||||
|  |   isStatsBuild?: boolean; | ||||||
|  |   isTestBuild?: boolean; | ||||||
|  |   isHassioBuild?: boolean; | ||||||
|  |   dontHash?: Set<string>; | ||||||
| }) => { | }) => { | ||||||
|   if (!dontHash) { |   if (!dontHash) { | ||||||
|     dontHash = new Set(); |     dontHash = new Set(); | ||||||
|   } |   } | ||||||
|   const ignorePackages = bundle.ignorePackages({ latestBuild }); |   return defineConfig({ | ||||||
|   return { |  | ||||||
|     name, |     name, | ||||||
|     mode: isProdBuild ? "production" : "development", |     mode: isProdBuild ? "production" : "development", | ||||||
|     target: `browserslist:${latestBuild ? "modern" : "legacy"}`, |     target: `browserslist:${latestBuild ? "modern" : "legacy"}`, | ||||||
| @@ -70,7 +86,7 @@ const createRspackConfig = ({ | |||||||
|             { |             { | ||||||
|               loader: "babel-loader", |               loader: "babel-loader", | ||||||
|               options: { |               options: { | ||||||
|                 ...bundle.babelOptions({ |                 ...babelOptions({ | ||||||
|                   latestBuild, |                   latestBuild, | ||||||
|                   isProdBuild, |                   isProdBuild, | ||||||
|                   isTestBuild, |                   isTestBuild, | ||||||
| @@ -82,7 +98,7 @@ const createRspackConfig = ({ | |||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|               loader: "builtin:swc-loader", |               loader: "builtin:swc-loader", | ||||||
|               options: bundle.swcOptions(), |               options: swcOptions(), | ||||||
|             }, |             }, | ||||||
|           ], |           ], | ||||||
|           resolve: { |           resolve: { | ||||||
| @@ -103,7 +119,7 @@ const createRspackConfig = ({ | |||||||
|         new TerserPlugin({ |         new TerserPlugin({ | ||||||
|           parallel: true, |           parallel: true, | ||||||
|           extractComments: true, |           extractComments: true, | ||||||
|           terserOptions: bundle.terserOptions({ latestBuild, isTestBuild }), |           terserOptions: terserOptions({ latestBuild, isTestBuild }), | ||||||
|         }), |         }), | ||||||
|       ], |       ], | ||||||
|       moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", |       moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", | ||||||
| @@ -122,7 +138,7 @@ const createRspackConfig = ({ | |||||||
|               !chunk.canBeInitial() && |               !chunk.canBeInitial() && | ||||||
|               !new RegExp( |               !new RegExp( | ||||||
|                 `^.+-work${latestBuild ? "(?:let|er)" : "let"}$` |                 `^.+-work${latestBuild ? "(?:let|er)" : "let"}$` | ||||||
|               ).test(chunk.name), |               ).test(chunk?.name || ""), | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     plugins: [ |     plugins: [ | ||||||
| @@ -131,44 +147,11 @@ const createRspackConfig = ({ | |||||||
|         // Only include the JS of entrypoints
 |         // Only include the JS of entrypoints
 | ||||||
|         filter: (file) => file.isInitial && !file.name.endsWith(".map"), |         filter: (file) => file.isInitial && !file.name.endsWith(".map"), | ||||||
|       }), |       }), | ||||||
|       new rspack.DefinePlugin( |       new DefinePlugin( | ||||||
|         bundle.definedVars({ isProdBuild, latestBuild, defineOverlay }) |         definedVars({ isProdBuild, latestBuild, defineOverlay }) | ||||||
|       ), |       ), | ||||||
|       new rspack.IgnorePlugin({ |       new NormalModuleReplacementPlugin( | ||||||
|         checkResource(resource, context) { |         new RegExp(emptyPackages({ isHassioBuild }).join("|")), | ||||||
|           // Only use ignore to intercept imports that we don't control
 |  | ||||||
|           // inside node_module dependencies.
 |  | ||||||
|           if ( |  | ||||||
|             !context.includes("/node_modules/") || |  | ||||||
|             // calling define.amd will call require("!!webpack amd options")
 |  | ||||||
|             resource.startsWith("!!webpack") || |  | ||||||
|             // loaded by webpack dev server but doesn't exist.
 |  | ||||||
|             resource === "webpack/hot" || |  | ||||||
|             resource.startsWith("@swc/helpers") |  | ||||||
|           ) { |  | ||||||
|             return false; |  | ||||||
|           } |  | ||||||
|           let fullPath; |  | ||||||
|           try { |  | ||||||
|             fullPath = resource.startsWith(".") |  | ||||||
|               ? path.resolve(context, resource) |  | ||||||
|               : require.resolve(resource); |  | ||||||
|           } catch (err) { |  | ||||||
|             console.error( |  | ||||||
|               "Error in Home Assistant ignore plugin", |  | ||||||
|               resource, |  | ||||||
|               context |  | ||||||
|             ); |  | ||||||
|             throw err; |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           return ignorePackages.some((toIgnorePath) => |  | ||||||
|             fullPath.startsWith(toIgnorePath) |  | ||||||
|           ); |  | ||||||
|         }, |  | ||||||
|       }), |  | ||||||
|       new rspack.NormalModuleReplacementPlugin( |  | ||||||
|         new RegExp(bundle.emptyPackages({ isHassioBuild }).join("|")), |  | ||||||
|         path.resolve(paths.root_dir, "src/util/empty.js") |         path.resolve(paths.root_dir, "src/util/empty.js") | ||||||
|       ), |       ), | ||||||
|       !isProdBuild && new LogStartCompilePlugin(), |       !isProdBuild && new LogStartCompilePlugin(), | ||||||
| @@ -184,7 +167,9 @@ const createRspackConfig = ({ | |||||||
|       isProdBuild && |       isProdBuild && | ||||||
|         isStatsBuild && |         isStatsBuild && | ||||||
|         new RsdoctorRspackPlugin({ |         new RsdoctorRspackPlugin({ | ||||||
|  |           output: { | ||||||
|             reportDir: path.join(paths.build_dir, "rsdoctor"), |             reportDir: path.join(paths.build_dir, "rsdoctor"), | ||||||
|  |           }, | ||||||
|           features: ["plugins", "bundle"], |           features: ["plugins", "bundle"], | ||||||
|           supports: { |           supports: { | ||||||
|             generateTileGraph: true, |             generateTileGraph: true, | ||||||
| @@ -219,7 +204,9 @@ const createRspackConfig = ({ | |||||||
|     output: { |     output: { | ||||||
|       module: latestBuild, |       module: latestBuild, | ||||||
|       filename: ({ chunk }) => |       filename: ({ chunk }) => | ||||||
|         !isProdBuild || isStatsBuild || dontHash.has(chunk.name) |         !isProdBuild || | ||||||
|  |         isStatsBuild || | ||||||
|  |         (chunk?.name && dontHash.has(chunk.name)) | ||||||
|           ? "[name].js" |           ? "[name].js" | ||||||
|           : "[name].[contenthash].js", |           : "[name].[contenthash].js", | ||||||
|       chunkFilename: |       chunkFilename: | ||||||
| @@ -250,7 +237,7 @@ const createRspackConfig = ({ | |||||||
|                   // dev tools, and they stay happy getting 404s with valid requests.
 |                   // dev tools, and they stay happy getting 404s with valid requests.
 | ||||||
|                   return `/unknown${path.resolve("/", info.resourcePath)}`; |                   return `/unknown${path.resolve("/", info.resourcePath)}`; | ||||||
|                 } |                 } | ||||||
|                 return new URL(info.resourcePath, bundle.sourceMapURL()).href; |                 return new URL(info.resourcePath, sourceMapURL()).href; | ||||||
|               } |               } | ||||||
|             : undefined, |             : undefined, | ||||||
|         ]) |         ]) | ||||||
| @@ -260,35 +247,51 @@ const createRspackConfig = ({ | |||||||
|       layers: true, |       layers: true, | ||||||
|       outputModule: true, |       outputModule: true, | ||||||
|     }, |     }, | ||||||
|   }; |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const createAppConfig = ({ | export const createAppConfig = ({ | ||||||
|   isProdBuild, |   isProdBuild, | ||||||
|   latestBuild, |   latestBuild, | ||||||
|   isStatsBuild, |   isStatsBuild, | ||||||
|   isTestBuild, |   isTestBuild, | ||||||
|  | }: { | ||||||
|  |   isProdBuild?: boolean; | ||||||
|  |   latestBuild?: boolean; | ||||||
|  |   isStatsBuild?: boolean; | ||||||
|  |   isTestBuild?: boolean; | ||||||
| }) => | }) => | ||||||
|   createRspackConfig( |   createRspackConfig( | ||||||
|     bundle.config.app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild }) |     config.app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild }) | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => | export const createDemoConfig = ({ | ||||||
|   createRspackConfig( |   isProdBuild, | ||||||
|     bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild }) |   latestBuild, | ||||||
|   ); |   isStatsBuild, | ||||||
|  | }: { | ||||||
|  |   isProdBuild?: boolean; | ||||||
|  |   latestBuild?: boolean; | ||||||
|  |   isStatsBuild?: boolean; | ||||||
|  | }) => | ||||||
|  |   createRspackConfig(config.demo({ isProdBuild, latestBuild, isStatsBuild })); | ||||||
| 
 | 
 | ||||||
| const createCastConfig = ({ isProdBuild, latestBuild }) => | export const createCastConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   createRspackConfig(bundle.config.cast({ isProdBuild, latestBuild })); |   createRspackConfig(config.cast({ isProdBuild, latestBuild })); | ||||||
| 
 | 
 | ||||||
| const createHassioConfig = ({ | export const createHassioConfig = ({ | ||||||
|   isProdBuild, |   isProdBuild, | ||||||
|   latestBuild, |   latestBuild, | ||||||
|   isStatsBuild, |   isStatsBuild, | ||||||
|   isTestBuild, |   isTestBuild, | ||||||
|  | }: { | ||||||
|  |   isProdBuild?: boolean; | ||||||
|  |   latestBuild?: boolean; | ||||||
|  |   isStatsBuild?: boolean; | ||||||
|  |   isTestBuild?: boolean; | ||||||
| }) => | }) => | ||||||
|   createRspackConfig( |   createRspackConfig( | ||||||
|     bundle.config.hassio({ |     config.hassio({ | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|       isStatsBuild, |       isStatsBuild, | ||||||
| @@ -296,18 +299,8 @@ const createHassioConfig = ({ | |||||||
|     }) |     }) | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
| const createGalleryConfig = ({ isProdBuild, latestBuild }) => | export const createGalleryConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   createRspackConfig(bundle.config.gallery({ isProdBuild, latestBuild })); |   createRspackConfig(config.gallery({ isProdBuild, latestBuild })); | ||||||
| 
 | 
 | ||||||
| const createLandingPageConfig = ({ isProdBuild, latestBuild }) => | export const createLandingPageConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   createRspackConfig(bundle.config.landingPage({ isProdBuild, latestBuild })); |   createRspackConfig(config.landingPage({ isProdBuild, latestBuild })); | ||||||
| 
 |  | ||||||
| module.exports = { |  | ||||||
|   createAppConfig, |  | ||||||
|   createDemoConfig, |  | ||||||
|   createCastConfig, |  | ||||||
|   createHassioConfig, |  | ||||||
|   createGalleryConfig, |  | ||||||
|   createRspackConfig, |  | ||||||
|   createLandingPageConfig, |  | ||||||
| }; |  | ||||||
							
								
								
									
										42
									
								
								build-scripts/runTask.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								build-scripts/runTask.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | // run-build.ts | ||||||
|  | import { series } from "gulp"; | ||||||
|  | import { availableParallelism } from "node:os"; | ||||||
|  | import tasks from "./gulp/index.ts"; | ||||||
|  |  | ||||||
|  | process.env.UV_THREADPOOL_SIZE = availableParallelism().toString(); | ||||||
|  |  | ||||||
|  | const runGulpTask = async (runTasks: string[]) => { | ||||||
|  |   try { | ||||||
|  |     for (const taskName of runTasks) { | ||||||
|  |       if (tasks[taskName] === undefined) { | ||||||
|  |         console.error(`Gulp task "${taskName}" does not exist.`); | ||||||
|  |         console.log("Available tasks:"); | ||||||
|  |         Object.keys(tasks).forEach((task) => { | ||||||
|  |           console.log(`  - ${task}`); | ||||||
|  |         }); | ||||||
|  |         process.exit(1); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await new Promise((resolve, reject) => { | ||||||
|  |       series(...runTasks.map((taskName) => tasks[taskName]))((err?: Error) => { | ||||||
|  |         if (err) { | ||||||
|  |           reject(err); | ||||||
|  |         } else { | ||||||
|  |           resolve(null); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     process.exit(0); | ||||||
|  |   } catch (error: any) { | ||||||
|  |     console.error(`Error running Gulp task "${runTasks}":`, error); | ||||||
|  |     process.exit(1); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Get the task name from command line arguments | ||||||
|  | // TODO arg validation | ||||||
|  | const tasksToRun = process.argv.slice(2); | ||||||
|  |  | ||||||
|  | runGulpTask(tasksToRun); | ||||||
| @@ -14,5 +14,5 @@ | |||||||
|   "name": "Home Assistant Cast", |   "name": "Home Assistant Cast", | ||||||
|   "short_name": "HA Cast", |   "short_name": "HA Cast", | ||||||
|   "start_url": "/?homescreen=1", |   "start_url": "/?homescreen=1", | ||||||
|   "theme_color": "#009ac7" |   "theme_color": "#03A9F4" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp build-cast | yarn run-task build-cast | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp develop-cast | yarn run-task develop-cast | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import "@material/mwc-button/mwc-button"; | ||||||
|  |  | ||||||
| import type { ActionDetail } from "@material/mwc-list/mwc-list"; | import type { ActionDetail } from "@material/mwc-list/mwc-list"; | ||||||
| import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js"; | import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js"; | ||||||
| import type { Auth, Connection } from "home-assistant-js-websocket"; | import type { Auth, Connection } from "home-assistant-js-websocket"; | ||||||
| @@ -18,7 +20,6 @@ import { atLeastVersion } from "../../../../src/common/config/version"; | |||||||
| import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute"; | import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute"; | ||||||
| import "../../../../src/components/ha-icon"; | import "../../../../src/components/ha-icon"; | ||||||
| import "../../../../src/components/ha-list"; | import "../../../../src/components/ha-list"; | ||||||
| import "../../../../src/components/ha-button"; |  | ||||||
| import "../../../../src/components/ha-list-item"; | import "../../../../src/components/ha-list-item"; | ||||||
| import "../../../../src/components/ha-svg-icon"; | import "../../../../src/components/ha-svg-icon"; | ||||||
| import { | import { | ||||||
| @@ -62,20 +63,12 @@ class HcCast extends LitElement { | |||||||
|               <p class="question action-item"> |               <p class="question action-item"> | ||||||
|                 Stay logged in? |                 Stay logged in? | ||||||
|                 <span> |                 <span> | ||||||
|                   <ha-button |                   <mwc-button @click=${this._handleSaveTokens}> | ||||||
|                     appearance="plain" |  | ||||||
|                     size="small" |  | ||||||
|                     @click=${this._handleSaveTokens} |  | ||||||
|                   > |  | ||||||
|                     YES |                     YES | ||||||
|                   </ha-button> |                   </mwc-button> | ||||||
|                   <ha-button |                   <mwc-button @click=${this._handleSkipSaveTokens}> | ||||||
|                     appearance="plain" |  | ||||||
|                     size="small" |  | ||||||
|                     @click=${this._handleSkipSaveTokens} |  | ||||||
|                   > |  | ||||||
|                     NO |                     NO | ||||||
|                   </ha-button> |                   </mwc-button> | ||||||
|                 </span> |                 </span> | ||||||
|               </p> |               </p> | ||||||
|             ` |             ` | ||||||
| @@ -85,10 +78,10 @@ class HcCast extends LitElement { | |||||||
|           : !this.castManager.status |           : !this.castManager.status | ||||||
|             ? html` |             ? html` | ||||||
|                 <p class="center-item"> |                 <p class="center-item"> | ||||||
|                   <ha-button @click=${this._handleLaunch}> |                   <mwc-button raised @click=${this._handleLaunch}> | ||||||
|                     <ha-svg-icon slot="start" .path=${mdiCast}></ha-svg-icon> |                     <ha-svg-icon .path=${mdiCast}></ha-svg-icon> | ||||||
|                     Start Casting |                     Start Casting | ||||||
|                   </ha-button> |                   </mwc-button> | ||||||
|                 </p> |                 </p> | ||||||
|               ` |               ` | ||||||
|             : html` |             : html` | ||||||
| @@ -128,22 +121,14 @@ class HcCast extends LitElement { | |||||||
|         <div class="card-actions"> |         <div class="card-actions"> | ||||||
|           ${this.castManager.status |           ${this.castManager.status | ||||||
|             ? html` |             ? html` | ||||||
|                 <ha-button appearance="plain" @click=${this._handleLaunch}> |                 <mwc-button @click=${this._handleLaunch}> | ||||||
|                   <ha-svg-icon |                   <ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon> | ||||||
|                     slot="start" |  | ||||||
|                     .path=${mdiCastConnected} |  | ||||||
|                   ></ha-svg-icon> |  | ||||||
|                   Manage |                   Manage | ||||||
|                 </ha-button> |                 </mwc-button> | ||||||
|               ` |               ` | ||||||
|             : ""} |             : ""} | ||||||
|           <div class="spacer"></div> |           <div class="spacer"></div> | ||||||
|           <ha-button |           <mwc-button @click=${this._handleLogout}>Log out</mwc-button> | ||||||
|             variant="danger" |  | ||||||
|             appearance="plain" |  | ||||||
|             @click=${this._handleLogout} |  | ||||||
|             >Log out</ha-button |  | ||||||
|           > |  | ||||||
|         </div> |         </div> | ||||||
|       </hc-layout> |       </hc-layout> | ||||||
|     `; |     `; | ||||||
| @@ -242,7 +227,7 @@ class HcCast extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     .question:before { |     .question:before { | ||||||
|       border-radius: var(--ha-border-radius-sm); |       border-radius: 4px; | ||||||
|       position: absolute; |       position: absolute; | ||||||
|       top: 0; |       top: 0; | ||||||
|       right: 0; |       right: 0; | ||||||
| @@ -260,6 +245,13 @@ class HcCast extends LitElement { | |||||||
|       color: var(--secondary-text-color); |       color: var(--secondary-text-color); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     mwc-button ha-svg-icon { | ||||||
|  |       margin-right: 8px; | ||||||
|  |       margin-inline-end: 8px; | ||||||
|  |       margin-inline-start: initial; | ||||||
|  |       height: 18px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ha-list-item ha-icon, |     ha-list-item ha-icon, | ||||||
|     ha-list-item ha-svg-icon { |     ha-list-item ha-svg-icon { | ||||||
|       padding: 12px; |       padding: 12px; | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import "@material/mwc-button"; | ||||||
| import { mdiCastConnected, mdiCast } from "@mdi/js"; | import { mdiCastConnected, mdiCast } from "@mdi/js"; | ||||||
| import type { | import type { | ||||||
|   Auth, |   Auth, | ||||||
| @@ -27,7 +28,6 @@ import "../../../../src/layouts/hass-loading-screen"; | |||||||
| import { registerServiceWorker } from "../../../../src/util/register-service-worker"; | import { registerServiceWorker } from "../../../../src/util/register-service-worker"; | ||||||
| import "./hc-layout"; | import "./hc-layout"; | ||||||
| import "../../../../src/components/ha-textfield"; | import "../../../../src/components/ha-textfield"; | ||||||
| import "../../../../src/components/ha-button"; |  | ||||||
|  |  | ||||||
| const seeFAQ = (qid) => html` | const seeFAQ = (qid) => html` | ||||||
|   See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more |   See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more | ||||||
| @@ -83,14 +83,11 @@ export class HcConnect extends LitElement { | |||||||
|             Unable to connect to ${tokens!.hassUrl}. |             Unable to connect to ${tokens!.hassUrl}. | ||||||
|           </div> |           </div> | ||||||
|           <div class="card-actions"> |           <div class="card-actions"> | ||||||
|             <ha-button appearance="plain" href="/">Retry</ha-button> |             <a href="/"> | ||||||
|  |               <mwc-button> Retry </mwc-button> | ||||||
|  |             </a> | ||||||
|             <div class="spacer"></div> |             <div class="spacer"></div> | ||||||
|             <ha-button |             <mwc-button @click=${this._handleLogout}>Log out</mwc-button> | ||||||
|               appearance="plain" |  | ||||||
|               variant="danger" |  | ||||||
|               @click=${this._handleLogout} |  | ||||||
|               >Log out</ha-button |  | ||||||
|             > |  | ||||||
|           </div> |           </div> | ||||||
|         </hc-layout> |         </hc-layout> | ||||||
|       `; |       `; | ||||||
| @@ -131,19 +128,16 @@ export class HcConnect extends LitElement { | |||||||
|             ${this.error ? html` <p class="error">${this.error}</p> ` : ""} |             ${this.error ? html` <p class="error">${this.error}</p> ` : ""} | ||||||
|           </div> |           </div> | ||||||
|           <div class="card-actions"> |           <div class="card-actions"> | ||||||
|             <ha-button appearance="plain" @click=${this._handleDemo}> |             <mwc-button @click=${this._handleDemo}> | ||||||
|               Show Demo |               Show Demo | ||||||
|               <ha-svg-icon |               <ha-svg-icon | ||||||
|                 slot="end" |  | ||||||
|                 .path=${this.castManager.castState === "CONNECTED" |                 .path=${this.castManager.castState === "CONNECTED" | ||||||
|                   ? mdiCastConnected |                   ? mdiCastConnected | ||||||
|                   : mdiCast} |                   : mdiCast} | ||||||
|               ></ha-svg-icon> |               ></ha-svg-icon> | ||||||
|             </ha-button> |             </mwc-button> | ||||||
|             <div class="spacer"></div> |             <div class="spacer"></div> | ||||||
|             <ha-button appearance="plain" @click=${this._handleConnect} |             <mwc-button @click=${this._handleConnect}>Authorize</mwc-button> | ||||||
|               >Authorize</ha-button |  | ||||||
|             > |  | ||||||
|           </div> |           </div> | ||||||
|         </hc-layout> |         </hc-layout> | ||||||
|       `; |       `; | ||||||
| @@ -315,6 +309,10 @@ export class HcConnect extends LitElement { | |||||||
|       color: darkred; |       color: darkred; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     mwc-button ha-svg-icon { | ||||||
|  |       margin-left: 8px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     .spacer { |     .spacer { | ||||||
|       flex: 1; |       flex: 1; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -95,8 +95,7 @@ class HcLayout extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     .hero { |     .hero { | ||||||
|       border-radius: var(--ha-border-radius-sm) var(--ha-border-radius-sm) |       border-radius: 4px 4px 0 0; | ||||||
|         var(--ha-border-radius-square) var(--ha-border-radius-square); |  | ||||||
|     } |     } | ||||||
|     .subtitle { |     .subtitle { | ||||||
|       font-size: var(--ha-font-size-m); |       font-size: var(--ha-font-size-m); | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ export const castDemoEntities: () => Entity[] = () => | |||||||
|         longitude: 4.8903147, |         longitude: 4.8903147, | ||||||
|         radius: 100, |         radius: 100, | ||||||
|         friendly_name: "Home", |         friendly_name: "Home", | ||||||
|         icon: "mdi:home", |         icon: "hass:home", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     "input_number.harmonyvolume": { |     "input_number.harmonyvolume": { | ||||||
| @@ -88,7 +88,7 @@ export const castDemoEntities: () => Entity[] = () => | |||||||
|         step: 1, |         step: 1, | ||||||
|         mode: "slider", |         mode: "slider", | ||||||
|         friendly_name: "Volume", |         friendly_name: "Volume", | ||||||
|         icon: "mdi:volume-high", |         icon: "hass:volume-high", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     "climate.upstairs": { |     "climate.upstairs": { | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ export const castDemoLovelace: () => LovelaceConfig = () => { | |||||||
|                 type: "weblink", |                 type: "weblink", | ||||||
|                 url: "/lovelace/climate", |                 url: "/lovelace/climate", | ||||||
|                 name: "Climate controls", |                 name: "Climate controls", | ||||||
|                 icon: "mdi:arrow-right", |                 icon: "hass:arrow-right", | ||||||
|               }, |               }, | ||||||
|             ], |             ], | ||||||
|           }, |           }, | ||||||
| @@ -76,7 +76,7 @@ export const castDemoLovelace: () => LovelaceConfig = () => { | |||||||
|                 type: "weblink", |                 type: "weblink", | ||||||
|                 url: "/lovelace/overview", |                 url: "/lovelace/overview", | ||||||
|                 name: "Back", |                 name: "Back", | ||||||
|                 icon: "mdi:arrow-left", |                 icon: "hass:arrow-left", | ||||||
|               }, |               }, | ||||||
|             ], |             ], | ||||||
|           }, |           }, | ||||||
|   | |||||||
| @@ -75,5 +75,5 @@ | |||||||
|   "name": "Home Assistant Demo", |   "name": "Home Assistant Demo", | ||||||
|   "short_name": "HA Demo", |   "short_name": "HA Demo", | ||||||
|   "start_url": "/?homescreen=1", |   "start_url": "/?homescreen=1", | ||||||
|   "theme_color": "#009ac7" |   "theme_color": "#03A9F4" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp build-demo | yarn run-task build-demo | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp develop-demo | yarn run-task develop-demo | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp analyze-demo | yarn run-task analyze-demo | ||||||
| @@ -143,7 +143,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | |||||||
|       state: "on", |       state: "on", | ||||||
|       attributes: { |       attributes: { | ||||||
|         friendly_name: "Home Automation", |         friendly_name: "Home Automation", | ||||||
|         icon: "mdi:home-automation", |         icon: "hass:home-automation", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     "input_boolean.tvtime": { |     "input_boolean.tvtime": { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | |||||||
|   title: "Home Assistant", |   title: "Home Assistant", | ||||||
|   views: [ |   views: [ | ||||||
|     { |     { | ||||||
|       icon: "mdi:home-assistant", |       icon: "hass:home-assistant", | ||||||
|       id: "home", |       id: "home", | ||||||
|       title: "Home", |       title: "Home", | ||||||
|       cards: [ |       cards: [ | ||||||
|   | |||||||
| @@ -1236,7 +1236,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | |||||||
|         }, |         }, | ||||||
|       ], |       ], | ||||||
|       path: "security", |       path: "security", | ||||||
|       icon: "mdi:shield-home", |       icon: "hass:shield-home", | ||||||
|       name: "Security", |       name: "Security", | ||||||
|       background: |       background: | ||||||
|         'center / cover no-repeat url("/assets/jimpower/background-15.jpg") fixed', |         'center / cover no-repeat url("/assets/jimpower/background-15.jpg") fixed', | ||||||
|   | |||||||
| @@ -89,14 +89,11 @@ export class HADemoCard extends LitElement implements LovelaceCard { | |||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
|         <div class="actions small-hidden"> |         <div class="actions small-hidden"> | ||||||
|           <ha-button |           <a href="https://www.home-assistant.io" target="_blank"> | ||||||
|             appearance="plain" |             <ha-button> | ||||||
|             size="small" |  | ||||||
|             href="https://www.home-assistant.io" |  | ||||||
|             target="_blank" |  | ||||||
|           > |  | ||||||
|               ${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")} |               ${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")} | ||||||
|             </ha-button> |             </ha-button> | ||||||
|  |           </a> | ||||||
|         </div> |         </div> | ||||||
|       </ha-card> |       </ha-card> | ||||||
|     `; |     `; | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ | |||||||
|       } |       } | ||||||
|       #ha-launch-screen .ha-launch-screen-spacer-top { |       #ha-launch-screen .ha-launch-screen-spacer-top { | ||||||
|         flex: 1; |         flex: 1; | ||||||
|         margin-top: calc( 2 * max(var(--safe-area-inset-top, 0px), 48px) + 46px ); |         margin-top: calc( 2 * max(var(--safe-area-inset-bottom), 48px) + 46px ); | ||||||
|         padding-top: 48px; |         padding-top: 48px; | ||||||
|       } |       } | ||||||
|       #ha-launch-screen .ha-launch-screen-spacer-bottom { |       #ha-launch-screen .ha-launch-screen-spacer-bottom { | ||||||
| @@ -76,7 +76,7 @@ | |||||||
|         padding-top: 48px; |         padding-top: 48px; | ||||||
|       } |       } | ||||||
|       .ohf-logo { |       .ohf-logo { | ||||||
|         margin: max(var(--safe-area-inset-bottom, 0px), 48px) 0; |         margin: max(var(--safe-area-inset-bottom), 48px) 0; | ||||||
|         display: flex; |         display: flex; | ||||||
|         flex-direction: column; |         flex-direction: column; | ||||||
|         align-items: center; |         align-items: center; | ||||||
|   | |||||||
| @@ -56,15 +56,6 @@ export default tseslint.config( | |||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     settings: { |  | ||||||
|       "import/resolver": { |  | ||||||
|         webpack: { |  | ||||||
|           config: "./rspack.config.cjs", |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|  |  | ||||||
|     rules: { |     rules: { | ||||||
|       "class-methods-use-this": "off", |       "class-methods-use-this": "off", | ||||||
|       "new-cap": "off", |       "new-cap": "off", | ||||||
| @@ -187,5 +178,12 @@ export default tseslint.config( | |||||||
|       ], |       ], | ||||||
|       "no-use-before-define": "off", |       "no-use-before-define": "off", | ||||||
|     }, |     }, | ||||||
|  |     settings: { | ||||||
|  |       "import/resolver": { | ||||||
|  |         node: { | ||||||
|  |           extensions: [".ts", ".js"], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|   } |   } | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp build-gallery | yarn run-task build-gallery | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ set -e | |||||||
|  |  | ||||||
| cd "$(dirname "$0")/../.." | cd "$(dirname "$0")/../.." | ||||||
|  |  | ||||||
| ./node_modules/.bin/gulp develop-gallery | yarn run-task develop-gallery | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
|  | import "@material/mwc-button/mwc-button"; | ||||||
|  | import type { Button } from "@material/mwc-button"; | ||||||
| import type { TemplateResult } from "lit"; | import type { TemplateResult } from "lit"; | ||||||
| import { html, LitElement, css, nothing } from "lit"; | import { html, LitElement, css, nothing } from "lit"; | ||||||
| import { customElement, property } from "lit/decorators"; | import { customElement, property } from "lit/decorators"; | ||||||
| import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | ||||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||||
| import "../../../src/components/ha-card"; | import "../../../src/components/ha-card"; | ||||||
| import "../../../src/components/ha-button"; |  | ||||||
| import type { HaButton } from "../../../src/components/ha-button"; |  | ||||||
|  |  | ||||||
| @customElement("demo-black-white-row") | @customElement("demo-black-white-row") | ||||||
| class DemoBlackWhiteRow extends LitElement { | class DemoBlackWhiteRow extends LitElement { | ||||||
| @@ -25,9 +25,12 @@ class DemoBlackWhiteRow extends LitElement { | |||||||
|               <slot name="light"></slot> |               <slot name="light"></slot> | ||||||
|             </div> |             </div> | ||||||
|             <div class="card-actions"> |             <div class="card-actions"> | ||||||
|               <ha-button .disabled=${this.disabled} @click=${this.handleSubmit}> |               <mwc-button | ||||||
|  |                 .disabled=${this.disabled} | ||||||
|  |                 @click=${this.handleSubmit} | ||||||
|  |               > | ||||||
|                 Submit |                 Submit | ||||||
|               </ha-button> |               </mwc-button> | ||||||
|             </div> |             </div> | ||||||
|           </ha-card> |           </ha-card> | ||||||
|         </div> |         </div> | ||||||
| @@ -37,9 +40,12 @@ class DemoBlackWhiteRow extends LitElement { | |||||||
|               <slot name="dark"></slot> |               <slot name="dark"></slot> | ||||||
|             </div> |             </div> | ||||||
|             <div class="card-actions"> |             <div class="card-actions"> | ||||||
|               <ha-button .disabled=${this.disabled} @click=${this.handleSubmit}> |               <mwc-button | ||||||
|  |                 .disabled=${this.disabled} | ||||||
|  |                 @click=${this.handleSubmit} | ||||||
|  |               > | ||||||
|                 Submit |                 Submit | ||||||
|               </ha-button> |               </mwc-button> | ||||||
|             </div> |             </div> | ||||||
|           </ha-card> |           </ha-card> | ||||||
|           ${this.value |           ${this.value | ||||||
| @@ -68,7 +74,7 @@ class DemoBlackWhiteRow extends LitElement { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleSubmit(ev) { |   handleSubmit(ev) { | ||||||
|     const content = (ev.target as HaButton).closest(".content")!; |     const content = (ev.target as Button).closest(".content")!; | ||||||
|     fireEvent(this, "submitted" as any, { |     fireEvent(this, "submitted" as any, { | ||||||
|       slot: content.classList.contains("light") ? "light" : "dark", |       slot: content.classList.contains("light") ? "light" : "dark", | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -1,11 +1,10 @@ | |||||||
| import { LitElement, css, html, nothing } from "lit"; | import { LitElement, css, html } from "lit"; | ||||||
| import { customElement, property } from "lit/decorators"; | import { customElement, property } from "lit/decorators"; | ||||||
| import "../../../src/components/ha-card"; | import "../../../src/components/ha-card"; | ||||||
| import "../../../src/dialogs/more-info/more-info-content"; | import "../../../src/dialogs/more-info/more-info-content"; | ||||||
| import "../../../src/state-summary/state-card-content"; | import "../../../src/state-summary/state-card-content"; | ||||||
| import "../ha-demo-options"; | import "../ha-demo-options"; | ||||||
| import type { HomeAssistant } from "../../../src/types"; | import type { HomeAssistant } from "../../../src/types"; | ||||||
| import { computeShowNewMoreInfo } from "../../../src/dialogs/more-info/const"; |  | ||||||
|  |  | ||||||
| @customElement("demo-more-info") | @customElement("demo-more-info") | ||||||
| class DemoMoreInfo extends LitElement { | class DemoMoreInfo extends LitElement { | ||||||
| @@ -22,13 +21,11 @@ class DemoMoreInfo extends LitElement { | |||||||
|       <div class="root"> |       <div class="root"> | ||||||
|         <div id="card"> |         <div id="card"> | ||||||
|           <ha-card> |           <ha-card> | ||||||
|             ${!computeShowNewMoreInfo(state) |             <state-card-content | ||||||
|               ? html`<state-card-content |  | ||||||
|               .stateObj=${state} |               .stateObj=${state} | ||||||
|               .hass=${this.hass} |               .hass=${this.hass} | ||||||
|               in-dialog |               in-dialog | ||||||
|                 ></state-card-content>` |             ></state-card-content> | ||||||
|               : nothing} |  | ||||||
|  |  | ||||||
|             <more-info-content |             <more-info-content | ||||||
|               .hass=${this.hass} |               .hass=${this.hass} | ||||||
|   | |||||||
| @@ -1106,7 +1106,7 @@ export default { | |||||||
|       friendly_name: "Philips Hue", |       friendly_name: "Philips Hue", | ||||||
|       entity_picture: null, |       entity_picture: null, | ||||||
|       description: |       description: | ||||||
|         "Press the button on the bridge to register Philips Hue with Home Assistant.", |         "Press the button on the bridge to register Philips Hue with Home Assistant.\n\n", | ||||||
|       submit_caption: "I have pressed the button", |       submit_caption: "I have pressed the button", | ||||||
|     }, |     }, | ||||||
|     last_changed: "2018-07-19T10:44:46.515160+00:00", |     last_changed: "2018-07-19T10:44:46.515160+00:00", | ||||||
|   | |||||||
| @@ -17,10 +17,6 @@ export const createMediaPlayerEntities = () => [ | |||||||
|       new Date().getTime() - 23000 |       new Date().getTime() - 23000 | ||||||
|     ).toISOString(), |     ).toISOString(), | ||||||
|     volume_level: 0.5, |     volume_level: 0.5, | ||||||
|     source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], |  | ||||||
|     source: "AirPlay", |  | ||||||
|     sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"], |  | ||||||
|     sound_mode: "Music", |  | ||||||
|   }), |   }), | ||||||
|   getEntity("media_player", "music_playing", "playing", { |   getEntity("media_player", "music_playing", "playing", { | ||||||
|     friendly_name: "Playing The Music", |     friendly_name: "Playing The Music", | ||||||
| @@ -28,8 +24,8 @@ export const createMediaPlayerEntities = () => [ | |||||||
|     media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", |     media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", | ||||||
|     media_artist: "Technohead", |     media_artist: "Technohead", | ||||||
|     // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + |     // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + | ||||||
|     // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping |     // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media | ||||||
|     supported_features: 784959, |     supported_features: 195135, | ||||||
|     entity_picture: "/images/album_cover.jpg", |     entity_picture: "/images/album_cover.jpg", | ||||||
|     media_duration: 300, |     media_duration: 300, | ||||||
|     media_position: 0, |     media_position: 0, | ||||||
| @@ -38,9 +34,6 @@ export const createMediaPlayerEntities = () => [ | |||||||
|       new Date().getTime() - 23000 |       new Date().getTime() - 23000 | ||||||
|     ).toISOString(), |     ).toISOString(), | ||||||
|     volume_level: 0.5, |     volume_level: 0.5, | ||||||
|     sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"], |  | ||||||
|     sound_mode: "Music", |  | ||||||
|     group_members: ["media_player.playing", "media_player.stream_playing"], |  | ||||||
|   }), |   }), | ||||||
|   getEntity("media_player", "stream_playing", "playing", { |   getEntity("media_player", "stream_playing", "playing", { | ||||||
|     friendly_name: "Playing the Stream", |     friendly_name: "Playing the Stream", | ||||||
| @@ -156,18 +149,15 @@ export const createMediaPlayerEntities = () => [ | |||||||
|   }), |   }), | ||||||
|   getEntity("media_player", "receiver_on", "on", { |   getEntity("media_player", "receiver_on", "on", { | ||||||
|     source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], |     source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], | ||||||
|     sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"], |  | ||||||
|     volume_level: 0.63, |     volume_level: 0.63, | ||||||
|     is_volume_muted: false, |     is_volume_muted: false, | ||||||
|     source: "TV", |     source: "TV", | ||||||
|     sound_mode: "Movie", |  | ||||||
|     friendly_name: "Receiver (selectable sources)", |     friendly_name: "Receiver (selectable sources)", | ||||||
|     // Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode |     // Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode | ||||||
|     supported_features: 84364, |     supported_features: 84364, | ||||||
|   }), |   }), | ||||||
|   getEntity("media_player", "receiver_off", "off", { |   getEntity("media_player", "receiver_off", "off", { | ||||||
|     source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], |     source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], | ||||||
|     sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"], |  | ||||||
|     friendly_name: "Receiver (selectable sources)", |     friendly_name: "Receiver (selectable sources)", | ||||||
|     // Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode |     // Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode | ||||||
|     supported_features: 84364, |     supported_features: 84364, | ||||||
|   | |||||||
| @@ -208,7 +208,7 @@ class HaGallery extends LitElement { | |||||||
|       } |       } | ||||||
|  |  | ||||||
|       .sidebar a[active]::before { |       .sidebar a[active]::before { | ||||||
|         border-radius: var(--ha-border-radius-lg); |         border-radius: 12px; | ||||||
|         position: absolute; |         position: absolute; | ||||||
|         top: 0; |         top: 0; | ||||||
|         right: 2px; |         right: 2px; | ||||||
| @@ -241,7 +241,7 @@ class HaGallery extends LitElement { | |||||||
|         text-align: center; |         text-align: center; | ||||||
|         margin: 16px; |         margin: 16px; | ||||||
|         padding: 16px; |         padding: 16px; | ||||||
|         border-radius: var(--ha-border-radius-lg); |         border-radius: 12px; | ||||||
|         background-color: var(--primary-background-color); |         background-color: var(--primary-background-color); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ import { HaDeviceAction } from "../../../../src/panels/config/automation/action/ | |||||||
| import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event"; | import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event"; | ||||||
| import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if"; | import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if"; | ||||||
| import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel"; | import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel"; | ||||||
|  | import { HaPlayMediaAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-play_media"; | ||||||
| import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat"; | import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat"; | ||||||
| import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence"; | import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence"; | ||||||
| import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service"; | import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service"; | ||||||
| @@ -31,6 +32,7 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [ | |||||||
|   { name: "Service", actions: [HaServiceAction.defaultConfig] }, |   { name: "Service", actions: [HaServiceAction.defaultConfig] }, | ||||||
|   { name: "Condition", actions: [HaConditionAction.defaultConfig] }, |   { name: "Condition", actions: [HaConditionAction.defaultConfig] }, | ||||||
|   { name: "Delay", actions: [HaDelayAction.defaultConfig] }, |   { name: "Delay", actions: [HaDelayAction.defaultConfig] }, | ||||||
|  |   { name: "Play media", actions: [HaPlayMediaAction.defaultConfig] }, | ||||||
|   { name: "Wait", actions: [HaWaitAction.defaultConfig] }, |   { name: "Wait", actions: [HaWaitAction.defaultConfig] }, | ||||||
|   { name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] }, |   { name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] }, | ||||||
|   { name: "Repeat", actions: [HaRepeatAction.defaultConfig] }, |   { name: "Repeat", actions: [HaRepeatAction.defaultConfig] }, | ||||||
|   | |||||||
| @@ -147,13 +147,13 @@ The `title ` option should not be used without a description. | |||||||
|  |  | ||||||
| <ha-alert alert-type="success"> | <ha-alert alert-type="success"> | ||||||
|   This is a success alert — check it out! |   This is a success alert — check it out! | ||||||
|   <ha-button slot="action">Undo</ha-button> |   <mwc-button slot="action" label="Undo"></mwc-button> | ||||||
| </ha-alert> | </ha-alert> | ||||||
|  |  | ||||||
| ```html | ```html | ||||||
| <ha-alert alert-type="success"> | <ha-alert alert-type="success"> | ||||||
|   This is a success alert — check it out! |   This is a success alert — check it out! | ||||||
|   <ha-button slot="action">Undo</ha-button> |   <mwc-button slot="action" label="Undo"></mwc-button> | ||||||
| </ha-alert> | </ha-alert> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
|  | import "@material/mwc-button/mwc-button"; | ||||||
| import type { TemplateResult } from "lit"; | import type { TemplateResult } from "lit"; | ||||||
| import { css, html, LitElement } from "lit"; | import { css, html, LitElement } from "lit"; | ||||||
| import { customElement } from "lit/decorators"; | import { customElement } from "lit/decorators"; | ||||||
| import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; | import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; | ||||||
| import "../../../../src/components/ha-alert"; | import "../../../../src/components/ha-alert"; | ||||||
| import "../../../../src/components/ha-card"; | import "../../../../src/components/ha-card"; | ||||||
| import "../../../../src/components/ha-button"; |  | ||||||
| import "../../../../src/components/ha-logo-svg"; | import "../../../../src/components/ha-logo-svg"; | ||||||
|  |  | ||||||
| const alerts: { | const alerts: { | ||||||
| @@ -78,13 +78,13 @@ const alerts: { | |||||||
|     title: "Error with action", |     title: "Error with action", | ||||||
|     description: "This is a test error alert with action", |     description: "This is a test error alert with action", | ||||||
|     type: "error", |     type: "error", | ||||||
|     actionSlot: html`<ha-button size="small" slot="action">restart</ha-button>`, |     actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     title: "Unsaved data", |     title: "Unsaved data", | ||||||
|     description: "You have unsaved data", |     description: "You have unsaved data", | ||||||
|     type: "warning", |     type: "warning", | ||||||
|     actionSlot: html`<ha-button size="small" slot="action">save</ha-button>`, |     actionSlot: html`<mwc-button slot="action" label="save"></mwc-button>`, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     title: "Slotted icon", |     title: "Slotted icon", | ||||||
| @@ -108,7 +108,7 @@ const alerts: { | |||||||
|     title: "Slotted action", |     title: "Slotted action", | ||||||
|     description: "Alert with slotted action", |     description: "Alert with slotted action", | ||||||
|     type: "info", |     type: "info", | ||||||
|     actionSlot: html`<ha-button slot="action">action</ha-button>`, |     actionSlot: html`<mwc-button slot="action" label="action"></mwc-button>`, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     description: "Dismissable information (RTL)", |     description: "Dismissable information (RTL)", | ||||||
| @@ -120,7 +120,7 @@ const alerts: { | |||||||
|     title: "Error with action", |     title: "Error with action", | ||||||
|     description: "This is a test error alert with action (RTL)", |     description: "This is a test error alert with action (RTL)", | ||||||
|     type: "error", |     type: "error", | ||||||
|     actionSlot: html`<ha-button slot="action">restart</ha-button>`, |     actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`, | ||||||
|     rtl: true, |     rtl: true, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
| @@ -211,7 +211,7 @@ export class DemoHaAlert extends LitElement { | |||||||
|       max-height: 24px; |       max-height: 24px; | ||||||
|       width: 24px; |       width: 24px; | ||||||
|     } |     } | ||||||
|     ha-button { |     mwc-button { | ||||||
|       --mdc-theme-primary: var(--primary-text-color); |       --mdc-theme-primary: var(--primary-text-color); | ||||||
|     } |     } | ||||||
|   `; |   `; | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ export class DemoHaBadge extends LitElement { | |||||||
|     } |     } | ||||||
|     .card-content { |     .card-content { | ||||||
|       display: flex; |       display: flex; | ||||||
|       gap: var(--ha-space-6); |       gap: 24px; | ||||||
|     } |     } | ||||||
|   `; |   `; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,67 +0,0 @@ | |||||||
| --- |  | ||||||
| title: Button |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| <style> |  | ||||||
|   .wrapper { |  | ||||||
|     display: flex; |  | ||||||
|     gap: 24px; |  | ||||||
|   } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
| # Button `<ha-button>` |  | ||||||
|  |  | ||||||
| ## Implementation |  | ||||||
|  |  | ||||||
| ### Example Usage |  | ||||||
|  |  | ||||||
| <div class="wrapper"> |  | ||||||
|   <ha-button> |  | ||||||
|     simple button |  | ||||||
|   </ha-button> |  | ||||||
|   <ha-button appearance="plain"> |  | ||||||
|     plain button |  | ||||||
|   </ha-button> |  | ||||||
|   <ha-button appearance="filled"> |  | ||||||
|     filled button |  | ||||||
|   </ha-button> |  | ||||||
|  |  | ||||||
|   <ha-button size="small"> |  | ||||||
|     small |  | ||||||
|   </ha-button> |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
| ```html |  | ||||||
| <ha-button> simple button </ha-button> |  | ||||||
|  |  | ||||||
| <ha-button size="small"> small </ha-button> |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### API |  | ||||||
|  |  | ||||||
| This component is based on the webawesome button component. |  | ||||||
| Check the [webawesome documentation](https://webawesome.com/docs/components/button/) for more details. |  | ||||||
|  |  | ||||||
| **Slots** |  | ||||||
|  |  | ||||||
| - default slot: Label of the button |  | ||||||
|   ` - no default |  | ||||||
| - `start`: The prefix container (usually for icons). |  | ||||||
|   ` - no default |  | ||||||
| - `end`: The suffix container (usually for icons). |  | ||||||
|   ` - no default |  | ||||||
|  |  | ||||||
| **Properties/Attributes** |  | ||||||
|  |  | ||||||
| | Name       | Type                                           | Default  | Description                                                                       | |  | ||||||
| | ---------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------- | |  | ||||||
| | appearance | "accent"/"filled"/"plain"                      | "accent" | Sets the button appearance.                                                       | |  | ||||||
| | variants   | "brand"/"danger"/"neutral"/"warning"/"success" | "brand"  | Sets the button color variant. "brand" is default.                                | |  | ||||||
| | size       | "small"/"medium"                               | "medium" | Sets the button size.                                                             | |  | ||||||
| | loading    | Boolean                                        | false    | Shows a loading indicator instead of the buttons label and disable buttons click. | |  | ||||||
| | disabled   | Boolean                                        | false    | Disables the button and prevents user interaction.                                | |  | ||||||
|  |  | ||||||
| **CSS Custom Properties** |  | ||||||
|  |  | ||||||
| - `--ha-button-height` - Height of the button. |  | ||||||
| - `--ha-button-border-radius` - Border radius of the button. Defaults to `var(--ha-border-radius-pill)`. |  | ||||||
| @@ -1,171 +0,0 @@ | |||||||
| import { mdiHome } from "@mdi/js"; |  | ||||||
| import type { TemplateResult } from "lit"; |  | ||||||
| import { css, html, LitElement } from "lit"; |  | ||||||
| import { customElement } from "lit/decorators"; |  | ||||||
| import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; |  | ||||||
| import { titleCase } from "../../../../src/common/string/title-case"; |  | ||||||
| import "../../../../src/components/ha-button"; |  | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-svg-icon"; |  | ||||||
| import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg"; |  | ||||||
|  |  | ||||||
| const appearances = ["accent", "filled", "plain"]; |  | ||||||
| const variants = ["brand", "danger", "neutral", "warning", "success"]; |  | ||||||
|  |  | ||||||
| @customElement("demo-components-ha-button") |  | ||||||
| export class DemoHaButton extends LitElement { |  | ||||||
|   protected render(): TemplateResult { |  | ||||||
|     return html` |  | ||||||
|       ${["light", "dark"].map( |  | ||||||
|         (mode) => html` |  | ||||||
|           <div class=${mode}> |  | ||||||
|             <ha-card header="ha-button in ${mode}"> |  | ||||||
|               <div class="card-content"> |  | ||||||
|                 ${variants.map( |  | ||||||
|                   (variant) => html` |  | ||||||
|                     <div> |  | ||||||
|                       ${appearances.map( |  | ||||||
|                         (appearance) => html` |  | ||||||
|                           <ha-button |  | ||||||
|                             .appearance=${appearance} |  | ||||||
|                             .variant=${variant} |  | ||||||
|                           > |  | ||||||
|                             <ha-svg-icon |  | ||||||
|                               .path=${mdiHomeAssistant} |  | ||||||
|                               slot="start" |  | ||||||
|                             ></ha-svg-icon> |  | ||||||
|                             ${titleCase(`${variant} ${appearance}`)} |  | ||||||
|                             <ha-svg-icon |  | ||||||
|                               .path=${mdiHome} |  | ||||||
|                               slot="end" |  | ||||||
|                             ></ha-svg-icon> |  | ||||||
|                           </ha-button> |  | ||||||
|                         ` |  | ||||||
|                       )} |  | ||||||
|                     </div> |  | ||||||
|                     <div> |  | ||||||
|                       ${appearances.map( |  | ||||||
|                         (appearance) => html` |  | ||||||
|                           <ha-button |  | ||||||
|                             .appearance=${appearance} |  | ||||||
|                             .variant=${variant} |  | ||||||
|                             size="small" |  | ||||||
|                           > |  | ||||||
|                             ${titleCase(`${variant} ${appearance}`)} |  | ||||||
|                           </ha-button> |  | ||||||
|                         ` |  | ||||||
|                       )} |  | ||||||
|                     </div> |  | ||||||
|                     <div> |  | ||||||
|                       ${appearances.map( |  | ||||||
|                         (appearance) => html` |  | ||||||
|                           <ha-button |  | ||||||
|                             .appearance=${appearance} |  | ||||||
|                             .variant=${variant} |  | ||||||
|                             loading |  | ||||||
|                           > |  | ||||||
|                             <ha-svg-icon |  | ||||||
|                               .path=${mdiHomeAssistant} |  | ||||||
|                               slot="start" |  | ||||||
|                             ></ha-svg-icon> |  | ||||||
|                             ${titleCase(`${variant} ${appearance}`)} |  | ||||||
|                             <ha-svg-icon |  | ||||||
|                               .path=${mdiHome} |  | ||||||
|                               slot="end" |  | ||||||
|                             ></ha-svg-icon> |  | ||||||
|                           </ha-button> |  | ||||||
|                         ` |  | ||||||
|                       )} |  | ||||||
|                     </div> |  | ||||||
|                   ` |  | ||||||
|                 )} |  | ||||||
|                 ${variants.map( |  | ||||||
|                   (variant) => html` |  | ||||||
|                     <div> |  | ||||||
|                       ${appearances.map( |  | ||||||
|                         (appearance) => html` |  | ||||||
|                           <ha-button |  | ||||||
|                             .variant=${variant} |  | ||||||
|                             .appearance=${appearance} |  | ||||||
|                             disabled |  | ||||||
|                           > |  | ||||||
|                             ${titleCase(`${appearance}`)} |  | ||||||
|                           </ha-button> |  | ||||||
|                         ` |  | ||||||
|                       )} |  | ||||||
|                     </div> |  | ||||||
|                     <div> |  | ||||||
|                       ${appearances.map( |  | ||||||
|                         (appearance) => html` |  | ||||||
|                           <ha-button |  | ||||||
|                             .variant=${variant} |  | ||||||
|                             .appearance=${appearance} |  | ||||||
|                             size="small" |  | ||||||
|                             disabled |  | ||||||
|                           > |  | ||||||
|                             ${titleCase(`${appearance}`)} |  | ||||||
|                           </ha-button> |  | ||||||
|                         ` |  | ||||||
|                       )} |  | ||||||
|                     </div> |  | ||||||
|                   ` |  | ||||||
|                 )} |  | ||||||
|               </div> |  | ||||||
|             </ha-card> |  | ||||||
|           </div> |  | ||||||
|         ` |  | ||||||
|       )} |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   firstUpdated(changedProps) { |  | ||||||
|     super.firstUpdated(changedProps); |  | ||||||
|     applyThemesOnElement( |  | ||||||
|       this.shadowRoot!.querySelector(".dark"), |  | ||||||
|       { |  | ||||||
|         default_theme: "default", |  | ||||||
|         default_dark_theme: "default", |  | ||||||
|         themes: {}, |  | ||||||
|         darkMode: true, |  | ||||||
|         theme: "default", |  | ||||||
|       }, |  | ||||||
|       undefined, |  | ||||||
|       undefined, |  | ||||||
|       true |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static styles = css` |  | ||||||
|     :host { |  | ||||||
|       display: flex; |  | ||||||
|       justify-content: center; |  | ||||||
|     } |  | ||||||
|     .dark, |  | ||||||
|     .light { |  | ||||||
|       display: block; |  | ||||||
|       background-color: var(--primary-background-color); |  | ||||||
|       padding: 0 50px; |  | ||||||
|     } |  | ||||||
|     .button { |  | ||||||
|       padding: unset; |  | ||||||
|     } |  | ||||||
|     ha-card { |  | ||||||
|       margin: 24px auto; |  | ||||||
|     } |  | ||||||
|     .card-content { |  | ||||||
|       display: flex; |  | ||||||
|       flex-direction: column; |  | ||||||
|       gap: var(--ha-space-6); |  | ||||||
|     } |  | ||||||
|     .card-content div { |  | ||||||
|       display: flex; |  | ||||||
|       gap: var(--ha-space-2); |  | ||||||
|     } |  | ||||||
|   `; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| declare global { |  | ||||||
|   interface HTMLElementTagNameMap { |  | ||||||
|     "demo-components-ha-button": DemoHaButton; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -9,10 +9,10 @@ import { css, html, LitElement } from "lit"; | |||||||
| import { customElement } from "lit/decorators"; | import { customElement } from "lit/decorators"; | ||||||
| import { ifDefined } from "lit/directives/if-defined"; | import { ifDefined } from "lit/directives/if-defined"; | ||||||
| import { repeat } from "lit/directives/repeat"; | import { repeat } from "lit/directives/repeat"; | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-control-button"; | import "../../../../src/components/ha-control-button"; | ||||||
| import "../../../../src/components/ha-control-button-group"; | import "../../../../src/components/ha-card"; | ||||||
| import "../../../../src/components/ha-svg-icon"; | import "../../../../src/components/ha-svg-icon"; | ||||||
|  | import "../../../../src/components/ha-control-button-group"; | ||||||
|  |  | ||||||
| interface Button { | interface Button { | ||||||
|   label: string; |   label: string; | ||||||
| @@ -156,17 +156,17 @@ export class DemoHaBarButton extends LitElement { | |||||||
|       --control-button-icon-color: var(--primary-color); |       --control-button-icon-color: var(--primary-color); | ||||||
|       --control-button-background-color: var(--primary-color); |       --control-button-background-color: var(--primary-color); | ||||||
|       --control-button-background-opacity: 0.2; |       --control-button-background-opacity: 0.2; | ||||||
|       --control-button-border-radius: var(--ha-border-radius-xl); |       --control-button-border-radius: 18px; | ||||||
|       height: 100px; |       height: 100px; | ||||||
|       width: 100px; |       width: 100px; | ||||||
|     } |     } | ||||||
|     .custom-group { |     .custom-group { | ||||||
|       --control-button-group-thickness: 100px; |       --control-button-group-thickness: 100px; | ||||||
|       --control-button-group-border-radius: var(--ha-border-radius-6xl); |       --control-button-group-border-radius: 36px; | ||||||
|       --control-button-group-spacing: 20px; |       --control-button-group-spacing: 20px; | ||||||
|     } |     } | ||||||
|     .custom-group ha-control-button { |     .custom-group ha-control-button { | ||||||
|       --control-button-border-radius: var(--ha-border-radius-xl); |       --control-button-border-radius: 18px; | ||||||
|       --mdc-icon-size: 32px; |       --mdc-icon-size: 32px; | ||||||
|     } |     } | ||||||
|     .vertical-buttons { |     .vertical-buttons { | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| import type { TemplateResult } from "lit"; | import type { TemplateResult } from "lit"; | ||||||
| import { LitElement, css, html } from "lit"; | import { LitElement, css, html } from "lit"; | ||||||
| import { customElement, state } from "lit/decorators"; | import { customElement, state } from "lit/decorators"; | ||||||
| import { ifDefined } from "lit/directives/if-defined"; |  | ||||||
| import { repeat } from "lit/directives/repeat"; |  | ||||||
| import "../../../../src/components/ha-card"; | import "../../../../src/components/ha-card"; | ||||||
| import "../../../../src/components/ha-control-number-buttons"; | import "../../../../src/components/ha-control-number-buttons"; | ||||||
|  | import { repeat } from "lit/directives/repeat"; | ||||||
|  | import { ifDefined } from "lit/directives/if-defined"; | ||||||
|  |  | ||||||
| const buttons: { | const buttons: { | ||||||
|   id: string; |   id: string; | ||||||
| @@ -94,7 +94,7 @@ export class DemoHarControlNumberButtons extends LitElement { | |||||||
|       --control-number-buttons-background-color: #2196f3; |       --control-number-buttons-background-color: #2196f3; | ||||||
|       --control-number-buttons-background-opacity: 0.1; |       --control-number-buttons-background-opacity: 0.1; | ||||||
|       --control-number-buttons-thickness: 100px; |       --control-number-buttons-thickness: 100px; | ||||||
|       --control-number-buttons-border-radius: var(--ha-border-radius-6xl); |       --control-number-buttons-border-radius: 36px; | ||||||
|     } |     } | ||||||
|   `; |   `; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -131,7 +131,7 @@ export class DemoHaControlSelectMenu extends LitElement { | |||||||
|       --control-button-icon-color: var(--primary-color); |       --control-button-icon-color: var(--primary-color); | ||||||
|       --control-button-background-color: var(--primary-color); |       --control-button-background-color: var(--primary-color); | ||||||
|       --control-button-background-opacity: 0.2; |       --control-button-background-opacity: 0.2; | ||||||
|       --control-button-border-radius: var(--ha-border-radius-xl); |       --control-button-border-radius: 18px; | ||||||
|       height: 100px; |       height: 100px; | ||||||
|       width: 100px; |       width: 100px; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -187,7 +187,7 @@ export class DemoHaControlSelect extends LitElement { | |||||||
|       --mdc-icon-size: 24px; |       --mdc-icon-size: 24px; | ||||||
|       --control-select-color: var(--state-fan-active-color); |       --control-select-color: var(--state-fan-active-color); | ||||||
|       --control-select-thickness: 130px; |       --control-select-thickness: 130px; | ||||||
|       --control-select-border-radius: var(--ha-border-radius-6xl); |       --control-select-border-radius: 36px; | ||||||
|     } |     } | ||||||
|     .vertical-selects { |     .vertical-selects { | ||||||
|       height: 300px; |       height: 300px; | ||||||
|   | |||||||
| @@ -3,8 +3,8 @@ import { css, html, LitElement } from "lit"; | |||||||
| import { customElement, state } from "lit/decorators"; | import { customElement, state } from "lit/decorators"; | ||||||
| import { ifDefined } from "lit/directives/if-defined"; | import { ifDefined } from "lit/directives/if-defined"; | ||||||
| import { repeat } from "lit/directives/repeat"; | import { repeat } from "lit/directives/repeat"; | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-control-slider"; | import "../../../../src/components/ha-control-slider"; | ||||||
|  | import "../../../../src/components/ha-card"; | ||||||
|  |  | ||||||
| const sliders: { | const sliders: { | ||||||
|   id: string; |   id: string; | ||||||
| @@ -151,7 +151,7 @@ export class DemoHaBarSlider extends LitElement { | |||||||
|       --control-slider-background: #ffcf4c; |       --control-slider-background: #ffcf4c; | ||||||
|       --control-slider-background-opacity: 0.2; |       --control-slider-background-opacity: 0.2; | ||||||
|       --control-slider-thickness: 130px; |       --control-slider-thickness: 130px; | ||||||
|       --control-slider-border-radius: var(--ha-border-radius-6xl); |       --control-slider-border-radius: 36px; | ||||||
|     } |     } | ||||||
|     .vertical-sliders { |     .vertical-sliders { | ||||||
|       height: 300px; |       height: 300px; | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ import { css, html, LitElement } from "lit"; | |||||||
| import { customElement, state } from "lit/decorators"; | import { customElement, state } from "lit/decorators"; | ||||||
| import { ifDefined } from "lit/directives/if-defined"; | import { ifDefined } from "lit/directives/if-defined"; | ||||||
| import { repeat } from "lit/directives/repeat"; | import { repeat } from "lit/directives/repeat"; | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-control-switch"; | import "../../../../src/components/ha-control-switch"; | ||||||
|  | import "../../../../src/components/ha-card"; | ||||||
|  |  | ||||||
| const switches: { | const switches: { | ||||||
|   id: string; |   id: string; | ||||||
| @@ -118,7 +118,7 @@ export class DemoHaControlSwitch extends LitElement { | |||||||
|       --control-switch-on-color: var(--green-color); |       --control-switch-on-color: var(--green-color); | ||||||
|       --control-switch-off-color: var(--red-color); |       --control-switch-off-color: var(--red-color); | ||||||
|       --control-switch-thickness: 130px; |       --control-switch-thickness: 130px; | ||||||
|       --control-switch-border-radius: var(--ha-border-radius-6xl); |       --control-switch-border-radius: 36px; | ||||||
|       --control-switch-padding: 6px; |       --control-switch-padding: 6px; | ||||||
|       --mdc-icon-size: 24px; |       --mdc-icon-size: 24px; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| /* eslint-disable lit/no-template-arrow */ | /* eslint-disable lit/no-template-arrow */ | ||||||
|  | import "@material/mwc-button"; | ||||||
| import type { TemplateResult } from "lit"; | import type { TemplateResult } from "lit"; | ||||||
| import { html, LitElement } from "lit"; | import { html, LitElement } from "lit"; | ||||||
| import { customElement, state } from "lit/decorators"; | import { customElement, state } from "lit/decorators"; | ||||||
|   | |||||||
| @@ -1,37 +0,0 @@ | |||||||
| --- |  | ||||||
| title: Marquee Text |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| # Marquee Text `<ha-marquee-text>` |  | ||||||
|  |  | ||||||
| Marquee text component scrolls text horizontally if it overflows its container. It supports pausing on hover and customizable speed and pause duration. |  | ||||||
|  |  | ||||||
| ## Implementation |  | ||||||
|  |  | ||||||
| ### Example Usage |  | ||||||
|  |  | ||||||
| <ha-marquee-text style="width: 200px;"> |  | ||||||
|     This is a long text that will scroll horizontally if it overflows the container. |  | ||||||
| </ha-marquee-text> |  | ||||||
|  |  | ||||||
| ```html |  | ||||||
| <ha-marquee-text style="width: 200px;"> |  | ||||||
|   This is a long text that will scroll horizontally if it overflows the |  | ||||||
|   container. |  | ||||||
| </ha-marquee-text> |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### API |  | ||||||
|  |  | ||||||
| **Slots** |  | ||||||
|  |  | ||||||
| - default slot: The text content to be displayed and scrolled. |  | ||||||
|   - no default |  | ||||||
|  |  | ||||||
| **Properties/Attributes** |  | ||||||
|  |  | ||||||
| | Name           | Type    | Default | Description                                                                  | |  | ||||||
| | -------------- | ------- | ------- | ---------------------------------------------------------------------------- | |  | ||||||
| | speed          | number  | `15`    | The speed of the scrolling animation. Higher values result in faster scroll. | |  | ||||||
| | pause-on-hover | boolean | `true`  | Whether to pause the scrolling animation when                                | |  | ||||||
| | pause-duration | number  | `1000`  | The delay in milliseconds before the scrolling animation starts/restarts.    | |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| import { css, LitElement } from "lit"; |  | ||||||
| import { customElement } from "lit/decorators"; |  | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-marquee-text"; |  | ||||||
|  |  | ||||||
| @customElement("demo-components-ha-marquee-text") |  | ||||||
| export class DemoHaMarqueeText extends LitElement { |  | ||||||
|   static styles = css` |  | ||||||
|     ha-card { |  | ||||||
|       max-width: 600px; |  | ||||||
|       margin: 24px auto; |  | ||||||
|     } |  | ||||||
|     .card-content { |  | ||||||
|       display: flex; |  | ||||||
|       flex-direction: column; |  | ||||||
|       align-items: flex-start; |  | ||||||
|     } |  | ||||||
|   `; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| declare global { |  | ||||||
|   interface HTMLElementTagNameMap { |  | ||||||
|     "demo-components-ha-marquee-text": DemoHaMarqueeText; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| --- |  | ||||||
| title: Progress Button |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| <style> |  | ||||||
|   .wrapper { |  | ||||||
|     display: flex; |  | ||||||
|     gap: 24px; |  | ||||||
|   } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
| # Progress Button `<ha-progress-button>` |  | ||||||
|  |  | ||||||
| ### API |  | ||||||
|  |  | ||||||
| This component is a wrapper around `<ha-button>` that adds support for showing progress |  | ||||||
|  |  | ||||||
| **Slots** |  | ||||||
|  |  | ||||||
| - default slot: Label of the button |  | ||||||
|   ` - no default |  | ||||||
|  |  | ||||||
| **Properties/Attributes** |  | ||||||
|  |  | ||||||
| | Name       | Type                                           | Default   | Description                                        | |  | ||||||
| | ---------- | ---------------------------------------------- | --------- | -------------------------------------------------- | |  | ||||||
| | label      | string                                         | "accent"  | Sets the button label.                             | |  | ||||||
| | disabled   | Boolean                                        | false     | Disables the button if true.                       | |  | ||||||
| | progress   | Boolean                                        | false     | Shows a progress indicator on the button.          | |  | ||||||
| | appearance | "accent"/"filled"/"plain"                      | "accent"  | Sets the button appearance.                        | |  | ||||||
| | variants   | "brand"/"danger"/"neutral"/"warning"/"success" | "brand"   | Sets the button color variant. "brand" is default. | |  | ||||||
| | iconPath   | string                                         | undefined | Sets the icon path for the button.                 | |  | ||||||
| @@ -1,139 +0,0 @@ | |||||||
| import type { TemplateResult } from "lit"; |  | ||||||
| import { css, html, LitElement } from "lit"; |  | ||||||
| import { customElement } from "lit/decorators"; |  | ||||||
| import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; |  | ||||||
| import "../../../../src/components/buttons/ha-progress-button"; |  | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-svg-icon"; |  | ||||||
| import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg"; |  | ||||||
|  |  | ||||||
| @customElement("demo-components-ha-progress-button") |  | ||||||
| export class DemoHaProgressButton extends LitElement { |  | ||||||
|   protected render(): TemplateResult { |  | ||||||
|     return html` |  | ||||||
|       ${["light", "dark"].map( |  | ||||||
|         (mode) => html` |  | ||||||
|           <div class=${mode}> |  | ||||||
|             <ha-card header="ha-progress-button in ${mode}"> |  | ||||||
|               <div class="card-content"> |  | ||||||
|                 <ha-progress-button @click=${this._clickedSuccess}> |  | ||||||
|                   Success |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button @click=${this._clickedFail}> |  | ||||||
|                   Fail |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button size="small" @click=${this._clickedSuccess}> |  | ||||||
|                   small |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button |  | ||||||
|                   appearance="filled" |  | ||||||
|                   @click=${this._clickedSuccess} |  | ||||||
|                 > |  | ||||||
|                   filled |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button |  | ||||||
|                   appearance="plain" |  | ||||||
|                   @click=${this._clickedSuccess} |  | ||||||
|                 > |  | ||||||
|                   plain |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button |  | ||||||
|                   variant="warning" |  | ||||||
|                   @click=${this._clickedSuccess} |  | ||||||
|                 > |  | ||||||
|                   warning |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button |  | ||||||
|                   variant="neutral" |  | ||||||
|                   @click=${this._clickedSuccess} |  | ||||||
|                   label="with icon" |  | ||||||
|                   .iconPath=${mdiHomeAssistant} |  | ||||||
|                 > |  | ||||||
|                   With Icon |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button progress @click=${this._clickedSuccess}> |  | ||||||
|                   progress |  | ||||||
|                 </ha-progress-button> |  | ||||||
|                 <ha-progress-button disabled @click=${this._clickedSuccess}> |  | ||||||
|                   disabled |  | ||||||
|                 </ha-progress-button> |  | ||||||
|               </div> |  | ||||||
|             </ha-card> |  | ||||||
|           </div> |  | ||||||
|         ` |  | ||||||
|       )} |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   firstUpdated(changedProps) { |  | ||||||
|     super.firstUpdated(changedProps); |  | ||||||
|     applyThemesOnElement( |  | ||||||
|       this.shadowRoot!.querySelector(".dark"), |  | ||||||
|       { |  | ||||||
|         default_theme: "default", |  | ||||||
|         default_dark_theme: "default", |  | ||||||
|         themes: {}, |  | ||||||
|         darkMode: true, |  | ||||||
|         theme: "default", |  | ||||||
|       }, |  | ||||||
|       undefined, |  | ||||||
|       undefined, |  | ||||||
|       true |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async _clickedSuccess(ev: CustomEvent): Promise<void> { |  | ||||||
|     console.log("Clicked success"); |  | ||||||
|     const button = ev.currentTarget as any; |  | ||||||
|     button.progress = true; |  | ||||||
|  |  | ||||||
|     setTimeout(() => { |  | ||||||
|       button.actionSuccess(); |  | ||||||
|       button.progress = false; |  | ||||||
|     }, 1000); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async _clickedFail(ev: CustomEvent): Promise<void> { |  | ||||||
|     const button = ev.currentTarget as any; |  | ||||||
|     button.progress = true; |  | ||||||
|  |  | ||||||
|     setTimeout(() => { |  | ||||||
|       button.actionError(); |  | ||||||
|       button.progress = false; |  | ||||||
|     }, 1000); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static styles = css` |  | ||||||
|     :host { |  | ||||||
|       display: flex; |  | ||||||
|       justify-content: center; |  | ||||||
|     } |  | ||||||
|     .dark, |  | ||||||
|     .light { |  | ||||||
|       display: block; |  | ||||||
|       background-color: var(--primary-background-color); |  | ||||||
|       padding: 0 50px; |  | ||||||
|     } |  | ||||||
|     .button { |  | ||||||
|       padding: unset; |  | ||||||
|     } |  | ||||||
|     ha-card { |  | ||||||
|       margin: 24px auto; |  | ||||||
|     } |  | ||||||
|     .card-content { |  | ||||||
|       display: flex; |  | ||||||
|       flex-direction: column; |  | ||||||
|       gap: var(--ha-space-6); |  | ||||||
|     } |  | ||||||
|     .card-content div { |  | ||||||
|       display: flex; |  | ||||||
|       gap: var(--ha-space-2); |  | ||||||
|     } |  | ||||||
|   `; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| declare global { |  | ||||||
|   interface HTMLElementTagNameMap { |  | ||||||
|     "demo-components-ha-progress-button": DemoHaProgressButton; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -131,7 +131,7 @@ export class DemoHaSelectBox extends LitElement { | |||||||
|       --mdc-icon-size: 24px; |       --mdc-icon-size: 24px; | ||||||
|       --control-select-color: var(--state-fan-active-color); |       --control-select-color: var(--state-fan-active-color); | ||||||
|       --control-select-thickness: 130px; |       --control-select-thickness: 130px; | ||||||
|       --control-select-border-radius: var(--ha-border-radius-6xl); |       --control-select-border-radius: 36px; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     p.title { |     p.title { | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import "@material/mwc-button"; | ||||||
| import type { TemplateResult } from "lit"; | import type { TemplateResult } from "lit"; | ||||||
| import { css, html, LitElement } from "lit"; | import { css, html, LitElement } from "lit"; | ||||||
| import { customElement, state } from "lit/decorators"; | import { customElement, state } from "lit/decorators"; | ||||||
|   | |||||||
| @@ -1,38 +0,0 @@ | |||||||
| --- |  | ||||||
| title: Slider |  | ||||||
| subtitle: A slider component for selecting a value from a range. |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| <style> |  | ||||||
|   .wrapper { |  | ||||||
|     display: flex; |  | ||||||
|     gap: 24px; |  | ||||||
|   } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
| # Slider `<ha-slider>` |  | ||||||
|  |  | ||||||
| ## Implementation |  | ||||||
|  |  | ||||||
| ### Example Usage |  | ||||||
|  |  | ||||||
| <div class="wrapper"> |  | ||||||
|   <ha-slider size="small" with-markers min="0" max="8" value="4"></ha-slider> |  | ||||||
|   <ha-slider size="medium"></ha-slider> |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
| ```html |  | ||||||
| <ha-slider size="small" with-markers min="0" max="8" value="4"></ha-slider> |  | ||||||
| <ha-slider size="medium"></ha-slider> |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### API |  | ||||||
|  |  | ||||||
| This component is based on the webawesome slider component. |  | ||||||
| Check the [webawesome documentation](https://webawesome.com/docs/components/slider/) for more details. |  | ||||||
|  |  | ||||||
| **CSS Custom Properties** |  | ||||||
|  |  | ||||||
| - `--ha-slider-track-size` - Height of the slider track. Defaults to `4px`. |  | ||||||
| - `--ha-slider-thumb-color` - Color of the slider thumb. Defaults to `var(--primary-color)`. |  | ||||||
| - `--ha-slider-indicator-color` - Color of the filled portion of the slider track. Defaults to `var(--primary-color)`. |  | ||||||
| @@ -1,100 +0,0 @@ | |||||||
| import type { TemplateResult } from "lit"; |  | ||||||
| import { css, html, LitElement } from "lit"; |  | ||||||
| import { customElement, property } from "lit/decorators"; |  | ||||||
| import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; |  | ||||||
| import "../../../../src/components/ha-bar"; |  | ||||||
| import "../../../../src/components/ha-card"; |  | ||||||
| import "../../../../src/components/ha-spinner"; |  | ||||||
| import "../../../../src/components/ha-slider"; |  | ||||||
| import type { HomeAssistant } from "../../../../src/types"; |  | ||||||
|  |  | ||||||
| @customElement("demo-components-ha-slider") |  | ||||||
| export class DemoHaSlider extends LitElement { |  | ||||||
|   @property({ attribute: false }) hass!: HomeAssistant; |  | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |  | ||||||
|     return html` |  | ||||||
|       ${["light", "dark"].map( |  | ||||||
|         (mode) => html` |  | ||||||
|           <div class=${mode}> |  | ||||||
|             <ha-card header="ha-slider ${mode} demo"> |  | ||||||
|               <div class="card-content"> |  | ||||||
|                 <span>Default (disabled)</span> |  | ||||||
|                 <ha-slider |  | ||||||
|                   disabled |  | ||||||
|                   min="0" |  | ||||||
|                   max="8" |  | ||||||
|                   value="4" |  | ||||||
|                   with-markers |  | ||||||
|                 ></ha-slider> |  | ||||||
|                 <span>Small</span> |  | ||||||
|                 <ha-slider |  | ||||||
|                   size="small" |  | ||||||
|                   min="0" |  | ||||||
|                   max="8" |  | ||||||
|                   value="4" |  | ||||||
|                   with-markers |  | ||||||
|                 ></ha-slider> |  | ||||||
|                 <span>Medium</span> |  | ||||||
|                 <ha-slider |  | ||||||
|                   size="medium" |  | ||||||
|                   min="0" |  | ||||||
|                   max="8" |  | ||||||
|                   value="4" |  | ||||||
|                   with-markers |  | ||||||
|                 ></ha-slider> |  | ||||||
|               </div> |  | ||||||
|             </ha-card> |  | ||||||
|           </div> |  | ||||||
|         ` |  | ||||||
|       )} |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   firstUpdated(changedProps) { |  | ||||||
|     super.firstUpdated(changedProps); |  | ||||||
|     applyThemesOnElement( |  | ||||||
|       this.shadowRoot!.querySelector(".dark"), |  | ||||||
|       { |  | ||||||
|         default_theme: "default", |  | ||||||
|         default_dark_theme: "default", |  | ||||||
|         themes: {}, |  | ||||||
|         darkMode: true, |  | ||||||
|         theme: "default", |  | ||||||
|       }, |  | ||||||
|       undefined, |  | ||||||
|       undefined, |  | ||||||
|       true |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static styles = css` |  | ||||||
|     :host { |  | ||||||
|       display: flex; |  | ||||||
|       justify-content: center; |  | ||||||
|     } |  | ||||||
|     .dark, |  | ||||||
|     .light { |  | ||||||
|       display: block; |  | ||||||
|       background-color: var(--primary-background-color); |  | ||||||
|       padding: 0 50px; |  | ||||||
|       margin: 16px; |  | ||||||
|       border-radius: var(--ha-border-radius-md); |  | ||||||
|     } |  | ||||||
|     ha-card { |  | ||||||
|       margin: 24px auto; |  | ||||||
|     } |  | ||||||
|     .card-content { |  | ||||||
|       display: flex; |  | ||||||
|       flex-direction: column; |  | ||||||
|       align-items: center; |  | ||||||
|       gap: var(--ha-space-6); |  | ||||||
|     } |  | ||||||
|   `; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| declare global { |  | ||||||
|   interface HTMLElementTagNameMap { |  | ||||||
|     "demo-components-ha-slider": DemoHaSlider; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user