From af69f7edd27f7eaa609f62102f300ad37904bf29 Mon Sep 17 00:00:00 2001 From: Kevin Meinhardt Date: Tue, 18 Mar 2025 20:34:06 +0100 Subject: [PATCH 1/2] Migrate remaining files to vite --- package-lock.json | 24 +- package.json | 2 +- .../management/commands/compress_assets.py | 5 +- src/olympia/core/apps.py | 48 +- src/olympia/core/tests/test_apps.py | 132 +- src/olympia/devhub/templates/devhub/base.html | 4 +- .../devhub/templates/devhub/index.html | 13 +- src/olympia/lib/settings_base.py | 129 +- src/olympia/lib/storage.py | 34 - .../reviewers/templates/reviewers/base.html | 6 +- src/olympia/stats/templates/stats/stats.html | 7 +- src/olympia/templates/base.html | 21 +- static/css/devhub-new-landing.less | 1 + static/css/devhub.less | 18 + .../devhub/new-landing/sections/banner.less | 2 +- .../devhub/new-landing/sections/connect.less | 8 +- static/css/fonts.less | 1 + static/css/footer.less | 1 + static/css/jquery-ui.less | 1 + static/css/nojs.less | 1 + static/css/restyle.less | 1 + static/css/reviewers.less | 2 + static/css/stats.less | 1 + static/css/zamboni.less | 11 + static/js/admin.js | 1 - static/js/common.js | 7 + static/js/common/lang_switcher.js | 2 + static/js/common/upload-addon.js | 1248 ++++++++--------- static/js/common/upload-base.js | 234 ++-- static/js/common/upload-image.js | 186 +-- static/js/debug/less_live.js | 4 - static/js/debug/less_setup.js | 6 - static/js/devhub-new-landing.js | 2 + static/js/devhub.js | 14 + static/js/exports.js | 2 - static/js/lib/basket-client.js | 38 +- static/js/lib/format.js | 33 +- static/js/lib/highcharts-module.js | 5 + static/js/lib/jquery.hoverIntent.js | 160 +-- static/js/lib/jquery.zoomBox.js | 224 +-- static/js/lib/ngettext-overload.js | 24 - static/js/lib/prevent-default.js | 7 + static/js/lib/truncate.js | 40 +- static/js/node_lib/.keep | 0 static/js/node_lib/ui/.keep | 0 static/js/preload.js | 1 + static/js/reviewers.js | 5 + static/js/stats.js | 10 + static/js/stats/chart.js | 73 +- static/js/stats/controls.js | 147 +- static/js/stats/csv_keys.js | 2 +- static/js/stats/dateutils.js | 194 +-- static/js/stats/helpers.js | 18 +- static/js/stats/manager.js | 77 +- static/js/stats/overview.js | 142 +- static/js/stats/stats.js | 237 ++-- static/js/stats/table.js | 242 ++-- static/js/stats/topchart.js | 289 ++-- static/js/zamboni/analytics.js | 2 +- static/js/zamboni/capabilities.js | 62 +- static/js/zamboni/devhub.js | 377 ++--- static/js/zamboni/form-data.js | 79 ++ static/js/zamboni/global.js | 156 +-- static/js/zamboni/helpers.js | 15 +- static/js/zamboni/init.js | 28 +- static/js/{common => zamboni}/keys.js | 3 +- static/js/zamboni/l10n.js | 291 ++-- static/js/zamboni/reviewers.js | 117 +- static/js/zamboni/static_theme.js | 68 +- static/js/zamboni/storage.js | 71 +- static/js/zamboni/themes_review.js | 179 +-- static/js/zamboni/themes_review_templates.js | 100 +- static/js/zamboni/truncation.js | 13 +- static/js/zamboni/unicode.js | 3 +- static/js/zamboni/users.js | 2 + static/js/zamboni/validator.js | 61 +- static/js/zamboni/z.js | 1 + tests/js/zamboni/stats.spec.js | 52 +- vite.config.js | 7 + vitest.config.js | 8 + 80 files changed, 2866 insertions(+), 2976 deletions(-) delete mode 100644 src/olympia/lib/storage.py create mode 100644 static/css/devhub-new-landing.less create mode 100644 static/css/devhub.less create mode 100644 static/css/fonts.less create mode 100644 static/css/footer.less create mode 100644 static/css/jquery-ui.less create mode 100644 static/css/nojs.less create mode 100644 static/css/restyle.less create mode 100644 static/css/reviewers.less create mode 100644 static/css/stats.less create mode 100644 static/css/zamboni.less create mode 100644 static/js/common.js delete mode 100644 static/js/debug/less_live.js delete mode 100644 static/js/debug/less_setup.js create mode 100644 static/js/devhub-new-landing.js create mode 100644 static/js/devhub.js delete mode 100644 static/js/exports.js create mode 100644 static/js/lib/highcharts-module.js delete mode 100644 static/js/lib/ngettext-overload.js create mode 100644 static/js/lib/prevent-default.js delete mode 100644 static/js/node_lib/.keep delete mode 100644 static/js/node_lib/ui/.keep create mode 100644 static/js/reviewers.js create mode 100644 static/js/stats.js create mode 100644 static/js/zamboni/form-data.js rename static/js/{common => zamboni}/keys.js (85%) create mode 100644 static/js/zamboni/z.js diff --git a/package-lock.json b/package-lock.json index 8e2131f163c8..6c427861062c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "less": "4.2.2", "netmask": "2.0.2", "source-map": "0.7.4", - "timeago": "1.6.7", + "timeago.js": "^4.0.2", "underscore": "1.13.7" }, "devDependencies": { @@ -6173,13 +6173,10 @@ "real-require": "^0.2.0" } }, - "node_modules/timeago": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/timeago/-/timeago-1.6.7.tgz", - "integrity": "sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ==", - "dependencies": { - "jquery": ">=1.5.0 <4.0" - } + "node_modules/timeago.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz", + "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==" }, "node_modules/tinybench": { "version": "2.9.0", @@ -10888,13 +10885,10 @@ "real-require": "^0.2.0" } }, - "timeago": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/timeago/-/timeago-1.6.7.tgz", - "integrity": "sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ==", - "requires": { - "jquery": ">=1.5.0 <4.0" - } + "timeago.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz", + "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==" }, "tinybench": { "version": "2.9.0", diff --git a/package.json b/package.json index 743347a1e5f4..6cb47d91d7ac 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "less": "4.2.2", "netmask": "2.0.2", "source-map": "0.7.4", - "timeago": "1.6.7", + "timeago.js": "^4.0.2", "underscore": "1.13.7" }, "devDependencies": { diff --git a/src/olympia/amo/management/commands/compress_assets.py b/src/olympia/amo/management/commands/compress_assets.py index b12da242877d..b0b8a0e8ec65 100644 --- a/src/olympia/amo/management/commands/compress_assets.py +++ b/src/olympia/amo/management/commands/compress_assets.py @@ -103,10 +103,7 @@ def handle(self, **options): # Concat all the files. tmp_concatted = '%s.tmp' % concatted_file if len(files_all) == 0: - raise CommandError( - 'No input files specified in ' - 'MINIFY_BUNDLES["%s"]["%s"] in settings.py!' % (ftype, name) - ) + return with open(tmp_concatted, 'w') as f: f.write(''.join(contents)) diff --git a/src/olympia/core/apps.py b/src/olympia/core/apps.py index 3d7a6aebdd22..2d0e0b7bdf7a 100644 --- a/src/olympia/core/apps.py +++ b/src/olympia/core/apps.py @@ -3,14 +3,12 @@ import os import subprocess import warnings -from io import StringIO +from pathlib import Path from pwd import getpwnam from django.apps import AppConfig from django.conf import settings from django.core.checks import Error, Tags, register -from django.core.management import call_command -from django.core.management.base import CommandError from django.db import connection from django.utils.translation import gettext_lazy as _ @@ -87,68 +85,38 @@ def version_check(app_configs, **kwargs): @register(CustomTags.custom_setup) def static_check(app_configs, **kwargs): errors = [] - output = StringIO() version = get_version_json() # We only run this check in production images. if version.get('target') != 'production': return [] - try: - call_command('compress_assets', dry_run=True, stdout=output) - stripped_output = output.getvalue().strip() - - if stripped_output: - file_paths = stripped_output.split('\n') - for file_path in file_paths: - if not os.path.exists(file_path): - error = f'Compressed asset file does not exist: {file_path}' - errors.append( - Error( - error, - id='setup.E003', - ) - ) - else: - errors.append( - Error( - 'No compressed asset files were found.', - id='setup.E003', - ) - ) - - except CommandError as e: - errors.append( - Error( - f'Error running compress_assets command: {str(e)}', - id='setup.E004', - ) - ) + manifest_path = Path(settings.STATIC_BUILD_MANIFEST_PATH) - if not os.path.exists(settings.STATIC_BUILD_MANIFEST_PATH): + if not manifest_path.exists(): errors.append( Error( ( 'Static build manifest file ' - f'does not exist: {settings.STATIC_BUILD_MANIFEST_PATH}' + f'does not exist: {manifest_path.as_posix()}' ), id='setup.E003', ) ) else: - with open(settings.STATIC_BUILD_MANIFEST_PATH, 'r') as f: + with open(manifest_path.as_posix(), 'r') as f: manifest = json.load(f) for name, asset in manifest.items(): # Assets compiled by vite are in the static root directory # after running collectstatic. So we should look there. - path = os.path.join(settings.STATIC_ROOT, asset['file']) - if not os.path.exists(path): + path = Path(settings.STATIC_ROOT) / asset['file'] + if not path.exists(): errors.append( Error( ( f'Static asset {name} does not exist at ' - f'expected path: {path}' + f'expected path: {path.as_posix()}' ), id='setup.E003', ) diff --git a/src/olympia/core/tests/test_apps.py b/src/olympia/core/tests/test_apps.py index 8c6823aeca15..214282740e02 100644 --- a/src/olympia/core/tests/test_apps.py +++ b/src/olympia/core/tests/test_apps.py @@ -1,8 +1,9 @@ +import json import os import tempfile from unittest import mock -from django.core.management import CommandError, call_command +from django.core.management import call_command from django.core.management.base import SystemCheckError from django.test import TestCase from django.test.utils import override_settings @@ -34,14 +35,11 @@ def setUp(self): with open(self.fake_css_file, 'w') as f: f.write('body { background: red; }') - patch_command = mock.patch('olympia.core.apps.call_command') - self.mock_call_command = patch_command.start() - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write(f'{self.fake_css_file}\n') - ) - self.addCleanup(patch_command.stop) - self.media_root = tempfile.mkdtemp(prefix='media-root') + self.static_root = tempfile.mkdtemp(prefix='static-root') + self.manifest_path = os.path.join( + tempfile.mkdtemp(prefix='manifest-dir'), 'manifest.json' + ) @mock.patch('olympia.core.apps.connection.cursor') def test_db_charset_check(self, mock_cursor): @@ -104,63 +102,87 @@ def test_illegal_override_uid_check(self, mock_getpwnam): with override_settings(HOST_UID=1000): call_command('check') - def test_static_check_no_assets_found(self): - """ - Test static_check fails if compress_assets reports no files. - """ - self.mock_get_version_json.return_value['target'] = 'production' - # Simulate "compress_assets" returning no file paths. - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write('') - ) + @override_settings(STATIC_BUILD_MANIFEST_PATH='/nonexistent/path/manifest.json') + def test_static_check_manifest_does_not_exist(self): + """Test that an error is raised when the manifest file doesn't exist.""" with self.assertRaisesMessage( - SystemCheckError, 'No compressed asset files were found.' + SystemCheckError, + 'Static build manifest file does not exist: ' + '/nonexistent/path/manifest.json', ): call_command('check') - @mock.patch('os.path.exists') - def test_static_check_missing_assets(self, mock_exists): - """ - Test static_check fails if at least one specified compressed - asset file does not exist. - """ - self.mock_get_version_json.return_value['target'] = 'production' - # Simulate "compress_assets" returning a couple of files. - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write( - f'{self.fake_css_file}\nfoo.js\n' - ) - ) - # Pretend neither file exists on disk. - mock_exists.return_value = False + @override_settings(STATIC_ROOT='/static/root', STATIC_BUILD_MANIFEST_PATH=None) + def test_static_check_manifest_references_nonexistent_files(self): + """Test that an error is raised when manifest references non-existent files.""" + # Create a temporary manifest file with references to non-existent files + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + manifest_content = { + 'app.js': {'file': 'app.123abc.js'}, + 'styles.css': {'file': 'styles.456def.css'}, + } + json.dump(manifest_content, f) + manifest_path = f.name + + with override_settings(STATIC_BUILD_MANIFEST_PATH=manifest_path): + with self.assertRaisesMessage( + SystemCheckError, + 'Static asset app.js does not exist at expected path: ' + '/static/root/app.123abc.js', + ): + call_command('check') - with self.assertRaisesMessage( - SystemCheckError, - # Only the first missing file triggers the AssertionError message check - 'Compressed asset file does not exist: foo.js', - ): + os.unlink(manifest_path) + + @override_settings(STATIC_BUILD_MANIFEST_PATH=None) + def test_static_check_empty_manifest(self): + """Test that no error is raised with an empty manifest.""" + # Create an empty manifest file + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + json.dump({}, f) + manifest_path = f.name + + with override_settings(STATIC_BUILD_MANIFEST_PATH=manifest_path): + # No error should be raised with an empty manifest call_command('check') - def test_static_check_command_error(self): - """ - Test static_check fails if there's an error during compress_assets. - """ - self.mock_get_version_json.return_value['target'] = 'production' - self.mock_call_command.side_effect = CommandError('Oops') - with self.assertRaisesMessage( - SystemCheckError, 'Error running compress_assets command: Oops' + os.unlink(manifest_path) + + def test_static_check_valid_manifest(self): + """Test that no error is raised when manifest references existing files.""" + # Create a temporary static root with actual files + static_root = tempfile.mkdtemp(prefix='static-root-valid') + + # Create the asset files + asset1_path = os.path.join(static_root, 'app.123abc.js') + asset2_path = os.path.join(static_root, 'styles.456def.css') + + with open(asset1_path, 'w') as f: + f.write('console.log("test");') + + with open(asset2_path, 'w') as f: + f.write('body { color: blue; }') + + # Create a manifest file that references these files + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + manifest_content = { + 'app.js': {'file': 'app.123abc.js'}, + 'styles.css': {'file': 'styles.456def.css'}, + } + json.dump(manifest_content, f) + manifest_path = f.name + + with override_settings( + STATIC_ROOT=static_root, STATIC_BUILD_MANIFEST_PATH=manifest_path ): + # No error should be raised with a valid manifest call_command('check') - def test_static_check_command_success(self): - """ - Test static_check succeeds if compress_assets runs without errors. - """ - self.mock_get_version_json.return_value['target'] = 'production' - self.mock_call_command.side_effect = ( - lambda command, dry_run, stdout: stdout.write(f'{self.fake_css_file}\n') - ) - call_command('check') + # Clean up + os.unlink(manifest_path) + os.unlink(asset1_path) + os.unlink(asset2_path) + os.rmdir(static_root) def test_nginx_skips_check_on_production_target(self): fake_media_root = '/fake/not/real' diff --git a/src/olympia/devhub/templates/devhub/base.html b/src/olympia/devhub/templates/devhub/base.html index 174328b980ca..bd7f33c2ee0e 100644 --- a/src/olympia/devhub/templates/devhub/base.html +++ b/src/olympia/devhub/templates/devhub/base.html @@ -21,7 +21,7 @@ {% block title %}{{ dev_page_title() }}{% endblock %} {% block extrahead %} - {{ css('zamboni/devhub') }} + {{ vite_asset('css/devhub.less') }} {% endblock %} {% block site_header_title %} @@ -29,7 +29,7 @@ {% endblock %} {% block js %} - {{ js('zamboni/devhub') }} + {{ vite_asset('js/devhub.js') }} {% endblock %} {% block footer_extras %} diff --git a/src/olympia/devhub/templates/devhub/index.html b/src/olympia/devhub/templates/devhub/index.html index 657d2f824ba4..5a2c9b571b14 100644 --- a/src/olympia/devhub/templates/devhub/index.html +++ b/src/olympia/devhub/templates/devhub/index.html @@ -23,9 +23,9 @@ href="{{ url('amo.opensearch') }}" /> {% block site_css %} - {{ css('common/fonts') }} - {{ css('devhub/new-landing/css') }} - {{ css('common/footer') }} + {{ vite_asset('css/fonts.less') }} + {{ vite_asset('css/devhub-new-landing.less') }} + {{ vite_asset('css/footer.less') }} {% endblock %} {% block extrahead %}{% endblock %} @@ -36,13 +36,8 @@ {% if settings.DEV_MODE %} {{ vite_hmr_client() }} - {% if settings.LESS_LIVE_REFRESH %} - - {% endif %} - {{ js('debug') }} {% endif %} - {{ js('jquery_base') }} {{ vite_asset('js/preload.js') }} {% set user_authenticated = request.user.is_authenticated %} @@ -82,7 +77,7 @@ {% block site_js %} - {{ js('devhub/new-landing/js') }} + {{ vite_asset('js/devhub-new-landing.js') }} {% endblock %} diff --git a/src/olympia/lib/settings_base.py b/src/olympia/lib/settings_base.py index a1e57f994848..e7fd2f305db1 100644 --- a/src/olympia/lib/settings_base.py +++ b/src/olympia/lib/settings_base.py @@ -604,133 +604,10 @@ def get_db_config(environ_var, atomic_requests=True): # and js files that can be bundled together by the minify app. MINIFY_BUNDLES = { 'css': { - 'common/fonts': ('css/common/fonts.less',), - 'common/footer': ('css/common/footer.less',), - 'restyle/css': ('css/restyle/restyle.less',), - # CSS files our DevHub (currently only required for the - # new landing page) - 'devhub/new-landing/css': ('css/devhub/new-landing/base.less',), - # CSS files common to the entire site. - 'zamboni/css': ( - 'css/legacy/main.css', - 'css/legacy/main-mozilla.css', - 'css/zamboni/zamboni.css', - 'css/zamboni/tags.css', - 'css/zamboni/tabs.css', - 'css/impala/buttons.less', - 'css/impala/formset.less', - 'css/impala/suggestions.less', - 'css/impala/header.less', - 'css/impala/moz-tab.css', - 'css/impala/faux-zamboni.less', - ), - 'zamboni/stats': ('css/zamboni/stats.less',), - 'zamboni/devhub': ( - 'css/impala/tooltips.less', - 'css/zamboni/developers.css', - 'css/zamboni/docs.less', - 'css/impala/developers.less', - 'css/devhub/listing.less', - 'css/devhub/popups.less', - 'css/devhub/compat.less', - 'css/impala/formset.less', - 'css/devhub/forms.less', - 'css/common/invisible-upload.less', - 'css/devhub/submission.less', - 'css/devhub/refunds.less', - 'css/devhub/buttons.less', - 'css/devhub/in-app-config.less', - 'css/devhub/static-theme.less', - '@claviska/jquery-minicolors/jquery.minicolors.css', - 'css/impala/devhub-api.less', - 'css/devhub/dashboard.less', - ), - 'zamboni/reviewers': ( - 'css/zamboni/reviewers.less', - 'css/zamboni/unlisted.less', - ), - 'zamboni/themes_review': ( - 'css/zamboni/developers.css', - 'css/zamboni/reviewers.less', - 'css/zamboni/themes_review.less', - ), + 'common': (), }, 'js': { - # JS files common to the entire site, apart from dev-landing. - 'common': ( - 'underscore/underscore.js', - 'js/zamboni/init.js', - 'js/zamboni/capabilities.js', - 'js/lib/format.js', - 'jquery.cookie/jquery.cookie.js', - 'js/zamboni/storage.js', - 'js/common/keys.js', - 'js/zamboni/helpers.js', - 'js/zamboni/global.js', - 'js/zamboni/l10n.js', - # Unicode letters for our makeslug function - 'js/zamboni/unicode.js', - # Login tweaks - 'js/zamboni/users.js', - 'js/common/lang_switcher.js', - ), - # Things to be loaded at the top of the page - 'jquery_base': ( - 'jquery/dist/jquery.js', - 'jquery.browser/dist/jquery.browser.js', - ), - 'zamboni/devhub': ( - 'js/lib/truncate.js', - 'js/zamboni/truncation.js', - 'js/common/upload-base.js', - 'js/common/upload-addon.js', - 'js/common/upload-image.js', - 'js/zamboni/devhub.js', - 'js/zamboni/validator.js', - 'timeago/jquery.timeago.js', - 'js/zamboni/static_theme.js', - '@claviska/jquery-minicolors/jquery.minicolors.js', - 'jszip/dist/jszip.js', - # jQuery UI for sortable - 'jquery-ui/ui/data.js', - 'jquery-ui/ui/scroll-parent.js', - 'jquery-ui/ui/widget.js', - 'jquery-ui/ui/widgets/mouse.js', - 'jquery-ui/ui/widgets/sortable.js', - ), - 'devhub/new-landing/js': ( - # Note that new-landing (devhub/index.html) doesn't include - # zamboni/devhub or even common js bundles. - 'js/common/lang_switcher.js', - 'js/lib/basket-client.js', - ), - 'zamboni/reviewers': ( - 'js/lib/highcharts.src.js', - 'js/lib/jquery.hoverIntent.js', # Used by jquery.zoomBox. - 'js/lib/jquery.zoomBox.js', # Used by themes_review. - 'js/zamboni/reviewers.js', - 'js/zamboni/themes_review_templates.js', - 'js/zamboni/themes_review.js', - ), - 'zamboni/stats': ( - 'js/lib/highcharts.src.js', - 'js/stats/csv_keys.js', - 'js/stats/helpers.js', - 'js/stats/dateutils.js', - 'js/stats/manager.js', - 'js/stats/controls.js', - 'js/stats/overview.js', - 'js/stats/topchart.js', - 'js/stats/chart.js', - 'js/stats/table.js', - 'js/stats/stats.js', - ), - # This is included when DEV_MODE is True. Bundle in . - 'debug': ( - 'js/debug/less_setup.js', - 'less/dist/less.js', - 'js/debug/less_live.js', - ), + 'common': (), }, } @@ -1350,7 +1227,7 @@ def read_only_mode(env): STATIC_BUILD_PATH, ) -STATICFILES_STORAGE = 'olympia.lib.storage.OlympiaStaticFilesStorage' +STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' # Path related settings. In dev/stage/prod `NETAPP_STORAGE_ROOT` environment # variable will be set and point to our NFS/EFS storage diff --git a/src/olympia/lib/storage.py b/src/olympia/lib/storage.py deleted file mode 100644 index 19f35ae5f11d..000000000000 --- a/src/olympia/lib/storage.py +++ /dev/null @@ -1,34 +0,0 @@ -from django.conf import settings -from django.contrib.staticfiles.storage import ( - ManifestStaticFilesStorage, - StaticFilesStorage, -) - - -class ManifestStaticFilesStorageNotMaps(ManifestStaticFilesStorage): - patterns = ( - ( - '*.css', - ( - # These regexs are copied from HashedFilesMixin in django4.1+ - r"""(?Purl\(['"]{0,1}\s*(?P.*?)["']{0,1}\))""", - ( - r"""(?P@import\s*["']\s*(?P.*?)["'])""", - """@import url("%(url)s")""", - ), - # We are ommiting the sourceMappingURL regexs for .css and .js as they - # don't work how we copy over the souces in Makefile-docker.copy_node_js - ), - ), - ) - - def stored_name(self, name): - # Skip manifest lookup for vite assets - if name.startswith('assets/'): - return name - return super().stored_name(name) - - -OlympiaStaticFilesStorage = ( - StaticFilesStorage if settings.DEV_MODE else ManifestStaticFilesStorageNotMaps -) diff --git a/src/olympia/reviewers/templates/reviewers/base.html b/src/olympia/reviewers/templates/reviewers/base.html index ec5652a3c28b..9f611ed8849f 100644 --- a/src/olympia/reviewers/templates/reviewers/base.html +++ b/src/olympia/reviewers/templates/reviewers/base.html @@ -17,12 +17,12 @@

{% block header_search %}{% endblock %} {% block extrahead %} - {{ css('zamboni/devhub') }} - {{ css('zamboni/reviewers') }} + {{ vite_asset('css/devhub.less') }} + {{ vite_asset('css/reviewers.less') }} {% endblock %} {% block js %} - {{ js('zamboni/reviewers') }} + {{ vite_asset('js/reviewers.js') }} {% endblock %} {% block outer_content %} diff --git a/src/olympia/stats/templates/stats/stats.html b/src/olympia/stats/templates/stats/stats.html index 83c7500dac1d..5e18a863e26c 100644 --- a/src/olympia/stats/templates/stats/stats.html +++ b/src/olympia/stats/templates/stats/stats.html @@ -7,9 +7,8 @@ {% block bodyclass %}statistics{% endblock %} {% block extrahead %} - {{ css('zamboni/stats') }} - + {{ vite_asset('css/stats.less') }} + {{ vite_asset('css/jquery-ui.less') }} {% endblock %} {% block title %} @@ -185,5 +184,5 @@

{% endblock %} {% block js %} -{{ js('zamboni/stats') }} +{{ vite_asset('js/stats.js') }} {% endblock %} diff --git a/src/olympia/templates/base.html b/src/olympia/templates/base.html index 2cd30e3f1a49..08382c1a3089 100644 --- a/src/olympia/templates/base.html +++ b/src/olympia/templates/base.html @@ -19,32 +19,29 @@ href="{{ url('amo.opensearch') }}" /> {% block site_css %} - {{ css('common/fonts') }} - {{ css('zamboni/css') }} - {{ css('common/footer') }} + {{ vite_asset('css/fonts.less') }} + {{ vite_asset('css/zamboni.less') }} + {{ vite_asset('css/footer.less') }} {% endblock %} {% block extrahead %}{% endblock %} {% block restyle %} - {{ css('restyle/css') }} + {{ vite_asset('css/restyle.less') }} {% endblock %} - + {% if settings.DEV_MODE %} {{ vite_hmr_client() }} - {% if settings.LESS_LIVE_REFRESH %} - - {% endif %} - {{ js('debug') }} {% endif %} {% if user.is_authenticated %} {% endif %} - {{ js('jquery_base') }} {{ vite_asset('js/preload.js') }} {# js #} {% block site_js %} - - {{ js('common') }} + + {{ vite_asset('js/common.js') }} {% endblock %} {% block js %}{% endblock %} {% block footer %} diff --git a/static/css/devhub-new-landing.less b/static/css/devhub-new-landing.less new file mode 100644 index 000000000000..a594ab61061b --- /dev/null +++ b/static/css/devhub-new-landing.less @@ -0,0 +1 @@ +@import './devhub/new-landing/base.less'; diff --git a/static/css/devhub.less b/static/css/devhub.less new file mode 100644 index 000000000000..a505ab0b1597 --- /dev/null +++ b/static/css/devhub.less @@ -0,0 +1,18 @@ +@import "./impala/tooltips.less"; +@import "./zamboni/developers.css"; +@import "./zamboni/docs.less"; +@import "./impala/developers.less"; +@import "./devhub/listing.less"; +@import "./devhub/popups.less"; +@import "./devhub/compat.less"; +@import "./impala/formset.less"; +@import "./devhub/forms.less"; +@import "./common/invisible-upload.less"; +@import "./devhub/submission.less"; +@import "./devhub/refunds.less"; +@import "./devhub/buttons.less"; +@import "./devhub/in-app-config.less"; +@import "./devhub/static-theme.less"; +@import "@claviska/jquery-minicolors/jquery.minicolors.css"; +@import "./impala/devhub-api.less"; +@import "./devhub/dashboard.less"; diff --git a/static/css/devhub/new-landing/sections/banner.less b/static/css/devhub/new-landing/sections/banner.less index 1fcd531bcab6..6fb35ae2b625 100644 --- a/static/css/devhub/new-landing/sections/banner.less +++ b/static/css/devhub/new-landing/sections/banner.less @@ -28,7 +28,7 @@ } .DevHub-callout-box--banner::before { - background: no-repeat bottom left url(../../../img/developers/new-landing/banner-background.svg); + background: no-repeat bottom left url(../../../../img/developers/new-landing/banner-background.svg); background-size: (@grid-max - (@side-margin *2)) auto; content: ''; position: absolute; diff --git a/static/css/devhub/new-landing/sections/connect.less b/static/css/devhub/new-landing/sections/connect.less index 952c3e17c958..28477ccec009 100644 --- a/static/css/devhub/new-landing/sections/connect.less +++ b/static/css/devhub/new-landing/sections/connect.less @@ -158,14 +158,16 @@ a.Before-Icon::before { } } +// TODO: fix this for some reason images are not being mapped by vite correctly +// additionally it looks like the import paths here were wrong already? a.Before-Icon-twitter::before { - content: url(../../../img/icons/twitter.svg); + content: url(../../../../img/icons/twitter.svg); } a.Before-Icon-matrix::before { - content: url(../../../img/icons/matrix.svg); + content: url(../../../../img/icons/matrix.svg); } a.Before-Icon-mail::before { - content: url(../../../img/icons/mail.svg); + content: url(../../../../img/icons/mail.svg); } diff --git a/static/css/fonts.less b/static/css/fonts.less new file mode 100644 index 000000000000..55da91deb360 --- /dev/null +++ b/static/css/fonts.less @@ -0,0 +1 @@ +@import './common/fonts.less'; diff --git a/static/css/footer.less b/static/css/footer.less new file mode 100644 index 000000000000..1722a986ef28 --- /dev/null +++ b/static/css/footer.less @@ -0,0 +1 @@ +@import './common//footer.less'; diff --git a/static/css/jquery-ui.less b/static/css/jquery-ui.less new file mode 100644 index 000000000000..246192953f77 --- /dev/null +++ b/static/css/jquery-ui.less @@ -0,0 +1 @@ +@import './zamboni/jquery-ui/custom-1.7.2.css'; diff --git a/static/css/nojs.less b/static/css/nojs.less new file mode 100644 index 000000000000..2501af8c6712 --- /dev/null +++ b/static/css/nojs.less @@ -0,0 +1 @@ +@import './legacy//nojs.css'; diff --git a/static/css/restyle.less b/static/css/restyle.less new file mode 100644 index 000000000000..d5416839a585 --- /dev/null +++ b/static/css/restyle.less @@ -0,0 +1 @@ +@import './restyle/restyle.less'; diff --git a/static/css/reviewers.less b/static/css/reviewers.less new file mode 100644 index 000000000000..0ac99365db9e --- /dev/null +++ b/static/css/reviewers.less @@ -0,0 +1,2 @@ +@import "./zamboni/reviewers.less"; +@import "./zamboni/unlisted.less"; diff --git a/static/css/stats.less b/static/css/stats.less new file mode 100644 index 000000000000..17c4852496cc --- /dev/null +++ b/static/css/stats.less @@ -0,0 +1 @@ +@import './zamboni/stats.less'; diff --git a/static/css/zamboni.less b/static/css/zamboni.less new file mode 100644 index 000000000000..ff269cea3c20 --- /dev/null +++ b/static/css/zamboni.less @@ -0,0 +1,11 @@ +@import "./legacy/main.css"; +@import "./legacy/main-mozilla.css"; +@import "./zamboni/zamboni.css"; +@import "./zamboni/tags.css"; +@import "./zamboni/tabs.css"; +@import "./impala/buttons.less"; +@import "./impala/formset.less"; +@import "./impala/suggestions.less"; +@import "./impala/header.less"; +@import "./impala/moz-tab.css"; +@import "./impala/faux-zamboni.less"; diff --git a/static/js/admin.js b/static/js/admin.js index 0f2575fd7c76..5e5a1aa04162 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -1,2 +1 @@ import './admin/ip_address_search.js'; -import './exports.js'; diff --git a/static/js/common.js b/static/js/common.js new file mode 100644 index 000000000000..8f41361fb81c --- /dev/null +++ b/static/js/common.js @@ -0,0 +1,7 @@ +import './zamboni/init'; +import './zamboni/helpers'; +import './zamboni/global'; +import './zamboni/l10n'; +import './zamboni/unicode'; +import './zamboni/users'; +import './common/lang_switcher'; diff --git a/static/js/common/lang_switcher.js b/static/js/common/lang_switcher.js index 6ebbeaac833d..33d15e74f399 100644 --- a/static/js/common/lang_switcher.js +++ b/static/js/common/lang_switcher.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + /* Remove "Go" buttons from
', { + class: 'invisible-upload prominent cta', + id: 'upload-file-widget', + }), + ui_link = $('', { + class: 'button prominent', + href: '#', + text: gettext('Select a file...'), + }), + ui_details = $('
', { + class: 'upload-details', + text: gettext('Your add-on should end with .zip, .xpi or .crx'), + }); - var ui_parent = $('
', { - class: 'invisible-upload prominent cta', - id: 'upload-file-widget', - }), - ui_link = $('', { - class: 'button prominent', - href: '#', - text: gettext('Select a file...'), - }), - ui_details = $('
', { - class: 'upload-details', - text: gettext('Your add-on should end with .zip, .xpi or .crx'), - }); + ui_link.toggleClass('disabled', settings.submissionsDisabled); + $upload_field.prop('disabled', settings.submissionsDisabled); - ui_link.toggleClass('disabled', settings.submissionsDisabled); - $upload_field.prop('disabled', settings.submissionsDisabled); + $upload_field.wrap(ui_parent); + $upload_field.before(ui_link); + $upload_field.parent().after(ui_details); - $upload_field.wrap(ui_parent); - $upload_field.before(ui_link); - $upload_field.parent().after(ui_details); + if (!capabilities.fileAPI) { + $('.invisible-upload').addClass('legacy'); + } - if (!z.capabilities.fileAPI) { - $('.invisible-upload').addClass('legacy'); + /* Get things started */ + + let upload_box, + upload_title, + upload_progress_outside, + upload_progress_inside, + upload_status, + upload_results, + upload_status_percent, + upload_status_progress, + upload_status_cancel, + upload_status_cancel_a; + + $upload_field.fileUploader(settings); + + function updateStatus(percentage, size) { + if (percentage) { + upload_status.show(); + const p = Math.round(percentage); + size = (p / 100) * size; + + // L10n: {0} is the percent of the file that has been uploaded. + upload_status_percent.text(format(gettext('{0}% complete'), [p])); + + // L10n: "{bytes uploaded} of {total filesize}". + upload_status_progress.text( + format(gettext('{0} of {1}'), [ + formatFileSize(size), + formatFileSize(file.size), + ]), + ); } + } - /* Get things started */ - - var upload_box, - upload_title, - upload_progress_outside, - upload_progress_inside, - upload_status, - upload_results, - upload_status_percent, - upload_status_progress, - upload_status_cancel; - - $upload_field.fileUploader(settings); - - function updateStatus(percentage, size) { - if (percentage) { - upload_status.show(); - p = Math.round(percentage); - size = (p / 100) * size; - - // L10n: {0} is the percent of the file that has been uploaded. - upload_status_percent.text(format(gettext('{0}% complete'), [p])); - - // L10n: "{bytes uploaded} of {total filesize}". - upload_status_progress.text( - format(gettext('{0} of {1}'), [ - formatFileSize(size), - formatFileSize(file.size), - ]), - ); - } - } + /* Bind the events */ + + $upload_field.on('upload_start', function (e, _file) { + file = _file; - /* Bind the events */ + /* Remove old upload box */ + if (upload_box) { + upload_box.remove(); + } - $upload_field.on('upload_start', function (e, _file) { - file = _file; + /* Remove old errors */ + $upload_field.closest('form').find('.errorlist').remove(); + + /* Set defaults */ + $('#id_is_manual_review').prop('checked', false); + + /* Don't allow submitting */ + // The create theme wizard button is actually a link, + // so it's pointless to set the disabled property on it, + // instead add the special "concealed" class. + $('.addon-create-theme-section .button').addClass('concealed'); + $('.addon-upload-dependant').prop('disabled', true); + $('.addon-upload-failure-dependant').prop({ + disabled: true, + checked: false, + }); - /* Remove old upload box */ - if (upload_box) { - upload_box.remove(); - } + /* Create elements */ + upload_title = $('', { id: 'upload-status-text' }); + upload_progress_outside = $('
', { id: 'upload-status-bar' }); + upload_progress_inside = $('
').css('width', 0); + upload_status = $('
', { id: 'uploadstatus' }).hide(); + upload_status_percent = $(''); + upload_status_progress = $(''); + upload_status_cancel_a = $('', { + href: '#', + text: gettext('Cancel'), + }); + upload_status_cancel = $(' · '); + upload_results = $('
', { id: 'upload-status-results' }); + upload_box = $('
', { class: 'upload-status ajax-loading' }).hide(); + + /* Set up structure */ + upload_box.append(upload_title); + upload_progress_outside.append(upload_progress_inside); + upload_box.append(upload_progress_outside); + upload_status.append(upload_status_percent); + upload_status.append(' · '); + upload_status.append(upload_status_progress); + upload_status.append(upload_status_cancel); + upload_status_cancel.append(upload_status_cancel_a); + + upload_box.append(upload_status); + upload_box.append(upload_results); + + /* Add to the dom and clean up upload_field */ + ui_details.after(upload_box); + + /* It's showtime! */ + upload_title.html(format(gettext('Uploading {0}'), [escape_(file.name)])); + upload_box.show(); + + upload_box.addClass('ajax-loading'); + + upload_status_cancel_a.click( + _pd(function () { + $upload_field.trigger('upload_action_abort'); + }), + ); + }); - /* Remove old errors */ - $upload_field.closest('form').find('.errorlist').remove(); + $upload_field.on('upload_progress', function (e, file, pct) { + upload_progress_inside.animate( + { width: pct + '%' }, + { + duration: 300, + step: function (i) { + updateStatus(i, file.size); + }, + }, + ); + }); - /* Set defaults */ - $('#id_is_manual_review').prop('checked', false); + $upload_field.on('upload_errors', function (e, file, errors, results) { + let all_errors = $.extend([], errors); // be nice to other handlers + upload_progress_inside.stop().css({ width: '100%' }); - /* Don't allow submitting */ - // The create theme wizard button is actually a link, - // so it's pointless to set the disabled property on it, - // instead add the special "concealed" class. - $('.addon-create-theme-section .button').addClass('concealed'); - $('.addon-upload-dependant').prop('disabled', true); + if ($('input#id_upload').val()) { $('.addon-upload-failure-dependant').prop({ - disabled: true, + disabled: false, checked: false, }); + } - /* Create elements */ - upload_title = $('', { id: 'upload-status-text' }); - upload_progress_outside = $('
', { id: 'upload-status-bar' }); - upload_progress_inside = $('
').css('width', 0); - upload_status = $('
', { id: 'uploadstatus' }).hide(); - upload_status_percent = $(''); - upload_status_progress = $(''); - upload_status_cancel_a = $('', { - href: '#', - text: gettext('Cancel'), - }); - upload_status_cancel = $(' · '); - upload_results = $('
', { id: 'upload-status-results' }); - upload_box = $('
', { class: 'upload-status ajax-loading' }).hide(); - - /* Set up structure */ - upload_box.append(upload_title); - upload_progress_outside.append(upload_progress_inside); - upload_box.append(upload_progress_outside); - upload_status.append(upload_status_percent); - upload_status.append(' · '); - upload_status.append(upload_status_progress); - upload_status.append(upload_status_cancel); - upload_status_cancel.append(upload_status_cancel_a); - - upload_box.append(upload_status); - upload_box.append(upload_results); - - /* Add to the dom and clean up upload_field */ - ui_details.after(upload_box); - - /* It's showtime! */ - upload_title.html( - format(gettext('Uploading {0}'), [escape_(file.name)]), - ); - upload_box.show(); - - upload_box.addClass('ajax-loading'); + $('.addon-create-theme-section .button').removeClass('concealed'); + $upload_field.val('').prop('disabled', false); + $upload_field.trigger('reenable_uploader'); - upload_status_cancel_a.click( - _pd(function () { - $upload_field.trigger('upload_action_abort'); - }), - ); - }); - - $upload_field.on('upload_progress', function (e, file, pct) { - upload_progress_inside.animate( - { width: pct + '%' }, - { - duration: 300, - step: function (i) { - updateStatus(i, file.size); - }, - }, - ); - }); + upload_title.html( + format(gettext('Error with {0}'), [escape_(file.name)]), + ); - $upload_field.on('upload_errors', function (e, file, errors, results) { - var all_errors = $.extend([], errors); // be nice to other handlers - upload_progress_inside.stop().css({ width: '100%' }); + upload_progress_outside.attr('class', 'bar-fail'); + upload_progress_inside.fadeOut(); - if ($('input#id_upload').val()) { - $('.addon-upload-failure-dependant').prop({ - disabled: false, - checked: false, - }); - } + $('') + .text( + gettext( + 'Please make sure to report any linting related issues on GitHub', + ), + ) + .attr('href', 'https://github.com/mozilla/addons-linter/') + .attr('class', 'addons-linter-info') + .attr('target', '_blank') + .attr('rel', 'noopener noreferrer') + .appendTo(upload_results); + + let error_message = format( + ngettext( + 'Your add-on failed validation with {0} error.', + 'Your add-on failed validation with {0} errors.', + all_errors.length, + ), + [all_errors.length], + ); - $('.addon-create-theme-section .button').removeClass('concealed'); - $upload_field.val('').prop('disabled', false); - $upload_field.trigger('reenable_uploader'); + $('').text(error_message).appendTo(upload_results); - upload_title.html( - format(gettext('Error with {0}'), [escape_(file.name)]), - ); + let errors_ul = $('