pax_global_header00006660000000000000000000000064152444322010014507gustar00rootroot0000000000000052 comment=9c335cc2a7df318d56a0f9bef4d4e049ebc923db appstream-generator-1.0.0/000077500000000000000000000000001524443220100154655ustar00rootroot00000000000000appstream-generator-1.0.0/.clang-format000066400000000000000000000043111524443220100200370ustar00rootroot00000000000000--- BasedOnStyle: LLVM IndentWidth: 4 --- Language: Cpp Standard: c++20 ColumnLimit: 120 BreakBeforeBraces: Linux PointerAlignment: Right AllowAllParametersOfDeclarationOnNextLine: false AlwaysBreakBeforeMultilineStrings: true BreakBeforeBinaryOperators: NonAssignment AlignArrayOfStructures: Left AlignAfterOpenBracket: true BreakAfterOpenBracketBracedList: true BreakAfterOpenBracketFunction: true BreakAfterOpenBracketIf: true # format C++11 braced lists like function calls Cpp11BracedListStyle: true # do not put a space before C++11 braced lists SpaceBeforeCpp11BracedList: false # no namespace indentation to keep indent level low NamespaceIndentation: None # we use template< without space. SpaceAfterTemplateKeyword: false # Always break after template declaration AlwaysBreakTemplateDeclarations: true # keep lambda formatting multi-line if not empty AllowShortLambdasOnASingleLine: Empty # return types should not be on their own lines AlwaysBreakAfterReturnType: None PenaltyReturnTypeOnItsOwnLine: 1000 AlwaysBreakAfterDefinitionReturnType: None # Break constructor initializers before the colon and after the commas, # and never put the all in one line. BreakConstructorInitializers: BeforeColon BreakInheritanceList: BeforeColon PackConstructorInitializers: Never # Place ternary operators after line breaks BreakBeforeTernaryOperators: true # No own indentation level for access modifiers IndentAccessModifiers: false AccessModifierOffset: -4 # Add empty line only when access modifier starts a new logical block. EmptyLineBeforeAccessModifier: LogicalBlock # Only merge empty functions. AllowShortFunctionsOnASingleLine: Empty # Don't indent case labels. IndentCaseLabels: false # No space after C-style cast SpaceAfterCStyleCast: false # Never pack arguments or parameters BinPackArguments: false BinPackParameters: false # Avoid breaking around an assignment operator PenaltyBreakAssignment: 150 # Left-align newline escapes, e.g. in macros AlignEscapedNewlines: Left # Enums should be one entry per line AllowShortEnumsOnASingleLine: false # we want consecutive macros to be aligned AlignConsecutiveMacros: true # never sort includes, only regroup (in rare cases) IncludeBlocks: Regroup SortIncludes: Never appstream-generator-1.0.0/.editorconfig000066400000000000000000000006661524443220100201520ustar00rootroot00000000000000# See https://editorconfig.org/ root = true [*] end_of_line = lf trim_trailing_whitespace = true charset = utf-8 indent_style = space indent_size = 4 [*.d] max_line_length = 120 dfmt_brace_style = knr dfmt_soft_max_line_length = 100 dfmt_align_switch_statements = false dfmt_space_before_function_parameters = true dfmt_keep_line_breaks = true [*.yml] indent_style = space indent_size = 2 [*.xml] indent_style = space indent_size = 2 appstream-generator-1.0.0/.github/000077500000000000000000000000001524443220100170255ustar00rootroot00000000000000appstream-generator-1.0.0/.github/dependabot.yml000066400000000000000000000003161524443220100216550ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" - package-ecosystem: "npm" directory: "/" schedule: interval: "monthly" appstream-generator-1.0.0/.github/workflows/000077500000000000000000000000001524443220100210625ustar00rootroot00000000000000appstream-generator-1.0.0/.github/workflows/build-test.yml000066400000000000000000000043151524443220100236640ustar00rootroot00000000000000name: Build Test permissions: contents: read on: [push, pull_request] jobs: build-deblike: name: ${{ matrix.display_name }} (${{ matrix.arch }}) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - display_name: Debian 13 suite: trixie runner: ubuntu-latest arch: amd64 - display_name: Debian Testing suite: forky runner: ubuntu-latest arch: amd64 - display_name: Debian Testing suite: forky runner: ubuntu-26.04-arm arch: arm64 - display_name: Ubuntu 26.04 suite: resolute components: main universe runner: ubuntu-latest arch: amd64 steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Create Build Environment uses: ximion/debspawn-action/setup@v1.0.0 with: suite: ${{ matrix.suite }} components: ${{ matrix.components }} packages: >- git curl ca-certificates script: | # install build dependencies ./tests/ci/install-deps-deb.sh # install 3rd-party stuff ./tests/ci/ci-install-extern.sh - name: Build uses: ximion/debspawn-action/run@v1 with: suite: ${{ matrix.suite }} summary: "Build on ${{ matrix.display_name }}" commands: | CC=gcc CXX=g++ ./tests/ci/run-build.sh - name: Tests uses: ximion/debspawn-action/run@v1 with: suite: ${{ matrix.suite }} summary: "Test on ${{ matrix.display_name }}" commands: | ./tests/ci/run-tests.sh build-fedora-latest: name: Fedora Latest (amd64) runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Create Build Environment run: cd tests/ci/ && podman build -t asgen -f ./Dockerfile-fedora-latest . - name: Build run: podman run -a stdout -a stderr -e CXX=g++ -v `pwd`:/build asgen ./tests/ci/run-build.sh - name: Tests run: podman run -a stdout -a stderr -e CXX=g++ -v `pwd`:/build asgen ./tests/ci/run-tests.sh appstream-generator-1.0.0/.github/workflows/codeql.yml000066400000000000000000000013501524443220100230530ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ master ] pull_request: branches: [ master ] schedule: - cron: '36 8 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-26.04 strategy: fail-fast: false steps: - name: Checkout repository uses: actions/checkout@v7 - name: Initialize CodeQL uses: github/codeql-action/init@v4.37.8 with: languages: cpp - name: Create Build Environment run: sudo ./tests/ci/install-deps-deb.sh - name: Make & Install 3rd-party run: sudo ./tests/ci/ci-install-extern.sh - name: Build & Test run: ./tests/ci/run-build.sh codeql - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4.37.8 appstream-generator-1.0.0/.github/workflows/coverity.yml000066400000000000000000000026211524443220100234520ustar00rootroot00000000000000name: Coverity Scan permissions: contents: read on: workflow_dispatch: schedule: - cron: '0 3 1,15 * *' jobs: coverity: if: github.repository == 'ximion/appstream-generator' runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v7 with: fetch-tags: true fetch-depth: 0 - name: Create Build Environment run: sudo ./tests/ci/install-deps-deb.sh - name: Make & Install 3rd-party run: sudo ./tests/ci/ci-install-extern.sh - name: Create Version String id: version run: | git_commit=$(git rev-parse --short HEAD) git_current_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo v1.0.0) git_commit_no=$(git rev-list --count "${git_current_tag}..HEAD" 2>/dev/null) git_version_full=${git_current_tag#v}; git_version_full=${git_version_full//-/.} if [ "$git_commit_no" -gt 0 ]; then git_version_full+=".git$git_commit_no" fi echo "Using version string: $git_version_full" echo "git_version_full=$git_version_full" >> $GITHUB_OUTPUT - name: Coverity uses: vapier/coverity-scan-action@346291b388d0a99b2d82c8d46f5088d0fc494844 with: email: ${{ secrets.COVERITY_SCAN_EMAIL }} token: ${{ secrets.COVERITY_SCAN_TOKEN }} version: ${{ steps.version.outputs.git_version_full }} command: './tests/ci/run-build.sh coverity' appstream-generator-1.0.0/.gitignore000066400000000000000000000001121524443220100174470ustar00rootroot00000000000000build/ .dub docs.json dub.selections.json __dummy.html *.o *.obj *.so *.a appstream-generator-1.0.0/CODE_OF_CONDUCT.md000066400000000000000000000065671524443220100203020ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at appstream@lists.freedesktop.org (public list) or the team lead in private at matthias@tenstral.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq appstream-generator-1.0.0/LICENSE000066400000000000000000000167431524443220100165050ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. appstream-generator-1.0.0/MAINTAINERS000066400000000000000000000000471524443220100171630ustar00rootroot00000000000000Matthias Klumpp E-mail: mak@debian.org appstream-generator-1.0.0/NEWS000066400000000000000000001102061524443220100161640ustar00rootroot00000000000000Version 1.0.0 ~~~~~~~~~~~~~ Released: 2026-08-28 Notes: * This release allows writing your configuration in YAML, switches the default icon format to JPEG-XL (with an option to set it to PNG, globally or per-suite) and stops processing TIFF/BMP/XPM images. * Please note that media processing is now lightly sandboxed on Linux on machines that have the Landlock LSM available. Setting the `ASC_NO_SANDBOX=1` environment variable will disable the sandbox. Features: * Store statistics data in the original JSON format again * Serve statistics file in compressed form only * Officially support YAML as configuration file format * Port logging to Quill * Port to the libappstream-compose 1.2 * Switch media image default to JPEG-XL * Move statistics data into a separate database * engine: Make produced icon tarballs deterministic and only contain one format * Generate more detailed, fine-grained statistics for better overview pages * Drop flot & jquery, vendor uplot & highlight.js, drop NPM * Rebuild the HTML report pages on uPlot and the new statistics * reports: html: Create nicer frontpage as well, using tiles for the suites * reports: Don't reference index.html explicitly anymore in HTML reports * reports: Make HTML pages a single-column layout and center it * Ensure components and hints result files are always deterministic Bugfixes: * freebsd: little fixes for the packageForFile() code path * debian: Prevent deadlock when getFileData() triggers a package download * debian: Use a checksum instead of mtime to detect index changes * rpmmd: Fix package descriptions being dropped from generated metadata * IconHandler: set the extra prefix when creating a builtin hicolor theme * zarchive: Ensure parent directories are created for empty directories * Don't append section to origin if it's empty * Fix nondeterministic output when two packages claim the same metadata * Streamline hint JSON creation to not duplicate the schema in two places * Properly use a system copy of backward-cpp * Ensure a UTF-8 locale is enforced properly on startup * zarchive: Confine archive extraction to the destination directory * zarchive: Make temporary extraction behave like reading the archive directly * Fix icons randomly missing from the generated icon tarballs * Mention the source icon name when an icon is rejected (instead of the target name) * Fix memory leak in intermediate metadata validation * Refuse to register empty GCIDs in the ownership registry * Don't pile on error hints if a component was already rejected for icon issues * report: Fix hint accounting and global component statistics * Fix potential crash when statistics arrive out of order * Copy base static data when publishing a HTML report for a different template * Ensure the temp dir is safely created and shared with compose Miscellaneous: * Double-quote strings in YAML header * ci: Build on Debain & its derivatives using debspawn * ci: Bump Ubuntu version, update dependencies * docs: Port manpage to DocBook 5 * Add small helper webserver to test HTML output locally easier * snap: Update to new core snap * snap: Simplify packaging and make it build on core26 * snap: Amend libfyaml's install prefix * snap: Fix missing screenshots due to GdkPixbuf error * meson: Simplify template installation * Add security policy * Add fd.o code of conduct Contributors: Gleb Popov, Joey Riches, Matthias Klumpp, Nathan Pratta Teodosio Version 0.10.2 ~~~~~~~~~~~~~~ Released: 2026-02-10 Features: * Allow backends to define a custom, non-default data prefix Bugfixes: * arch: Do not include suite name in pkgRoot * freebsd: implement dataPrefix() using getlocalbase() * freebsd: Implement packageForFile() by accepting a port's STAGEDIR * Pass -fexperimental-library when building with Clang and libc++ * Fix icons and pixmaps paths when using prefix != /usr * tests: Combine results and registry tests to make them order-independent * archive: Don't use weakly_canonical on archive-content files for normalization * node{Bool,Int}Value: prevent read past scalar token Miscellaneous: * report: Use Inja's HTML-autoescape feature Contributors: Antonio Rojas, Gleb Popov, Matthias Klumpp, Miao Wang Version 0.10.1 ~~~~~~~~~~~~~~ Released: 2025-10-02 Features: * engine: Limit default parallelism for main processing operations * Skip intermediate data check if gcid is empty * Print if data remains after manually removing a package * engine: Add function to process all suitable data at once Bugfixes: * tests: Allow network test to have all tests skipped * utils: Just use C++ standard library for copying directories * Guard against null values in the database * zarchive: Properly normalize paths * yaml: Make sure Time field is always read as string * Explicitly set up UTF-8 locale * Ensure we aren't running on a bad libfyaml * engine: Actually open the locale unit, so it can be used to find locale * Don't just skip processing most new packages Miscellaneous: * Update the snapcraft file Contributors: Matthias Klumpp Version 0.10.0 ~~~~~~~~~~~~~~ Released: 2025-09-16 Notes: * This release switches from using the D programming language to C++23. This change should simplify maintenance of the tool and allow it to be more compatible with other tooling. C++23 adopted a lot of D's features, so this is a pretty direct port - still, issues may exist even though this release has been tested extensively. So, perform your own tests and report any issues! Features: * Translate the project from D to C++23 * zarchive: Support reading from hardlinks * Use backward-cpp if available for automated crash backtraces * Add optimization for repeated reads from large archives * Add support for loading JPEG-XL icons Bugfixes: * zarchive: Ensure parent path is created before opening file * debian: Ensure legacy-style repos get package descriptions for components * Create document timestamps in the right format * iconhandler: Render SVG with new API and properly report errors again Miscellaneous: * Add and enforce an npm lockfile * Avoid some unnecessary data copies and refcounting where possible Contributors: Joey Riches, LN Liberda, Matthias Klumpp Version 0.9.2 ~~~~~~~~~~~~~~ Released: 2025-08-12 Features: * data: Add tooltip hover for plot graph in section_pages.html Bugfixes: * snap: Don't include our glib * freebsd backend: Start from meta.conf when gathering repository metadata * freebsd: catch up with new pkg repository layout * rpmmd: Port to dxml * rpmmd: Download packages on-demand * Sanitize UTF-8 before adding it to the database * Drop removed no-bin-links argument from yarn invocation Miscellaneous: * Add AdwaitaLegacy to themeNames * snap: Update some dependency versions * snap: Build using system LDC and glibd * Install JS stuff with NPM * metainfo: Add developer name Contributors: Amin Bandali, Balló György, Gleb Popov, Joey Riches, Matthias Klumpp Version 0.9.1 ~~~~~~~~~~~~~~ Released: 2023-11-11 Features: * Implement a FreeBSD backend * Add I/O caching for the FreeBSD backend * Allow to manually specify a GIR directory at build time * Include Yarn lockfile * Add release information to metainfo file * Build against AppStream 1.0 Bugfixes: * Make building the RPMMD backend optional * web: Better split archive sections * ubuntu: Don't fail on repositories with no language packs * utils: mkdirRecurse instead of mkdir inside parallel Miscellaneous: * Snap updates * Add documentation how to install from Flathub * Autoformat source code Contributors: Gleb Popov, Heather Ellsworth, Matthias Klumpp, Pablo Correa Gómez Version 0.9.0 ~~~~~~~~~~~~~~ Released: 2023-01-26 Features: * Allow more complex repo-level metadata additions Documentation: * docs: Document modifications.json * docs: document values of "Backend" in asgen config * docs: clarify directory structure Bugfixes: * Make MetadataType override work again * Ensure all user feature flags are applied properly again * Retry failed download 4 times by default * Even process an empty repository if action is forced * Don't fail if icon directory for injected metadata is missing * ci: Improve scripts and don't use deprecated commands * ci: Install ffmpeg in the Fedora images Contributors: Matthias Klumpp, Miroslav Suchý, Neal Gompa Version 0.8.8 ~~~~~~~~~~~~~~ Released: 2022-04-10 Bugfixes: * Use higher stack size for archive read generator Contributors: Matthias Klumpp Version 0.8.7 ~~~~~~~~~~~~~~ Released: 2022-02-22 Bugfixes: * Fix build on 32-bit architectures * Set CAInfo on compose instance as well * Don't unlink components of fake packages * Process icons by suite/section across all architectures * Fix a bunch of Meson and D deprecation warnings Version 0.8.6 ~~~~~~~~~~~~~~ Released: 2021-12-22 Notes: * This is the first release to fully use libappstream-compose for almost all metadata analysis (except icon processing, which needs special care). Features: * Reimplement data extractor using the compose API * Enable screencasts in compose based on our screenshotVideos feature flag * Run external metadata through the exact same processing as all other data * Redirect glib debug messages to our own logging if in verbose mode * Adjust parallel processing work unit sizes for seeding * ci: Add Fedora to the CI environment Bugfixes: * Fix build with GLib >= 2.69 * engine: Protect iconTarFiles variable when scanning for icons in parallel * Resolve some issue tag conflicts with as-compose * Control batch processing better to enable better extractor reuse * Add duplicate-metainfo exemption for injected data * Improve explanation of the "no-metainfo" tag * Don't emit "description-from-package" if "no-metainfo" was already present Contributors: Neal Gompa (ニール・ゴンパ), Matthias Klumpp Version 0.8.5 ~~~~~~~~~~~~~~ Released: 2021-08-28 Features: * snap: Set `grade` to `stable` * Improve metainfo/desktop deduplication code * Use ascompose for adding translation status data Bugfixes: * Always mark desktop-file as handled when handling its metainfo file (#91) * Fix compilation due to broken generated GdkPixbuf code * config: Move setUseOptipng call after we determine if optipng is available * snap: Add glib{,-networking} to asgen's stage-packages * snap: Build libas from a tag * Adjust for compose API changes Contributors: Balló György, Dan Printzell, Iain Lane and Matthias Klumpp Version 0.8.4 ~~~~~~~~~~~~~~ Released: 2021-03-02 Features: * Replace our HashMap with native associative arrays * Switch back to using the curl-based downloader again * Use the desktop-entry parsing code from appstream-compose * Try finding icons in /usr/share/icons root directory as well * Update our own metainfo file and validate it Bugfixes: * snap: Set LD_LIBRARY_PATH to the stage dir for asgen's build * snap: Install curl as a normal build-package * snap: Build with -j1 * debian: Forget a package's filename when removing its temporary dir * debian: Add more synchronization * zarchive: Log the error when we can't open an archive * various: Improve builddir != srcdir builds and build the snap like this * Replace theme icon generator concurrency with a range * snap: Build AppStream in debugoptimized mode instead of release mode Contributors: Iain Lane, Matthias Klumpp Version 0.8.3 ~~~~~~~~~~~~~~ Released: 2021-02-02 Notes: * This release requires libappstream-compose from the AppStream project to build. The library is still in progress and currently has an unstable API, but is developed in sync with appstream-generator, so asgen can safely depend on it. Features: * Elevate no-metainfo presence to a warning by default (Matthias Klumpp) * Ignore all apps with only desktop-entry files using OnlyShowIn (Matthias Klumpp) * Make use of the helper classes provided by appstream-compose (Matthias Klumpp) * Add editorconfig (Matthias Klumpp) * Use ascompose result type as base for the generator result container (Matthias Klumpp) * Use the validator helper function from appstream-compose (Matthias Klumpp) * Use metainfo parsing helper from appstream-compose (Matthias Klumpp) * alpine: add capabilities to download packages via HTTP (Rasmus Thomsen) * config: allow overriding export dir via --export-dir (Rasmus Thomsen) Bugfixes: * Add explicit option to disable network-dependent tests (Matthias Klumpp) * Add missing CSS styling for permalinks to Debian template (Matthias Klumpp) * Captialize "MetaInfo" the same everywhere in hint messages (Matthias Klumpp) * Use binding generator to create the missing AppStream Utils functions (Matthias Klumpp) * Never open contents cache DB env more than once in the same thread (Matthias Klumpp) * Re-enable LMDB TLS support (Matthias Klumpp) * downloader: Check read byte count before appending to buffer (Matthias Klumpp) * Ensure export directory paths are sane, absolute paths all the time (Matthias Klumpp) Version 0.8.2 ~~~~~~~~~~~~~~ Released: 2020-05-12 Features: * Add experimental Snapcraft build definition * snap: Move to strict confinement (Iain Lane) * snap: Lots of debugging & integration work (Iain Lane) * Find external binaries in PATH instead of hardcoding absolute paths to them * Add static bindings for libsoup * Switch to new libsoup-based downloader, drop Curl * Always log the generator version for some operations * Display more verbose debug messages when downloads are retried * Better messages for network connection errors * Support Ubuntu language packs for l10n status info extraction * Make permalink anchors visible in HTML output * alpine: Add new backend for Alpine Linux (Rasmus Thomsen) Bugfixes: * Pull a few Mustache engine code improvements from upstream * Throw a better error message in unit tests when ffprobe isn't found * tests: Skip video metadata check if ffprobe can not be found * Check if our GdkPixbuf is able to handle all essential image formats * Slighty reduce default logging verbosity when finding icons * Give up icon search when component was rejected in icon storage routine * Swallow bad last-modified times from servers instead of freaking out * Make downloader a thread-local singleton (instead of thread-global) * Create XDG runtime dir in case it is missing * Make a failure to read an archive symlink target non-fatal * Print available font names in issue report if no matching font was found * Handle unexpected NULL-byte files better * Find Yarn, even when it was renamed * Strip out release artifacts for components that have a package Version 0.8.1 ~~~~~~~~~~~~~~ Released: 2020-01-20 Bugfixes: * Don't crash if a gettext locale has no strings * Reject packages which should have a long description but don't have one * debian: Search for bz2 files again, i18n still uses those * Work around some curl/D behavior changes and make HTTPS downloads work again * Check suite status before removing hints/components * Don't fail download on HTTP status code 302 * Properly fail icon search in all failure cases, add more debug logging * Ensure icon tarballs are closed immediately after writing to them * Flip around sorting of suites on HTML reports Version 0.8.0 ~~~~~~~~~~~~~~ Released: 2019-09-24 Notes: * This release needs FFmpeg installed if video screenshots should be permitted. No transcoding will be done. Check out the options to modify this feature in the documentation. Features: * Add Igor Khasilev's container implementations to the source * Replace EMSI containers with built-in implementation * Bump dependency on AppStream * Fix build with latest AppStream * Implement improved validation issue reporting * Always build registry of all hint tags, instead of adding them on-demand * Add AV1 video sample for video support tests * Add video tag support, analyze and store videos * Use generic download method for downloading screenshots as well Bugfixes: * Make dscanner happy again * Drop wrap files: We don't need them anymore * debian: Don't check for bz2 compressed indices * Only add remote icons if we have a mediaBaseUrl and are permitting image caches * Validate injected metainfo files as well * Ignore pedantic issues in HTML reports * Fix getFileContents when acting on remote data * XML-escape validator explanations Version 0.7.7 ~~~~~~~~~~~~~~ Released: 2019-02-24 Features: * Speed up locale search by moving it to its own database cache (Matthias Klumpp) Bugfixes: * Component removal requests don't need to have a package-name set (Matthias Klumpp) Version 0.7.6 ~~~~~~~~~~~~~~ Released: 2019-01-10 Features: * Limit the amount of release metadata in output (Matthias Klumpp) * Implement icon loading for injected metadata (Matthias Klumpp) * Prefer some SFNT font metadata over internal heuristics (Matthias Klumpp) * Implement Gettext locale processing (Matthias Klumpp) * Use a struct instead of bitfield to store enabled generator features (Matthias Klumpp) * Refactor locale statistics loader (Matthias Klumpp) * Explicitly remove temporary package data after reading locale info (Matthias Klumpp) * Make arch-specific injected metadata override arch:all data (Matthias Klumpp) Bugfixes: * Complain if the user wants to associate a component with a fake package (Matthias Klumpp) * Check that fonts and OS components have an icon (Matthias Klumpp) * Sanitize image URLs before attempting download (Matthias Klumpp) Version 0.7.5 ~~~~~~~~~~~~~~ Released: 2019-01-04 Features: * Load components for Arch Linux (Balló György) * Embed mustache-d code (Matthias Klumpp) * Protect against HTTPS-to-HTTP downgrades (Matthias Klumpp) * Allow asgen maintainer to mark components for removal and inject additional metainfo files (Matthias Klumpp) * docs: Document the metainfo injection feature (Matthias Klumpp) Bugfixes: * Fix a few issues found by dscanner (Matthias Klumpp) * Properly error out on HTTP 404 status codes (Matthias Klumpp) * Add wrapfile for mir-core (Matthias Klumpp) * Also ignore web applications without an icon (Matthias Klumpp) Version 0.7.4 ~~~~~~~~~~~~~~ Released: 2018-08-04 Features: * Write CID<->GCID mapping table as additional output data (Matthias Klumpp) * Don't rebuild GLibD as part of asgen, use the shared library instead (Matthias Klumpp) * Add hint to add launchable tag if .desktop file is missing (Matthias Klumpp) * Use posix_spawn codepath for optipng if possible via GLib (Matthias Klumpp) * Unconditionally add stock-type icon if desktop-file allows for it (Matthias Klumpp) * Allow font languages to be specified in a languages tag (Matthias Klumpp) * Improve font language processing with metainfo hints (Matthias Klumpp) * fonts: Sort selected font styles, prefer regular style for samples (Matthias Klumpp) * fonts: Use a random pangram for fonts supporting English (Matthias Klumpp) * fonts: Assume 100% language support for all locale mentioned in font data (Matthias Klumpp) Bugfixes: * Fix build with phobos 2.081 (Antonio Rojas) * Depend on non-broken Meson version (Matthias Klumpp) * fonts: Always prefer English for samples if font supports it (Matthias Klumpp) * Initialize font icon lookup table only if there are fonts (Matthias Klumpp) Version 0.7.3 ~~~~~~~~~~~~~~ Released: 2018-04-26 Features: * Don't include desktop files with an empty OnlyShowIn and complain about that * Improve decompression code slightly * Improve code to check for inclusion of .desktop files * Optimize category filter function * ubuntu: Use GC managed arrays for langpack info storage * ubuntu: Hold copy of language pack array per package * ubuntu: Don't load language packs multiple times, cleanup index data properly * Close package from a base suite immediately in the seeding step Bugfixes: * debian: Make package index threadsafe again * Don't add data to contents store in parallel * Don't add the same architecture multiple times when generating reports * Enable static check for poor exception handling * Don't run through a synchronized section when fetching package filename * Update dscanner and invoke it from a more versatile Python script Version 0.7.2 ~~~~~~~~~~~~~~ Released: 2018-04-16 Features: * Dramatically reduce memory usage of cruft cleanup operation * Parallelize database cleanup a bit more * debian: Add basic in-memory deduplication of translated texts for packages Bugfixes: * Fix build on 32bit architectures * Make GStreamer entry entirely optional for packages * Ensure we properly merge in desktop-entry data from launchable tags * Never process desktop files twice * Don't append desktop data to mime/category lists * Update wrap files to work with gir-to-d >= 0.15 Version 0.7.1 ~~~~~~~~~~~~~~ Released: 2018-04-06 Features: * debian: Use TagFile class from Laniakea * debian: Use the packages real architecture in its identifier * debian: Implement packageForFile backend feature * Do more complex parsing of metainfo license expressions * ci: Enable more static analysis checks * Improve manual page Bugfixes: * Never upscale icons if we don't need to, use pixmaps directory as last resort * Update wrap files to work with gir-to-d >= 0.14 * Don't try to upscale tiny icons from the pixmaps directory * Register HiDPI icons for fonts properly * Fix some minor style issues found by D-Scanner * Document WorkspaceDir config file option Version 0.7.0 ~~~~~~~~~~~~~~ Released: 2018-04-04 Notes: * Processing single packages and calling publication and processing separately is not a fully supported feature yet! This release lays the groundwork for it, but its final implementation will happen later. Do not use this in production! Features: * stdx.allocator-backed containers library * Use stdx.allocator-backed HashMap in more places * Trigger GC less aggressively, use smaller parallel work unit sizes * Allow setting a workspace in config and calling asgen on a config file * Add backend interface for processing a single package * Allow calling only the metadata publication step * Add framework for extracting data directly from package files * Allow setting icon policy in configuration * Add support for more icon types and customizable icon policy * Allow icon upscaling in certain cases, but complain about it * Modernize Meson definitions * Document how to modify icon policies in configuration * Use AppStream 0.12.0 and format 0.12 by default Bugfixes: * Update wrap files to be compatible with gir-to-d >= 0.13 * Improve ArchiveDecompressor usage * For each array appender, try to guess capacity to reserve better * Properly add remote icon URLs * Fetch JavaScript bits with Yarn instead of Bower * Ensure we can run asgen again from its build directory * Ignore data from immutable suites on cleanup * Don't parallelize cleanup data collection routine * Release memory chunks faster during cleanup operations Version 0.6.8 ~~~~~~~~~~~~~~ Released: 2017-11-06 Notes: * The -Ddownload_js build flag is now -Ddownload-js to follow the naming of other Meson flags more closely. Features: * Encode AppStream library version in version info string on reports * Don't require an install candidate for a webapp * Update hicolor theme fallback definition * Try to filter out symbolic icons for apps * Allow processing only one section in a suite * Relax icon scaling rules Bugfixes: * Move time graph legend to the top left * ci: Use the packaged gir-to-d * Drop an unused gdc conditional * Ensure the validator never tests web URLs for validity * Drop LLVM DC bug workaround and allow cross-module inlining * Make D GIR interface build work again with recent Meson versions * Don't fail if we have a dupe ID with no pkgname, can happen now with webapps Version 0.6.7 ~~~~~~~~~~~~~~ Released: 2017-10-02 Features: * ci: Enable gdc again for testing (Matthias Klumpp) * Make rpmmd backend work (Matthias Klumpp) * Make an empty main function for embedded unittests (Matthias Klumpp) * Add a metainfo file (Matthias Klumpp) * Add a manual page (Matthias Klumpp) * Sort suite names on the index page (Matthias Klumpp) Bugfixes: * use file.exists to check file existence (Antonio Rojas) * arch: Don't ref generator results (Matthias Klumpp) * Improve the icon tarball generation code (Matthias Klumpp) * Resolve deprecation warning (Matthias Klumpp) Version 0.6.6 ~~~~~~~~~~~~~~ Released: 2017-09-22 Features: * Add basic HiDPI support (Corentin Noël) * Generate the HiDPI tarball (Corentin Noël) * Add an 'info' command to dump package information (Matthias Klumpp) * Handle the "Hidden" property in .desktop files and complain about it (Matthias Klumpp) Bugfixes: * arch: XML-escape package descriptions (Matthias Klumpp) * Check for availability of a component-id at the right time (Matthias Klumpp) * Make writing compressed results a bit more robust (Matthias Klumpp) * Resolve all deprecation warnings and slightly improve code (Matthias Klumpp) Version 0.6.5 ~~~~~~~~~~~~~~ Released: 2017-07-02 Features: * Generate type=codec metadata for gstreamer packages (Iain Lane) * Add a feature flag for GStreamer processing (Iain Lane) * ci: Switch to using upstream GirToD (Matthias Klumpp) * Set asgen version in defines.d by Meson as well (Matthias Klumpp) * Format graphs a bit nicer (Matthias Klumpp) Bugfixes: * Find files in DATADIR - not relative to the executable (Iain Lane) * Fix typo in metadata-path hint (Matthias Klumpp) * Try to be more informative when a component has no ID (Matthias Klumpp) * Ensure the rDNS scheme is really followed before editing an ID (Matthias Klumpp) Version 0.6.4 ~~~~~~~~~~~~~~ Released: 2017-05-25 Features: * Automatically generate GIR D bindings at build-time (Matthias Klumpp) * Remove dub build file (Matthias Klumpp) * Use Meson subproject/wrap to fetch mustache-d in case it is missing (Matthias Klumpp) * Add a launchable tag to output if we can add one (Matthias Klumpp) * Make legacy metainfo dir a warning (Matthias Klumpp) * Validate launchable entries and add heuristics in case they aren't present (Matthias Klumpp) Bugfixes: * Import buildPath directly from std.path (Antonio Rojas) * Remove spurious std.stream import (Matthias Klumpp) Version 0.6.3 ~~~~~~~~~~~~~~ Released: 2017-03-08 Features: * Experiment with scoped classes (Matthias Klumpp) * Drop embedded generator copy (Matthias Klumpp) * Use final classes whenever possible (Matthias Klumpp) * Centralize all export-dir locations in base config class (Matthias Klumpp) * Allow to manually configure data export locations (Matthias Klumpp) Bugfixes: * Fix some quirks and make the tests work again (Matthias Klumpp) * ci: Don't build with GDC (Matthias Klumpp) * Don't fail if a suite has no base suite (Matthias Klumpp) * engine: Process base suite packages for contents only (Iain Lane) * Update README.md (#39) (Blake Kostner) * debian: Only select the most recent packages for a scan (Matthias Klumpp) * Streamline final component validity check, resolve a crash (Matthias Klumpp) Version 0.6.2 ~~~~~~~~~~~~~~ Released: 2017-01-24 Notes: * GDC 6.x can't compile the project at time due to a GDC bug. See https://bugzilla.gdcproject.org/show_bug.cgi?id=251 for more information. Features: * Support desktop-app metainfo files without .desktop file if they have an icon set * Write log entry when starting/finishing icon tarball write * Update AppStream bindings * Allow to specify allowed keys * Don't show pedantic validator hints * Allow metainfo file to specify sample texts for fonts * Process XPM icons if they are large enough Bugfixes: * Safeguard against TLD checks with empty-or-null string * Ensure desktop-apps have at least one valid category set * Don't override metainfo name/summary with .desktop values * Quit immediately if the component type is unknown * Correctly read a font's full-name * Don't needlessly reprocess fonts Version 0.6.1 ~~~~~~~~~~~~~~ Released: 2016-12-26 Features: * Add a symlink so that Ubuntu uses the same template as Debian (Iain Lane) * Support linking to "old suites" in the HTML index (Iain Lane) * hints: icon-not-found: Explain the symlink problem for Debian & Ubuntu (Iain Lane) * Add preliminary new asgen logo (Matthias Klumpp) * Use struct for archive (Matthias Klumpp) * Reuse AsMetadata while processing metainfo files (Matthias Klumpp) Bugfixes: * Correctly install templates with Meson (Matthias Klumpp) * meson: Make finding mustache-d more robust (Matthias Klumpp) * Fix build with DMD (Antonio Rojas) * engine: Don't clean packages in base suites (Iain Lane) * download: If we have a last-modified date, set the mtime of the target file (Iain Lane) * Simplify zarchive code a little (Matthias Klumpp) * Use module initializer to initialize global static data (Matthias Klumpp) * Fix build on Debian with GDC (Matthias Klumpp) Version 0.6.0 ~~~~~~~~~~~~~~ Released: 2016-10-03 Notes: * If possible, you should build Meson with the LLVM D compiler and the Meson build system now. Features: * Produce better error messages on failed libarchive actions (Matthias Klumpp) * Add a hook to the desktop file parser to run backend specific code (Iain Lane) * Add an Ubuntu backend to retrieve langpack translations (Iain Lane) * Open databases with NOTLS (Matthias Klumpp) * Tweak the issue messages a bit (Matthias Klumpp) * Complain about stuff in legacy paths (Matthias Klumpp) * Only search for .desktop files if we have a DESKTOP_APP component (Matthias Klumpp) * Create new Font class to read font metadata (Matthias Klumpp) * Render an icon for fonts (Matthias Klumpp) * Add dependencies on Pango, FreeType and Fontconfig (Matthias Klumpp) * Determine languages a font supports (Matthias Klumpp) * Reorganize code for proper namespacing (Matthias Klumpp) * Render font screenshots (Matthias Klumpp) * Map font full names to files in a spec-compliant way (Matthias Klumpp) * Enable fonts support by default (Matthias Klumpp) * Share ContentsStore between threads again (Matthias Klumpp) * Make Meson a first-class buildsystem for asgen (Matthias Klumpp) * Update README (Matthias Klumpp) Bugfixes: * Fix build on non-64-bit architectures (Matthias Klumpp) * Work around LDC bug (Matthias Klumpp) * fclose() the memstream when we are done with it (Iain Lane) * debpkgindex: Be less noisy about translations in debug mode (Iain Lane) * Use getTestSamplesDir (Iain Lane) * Retry on all curl errors, not just timeouts (Iain Lane) * Synchronise downloading of files (Iain Lane) * ubuntu: Don't reference all packages, just the ones we need (langpacks) (Iain Lane) * Use an ugly mutex to work around Fontconfig issues (Matthias Klumpp) * Make FC mutex a bit more fine-grained (Matthias Klumpp) Version 0.5.0 ~~~~~~~~~~~~~~ Released: 2016-08-30 Features: * Add more speed and style optimizations, make Package an abstract class (Matthias Klumpp) * Use a string appender instead of a string array for writing output (Matthias Klumpp) * debian: Support downloading files from a mirror, instead of having them local (Iain Lane) * debian: Support DDTP translations (Iain Lane) * Ignore some more useless categories (Matthias Klumpp) * Add skeleton for RPMMD (Matthias Klumpp) * Add a few optimizations using immutable and appender (Matthias Klumpp) * Do not spawn compressors anymore, use libarchive directly (Matthias Klumpp) * Make compiling with LDC easier (Matthias Klumpp) * Add example for cruft collection script (Matthias Klumpp) * Allow cleaning up statistical data retroactively (Matthias Klumpp) * Rehash hash tables that we query often (Matthias Klumpp) * Always optimize debug builds (Matthias Klumpp) * Make a splitbuild with Meson and Ninja possible (Matthias Klumpp) * Use AppStream's knowledge about TLDs to build smarter global-component-ids (Matthias Klumpp) * Allow specifying the format version metadata should be built for (Matthias Klumpp) * Adjust for AppStream 0.10 release (Matthias Klumpp) * Perform legal checks before allowing metadata to be added to the pool (Matthias Klumpp) Bugfixes: * Fix dataUseJSTime in section overview template (Blake Kostner) * debian: Only retrieve DDTP translations from the section we're looking at (Iain Lane) * Fix deprecation warnings from LDC (Matthias Klumpp) * Work around some weird JSON parser quirk (int incompatible with uint) (Matthias Klumpp) * Prevent creating excess statistic entries (Matthias Klumpp) * Fix potential crash (Matthias Klumpp) * Use native generator when not compiling with GDC (Matthias Klumpp) * Work correctly if CIDs omit the .desktop suffix (Matthias Klumpp) * Only add valid category names to output (Matthias Klumpp) * Don't emit description-from-package multiple times per package. (Matthias Klumpp) Version 0.4.0 ~~~~~~~~~~~~~~ Released: 2016-07-12 Features: * README: Add link to D tour * Run optipng with standard settings * Add command to forget extracted data for a package * Implement immutable suites * Use appender more often and apply some style fixes * Build with parallel by default when using Makefile * Drop some deprecated stuff Bugfixes: * Display an error when processing a suite without section or arch * debian: Fix exception message if directory does not exist * Code improvements: More pure, more safe, more trusted * Drop global data validation result box * Don't crash if media pool directory does not exist and immutable suites are used Version 0.3.0 ~~~~~~~~~~~~~~ Released: 2016-05-24 Features: * Register new backend for Arch Linux (Matthias Klumpp) * arch: Add reader for lists index file and skeleton for PackageIndex (Matthias Klumpp) * Make generic archive class work well with non-Debian packages (Matthias Klumpp) * Add HTML anchors for maintainers on report pages (Matthias Klumpp) * html: Show suite name in section overview (Matthias Klumpp) * Allow not setting a MediaBaseUrl (Matthias Klumpp) * Add some font rendering experiments (Matthias Klumpp) * Allow storing multiple statitics entries per point in time (Matthias Klumpp) * Make icon tarball build reproducible (Matthias Klumpp) * Make ArchiveDecompressor API more powerful and always return const(ubyte)[] (Matthias Klumpp) * arch: Speed up backend by loading all data in one go (Matthias Klumpp) * Split "handleScreenshots" into more useful, fine-grained flags (Matthias Klumpp) * Add timestamp to output and only touch it if something has changed (Matthias Klumpp) * Only update metadata if the indices have been changed (Matthias Klumpp) * Store media in pool subdirectory (Matthias Klumpp) * Add flag to enforce metadata processing, even if nothing changed (Matthias Klumpp) * Allow disabling metadata timestamps (Matthias Klumpp) * Improve scan-skipping code (Matthias Klumpp) Bugfixes: * Fix build on non-64bit arches (Matthias Klumpp) * Handle compressed empty files correctly (Neil Mayhew) * Drop other, non-UTF-8 encodings from language codes too (Matthias Klumpp) * Drop non-printable characters from .desktop file values (Matthias Klumpp) * Loop over more things by-reference to reduce RAM usage (Matthias Klumpp) * debian: Read Packages.xz files if no .gz file is available (Matthias Klumpp) * Don't lie about thumbnail sizes in their filename (Matthias Klumpp) * Don't duplicate architecture tag if there are multiple issues (Matthias Klumpp) * Catch data serialization errors (Matthias Klumpp) * Run bower with allow root to be able to make js as root (Harald Sitter) Version 0.2.0 ~~~~~~~~~~~~~~ Released: 2016-04-24 Features: * Don't ship minified JS * Get rid of (almost) all the embedded JS copies, use Bower * Add makefile for convenience * Use Flot for drawing graphs * Update README * Document all asgen-config settings * Reserve subdb in contents database to cache icon data * Make IconHandler use a pre-filtered list of icons * Allow templates to override only parts of the default branding * Add some default branding for Debian * Show logo on generated HTML pages * debian: Link some interesting resources from the main page Bugfixes: * Find aliased icons correctly * Keep priority sorting of themes * Make the hicolor theme always-available, using an embedded index copy if necessary * Fix counting of issues * Do not accidentally upscale screenshots while creating thumbnails * debian: Don't make a missing package index fatal * Don't fail if we are trying to add statistics too quickly * Rename remove-valid to reflect what it actually does * Ignore errors if we are trying to decompress a 0-byte gzip file * Suite arguments aren't optional for process/remove-found * Demote screenshot-no-thumbnails to info Version 0.1.0 ~~~~~~~~~~~~~~ Released: 2016-04-18 Notes: * Initial release appstream-generator-1.0.0/README.md000066400000000000000000000075361524443220100167570ustar00rootroot00000000000000# AppStream Generator AppStream is an effort to provide additional metadata and unique IDs for all software available in a Linux system. This repository contains the server-side of the AppStream infrastructure, a tool to generate metadata from distribution packages. You can find out more about AppStream collection metadata at [Freedesktop](https://www.freedesktop.org/software/appstream/docs/chap-CollectionData.html). The AppStream generator is currently primarily used by Debian, but is written in a distribution agnostic way. Backends only need to implement [two interfaces](src/backends/interfaces.h) to be ready. If you are looking for the AppStream client-tools, the [AppStream repository](https://github.com/ximion/appstream) is where you want to go. ## Install from Flathub You can install an up-to-date version of AppStream Generator from [Flathub](https://flathub.org) if you just want to quickly test the software with your repository: ```ShellSession # Add Flathub remote sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo # Install appstream-generator flatpak install org.freedesktop.appstream.generator # Run appstream-generator flatpak run org.freedesktop.appstream.generator --help ``` You can use AppStream Generator as [documented](docs/usage.md), but you will need to replace all `appstream-generator` commands with `flatpak run org.freedesktop.appstream.generator` and may need to set the workspace as absolute path using `-w` instead of relying on autodetection. ## Usage Take a look at the [docs/](docs/index.md) directory in the source tree for information on how to use the generator and write configuration files for it. ## Development ![Build Test](https://github.com/ximion/appstream-generator/workflows/Build%20Test/badge.svg) ### Build dependencies This project requires a C++23-capable compiler, GCC >= 14 or Clang >= 18 is recommended. The following libraries and tools are required to build the generator: * Meson (>= 1.0) [1] * AppStream Compose (>= 1.2) [2] * libarchive (>= 3.2) [3] * libfyaml (>= 0.9) * libxml2 * LMDB [4] * Curl * Inja [5] * oneAPI TBB [6] * Quill [7] * Catch2 [8] * backward-cpp (optional) [1]: http://mesonbuild.com/ [2]: https://github.com/ximion/appstream [3]: https://libarchive.org/ [4]: https://symas.com/lmdb/ [5]: https://github.com/pantor/inja [6]: https://uxlfoundation.github.io/oneTBB/ [7]: https://github.com/odygrd/quill [8]: https://github.com/catchorg/Catch2 On Debian and derivatives of it, all build requirements can be installed using the following command: ```ShellSession sudo apt install meson g++ \ libappstream-dev libappstream-compose-dev libblake3-dev libarchive-dev libcurl4-gnutls-dev \ liblmdb-dev libtbb-dev quill-dev libcatch2-dev libfyaml-dev libxml2-dev \ libbackward-cpp-dev ``` ### Build instructions To build the tool with Meson, create a `build` subdirectory, change into it and run `meson .. && ninja` to build. In summary: ```ShellSession $ mkdir build && cd build $ meson .. $ ninja $ sudo ninja install ``` We support several options to be set to influence the build. Change into the build directory and run `mesonconf` to see them all. You might want to perform an optimized debug build by passing `--buildtype=debugoptimized` to `meson` or just do a release build straight away with `--buildtype=release` in case you want to use the resulting binaries productively. By default, the build happens without optimizations which slows down the generator. ## Hacking Pull-requests and patches are very welcome! Using C++23 features is encouraged, if sensible. Make sure your code compiles in maintainer mode, and format your changes to adhere to the project's coding style. To help with the latter we provide the `autoformat.py` helper script to format code via *clang-format*. appstream-generator-1.0.0/RELEASE000066400000000000000000000020011524443220100164610ustar00rootroot00000000000000AppStream Generator Release Notes 1. Write NEWS entries for AppStream Generator in the same format as usual. git shortlog v0.10.2.. | grep -i -v trivial | grep -v Merge > NEWS.new -------------------------------------------------------------------------------- Version 1.0.0 ~~~~~~~~~~~~~ Released: 2026-xx-xx Notes: Features: Bugfixes: Miscellaneous: Contributors: -------------------------------------------------------------------------------- 2. Commit changes in Git: git commit -a -m "Release version 1.0.0" git tag -s -f -m "Release 1.0.0" v1.0.0 git push --tags git push 3. Do post release version bump in meson.build, RELEASE 4. Commit trivial changes: git commit -a -m "trivial: post release version bump" git push 5. Send an email to appstream@lists.freedesktop.org ================================================= AppStream Generator 1.0.0 released! Tarballs available here: https://github.com/ximion/appstream-generator/releases ================================================= appstream-generator-1.0.0/SECURITY.md000066400000000000000000000005431524443220100172600ustar00rootroot00000000000000# Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | 1.0.x | :white_check_mark: | | 0.10.x | until 31.10.2026 | ## Reporting a Vulnerability We have enabled private reporting in GitHub, so please [follow these steps](https://github.com/ximion/appstream-generator) to report vulnerabilities. appstream-generator-1.0.0/TODO000066400000000000000000000004371524443220100161610ustar00rootroot00000000000000= AppStream Generator TODO List = === Known issues === * No persistent problems at time :-) === Planned Features === * Use feature from the upcoming libappstream-compose === Whishlist / Random Ideas === * Add an icon-cache so we don't render SVG icons in themes multiple times. appstream-generator-1.0.0/autoformat.py000077500000000000000000000075041524443220100202310ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (C) 2015-2022 Matthias Klumpp # # SPDX-License-Identifier: LGPL-2.1+ # # Format all AppStream source code in-place. # import os import sys import shutil import fnmatch import subprocess import tempfile from glob import glob INCLUDE_LOCATIONS = [ 'autoformat.py', 'src', 'tests', ] EXCLUDE_MATCH = [] EXTRA_STYLE_RULES_FOR = [] def format_cpp_sources(sources, style_fname=None, extra_styles: list[str] = None): """Format C/C++ sources with clang-format.""" if not sources: return command = ['clang-format', '-i'] if extra_styles: style_rules = [] if style_fname: with open(style_fname, 'r') as f: style_rules = [l.strip() for l in f.readlines()] with tempfile.NamedTemporaryFile(mode='w') as fp: style_rules.extend(extra_styles) fp.write('\n'.join(style_rules)) fp.flush() command.append('--style=file:{}'.format(fp.name)) command.extend(sources) subprocess.run(command, check=True) return if style_fname: command.append('--style=file:{}'.format(style_fname)) command.extend(sources) subprocess.run(command, check=True) def format_python_sources(sources): """Format Python sources with Black.""" command = [ 'black', '-S', # no string normalization '-l', '100', # line length '-t', 'py311', # minimum Python target ] command.extend(sources) subprocess.run(command, check=True) def run(current_dir, args): # check for tools if not shutil.which('clang-format'): print( 'The `clang-format` formatter is not installed. Please install it to continue!', file=sys.stderr, ) return 1 if not shutil.which('black'): print( 'The `black` formatter is not installed. Please install it to continue!', file=sys.stderr, ) return 1 # if no include directories are explicitly specified, we read all locations if not INCLUDE_LOCATIONS: INCLUDE_LOCATIONS.append('.') # collect sources cpp_sources = [] cpp_style_matches = [[]] * len(EXTRA_STYLE_RULES_FOR) py_sources = [] for il_path_base in INCLUDE_LOCATIONS: il_path = os.path.join(current_dir, il_path_base) if os.path.isfile(il_path): candidates = [il_path] else: candidates = glob(il_path + '/**/*', recursive=True) for filename in candidates: skip = False for exclude in EXCLUDE_MATCH: if fnmatch.fnmatch(filename, exclude): skip = True break if skip: continue if filename.endswith(('.c', '.cpp', '.h', '.hpp')): cpp_sources.append(filename) for i, er in enumerate(EXTRA_STYLE_RULES_FOR): for pattern in er[1]: if fnmatch.fnmatch(filename, pattern): cpp_style_matches[i].append(filename) break elif filename.endswith('.py'): py_sources.append(filename) # format format_python_sources(py_sources) format_cpp_sources(cpp_sources) for i, er in enumerate(EXTRA_STYLE_RULES_FOR): format_cpp_sources( cpp_style_matches[i], os.path.join(current_dir, '.clang-format'), er[0], ) return 0 if __name__ == '__main__': thisfile = __file__ if not os.path.isabs(thisfile): thisfile = os.path.normpath(os.path.join(os.getcwd(), thisfile)) thisdir = os.path.normpath(os.path.join(os.path.dirname(thisfile))) os.chdir(thisdir) sys.exit(run(thisdir, sys.argv[1:])) appstream-generator-1.0.0/contrib/000077500000000000000000000000001524443220100171255ustar00rootroot00000000000000appstream-generator-1.0.0/contrib/cleanup-cruft.sh.example000066400000000000000000000013511524443220100236630ustar00rootroot00000000000000#!/bin/bash # # Script cleaning up the AppStream metadata pool and cache. # This script should be run by a cronjob (e.g. every week). # set -e set -o pipefail set -u WORKSPACE_DIR="/srv/appstream/workspace" # only run one instance of the script LOCKFILE="$WORKSPACE_DIR/.lock" cleanup() { rm -f "$LOCKFILE" } if ! lockfile -r8 $LOCKFILE; then echo "aborting AppStream metadata cleanup because $LOCKFILE has already been locked" exit 0 fi trap cleanup 0 # Start logging logdir="$WORKSPACE_DIR/logs/`date "+%Y/%m"`" mkdir -p $logdir NOW=`date "+%d_%H%M"` LOGFILE="$logdir/${NOW}_cleanup.log" exec >> "$LOGFILE" 2>&1 cd $WORKSPACE_DIR # Cleanup superseded data appstream-generator cleanup # finish logging exec > /dev/null 2>&1 appstream-generator-1.0.0/contrib/serve-export.py000077500000000000000000000171541524443220100221550ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (C) 2026 Matthias Klumpp # # SPDX-License-Identifier: LGPL-3.0-or-later # """Serve a workspace's export directory locally, to look at the generated pages. The generated HTML refers to itself through the absolute ``HtmlBaseUrl`` and ``MediaBaseUrl`` from the workspace configuration, which usually point at the machine the data is published on - so simply opening the files in a browser leaves every stylesheet, icon and chart broken. This script serves the export directory and rewrites those two URLs to itself as it hands out pages, so the result behaves like the deployed site. Usage: contrib/serve-export.py [-w WORKSPACE] [-p PORT] """ import argparse import functools import http.server import io import json import os import re import socketserver import sys import urllib.parse import webbrowser from pathlib import Path try: import yaml PARSE_ERRORS: tuple = (ValueError, yaml.YAMLError) except ImportError: # only needed to read YAML configurations, JSON ones work without it yaml = None PARSE_ERRORS = (ValueError,) # Extensions the stock mimetypes database gets wrong or does not know about. Note that the # compressed files are deliberately *not* served with a "Content-Encoding: gzip" header: # they are downloads in their own right, and the report pages know how to unpack the # statistics themselves - which is also how the real deployments serve them. EXTRA_TYPES = { ".gz": "application/gzip", ".xz": "application/x-xz", ".yml": "text/yaml; charset=utf-8", ".jxl": "image/jxl", ".avif": "image/avif", ".webp": "image/webp", } # The names the generator itself looks for, in the order it tries them. CONFIG_NAMES = ("asgen-config.json", "asgen-config.yaml", "asgen-config.yml") def load_config(workspace: Path) -> dict: config_file = next((workspace / name for name in CONFIG_NAMES if (workspace / name).is_file()), None) if config_file is None: sys.exit(f"No asgen-config.{{json,yaml}} found in {workspace} - is that a workspace?") try: data = config_file.read_text(encoding="utf-8") except OSError as e: sys.exit(f"Unable to read {config_file}: {e}") # JSON is a subset of YAML, so the generator parses both with its YAML parser and we do # the same - but PyYAML may not be installed, and JSON configurations must work anyway. try: if yaml is not None: conf = yaml.safe_load(data) elif config_file.suffix == ".json": conf = json.loads(data) else: sys.exit(f"Unable to read {config_file}: PyYAML is required to read YAML configurations.") except PARSE_ERRORS as e: sys.exit(f"Unable to read {config_file}: {e}") if not isinstance(conf, dict): sys.exit(f"Unable to read {config_file}: the configuration is not a key/value mapping.") return conf def resolve_export_dir(workspace: Path, conf: dict, key: str, default: str) -> Path: """Resolve one of the configured export directories the way the generator does.""" value = conf.get("ExportDirs", {}).get(key, default) path = Path(value) if not path.is_absolute(): path = workspace / "export" / path return path.resolve() class RewritingHandler(http.server.SimpleHTTPRequestHandler): """Serves the export tree, pointing the pages' absolute base URLs back at us.""" # set by serve() rewrites: list = [] def guess_type(self, path): suffix = Path(path).suffix.lower() if suffix in EXTRA_TYPES: return EXTRA_TYPES[suffix] return super().guess_type(path) def send_head(self): path = Path(self.translate_path(self.path)) # A link like "../" asks for a directory. The base class knows how to turn that # into its index.html, but it would then serve the file without our rewriting, so # resolve it here instead and let the rewriting path below pick it up. if path.is_dir(): if not urllib.parse.urlsplit(self.path).path.endswith("/"): # hand the redirect to the trailing-slash form back to the base class return super().send_head() index = path / "index.html" if (path / "index.html").is_file() else None if index is None: # no index page: fall back to the directory listing return super().send_head() path = index if path.suffix.lower() != ".html" or not path.is_file(): return super().send_head() try: body = path.read_bytes() except OSError: self.send_error(404, "File not found") return None for pattern, replacement in self.rewrites: body = pattern.sub(replacement, body) self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() return io.BytesIO(body) def end_headers(self): # never let the browser hold on to a previous version of a page or a stylesheet self.send_header("Cache-Control", "no-store") super().end_headers() def log_message(self, fmt, *args): if self.path.endswith((".css", ".js", ".png", ".jxl", ".webp", ".svg")): return super().log_message(fmt, *args) class Server(socketserver.ThreadingTCPServer): allow_reuse_address = True daemon_threads = True def serve(workspace: Path, port: int, open_browser: bool) -> None: conf = load_config(workspace) html_dir = resolve_export_dir(workspace, conf, "Html", "html") media_dir = resolve_export_dir(workspace, conf, "Media", "media") if not html_dir.is_dir(): sys.exit(f"No generated HTML found at {html_dir} - run the generator first.") # Serve the common parent of both trees, so that pages and the media they reference are # reachable under one origin. root = Path(os.path.commonpath([html_dir, media_dir])) if media_dir.is_dir() else html_dir base = f"http://localhost:{port}" def url_for(path: Path) -> str: rel = path.relative_to(root).as_posix() return base if rel == "." else f"{base}/{rel}" rewrites = [] for key, path in (("HtmlBaseUrl", html_dir), ("MediaBaseUrl", media_dir)): configured = conf.get(key) if not configured: continue configured = configured.rstrip("/") rewrites.append((re.compile(re.escape(configured).encode()), url_for(path).encode())) print(f" {key}: {configured} -> {url_for(path)}") RewritingHandler.rewrites = rewrites handler = functools.partial(RewritingHandler, directory=str(root)) index = url_for(html_dir) print(f"\nServing {root}") print(f"Report pages: {index}") print("Press Ctrl+C to stop.\n") if open_browser: webbrowser.open(index) with Server(("127.0.0.1", port), handler) as httpd: try: httpd.serve_forever() except KeyboardInterrupt: print("\nBye.") def main() -> None: parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( "-w", "--workspace", default=".", help="the generator workspace to serve (default: the current directory)", ) parser.add_argument("-p", "--port", type=int, default=8080, help="port to listen on (default: 8080)") parser.add_argument("--open", action="store_true", help="open the report pages in a browser") args = parser.parse_args() serve(Path(args.workspace).resolve(), args.port, args.open) if __name__ == "__main__": main() appstream-generator-1.0.0/contrib/subprojects/000077500000000000000000000000001524443220100214705ustar00rootroot00000000000000appstream-generator-1.0.0/contrib/subprojects/.gitignore000066400000000000000000000000671524443220100234630ustar00rootroot00000000000000inja/ packagecache/ backward-cpp-*/ quill-*/ .wraplock appstream-generator-1.0.0/contrib/subprojects/backward-cpp.wrap000066400000000000000000000012411524443220100247170ustar00rootroot00000000000000[wrap-file] directory = backward-cpp-1.6 source_url = https://github.com/bombela/backward-cpp/archive/refs/tags/v1.6.tar.gz source_filename = backward-cpp-1.6.tar.gz source_hash = c654d0923d43f1cea23d086729673498e4741fb2457e806cfaeaea7b20c97c10 patch_filename = backward-cpp_1.6-6_patch.zip patch_url = https://wrapdb.mesonbuild.com/v2/backward-cpp_1.6-6/get_patch patch_hash = 2c952611584971cdcb04b5c4aeca3401f1fba9544e6203e08b933fd9c21427fc source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/backward-cpp_1.6-6/backward-cpp-1.6.tar.gz wrapdb_version = 1.6-6 [provide] backward-cpp = backward_dep backward-cpp-interface = backward_interface_dep appstream-generator-1.0.0/contrib/subprojects/inja.wrap000066400000000000000000000001271524443220100233040ustar00rootroot00000000000000[wrap-git] directory = inja url = https://github.com/pantor/inja.git revision = v3.5.0 appstream-generator-1.0.0/contrib/subprojects/nlohmann_json.wrap000066400000000000000000000007351524443220100252330ustar00rootroot00000000000000[wrap-file] directory = nlohmann_json-3.12.0 lead_directory_missing = true source_url = https://github.com/nlohmann/json/releases/download/v3.12.0/include.zip source_filename = nlohmann_json-3.12.0.zip source_hash = b8cb0ef2dd7f57f18933997c9934bb1fa962594f701cd5a8d3c2c80541559372 source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/nlohmann_json_3.12.0-1/nlohmann_json-3.12.0.zip wrapdb_version = 3.12.0-1 [provide] nlohmann_json = nlohmann_json_dep appstream-generator-1.0.0/contrib/subprojects/quill.wrap000066400000000000000000000006041524443220100235110ustar00rootroot00000000000000[wrap-file] directory = quill-11.1.0 source_url = https://github.com/odygrd/quill/archive/refs/tags/v11.1.0.tar.gz source_filename = quill-11.1.0.tar.gz source_hash = a4c41068ec51979e1c6d95ae9ab6efc09e654b9815dcb7a1b58b7a430a5cbd13 source_fallback_url = https://wrapdb.mesonbuild.com/v2/quill_11.1.0-1/get_source/quill-11.1.0.tar.gz wrapdb_version = 11.1.0-1 [provide] quill = quill_dep appstream-generator-1.0.0/contrib/update-metadata.sh.example000066400000000000000000000017501524443220100241560ustar00rootroot00000000000000#!/bin/bash # # Example script for updating AppStream metadata using appstream-generator. # This script can easily be run by a cronjob. # set -e set -o pipefail set -u SUITES="sid stretch" WORKSPACE_DIR="/srv/appstream/workspace" PUBLIC_DIR="/srv/appstream/public" # only run one instance of the script LOCKFILE="$WORKSPACE_DIR/.lock" cleanup() { rm -f "$LOCKFILE" } if ! lockfile -r8 $LOCKFILE; then echo "aborting AppStream metadata extraction because $LOCKFILE has already been locked" exit 0 fi trap cleanup 0 # Start logging logdir="$WORKSPACE_DIR/logs/`date "+%Y/%m"`" mkdir -p $logdir NOW=`date "+%d_%H%M"` LOGFILE="$logdir/${NOW}.log" exec >> "$LOGFILE" 2>&1 cd $WORKSPACE_DIR # generate fresh metadata for suite in $SUITES; do appstream-generator process $suite done # Sync updated data to public directory rsync -ak --delete-after --exclude='media/.staging' --link-dest="$PUBLIC_DIR/" "$WORKSPACE_DIR/export/" "$PUBLIC_DIR/" # finish logging exec > /dev/null 2>&1 appstream-generator-1.0.0/data/000077500000000000000000000000001524443220100163765ustar00rootroot00000000000000appstream-generator-1.0.0/data/asgen-hints.json000066400000000000000000000161361524443220100215200ustar00rootroot00000000000000{ "internal-error": { "text": [ "A fatal problem appeared in the generator.", "Please report a bug: {{msg}}"], "severity": "error" }, "internal-unknown-tag": { "text": ["The generator emitted a tag '{{tag}}' which is unknown. This is a bug in the metadata generator, please", "file a bugreport."], "severity": "warning" }, "icon-format-unsupported": { "text": "Icon file '{{icon_fname}}' uses an unsupported image file format.", "severity": "error" }, "icon-not-found": { "text": [ "The icon '{{icon_fname}}' was not found in the archive. This issue can have multiple reasons:", "
    ", "
  • The icon is not present in the archive.
  • ", "
  • The icon is in a wrong directory.
  • ", "
  • The icon is not available in a suitable size (at least 64x64px)
  • ", "
  • On Debian and Ubuntu, the icon is a symlink. The generator cannot read symlinks on these distributions - make the icon a real file.
  • ", "
", "To make the icon easier to find, place it in /usr/share/icons/hicolor/<size>/apps and ensure the Icon= value", "of the .desktop file is set correctly." ], "severity": "error" }, "icon-scaled-up": { "text": "Icon file '{{icon_name}}' was scaled up from {{icon_size}}px to {{scale_size}}px. Please try to supply a bigger icon.", "severity": "warning" }, "icon-too-small": { "text": [ "Only a very small icon ('{{icon_name}}', {{icon_size}}px) could be located.", "Please try to supply a bigger icon (at least 64x64px) in the Freedesktop icon path (/usr/share/icons/hicolor/%size%/apps/)." ], "severity": "error" }, "pkg-extract-error": { "text": "Could not extract file '{{fname}}' from package '{{pkg_fname}}'. Error: {{error}}", "severity": "error" }, "pkg-empty-file": { "text": ["Could not extract file '{{fname}}' from package '{{pkg_fname}}'.", "Icon data was empty. The icon might be a symbolic link pointing at a file outside of this package.", "Please do not do that and instead place the icons in their appropriate directories in /usr/share/icons/hicolor/."], "severity": "error" }, "image-write-error": { "text": ["Could write new image generated from '{{fname}}' (package '{{pkg_fname}}'): {{error}}"], "severity": "error" }, "media-worker-process-error": { "text": ["The helper process that processes images, fonts and videos failed while handling '{{fname}}'", "(package '{{pkg_fname}}'): {{error}}
", "This can be a problem with the appstream-generator installation or its environment,", "and not with the analyzed package."], "severity": "error" }, "metainfo-validation-error": { "text": "Validation of the MetaInfo file failed: {{msg}}", "severity": "warning" }, "no-install-candidate": { "text": "Component has no install candidate defined. A package must be associated with a package or bundle providing it.", "severity": "error" }, "metainfo-duplicate-id": { "text": ["The component-id '{{cid}}' already appeared in package '{{pkgname}}'. AppStream-IDs must be unique, please resolve which package will be", "providing this component by default.
", "This issue may happen temporarily when metadata is moved from one package to another. In that case, ignore this issue, it will vanish soon."], "severity": "error" }, "missing-desktop-file": { "text": ["Found an AppStream MetaInfo XML file, but the associated .desktop file is missing. This often happens when the .desktop file is renamed, but the", "<launchable type=\"desktop-id\"/> tag value of the AppStream MetaInfo file is not adapted as well, or if the MetaInfo file is located in a different package than the .desktop file.
", "Please fix the packaging or work with upstream to resolve this issue.
", "For older metadata, the desktop-id is inferred from the <id/> tag. If the component metadata has no launchable tag and no", "icon tag of type stock, check if a .desktop file named after the component-ID is located in the same package."], "severity": "error" }, "description-from-package": { "text": ["This software component gets its description from the package it is located in.
", "This has several disadvantages, like poor markup, too technical descriptions for users of software centers, different components having the same description, etc.
", "Please consider to either hide this .desktop file from AppStream by adding a X-AppStream-Ignore=true field to its .desktop file, or to write a MetaInfo file for ", "this component to take the long description upstream. In future, components without MetaInfo file might be dropped from the metadata entirely.", "You can consult the XML quickstart guides for more information on how to write a MetaInfo file."], "severity": "info" }, "no-metainfo": { "text": ["This software component is missing a MetaInfo file to provide metadata.
", "We currently took some data from its desktop-entry file and the long description of the package it is located in.
", "This has several disadvantages, like poor markup, too technical descriptions for users of software centers, different components having the same description, etc.
", "Additionally, a lot of software with desktop-entry files should either not be installable and searchable via the software catalog (like desktop-specific settings applications) or be tagged accordingly via MetaInfo files.
", "Please consider to either hide this desktop-entry file from AppStream by adding a X-AppStream-Ignore=true field to it, or to write a MetaInfo file for this component and send it upstream.
", "Generating components from non-MetaInfo files is deprecated, if you do not add a MetaInfo file, ", "this software may vanish from the metadata catalog (and if it is a GUI application, no longer be visible in software centers) in a future distribution release.
", "You can consult the MetaInfo quickstart guides for more information on how to write a MetaInfo file, ", "or file a bug with the upstream author of this software component."], "severity": "warning" }, "metadata-serialization-failed": { "text": ["Could not create the final metadata. This could have many causes, including a generator bug, but the likeliest cause is an error in the input metadata.
", "The error message was: {{msg}}"], "severity": "error" } } appstream-generator-1.0.0/data/asgen-logo.svg000066400000000000000000000556251524443220100211670ustar00rootroot00000000000000 image/svg+xml appstream-generator-1.0.0/data/hicolor-theme-index.theme000066400000000000000000001543231524443220100232760ustar00rootroot00000000000000[Icon Theme] Name=Hicolor Comment=Fallback icon theme Hidden=true Directories=16x16/actions,16x16@2/actions,16x16/animations,16x16@2/animations,16x16/apps,16x16@2/apps,16x16/categories,16x16@2/categories,16x16/devices,16x16@2/devices,16x16/emblems,16x16@2/emblems,16x16/emotes,16x16@2/emotes,16x16/filesystems,16x16@2/filesystems,16x16/intl,16x16@2/intl,16x16/mimetypes,16x16@2/mimetypes,16x16/places,16x16@2/places,16x16/status,16x16@2/status,16x16/stock/chart,16x16@2/stock/chart,16x16/stock/code,16x16@2/stock/code,16x16/stock/data,16x16@2/stock/data,16x16/stock/form,16x16@2/stock/form,16x16/stock/image,16x16@2/stock/image,16x16/stock/io,16x16@2/stock/io,16x16/stock/media,16x16@2/stock/media,16x16/stock/navigation,16x16@2/stock/navigation,16x16/stock/net,16x16@2/stock/net,16x16/stock/object,16x16@2/stock/object,16x16/stock/table,16x16@2/stock/table,16x16/stock/text,16x16@2/stock/text,22x22/actions,22x22@2/actions,22x22/animations,22x22@2/animations,22x22/apps,22x22@2/apps,22x22/categories,22x22@2/categories,22x22/devices,22x22@2/devices,22x22/emblems,22x22@2/emblems,22x22/emotes,22x22@2/emotes,22x22/filesystems,22x22@2/filesystems,22x22/intl,22x22@2/intl,22x22/mimetypes,22x22@2/mimetypes,22x22/places,22x22@2/places,22x22/status,22x22@2/status,22x22/stock/chart,22x22@2/stock/chart,22x22/stock/code,22x22@2/stock/code,22x22/stock/data,22x22@2/stock/data,22x22/stock/form,22x22@2/stock/form,22x22/stock/image,22x22@2/stock/image,22x22/stock/io,22x22@2/stock/io,22x22/stock/media,22x22@2/stock/media,22x22/stock/navigation,22x22@2/stock/navigation,22x22/stock/net,22x22@2/stock/net,22x22/stock/object,22x22@2/stock/object,22x22/stock/table,22x22@2/stock/table,22x22/stock/text,22x22@2/stock/text,24x24/actions,24x24@2/actions,24x24/animations,24x24@2/animations,24x24/apps,24x24@2/apps,24x24/categories,24x24@2/categories,24x24/devices,24x24@2/devices,24x24/emblems,24x24@2/emblems,24x24/emotes,24x24@2/emotes,24x24/filesystems,24x24@2/filesystems,24x24/intl,24x24@2/intl,24x24/mimetypes,24x24@2/mimetypes,24x24/places,24x24@2/places,24x24/status,24x24@2/status,24x24/stock/chart,24x24@2/stock/chart,24x24/stock/code,24x24@2/stock/code,24x24/stock/data,24x24@2/stock/data,24x24/stock/form,24x24@2/stock/form,24x24/stock/image,24x24@2/stock/image,24x24/stock/io,24x24@2/stock/io,24x24/stock/media,24x24@2/stock/media,24x24/stock/navigation,24x24@2/stock/navigation,24x24/stock/net,24x24@2/stock/net,24x24/stock/object,24x24@2/stock/object,24x24/stock/table,24x24@2/stock/table,24x24/stock/text,24x24@2/stock/text,32x32/actions,32x32@2/actions,32x32/animations,32x32@2/animations,32x32/apps,32x32@2/apps,32x32/categories,32x32@2/categories,32x32/devices,32x32@2/devices,32x32/emblems,32x32@2/emblems,32x32/emotes,32x32@2/emotes,32x32/filesystems,32x32@2/filesystems,32x32/intl,32x32@2/intl,32x32/mimetypes,32x32@2/mimetypes,32x32/places,32x32@2/places,32x32/status,32x32@2/status,32x32/stock/chart,32x32@2/stock/chart,32x32/stock/code,32x32@2/stock/code,32x32/stock/data,32x32@2/stock/data,32x32/stock/form,32x32@2/stock/form,32x32/stock/image,32x32@2/stock/image,32x32/stock/io,32x32@2/stock/io,32x32/stock/media,32x32@2/stock/media,32x32/stock/navigation,32x32@2/stock/navigation,32x32/stock/net,32x32@2/stock/net,32x32/stock/object,32x32@2/stock/object,32x32/stock/table,32x32@2/stock/table,32x32/stock/text,32x32@2/stock/text,36x36/actions,36x36@2/actions,36x36/animations,36x36@2/animations,36x36/apps,36x36@2/apps,36x36/categories,36x36@2/categories,36x36/devices,36x36@2/devices,36x36/emblems,36x36@2/emblems,36x36/emotes,36x36@2/emotes,36x36/filesystems,36x36@2/filesystems,36x36/intl,36x36@2/intl,36x36/mimetypes,36x36@2/mimetypes,36x36/places,36x36@2/places,36x36/status,36x36@2/status,36x36/stock/chart,36x36@2/stock/chart,36x36/stock/code,36x36@2/stock/code,36x36/stock/data,36x36@2/stock/data,36x36/stock/form,36x36@2/stock/form,36x36/stock/image,36x36@2/stock/image,36x36/stock/io,36x36@2/stock/io,36x36/stock/media,36x36@2/stock/media,36x36/stock/navigation,36x36@2/stock/navigation,36x36/stock/net,36x36@2/stock/net,36x36/stock/object,36x36@2/stock/object,36x36/stock/table,36x36@2/stock/table,36x36/stock/text,36x36@2/stock/text,48x48/actions,48x48@2/actions,48x48/animations,48x48@2/animations,48x48/apps,48x48@2/apps,48x48/categories,48x48@2/categories,48x48/devices,48x48@2/devices,48x48/emblems,48x48@2/emblems,48x48/emotes,48x48@2/emotes,48x48/filesystems,48x48@2/filesystems,48x48/intl,48x48@2/intl,48x48/mimetypes,48x48@2/mimetypes,48x48/places,48x48@2/places,48x48/status,48x48@2/status,48x48/stock/chart,48x48@2/stock/chart,48x48/stock/code,48x48@2/stock/code,48x48/stock/data,48x48@2/stock/data,48x48/stock/form,48x48@2/stock/form,48x48/stock/image,48x48@2/stock/image,48x48/stock/io,48x48@2/stock/io,48x48/stock/media,48x48@2/stock/media,48x48/stock/navigation,48x48@2/stock/navigation,48x48/stock/net,48x48@2/stock/net,48x48/stock/object,48x48@2/stock/object,48x48/stock/table,48x48@2/stock/table,48x48/stock/text,48x48@2/stock/text,64x64/actions,64x64@2/actions,64x64/animations,64x64@2/animations,64x64/apps,64x64@2/apps,64x64/categories,64x64@2/categories,64x64/devices,64x64@2/devices,64x64/emblems,64x64@2/emblems,64x64/emotes,64x64@2/emotes,64x64/filesystems,64x64@2/filesystems,64x64/intl,64x64@2/intl,64x64/mimetypes,64x64@2/mimetypes,64x64/places,64x64@2/places,64x64/status,64x64@2/status,64x64/stock/chart,64x64@2/stock/chart,64x64/stock/code,64x64@2/stock/code,64x64/stock/data,64x64@2/stock/data,64x64/stock/form,64x64@2/stock/form,64x64/stock/image,64x64@2/stock/image,64x64/stock/io,64x64@2/stock/io,64x64/stock/media,64x64@2/stock/media,64x64/stock/navigation,64x64@2/stock/navigation,64x64/stock/net,64x64@2/stock/net,64x64/stock/object,64x64@2/stock/object,64x64/stock/table,64x64@2/stock/table,64x64/stock/text,64x64@2/stock/text,72x72/actions,72x72@2/actions,72x72/animations,72x72@2/animations,72x72/apps,72x72@2/apps,72x72/categories,72x72@2/categories,72x72/devices,72x72@2/devices,72x72/emblems,72x72@2/emblems,72x72/emotes,72x72@2/emotes,72x72/filesystems,72x72@2/filesystems,72x72/intl,72x72@2/intl,72x72/mimetypes,72x72@2/mimetypes,72x72/places,72x72@2/places,72x72/status,72x72@2/status,72x72/stock/chart,72x72@2/stock/chart,72x72/stock/code,72x72@2/stock/code,72x72/stock/data,72x72@2/stock/data,72x72/stock/form,72x72@2/stock/form,72x72/stock/image,72x72@2/stock/image,72x72/stock/io,72x72@2/stock/io,72x72/stock/media,72x72@2/stock/media,72x72/stock/navigation,72x72@2/stock/navigation,72x72/stock/net,72x72@2/stock/net,72x72/stock/object,72x72@2/stock/object,72x72/stock/table,72x72@2/stock/table,72x72/stock/text,72x72@2/stock/text,96x96/actions,96x96@2/actions,96x96/animations,96x96@2/animations,96x96/apps,96x96@2/apps,96x96/categories,96x96@2/categories,96x96/devices,96x96@2/devices,96x96/emblems,96x96@2/emblems,96x96/emotes,96x96@2/emotes,96x96/filesystems,96x96@2/filesystems,96x96/intl,96x96@2/intl,96x96/mimetypes,96x96@2/mimetypes,96x96/places,96x96@2/places,96x96/status,96x96@2/status,96x96/stock/chart,96x96@2/stock/chart,96x96/stock/code,96x96@2/stock/code,96x96/stock/data,96x96@2/stock/data,96x96/stock/form,96x96@2/stock/form,96x96/stock/image,96x96@2/stock/image,96x96/stock/io,96x96@2/stock/io,96x96/stock/media,96x96@2/stock/media,96x96/stock/navigation,96x96@2/stock/navigation,96x96/stock/net,96x96@2/stock/net,96x96/stock/object,96x96@2/stock/object,96x96/stock/table,96x96@2/stock/table,96x96/stock/text,96x96@2/stock/text,128x128/actions,128x128@2/actions,128x128/animations,128x128@2/animations,128x128/apps,128x128@2/apps,128x128/categories,128x128@2/categories,128x128/devices,128x128@2/devices,128x128/emblems,128x128@2/emblems,128x128/emotes,128x128@2/emotes,128x128/filesystems,128x128@2/filesystems,128x128/intl,128x128@2/intl,128x128/mimetypes,128x128@2/mimetypes,128x128/places,128x128@2/places,128x128/status,128x128@2/status,128x128/stock/chart,128x128@2/stock/chart,128x128/stock/code,128x128@2/stock/code,128x128/stock/data,128x128@2/stock/data,128x128/stock/form,128x128@2/stock/form,128x128/stock/image,128x128@2/stock/image,128x128/stock/io,128x128@2/stock/io,128x128/stock/media,128x128@2/stock/media,128x128/stock/navigation,128x128@2/stock/navigation,128x128/stock/net,128x128@2/stock/net,128x128/stock/object,128x128@2/stock/object,128x128/stock/table,128x128@2/stock/table,128x128/stock/text,128x128@2/stock/text,192x192/actions,192x192@2/actions,192x192/animations,192x192@2/animations,192x192/apps,192x192@2/apps,192x192/categories,192x192@2/categories,192x192/devices,192x192@2/devices,192x192/emblems,192x192@2/emblems,192x192/emotes,192x192@2/emotes,192x192/filesystems,192x192@2/filesystems,192x192/intl,192x192@2/intl,192x192/mimetypes,192x192@2/mimetypes,192x192/places,192x192@2/places,192x192/status,192x192@2/status,192x192/stock/chart,192x192@2/stock/chart,192x192/stock/code,192x192@2/stock/code,192x192/stock/data,192x192@2/stock/data,192x192/stock/form,192x192@2/stock/form,192x192/stock/image,192x192@2/stock/image,192x192/stock/io,192x192@2/stock/io,192x192/stock/media,192x192@2/stock/media,192x192/stock/navigation,192x192@2/stock/navigation,192x192/stock/net,192x192@2/stock/net,192x192/stock/object,192x192@2/stock/object,192x192/stock/table,192x192@2/stock/table,192x192/stock/text,192x192@2/stock/text,256x256/actions,256x256@2/actions,256x256/animations,256x256@2/animations,256x256/apps,256x256@2/apps,256x256/categories,256x256@2/categories,256x256/devices,256x256@2/devices,256x256/emblems,256x256@2/emblems,256x256/emotes,256x256@2/emotes,256x256/filesystems,256x256@2/filesystems,256x256/intl,256x256@2/intl,256x256/mimetypes,256x256@2/mimetypes,256x256/places,256x256@2/places,256x256/status,256x256@2/status,256x256/stock/chart,256x256@2/stock/chart,256x256/stock/code,256x256@2/stock/code,256x256/stock/data,256x256@2/stock/data,256x256/stock/form,256x256@2/stock/form,256x256/stock/image,256x256@2/stock/image,256x256/stock/io,256x256@2/stock/io,256x256/stock/media,256x256@2/stock/media,256x256/stock/navigation,256x256@2/stock/navigation,256x256/stock/net,256x256@2/stock/net,256x256/stock/object,256x256@2/stock/object,256x256/stock/table,256x256@2/stock/table,256x256/stock/text,256x256@2/stock/text,512x512/actions,512x512@2/actions,512x512/animations,512x512@2/animations,512x512/apps,512x512@2/apps,512x512/categories,512x512@2/categories,512x512/devices,512x512@2/devices,512x512/emblems,512x512@2/emblems,512x512/emotes,512x512@2/emotes,512x512/filesystems,512x512@2/filesystems,512x512/intl,512x512@2/intl,512x512/mimetypes,512x512@2/mimetypes,512x512/places,512x512@2/places,512x512/status,512x512@2/status,512x512/stock/chart,512x512@2/stock/chart,512x512/stock/code,512x512@2/stock/code,512x512/stock/data,512x512@2/stock/data,512x512/stock/form,512x512@2/stock/form,512x512/stock/image,512x512@2/stock/image,512x512/stock/io,512x512@2/stock/io,512x512/stock/media,512x512@2/stock/media,512x512/stock/navigation,512x512@2/stock/navigation,512x512/stock/net,512x512@2/stock/net,512x512/stock/object,512x512@2/stock/object,512x512/stock/table,512x512@2/stock/table,512x512/stock/text,512x512@2/stock/text,scalable/actions,scalable/animations,scalable/apps,scalable/categories,scalable/devices,scalable/emblems,scalable/emotes,scalable/filesystems,scalable/intl,scalable/mimetypes,scalable/places,scalable/status,scalable/stock/chart,scalable/stock/code,scalable/stock/data,scalable/stock/form,scalable/stock/image,scalable/stock/io,scalable/stock/media,scalable/stock/navigation,scalable/stock/net,scalable/stock/object,scalable/stock/table,scalable/stock/text,symbolic/apps [16x16/actions] Size=16 Context=Actions Type=Threshold [16x16@2/actions] Size=16 Scale=2 Context=Actions Type=Threshold [16x16/animations] Size=16 Context=Animations Type=Threshold [16x16@2/animations] Size=16 Scale=2 Context=Animations Type=Threshold [16x16/apps] Size=16 Context=Applications Type=Threshold [16x16@2/apps] Size=16 Scale=2 Context=Applications Type=Threshold [16x16/categories] Size=16 Context=Categories Type=Threshold [16x16@2/categories] Size=16 Scale=2 Context=Categories Type=Threshold [16x16/devices] Size=16 Context=Devices Type=Threshold [16x16@2/devices] Size=16 Scale=2 Context=Devices Type=Threshold [16x16/emblems] Size=16 Context=Emblems Type=Threshold [16x16@2/emblems] Size=16 Scale=2 Context=Emblems Type=Threshold [16x16/emotes] Size=16 Context=Emotes Type=Threshold [16x16@2/emotes] Size=16 Scale=2 Context=Emotes Type=Threshold [16x16/filesystems] Size=16 Context=FileSystems Type=Threshold [16x16@2/filesystems] Size=16 Scale=2 Context=FileSystems Type=Threshold [16x16/intl] Size=16 Context=International Type=Threshold [16x16@2/intl] Size=16 Scale=2 Context=International Type=Threshold [16x16/mimetypes] Size=16 Context=MimeTypes Type=Threshold [16x16@2/mimetypes] Size=16 Scale=2 Context=MimeTypes Type=Threshold [16x16/places] Size=16 Context=Places Type=Threshold [16x16@2/places] Size=16 Scale=2 Context=Places Type=Threshold [16x16/status] Size=16 Context=Status Type=Threshold [16x16@2/status] Size=16 Scale=2 Context=Status Type=Threshold [16x16/stock/chart] Size=16 Context=Stock Type=Threshold [16x16@2/stock/chart] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/code] Size=16 Context=Stock Type=Threshold [16x16@2/stock/code] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/data] Size=16 Context=Stock Type=Threshold [16x16@2/stock/data] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/form] Size=16 Context=Stock Type=Threshold [16x16@2/stock/form] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/image] Size=16 Context=Stock Type=Threshold [16x16@2/stock/image] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/io] Size=16 Context=Stock Type=Threshold [16x16@2/stock/io] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/media] Size=16 Context=Stock Type=Threshold [16x16@2/stock/media] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/navigation] Size=16 Context=Stock Type=Threshold [16x16@2/stock/navigation] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/net] Size=16 Context=Stock Type=Threshold [16x16@2/stock/net] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/object] Size=16 Context=Stock Type=Threshold [16x16@2/stock/object] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/table] Size=16 Context=Stock Type=Threshold [16x16@2/stock/table] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/text] Size=16 Context=Stock Type=Threshold [16x16@2/stock/text] Size=16 Scale=2 Context=Stock Type=Threshold [22x22/actions] Size=22 Context=Actions Type=Threshold [22x22@2/actions] Size=22 Scale=2 Context=Actions Type=Threshold [22x22/animations] Size=22 Context=Animations Type=Threshold [22x22@2/animations] Size=22 Scale=2 Context=Animations Type=Threshold [22x22/apps] Size=22 Context=Applications Type=Threshold [22x22@2/apps] Size=22 Scale=2 Context=Applications Type=Threshold [22x22/categories] Size=22 Context=Categories Type=Threshold [22x22@2/categories] Size=22 Scale=2 Context=Categories Type=Threshold [22x22/devices] Size=22 Context=Devices Type=Threshold [22x22@2/devices] Size=22 Scale=2 Context=Devices Type=Threshold [22x22/emblems] Size=22 Context=Emblems Type=Threshold [22x22@2/emblems] Size=22 Scale=2 Context=Emblems Type=Threshold [22x22/emotes] Size=22 Context=Emotes Type=Threshold [22x22@2/emotes] Size=22 Scale=2 Context=Emotes Type=Threshold [22x22/filesystems] Size=22 Context=FileSystems Type=Threshold [22x22@2/filesystems] Size=22 Scale=2 Context=FileSystems Type=Threshold [22x22/intl] Size=22 Context=International Type=Threshold [22x22@2/intl] Size=22 Scale=2 Context=International Type=Threshold [22x22/mimetypes] Size=22 Context=MimeTypes Type=Threshold [22x22@2/mimetypes] Size=22 Scale=2 Context=MimeTypes Type=Threshold [22x22/places] Size=22 Context=Places Type=Threshold [22x22@2/places] Size=22 Scale=2 Context=Places Type=Threshold [22x22/status] Size=22 Context=Status Type=Threshold [22x22@2/status] Size=22 Scale=2 Context=Status Type=Threshold [22x22/stock/chart] Size=22 Context=Stock Type=Threshold [22x22@2/stock/chart] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/code] Size=22 Context=Stock Type=Threshold [22x22@2/stock/code] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/data] Size=22 Context=Stock Type=Threshold [22x22@2/stock/data] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/form] Size=22 Context=Stock Type=Threshold [22x22@2/stock/form] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/image] Size=22 Context=Stock Type=Threshold [22x22@2/stock/image] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/io] Size=22 Context=Stock Type=Threshold [22x22@2/stock/io] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/media] Size=22 Context=Stock Type=Threshold [22x22@2/stock/media] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/navigation] Size=22 Context=Stock Type=Threshold [22x22@2/stock/navigation] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/net] Size=22 Context=Stock Type=Threshold [22x22@2/stock/net] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/object] Size=22 Context=Stock Type=Threshold [22x22@2/stock/object] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/table] Size=22 Context=Stock Type=Threshold [22x22@2/stock/table] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/text] Size=22 Context=Stock Type=Threshold [22x22@2/stock/text] Size=22 Scale=2 Context=Stock Type=Threshold [24x24/actions] Size=24 Context=Actions Type=Threshold [24x24@2/actions] Size=24 Scale=2 Context=Actions Type=Threshold [24x24/animations] Size=24 Context=Animations Type=Threshold [24x24@2/animations] Size=24 Scale=2 Context=Animations Type=Threshold [24x24/apps] Size=24 Context=Applications Type=Threshold [24x24@2/apps] Size=24 Scale=2 Context=Applications Type=Threshold [24x24/categories] Size=24 Context=Categories Type=Threshold [24x24@2/categories] Size=24 Scale=2 Context=Categories Type=Threshold [24x24/devices] Size=24 Context=Devices Type=Threshold [24x24@2/devices] Size=24 Scale=2 Context=Devices Type=Threshold [24x24/emblems] Size=24 Context=Emblems Type=Threshold [24x24@2/emblems] Size=24 Scale=2 Context=Emblems Type=Threshold [24x24/emotes] Size=24 Context=Emotes Type=Threshold [24x24@2/emotes] Size=24 Scale=2 Context=Emotes Type=Threshold [24x24/filesystems] Size=24 Context=FileSystems Type=Threshold [24x24@2/filesystems] Size=24 Scale=2 Context=FileSystems Type=Threshold [24x24/intl] Size=24 Context=International Type=Threshold [24x24@2/intl] Size=24 Scale=2 Context=International Type=Threshold [24x24/mimetypes] Size=24 Context=MimeTypes Type=Threshold [24x24@2/mimetypes] Size=24 Scale=2 Context=MimeTypes Type=Threshold [24x24/places] Size=24 Context=Places Type=Threshold [24x24@2/places] Size=24 Scale=2 Context=Places Type=Threshold [24x24/status] Size=24 Context=Status Type=Threshold [24x24@2/status] Size=24 Scale=2 Context=Status Type=Threshold [24x24/stock/chart] Size=24 Context=Stock Type=Threshold [24x24@2/stock/chart] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/code] Size=24 Context=Stock Type=Threshold [24x24@2/stock/code] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/data] Size=24 Context=Stock Type=Threshold [24x24@2/stock/data] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/form] Size=24 Context=Stock Type=Threshold [24x24@2/stock/form] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/image] Size=24 Context=Stock Type=Threshold [24x24@2/stock/image] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/io] Size=24 Context=Stock Type=Threshold [24x24@2/stock/io] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/media] Size=24 Context=Stock Type=Threshold [24x24@2/stock/media] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/navigation] Size=24 Context=Stock Type=Threshold [24x24@2/stock/navigation] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/net] Size=24 Context=Stock Type=Threshold [24x24@2/stock/net] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/object] Size=24 Context=Stock Type=Threshold [24x24@2/stock/object] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/table] Size=24 Context=Stock Type=Threshold [24x24@2/stock/table] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/text] Size=24 Context=Stock Type=Threshold [24x24@2/stock/text] Size=24 Scale=2 Context=Stock Type=Threshold [32x32/actions] Size=32 Context=Actions Type=Threshold [32x32@2/actions] Size=32 Scale=2 Context=Actions Type=Threshold [32x32/animations] Size=32 Context=Animations Type=Threshold [32x32@2/animations] Size=32 Scale=2 Context=Animations Type=Threshold [32x32/apps] Size=32 Context=Applications Type=Threshold [32x32@2/apps] Size=32 Scale=2 Context=Applications Type=Threshold [32x32/categories] Size=32 Context=Categories Type=Threshold [32x32@2/categories] Size=32 Scale=2 Context=Categories Type=Threshold [32x32/devices] Size=32 Context=Devices Type=Threshold [32x32@2/devices] Size=32 Scale=2 Context=Devices Type=Threshold [32x32/emblems] Size=32 Context=Emblems Type=Threshold [32x32@2/emblems] Size=32 Scale=2 Context=Emblems Type=Threshold [32x32/emotes] Size=32 Context=Emotes Type=Threshold [32x32@2/emotes] Size=32 Scale=2 Context=Emotes Type=Threshold [32x32/filesystems] Size=32 Context=FileSystems Type=Threshold [32x32@2/filesystems] Size=32 Scale=2 Context=FileSystems Type=Threshold [32x32/intl] Size=32 Context=International Type=Threshold [32x32@2/intl] Size=32 Scale=2 Context=International Type=Threshold [32x32/mimetypes] Size=32 Context=MimeTypes Type=Threshold [32x32@2/mimetypes] Size=32 Scale=2 Context=MimeTypes Type=Threshold [32x32/places] Size=32 Context=Places Type=Threshold [32x32@2/places] Size=32 Scale=2 Context=Places Type=Threshold [32x32/status] Size=32 Context=Status Type=Threshold [32x32@2/status] Size=32 Scale=2 Context=Status Type=Threshold [32x32/stock/chart] Size=32 Context=Stock Type=Threshold [32x32@2/stock/chart] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/code] Size=32 Context=Stock Type=Threshold [32x32@2/stock/code] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/data] Size=32 Context=Stock Type=Threshold [32x32@2/stock/data] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/form] Size=32 Context=Stock Type=Threshold [32x32@2/stock/form] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/image] Size=32 Context=Stock Type=Threshold [32x32@2/stock/image] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/io] Size=32 Context=Stock Type=Threshold [32x32@2/stock/io] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/media] Size=32 Context=Stock Type=Threshold [32x32@2/stock/media] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/navigation] Size=32 Context=Stock Type=Threshold [32x32@2/stock/navigation] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/net] Size=32 Context=Stock Type=Threshold [32x32@2/stock/net] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/object] Size=32 Context=Stock Type=Threshold [32x32@2/stock/object] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/table] Size=32 Context=Stock Type=Threshold [32x32@2/stock/table] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/text] Size=32 Context=Stock Type=Threshold [32x32@2/stock/text] Size=32 Scale=2 Context=Stock Type=Threshold [36x36/actions] Size=36 Context=Actions Type=Threshold [36x36@2/actions] Size=36 Scale=2 Context=Actions Type=Threshold [36x36/animations] Size=36 Context=Animations Type=Threshold [36x36@2/animations] Size=36 Scale=2 Context=Animations Type=Threshold [36x36/apps] Size=36 Context=Applications Type=Threshold [36x36@2/apps] Size=36 Scale=2 Context=Applications Type=Threshold [36x36/categories] Size=36 Context=Categories Type=Threshold [36x36@2/categories] Size=36 Scale=2 Context=Categories Type=Threshold [36x36/devices] Size=36 Context=Devices Type=Threshold [36x36@2/devices] Size=36 Scale=2 Context=Devices Type=Threshold [36x36/emblems] Size=36 Context=Emblems Type=Threshold [36x36@2/emblems] Size=36 Scale=2 Context=Emblems Type=Threshold [36x36/emotes] Size=36 Context=Emotes Type=Threshold [36x36@2/emotes] Size=36 Scale=2 Context=Emotes Type=Threshold [36x36/filesystems] Size=36 Context=FileSystems Type=Threshold [36x36@2/filesystems] Size=36 Scale=2 Context=FileSystems Type=Threshold [36x36/intl] Size=36 Context=International Type=Threshold [36x36@2/intl] Size=36 Scale=2 Context=International Type=Threshold [36x36/mimetypes] Size=36 Context=MimeTypes Type=Threshold [36x36@2/mimetypes] Size=36 Scale=2 Context=MimeTypes Type=Threshold [36x36/places] Size=36 Context=Places Type=Threshold [36x36@2/places] Size=36 Scale=2 Context=Places Type=Threshold [36x36/status] Size=36 Context=Status Type=Threshold [36x36@2/status] Size=36 Scale=2 Context=Status Type=Threshold [36x36/stock/chart] Size=36 Context=Stock Type=Threshold [36x36@2/stock/chart] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/code] Size=36 Context=Stock Type=Threshold [36x36@2/stock/code] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/data] Size=36 Context=Stock Type=Threshold [36x36@2/stock/data] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/form] Size=36 Context=Stock Type=Threshold [36x36@2/stock/form] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/image] Size=36 Context=Stock Type=Threshold [36x36@2/stock/image] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/io] Size=36 Context=Stock Type=Threshold [36x36@2/stock/io] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/media] Size=36 Context=Stock Type=Threshold [36x36@2/stock/media] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/navigation] Size=36 Context=Stock Type=Threshold [36x36@2/stock/navigation] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/net] Size=36 Context=Stock Type=Threshold [36x36@2/stock/net] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/object] Size=36 Context=Stock Type=Threshold [36x36@2/stock/object] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/table] Size=36 Context=Stock Type=Threshold [36x36@2/stock/table] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/text] Size=36 Context=Stock Type=Threshold [36x36@2/stock/text] Size=36 Scale=2 Context=Stock Type=Threshold [48x48/actions] Size=48 Context=Actions Type=Threshold [48x48@2/actions] Size=48 Scale=2 Context=Actions Type=Threshold [48x48/animations] Size=48 Context=Animations Type=Threshold [48x48@2/animations] Size=48 Scale=2 Context=Animations Type=Threshold [48x48/apps] Size=48 Context=Applications Type=Threshold [48x48@2/apps] Size=48 Scale=2 Context=Applications Type=Threshold [48x48/categories] Size=48 Context=Categories Type=Threshold [48x48@2/categories] Size=48 Scale=2 Context=Categories Type=Threshold [48x48/devices] Size=48 Context=Devices Type=Threshold [48x48@2/devices] Size=48 Scale=2 Context=Devices Type=Threshold [48x48/emblems] Size=48 Context=Emblems Type=Threshold [48x48@2/emblems] Size=48 Scale=2 Context=Emblems Type=Threshold [48x48/emotes] Size=48 Context=Emotes Type=Threshold [48x48@2/emotes] Size=48 Scale=2 Context=Emotes Type=Threshold [48x48/filesystems] Size=48 Context=FileSystems Type=Threshold [48x48@2/filesystems] Size=48 Scale=2 Context=FileSystems Type=Threshold [48x48/intl] Size=48 Context=International Type=Threshold [48x48@2/intl] Size=48 Scale=2 Context=International Type=Threshold [48x48/mimetypes] Size=48 Context=MimeTypes Type=Threshold [48x48@2/mimetypes] Size=48 Scale=2 Context=MimeTypes Type=Threshold [48x48/places] Size=48 Context=Places Type=Threshold [48x48@2/places] Size=48 Scale=2 Context=Places Type=Threshold [48x48/status] Size=48 Context=Status Type=Threshold [48x48@2/status] Size=48 Scale=2 Context=Status Type=Threshold [48x48/stock/chart] Size=48 Context=Stock Type=Threshold [48x48@2/stock/chart] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/code] Size=48 Context=Stock Type=Threshold [48x48@2/stock/code] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/data] Size=48 Context=Stock Type=Threshold [48x48@2/stock/data] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/form] Size=48 Context=Stock Type=Threshold [48x48@2/stock/form] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/image] Size=48 Context=Stock Type=Threshold [48x48@2/stock/image] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/io] Size=48 Context=Stock Type=Threshold [48x48@2/stock/io] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/media] Size=48 Context=Stock Type=Threshold [48x48@2/stock/media] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/navigation] Size=48 Context=Stock Type=Threshold [48x48@2/stock/navigation] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/net] Size=48 Context=Stock Type=Threshold [48x48@2/stock/net] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/object] Size=48 Context=Stock Type=Threshold [48x48@2/stock/object] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/table] Size=48 Context=Stock Type=Threshold [48x48@2/stock/table] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/text] Size=48 Context=Stock Type=Threshold [48x48@2/stock/text] Size=48 Scale=2 Context=Stock Type=Threshold [64x64/actions] Size=64 Context=Actions Type=Threshold [64x64@2/actions] Size=64 Scale=2 Context=Actions Type=Threshold [64x64/animations] Size=64 Context=Animations Type=Threshold [64x64@2/animations] Size=64 Scale=2 Context=Animations Type=Threshold [64x64/apps] Size=64 Context=Applications Type=Threshold [64x64@2/apps] Size=64 Scale=2 Context=Applications Type=Threshold [64x64/categories] Size=64 Context=Categories Type=Threshold [64x64@2/categories] Size=64 Scale=2 Context=Categories Type=Threshold [64x64/devices] Size=64 Context=Devices Type=Threshold [64x64@2/devices] Size=64 Scale=2 Context=Devices Type=Threshold [64x64/emblems] Size=64 Context=Emblems Type=Threshold [64x64@2/emblems] Size=64 Scale=2 Context=Emblems Type=Threshold [64x64/emotes] Size=64 Context=Emotes Type=Threshold [64x64@2/emotes] Size=64 Scale=2 Context=Emotes Type=Threshold [64x64/filesystems] Size=64 Context=FileSystems Type=Threshold [64x64@2/filesystems] Size=64 Scale=2 Context=FileSystems Type=Threshold [64x64/intl] Size=64 Context=International Type=Threshold [64x64@2/intl] Size=64 Scale=2 Context=International Type=Threshold [64x64/mimetypes] Size=64 Context=MimeTypes Type=Threshold [64x64@2/mimetypes] Size=64 Scale=2 Context=MimeTypes Type=Threshold [64x64/places] Size=64 Context=Places Type=Threshold [64x64@2/places] Size=64 Scale=2 Context=Places Type=Threshold [64x64/status] Size=64 Context=Status Type=Threshold [64x64@2/status] Size=64 Scale=2 Context=Status Type=Threshold [64x64/stock/chart] Size=64 Context=Stock Type=Threshold [64x64@2/stock/chart] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/code] Size=64 Context=Stock Type=Threshold [64x64@2/stock/code] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/data] Size=64 Context=Stock Type=Threshold [64x64@2/stock/data] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/form] Size=64 Context=Stock Type=Threshold [64x64@2/stock/form] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/image] Size=64 Context=Stock Type=Threshold [64x64@2/stock/image] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/io] Size=64 Context=Stock Type=Threshold [64x64@2/stock/io] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/media] Size=64 Context=Stock Type=Threshold [64x64@2/stock/media] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/navigation] Size=64 Context=Stock Type=Threshold [64x64@2/stock/navigation] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/net] Size=64 Context=Stock Type=Threshold [64x64@2/stock/net] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/object] Size=64 Context=Stock Type=Threshold [64x64@2/stock/object] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/table] Size=64 Context=Stock Type=Threshold [64x64@2/stock/table] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/text] Size=64 Context=Stock Type=Threshold [64x64@2/stock/text] Size=64 Scale=2 Context=Stock Type=Threshold [72x72/actions] Size=72 Context=Actions Type=Threshold [72x72@2/actions] Size=72 Scale=2 Context=Actions Type=Threshold [72x72/animations] Size=72 Context=Animations Type=Threshold [72x72@2/animations] Size=72 Scale=2 Context=Animations Type=Threshold [72x72/apps] Size=72 Context=Applications Type=Threshold [72x72@2/apps] Size=72 Scale=2 Context=Applications Type=Threshold [72x72/categories] Size=72 Context=Categories Type=Threshold [72x72@2/categories] Size=72 Scale=2 Context=Categories Type=Threshold [72x72/devices] Size=72 Context=Devices Type=Threshold [72x72@2/devices] Size=72 Scale=2 Context=Devices Type=Threshold [72x72/emblems] Size=72 Context=Emblems Type=Threshold [72x72@2/emblems] Size=72 Scale=2 Context=Emblems Type=Threshold [72x72/emotes] Size=72 Context=Emotes Type=Threshold [72x72@2/emotes] Size=72 Scale=2 Context=Emotes Type=Threshold [72x72/filesystems] Size=72 Context=FileSystems Type=Threshold [72x72@2/filesystems] Size=72 Scale=2 Context=FileSystems Type=Threshold [72x72/intl] Size=72 Context=International Type=Threshold [72x72@2/intl] Size=72 Scale=2 Context=International Type=Threshold [72x72/mimetypes] Size=72 Context=MimeTypes Type=Threshold [72x72@2/mimetypes] Size=72 Scale=2 Context=MimeTypes Type=Threshold [72x72/places] Size=72 Context=Places Type=Threshold [72x72@2/places] Size=72 Scale=2 Context=Places Type=Threshold [72x72/status] Size=72 Context=Status Type=Threshold [72x72@2/status] Size=72 Scale=2 Context=Status Type=Threshold [72x72/stock/chart] Size=72 Context=Stock Type=Threshold [72x72@2/stock/chart] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/code] Size=72 Context=Stock Type=Threshold [72x72@2/stock/code] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/data] Size=72 Context=Stock Type=Threshold [72x72@2/stock/data] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/form] Size=72 Context=Stock Type=Threshold [72x72@2/stock/form] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/image] Size=72 Context=Stock Type=Threshold [72x72@2/stock/image] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/io] Size=72 Context=Stock Type=Threshold [72x72@2/stock/io] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/media] Size=72 Context=Stock Type=Threshold [72x72@2/stock/media] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/navigation] Size=72 Context=Stock Type=Threshold [72x72@2/stock/navigation] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/net] Size=72 Context=Stock Type=Threshold [72x72@2/stock/net] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/object] Size=72 Context=Stock Type=Threshold [72x72@2/stock/object] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/table] Size=72 Context=Stock Type=Threshold [72x72@2/stock/table] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/text] Size=72 Context=Stock Type=Threshold [72x72@2/stock/text] Size=72 Scale=2 Context=Stock Type=Threshold [96x96/actions] Size=96 Context=Actions Type=Threshold [96x96@2/actions] Size=96 Scale=2 Context=Actions Type=Threshold [96x96/animations] Size=96 Context=Animations Type=Threshold [96x96@2/animations] Size=96 Scale=2 Context=Animations Type=Threshold [96x96/apps] Size=96 Context=Applications Type=Threshold [96x96@2/apps] Size=96 Scale=2 Context=Applications Type=Threshold [96x96/categories] Size=96 Context=Categories Type=Threshold [96x96@2/categories] Size=96 Scale=2 Context=Categories Type=Threshold [96x96/devices] Size=96 Context=Devices Type=Threshold [96x96@2/devices] Size=96 Scale=2 Context=Devices Type=Threshold [96x96/emblems] Size=96 Context=Emblems Type=Threshold [96x96@2/emblems] Size=96 Scale=2 Context=Emblems Type=Threshold [96x96/emotes] Size=96 Context=Emotes Type=Threshold [96x96@2/emotes] Size=96 Scale=2 Context=Emotes Type=Threshold [96x96/filesystems] Size=96 Context=FileSystems Type=Threshold [96x96@2/filesystems] Size=96 Scale=2 Context=FileSystems Type=Threshold [96x96/intl] Size=96 Context=International Type=Threshold [96x96@2/intl] Size=96 Scale=2 Context=International Type=Threshold [96x96/mimetypes] Size=96 Context=MimeTypes Type=Threshold [96x96@2/mimetypes] Size=96 Scale=2 Context=MimeTypes Type=Threshold [96x96/places] Size=96 Context=Places Type=Threshold [96x96@2/places] Size=96 Scale=2 Context=Places Type=Threshold [96x96/status] Size=96 Context=Status Type=Threshold [96x96@2/status] Size=96 Scale=2 Context=Status Type=Threshold [96x96/stock/chart] Size=96 Context=Stock Type=Threshold [96x96@2/stock/chart] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/code] Size=96 Context=Stock Type=Threshold [96x96@2/stock/code] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/data] Size=96 Context=Stock Type=Threshold [96x96@2/stock/data] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/form] Size=96 Context=Stock Type=Threshold [96x96@2/stock/form] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/image] Size=96 Context=Stock Type=Threshold [96x96@2/stock/image] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/io] Size=96 Context=Stock Type=Threshold [96x96@2/stock/io] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/media] Size=96 Context=Stock Type=Threshold [96x96@2/stock/media] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/navigation] Size=96 Context=Stock Type=Threshold [96x96@2/stock/navigation] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/net] Size=96 Context=Stock Type=Threshold [96x96@2/stock/net] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/object] Size=96 Context=Stock Type=Threshold [96x96@2/stock/object] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/table] Size=96 Context=Stock Type=Threshold [96x96@2/stock/table] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/text] Size=96 Context=Stock Type=Threshold [96x96@2/stock/text] Size=96 Scale=2 Context=Stock Type=Threshold [128x128/actions] Size=128 Context=Actions Type=Threshold [128x128@2/actions] Size=128 Scale=2 Context=Actions Type=Threshold [128x128/animations] Size=128 Context=Animations Type=Threshold [128x128@2/animations] Size=128 Scale=2 Context=Animations Type=Threshold [128x128/apps] Size=128 Context=Applications Type=Threshold [128x128@2/apps] Size=128 Scale=2 Context=Applications Type=Threshold [128x128/categories] Size=128 Context=Categories Type=Threshold [128x128@2/categories] Size=128 Scale=2 Context=Categories Type=Threshold [128x128/devices] Size=128 Context=Devices Type=Threshold [128x128@2/devices] Size=128 Scale=2 Context=Devices Type=Threshold [128x128/emblems] Size=128 Context=Emblems Type=Threshold [128x128@2/emblems] Size=128 Scale=2 Context=Emblems Type=Threshold [128x128/emotes] Size=128 Context=Emotes Type=Threshold [128x128@2/emotes] Size=128 Scale=2 Context=Emotes Type=Threshold [128x128/filesystems] Size=128 Context=FileSystems Type=Threshold [128x128@2/filesystems] Size=128 Scale=2 Context=FileSystems Type=Threshold [128x128/intl] Size=128 Context=International Type=Threshold [128x128@2/intl] Size=128 Scale=2 Context=International Type=Threshold [128x128/mimetypes] Size=128 Context=MimeTypes Type=Threshold [128x128@2/mimetypes] Size=128 Scale=2 Context=MimeTypes Type=Threshold [128x128/places] Size=128 Context=Places Type=Threshold [128x128@2/places] Size=128 Scale=2 Context=Places Type=Threshold [128x128/status] Size=128 Context=Status Type=Threshold [128x128@2/status] Size=128 Scale=2 Context=Status Type=Threshold [128x128/stock/chart] Size=128 Context=Stock Type=Threshold [128x128@2/stock/chart] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/code] Size=128 Context=Stock Type=Threshold [128x128@2/stock/code] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/data] Size=128 Context=Stock Type=Threshold [128x128@2/stock/data] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/form] Size=128 Context=Stock Type=Threshold [128x128@2/stock/form] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/image] Size=128 Context=Stock Type=Threshold [128x128@2/stock/image] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/io] Size=128 Context=Stock Type=Threshold [128x128@2/stock/io] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/media] Size=128 Context=Stock Type=Threshold [128x128@2/stock/media] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/navigation] Size=128 Context=Stock Type=Threshold [128x128@2/stock/navigation] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/net] Size=128 Context=Stock Type=Threshold [128x128@2/stock/net] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/object] Size=128 Context=Stock Type=Threshold [128x128@2/stock/object] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/table] Size=128 Context=Stock Type=Threshold [128x128@2/stock/table] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/text] Size=128 Context=Stock Type=Threshold [128x128@2/stock/text] Size=128 Scale=2 Context=Stock Type=Threshold [192x192/actions] Size=192 Context=Actions Type=Threshold [192x192@2/actions] Size=192 Scale=2 Context=Actions Type=Threshold [192x192/animations] Size=192 Context=Animations Type=Threshold [192x192@2/animations] Size=192 Scale=2 Context=Animations Type=Threshold [192x192/apps] Size=192 Context=Applications Type=Threshold [192x192@2/apps] Size=192 Scale=2 Context=Applications Type=Threshold [192x192/categories] Size=192 Context=Categories Type=Threshold [192x192@2/categories] Size=192 Scale=2 Context=Categories Type=Threshold [192x192/devices] Size=192 Context=Devices Type=Threshold [192x192@2/devices] Size=192 Scale=2 Context=Devices Type=Threshold [192x192/emblems] Size=192 Context=Emblems Type=Threshold [192x192@2/emblems] Size=192 Scale=2 Context=Emblems Type=Threshold [192x192/emotes] Size=192 Context=Emotes Type=Threshold [192x192@2/emotes] Size=192 Scale=2 Context=Emotes Type=Threshold [192x192/filesystems] Size=192 Context=FileSystems Type=Threshold [192x192@2/filesystems] Size=192 Scale=2 Context=FileSystems Type=Threshold [192x192/intl] Size=192 Context=International Type=Threshold [192x192@2/intl] Size=192 Scale=2 Context=International Type=Threshold [192x192/mimetypes] Size=192 Context=MimeTypes Type=Threshold [192x192@2/mimetypes] Size=192 Scale=2 Context=MimeTypes Type=Threshold [192x192/places] Size=192 Context=Places Type=Threshold [192x192@2/places] Size=192 Scale=2 Context=Places Type=Threshold [192x192/status] Size=192 Context=Status Type=Threshold [192x192@2/status] Size=192 Scale=2 Context=Status Type=Threshold [192x192/stock/chart] Size=192 Context=Stock Type=Threshold [192x192@2/stock/chart] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/code] Size=192 Context=Stock Type=Threshold [192x192@2/stock/code] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/data] Size=192 Context=Stock Type=Threshold [192x192@2/stock/data] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/form] Size=192 Context=Stock Type=Threshold [192x192@2/stock/form] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/image] Size=192 Context=Stock Type=Threshold [192x192@2/stock/image] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/io] Size=192 Context=Stock Type=Threshold [192x192@2/stock/io] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/media] Size=192 Context=Stock Type=Threshold [192x192@2/stock/media] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/navigation] Size=192 Context=Stock Type=Threshold [192x192@2/stock/navigation] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/net] Size=192 Context=Stock Type=Threshold [192x192@2/stock/net] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/object] Size=192 Context=Stock Type=Threshold [192x192@2/stock/object] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/table] Size=192 Context=Stock Type=Threshold [192x192@2/stock/table] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/text] Size=192 Context=Stock Type=Threshold [192x192@2/stock/text] Size=192 Scale=2 Context=Stock Type=Threshold [256x256/actions] MinSize=64 Size=256 MaxSize=256 Context=Actions Type=Scalable [256x256@2/actions] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Actions Type=Scalable [256x256/animations] MinSize=64 Size=256 MaxSize=256 Context=Animations Type=Scalable [256x256@2/animations] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Animations Type=Scalable [256x256/apps] MinSize=64 Size=256 MaxSize=256 Context=Applications Type=Scalable [256x256@2/apps] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Applications Type=Scalable [256x256/categories] MinSize=64 Size=256 MaxSize=256 Context=Categories Type=Scalable [256x256@2/categories] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Categories Type=Scalable [256x256/devices] MinSize=64 Size=256 MaxSize=256 Context=Devices Type=Scalable [256x256@2/devices] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Devices Type=Scalable [256x256/emblems] MinSize=64 Size=256 MaxSize=256 Context=Emblems Type=Scalable [256x256@2/emblems] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Emblems Type=Scalable [256x256/emotes] MinSize=64 Size=256 MaxSize=256 Context=Emotes Type=Scalable [256x256@2/emotes] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Emotes Type=Scalable [256x256/filesystems] MinSize=64 Size=256 MaxSize=256 Context=FileSystems Type=Scalable [256x256@2/filesystems] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=FileSystems Type=Scalable [256x256/intl] MinSize=64 Size=256 MaxSize=256 Context=International Type=Scalable [256x256@2/intl] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=International Type=Scalable [256x256/mimetypes] MinSize=64 Size=256 MaxSize=256 Context=MimeTypes Type=Scalable [256x256@2/mimetypes] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=MimeTypes Type=Scalable [256x256/places] MinSize=64 Size=256 MaxSize=256 Context=Places Type=Scalable [256x256@2/places] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Places Type=Scalable [256x256/status] MinSize=64 Size=256 MaxSize=256 Context=Status Type=Scalable [256x256@2/status] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Status Type=Scalable [256x256/stock/chart] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/chart] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/code] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/code] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/data] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/data] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/form] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/form] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/image] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/image] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/io] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/io] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/media] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/media] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/navigation] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/navigation] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/net] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/net] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/object] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/object] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/table] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/table] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/text] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/text] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [512x512/actions] MinSize=64 Size=512 MaxSize=512 Context=Actions Type=Scalable [512x512@2/actions] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Actions Type=Scalable [512x512/animations] MinSize=64 Size=512 MaxSize=512 Context=Animations Type=Scalable [512x512@2/animations] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Animations Type=Scalable [512x512/apps] MinSize=64 Size=512 MaxSize=512 Context=Applications Type=Scalable [512x512@2/apps] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Applications Type=Scalable [512x512/categories] MinSize=64 Size=512 MaxSize=512 Context=Categories Type=Scalable [512x512@2/categories] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Categories Type=Scalable [512x512/devices] MinSize=64 Size=512 MaxSize=512 Context=Devices Type=Scalable [512x512@2/devices] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Devices Type=Scalable [512x512/emblems] MinSize=64 Size=512 MaxSize=512 Context=Emblems Type=Scalable [512x512@2/emblems] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Emblems Type=Scalable [512x512/emotes] MinSize=64 Size=512 MaxSize=512 Context=Emotes Type=Scalable [512x512@2/emotes] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Emotes Type=Scalable [512x512/filesystems] MinSize=64 Size=512 MaxSize=512 Context=FileSystems Type=Scalable [512x512@2/filesystems] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=FileSystems Type=Scalable [512x512/intl] MinSize=64 Size=512 MaxSize=512 Context=International Type=Scalable [512x512@2/intl] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=International Type=Scalable [512x512/mimetypes] MinSize=64 Size=512 MaxSize=512 Context=MimeTypes Type=Scalable [512x512@2/mimetypes] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=MimeTypes Type=Scalable [512x512/places] MinSize=64 Size=512 MaxSize=512 Context=Places Type=Scalable [512x512@2/places] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Places Type=Scalable [512x512/status] MinSize=64 Size=512 MaxSize=512 Context=Status Type=Scalable [512x512@2/status] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Status Type=Scalable [512x512/stock/chart] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/chart] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/code] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/code] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/data] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/data] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/form] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/form] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/image] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/image] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/io] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/io] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/media] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/media] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/navigation] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/navigation] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/net] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/net] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/object] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/object] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/table] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/table] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/text] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/text] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [scalable/actions] MinSize=1 Size=128 MaxSize=256 Context=Actions Type=Scalable [scalable/animations] MinSize=1 Size=128 MaxSize=256 Context=Animations Type=Scalable [scalable/apps] MinSize=1 Size=128 MaxSize=256 Context=Applications Type=Scalable [scalable/categories] MinSize=1 Size=128 MaxSize=256 Context=Categories Type=Scalable [scalable/devices] MinSize=1 Size=128 MaxSize=512 Context=Devices Type=Scalable [scalable/emblems] MinSize=1 Size=128 MaxSize=256 Context=Emblems Type=Scalable [scalable/emotes] MinSize=1 Size=128 MaxSize=512 Context=Emotes Type=Scalable [scalable/filesystems] MinSize=1 Size=128 MaxSize=256 Context=FileSystems Type=Scalable [scalable/intl] MinSize=1 Size=128 MaxSize=512 Context=International Type=Scalable [scalable/mimetypes] MinSize=1 Size=128 MaxSize=256 Context=MimeTypes Type=Scalable [scalable/places] MinSize=1 Size=128 MaxSize=512 Context=Places Type=Scalable [scalable/status] MinSize=1 Size=128 MaxSize=256 Context=Status Type=Scalable [scalable/stock/chart] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/code] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/data] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/form] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/image] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/io] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/media] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/navigation] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/net] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/object] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/table] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/text] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [symbolic/apps] MinSize=8 Size=16 MaxSize=512 Context=Applications Type=Scalable appstream-generator-1.0.0/data/meson.build000066400000000000000000000022731524443220100205440ustar00rootroot00000000000000 # data install_data('asgen-hints.json', install_dir: 'share/appstream') install_data('hicolor-theme-index.theme', install_dir: 'share/appstream') ascli_exe = find_program('appstreamcli', required: true) metainfo_filename = 'org.freedesktop.appstream.generator.metainfo.xml' metainfo_with_relinfo = custom_target('add-metainfo-releases', input : ['../NEWS', files(metainfo_filename)], output : [metainfo_filename], command : [ascli_exe, 'news-to-metainfo', '--limit=6', '@INPUT0@', '@INPUT1@', '@OUTPUT@'], install : true, install_dir : join_paths (get_option ('datadir'), 'metainfo') ) if ascli_exe.found() # Validate our MetaInfo file test('asgen-validate_metainfo', ascli_exe, args: ['validate', '--no-net', '--pedantic', files(metainfo_filename)] ) endif # templates install_subdir('templates', install_dir: 'share/appstream', exclude_files: ['.gitignore', 'default/static/js/highlight/core.js', 'default/static/js/highlight/README.source.md', 'default/static/js/uplot/uPlot.iife.js', 'default/static/js/uplot/README.source.md'], follow_symlinks: false ) appstream-generator-1.0.0/data/org.freedesktop.appstream.generator.metainfo.xml000066400000000000000000000037341524443220100300110ustar00rootroot00000000000000 org.freedesktop.appstream.generator FSFAP LGPL-3.0+ AppStream Generator A fast AppStream metadata generator

AppStream is a metadata specification which permits software components to provide information about themselves to automated systems and end-users before the software is actually installed.

The appstream-generator tool generates AppStream metadata from the repositories of a software distribution. It currently supports the following repository formats / distributions: Debian, Ubuntu, Arch Linux, RPM-MD (Fedora, Mageia).

The generator will produce AppStream catalog metadata files in the AppStream YAML or XML format to be shipped to users, as well as a detailed HTML report about found components and HTML and JSON reports on issues detected with the scanned metadata. It reads .desktop files as well as metainfo files, renders fonts, scales images, caches screenshots etc. to produce high-quality metadata for AppStream based software centers to consume. Usually, appstream-generator is integrated with the existing software build & packaging workflow of a distribution.

https://github.com/ximion/appstream-generator https://github.com/ximion/appstream-generator/blob/master/docs/index.md https://github.com/ximion/appstream-generator/issues Freedesktop Matthias Klumpp appstream-generator
appstream-generator-1.0.0/data/templates/000077500000000000000000000000001524443220100203745ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/000077500000000000000000000000001524443220100216165ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/base.html000077700000000000000000000000001524443220100270502../default/base.htmlustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/main.html000066400000000000000000000107651524443220100234410ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Start{% endblock %} {% block header_content %} AppStream data hints for {{project_name}} {% endblock %} {% block content %}

Welcome!

Welcome to the AppStream Generator HTML pages!

These pages exist to provide a user-friendly view on the issues discovered by the AppStream metadata generator while extracting metadata from packages in the {{project_name}} archive. They can also be used to take a look at the raw metadata, to spot possible problems with the data itself or the generation process.

Select a suite

    {% for suite in suites %}
  • {{suite.suite}}

    {% if existsIn(suite, "metainfo_count") %}

    {{suite.metainfo_count}} components in {{suite.section_count}} section{% if suite.section_count != 1 %}s{% endif %}

    {% if suite.health.total > 0 %}
      {% for seg in suite.health.segments %}
    • {{seg.label}} {{seg.count}}
    • {% endfor %}
    {% endif %} {% endif %}
  • {% endfor %}
{% if length(oldsuites) > 0 %}

No longer updated

{% endif %}
AppStream Generator Logo

What is AppStream and DEP-11?

AppStream is a cross-distro XML format to provide metadata for software components and to assign unique identifiers to software.
In Debian, we parse all XML provided by upstream projects as well as other metadata (.desktop-files, ...), and compile a single YAML metadata file from it, which is then shipped to users via APT.

While the official AppStream specification is based on XML, Debian uses a YAML version of the format for easier use in existing scripts and for better archive integration. This format is called DEP-11, and initially had a much wider scope in enhancing archive metadata than AppStream had. Today AppStream covers that as well, and DEP-11 is only a YAML version of AppStream.

The generated metadata can for example be used by software centers like GNOME Software or KDE Discover to display a user-friendly application-centric view on the package archive.
It can also be used by other software to find missing plugins, codecs, fonts, etc. or simply by users to install software on any Linux distribution without knowing the exact package name.

More information

See AppStream @ wiki.d.o for information about AppStream integration and usage in Debian.
The offical AppStream specification can be found at freedesktop.org, a description of the DEP-11 YAML format is hosted there as well.

You can find the source-code of the AppStream Generator here.

Log files of the generator runs are stored in logs/, machine-readable issue hints can be found in hints/, valid metadata is located in data/ and all exported media is made available via media/.

{% endblock %} appstream-generator-1.0.0/data/templates/debian/static/000077500000000000000000000000001524443220100231055ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/static/css/000077500000000000000000000000001524443220100236755ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/static/css/highlight.css000077700000000000000000000000001524443220100353762../../../default/static/css/highlight.cssustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/static/css/style.css000077700000000000000000000000001524443220100337602../../../default/static/css/style.cssustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/static/css/theme.css000066400000000000000000000001651524443220100255130ustar00rootroot00000000000000/* Debian's own accent, everything else is inherited from the shared stylesheet. */ :root { --accent: #d70a53; } appstream-generator-1.0.0/data/templates/debian/static/img000077700000000000000000000000001524443220100277162../../default/static/img/ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/debian/static/js000077700000000000000000000000001524443220100273372../../default/static/jsustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/000077500000000000000000000000001524443220100220205ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/base.html000066400000000000000000000024251524443220100236230ustar00rootroot00000000000000 AppStream Report for {{project_name}} - {% block title %}{% endblock %} {% block head_extra %}{% endblock %}
{% block header_content %}{% endblock %}
{% block float_right %}{% endblock %}
{% block content %}{% endblock %}

Generated by appstream-generator (v{{generator_version}}).

{% block page_details %}{% endblock %}
appstream-generator-1.0.0/data/templates/default/issues_index.html000066400000000000000000000023361524443220100254140ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Hints summary for {{suite}}/{{section}}{% endblock %} {% block header_content %} ⇦ | Hints summary for {{suite}}/{{section}} {% endblock %} {% block float_right %} Last updated on: {{time}} {% endblock %} {% block content %}

Metadata processing hints found for {{suite}}/{{section}}

{% for summary in summaries %}

{{summary.maintainer}}

    {% for package in summary.packages %}
  • {{package.pkgname}}  {% if package.info_count > 0 %} Infos: {{package.info_count}} {% endif %} {% if package.warning_count > 0 %} Warnings: {{package.warning_count}} {% endif %} {% if package.error_count > 0 %} Errors: {{package.error_count}} {% endif %}
  • {% endfor %}
{% endfor %} {% endblock %} appstream-generator-1.0.0/data/templates/default/issues_page.html000066400000000000000000000033771524443220100252270ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Issues for {{package_name}} in {{suite}}/{{section}}{% endblock %} {% block float_right %} Last updated on: {{time}} {% endblock %} {% block header_content %} ⇦ | {{package_name}} [{{section}}] {% endblock %} {% block content %}

Hints for {{package_name}} in {{section}}

{% for entry in entries %}

{{entry.component_id}} {% for arch in entry.architectures %} ⚙ {{arch.arch}} {% endfor %}

{% if entry.has_errors %}

Errors

    {% for error in entry.errors %}
  • {{error.error_tag}}
    {{error.error_description}}
  • {% endfor %}
{% endif %} {% if entry.has_warnings %}

Warnings

    {% for warning in entry.warnings %}
  • {{warning.warning_tag}}
    {{warning.warning_description}}
  • {% endfor %}
{% endif %} {% if entry.has_infos %}

Hints

    {% for info in entry.infos %}
  • {{info.info_tag}}
    {{info.info_description}}
  • {% endfor %}
{% endif %} {% endfor %}
{% endblock %} appstream-generator-1.0.0/data/templates/default/main.html000066400000000000000000000067451524443220100236460ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Start{% endblock %} {% block header_content %} AppStream data hints for {{project_name}} {% endblock %} {% block content %}

Welcome!

Welcome to the AppStream Generator HTML pages!

These pages exist to provide a user-friendly view on the issues discovered by the AppStream metadata generator while extracting metadata from packages in the {{project_name}} archive. They can also be used to take a look at the raw metadata, to spot possible problems with the data itself or the generation process.

Select a suite

    {% for suite in suites %}
  • {{suite.suite}}

    {% if existsIn(suite, "metainfo_count") %}

    {{suite.metainfo_count}} components in {{suite.section_count}} section{% if suite.section_count != 1 %}s{% endif %}

    {% if suite.health.total > 0 %}
      {% for seg in suite.health.segments %}
    • {{seg.label}} {{seg.count}}
    • {% endfor %}
    {% endif %} {% endif %}
  • {% endfor %}
{% if length(oldsuites) > 0 %}

No longer updated

{% endif %}
AppStream Generator Logo

What is AppStream?

AppStream is a cross-distro XML format to provide metadata for software components and to assign unique identifiers to software.
In {{project_name}}, we parse all XML provided by upstream projects as well as other metadata (.desktop-files, ...), and compile a single metadata file from it, which is then shipped to users.

The generated metadata can for example be used by software centers like GNOME Software or KDE Discover to display a user-friendly application-centric view on the package archive.
It can also be used by other software to find missing plugins, codecs, fonts, etc. or simply by users to install software on any Linux distribution without knowing the exact package name.

More information

The offical AppStream specification can be found at freedesktop.org.

You can find the source-code of the AppStream Generator here.

{% endblock %} appstream-generator-1.0.0/data/templates/default/metainfo_index.html000066400000000000000000000016741524443220100257070ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Components in {{suite}}/{{section}}{% endblock %} {% block header_content %} ⇦ | Components summary for {{suite}}/{{section}} {% endblock %} {% block float_right %} Last updated on: {{time}} {% endblock %} {% block content %}

Metadata for {{suite}}/{{section}}

{% for summary in summaries %}

{{summary.maintainer}}

    {% for package in summary.packages %}
  • {{package.pkgname}} 
      {% for component in package.components %}
    • {{component.cid}}
    • {% endfor %}
  • {% endfor %}
{% endfor %} {% endblock %} appstream-generator-1.0.0/data/templates/default/metainfo_page.html000066400000000000000000000024551524443220100255120ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}{{package_name}} in {{suite}}/{{section}}{% endblock %} {% block head_extra %} {% endblock %} {% block header_content %} ⇦ | {{package_name}} [{{section}}] {% endblock %} {% block float_right %} Last updated on: {{time}} {% endblock %} {% block content %}

Metadata for {{package_name}} in {{section}}

{% for cpt in cpts %}

{{cpt.component_id}} {% for arch in cpt.architectures %} ⚙ {{arch.arch}} {% endfor %}

Icon
{% endfor %}
{% endblock %} appstream-generator-1.0.0/data/templates/default/section_page.html000066400000000000000000000211121524443220100253430ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Data for {{suite}}/{{section}}{% endblock %} {% block head_extra %} {% endblock %} {% block header_content %} ⇦ | AppStream data for {{project_name}}/{{suite}}/{{section}} {% endblock %} {% block float_right %} Last updated on: {{time}} {% endblock %} {% block content %}

Overview for {{suite}}/{{section}}

Data

Issues - Issues found while extracting the data

Metainfo - Per-package view of the generated data

Health

{% if existsIn(health, "total") and health.total > 0 %}
    {% for seg in health.segments %}
  • {{seg.label}} {{seg.count}}
  • {% endfor %}

Each of the {{health.total}} components we tried to generate is counted once here.

{% endif %}
  • {{metainfo_count}} valid components
  • {{error_count}} errors
  • {{warning_count}} warnings
  • {{info_count}} infos/hints

History

Components

How many components we generated, and how many of them came out without a single warning.

Issues

How often each kind of issue occurred. A component can raise several issues, so these do not add up to a number of components.

{% endblock %} appstream-generator-1.0.0/data/templates/default/sections_index.html000066400000000000000000000032001524443220100257170ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}Data for the {{suite}} suite{% endblock %} {% block header_content %} ⇦ | AppStream data for {{project_name}}/{{suite}} {% endblock %} {% block float_right %} Last updated on: {{time}} {% endblock %} {% block content %}

Select an archive section in "{{suite}}"


    {% for section in sections %}
  • {{section.section}}

    {% if existsIn(section, "metainfo_count") %}{{section.metainfo_count}}{% else %}–{% endif %} valid components

    {% if existsIn(section, "sparkline") %}{{section.sparkline}}{% endif %}
    {% if existsIn(section, "health") and section.health.total > 0 %}
      {% for seg in section.health.segments %}
    • {{seg.label}} {{seg.count}}
    • {% endfor %}
    {% endif %}
  • {% endfor %}
{% endblock %} appstream-generator-1.0.0/data/templates/default/static/000077500000000000000000000000001524443220100233075ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/static/css/000077500000000000000000000000001524443220100240775ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/static/css/highlight.css000066400000000000000000000035231524443220100265630ustar00rootroot00000000000000/* github.com style (c) Vasily Polovnyov */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f5f5f5; } .hljs-comment, .hljs-quote { color: #998; font-style: italic; } .hljs-keyword, .hljs-selector-tag, .hljs-subst { color: #333; font-weight: bold; } .hljs-number, .hljs-literal, .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr { color: #008080; } .hljs-string, .hljs-char, .hljs-doctag { color: #d14; } .hljs-title, .hljs-section, .hljs-selector-id { color: #900; font-weight: bold; } .hljs-subst { font-weight: normal; } .hljs-type, .hljs-class .hljs-title { color: #458; font-weight: bold; } .hljs-tag, .hljs-name, .hljs-attribute { color: #000080; font-weight: normal; } .hljs-regexp, .hljs-link { color: #009926; } .hljs-symbol, .hljs-bullet { color: #990073; } .hljs-built_in, .hljs-builtin-name { color: #0086b3; } .hljs-meta { color: #999; font-weight: bold; } .hljs-deletion { background: #fdd; } .hljs-addition { background: #dfd; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } /* * The theme above is written for a light page. Rather than carrying a second full theme, * we only re-point the surface and the few tones that would otherwise be unreadable on a * dark one; the syntax hues themselves stay as they are. */ @media (prefers-color-scheme: dark) { .hljs { color: #d6d5cd; background: var(--surface-sunken); } .hljs-comment, .hljs-quote { color: #8b8b80; } .hljs-keyword, .hljs-selector-tag, .hljs-subst { color: #d6d5cd; } .hljs-title, .hljs-section, .hljs-selector-id { color: #e0a3a1; } .hljs-string, .hljs-char, .hljs-doctag { color: #e88f8d; } .hljs-type, .hljs-class .hljs-title { color: #86b6ef; } } appstream-generator-1.0.0/data/templates/default/static/css/style.css000066400000000000000000000254161524443220100257610ustar00rootroot00000000000000:root { color-scheme: light; /* One column for the whole page. The header bar's contents and the page body share it, so the title in the bar and the headings underneath it sit on the same axis. */ --content-width: 1180px; --surface: #ffffff; --surface-sunken: #f5f5f5; --surface-raised: #f8f8f8; --text-primary: #333333; --text-secondary: #52514e; --text-muted: #898781; --link: #337ab7; --hairline: #e3e3e3; --gridline: #e1e0d9; --accent: #0a630d; /* Darkened from the usual status green so that it stays apart from the warning amber it sits next to in the health bar - the two are the classic red-green confusion pair and were indistinguishable for protanopia at the lighter step. */ --sev-clean: #0a7a0a; --sev-info: #2a78d6; --sev-warning: #c98500; --sev-error: #d03b3b; /* The component totals - in the chart and on the sparkline. A count of everything we have is not a severity, so it is painted outside the severity colours and cannot be taken for one of them; green stays reserved for "clean" on both the bar and the chart. */ --chart-total: #7c3aed; --code-bg: #f9f2f4; --code-fg: #c7254e; } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; --surface: #1a1a19; --surface-sunken: #232322; --surface-raised: #232322; --text-primary: #e6e5df; --text-secondary: #c3c2b7; --text-muted: #898781; --link: #6da7ec; --hairline: #383835; --gridline: #2c2c2a; --sev-info: #3987e5; --sev-error: #e34948; --chart-total: #9d5ce8; --code-bg: #2c2226; --code-fg: #e87ba4; } } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:100%; height:100%; } body { border-sizing: border-box; font-family: Cantarell,"Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; line-height: 20px; color: var(--text-primary); background-color: var(--surface); margin: 0; height: 93%; } a { color: var(--link); text-decoration: none; background-color: transparent; } .headbar { border-radius: 4px; display: block; border: 1px solid transparent; margin-bottom: 20px; min-height: 50px; position: relative; background-color: var(--surface-raised); border-color: var(--hairline); border-width: 0 0 1px; z-index: 1000; border-radius: 0; margin-bottom: 14px; } .headbar-inner { box-sizing: border-box; max-width: var(--content-width); margin: 0 auto; padding: 0 1em; display: flex; align-items: center; justify-content: space-between; /* On a phone the title and the timestamp cannot share a line - let the timestamp drop below it rather than squeezing both. */ flex-wrap: wrap; gap: 0 15px; } .headbar-content { font-size: 18px; line-height: 20px; padding: 15px 0; } .headbar-content-right { font-size: 14px; line-height: 20px; padding: 15px 0; } .content { box-sizing: border-box; max-width: var(--content-width); margin: 0 auto; padding: 0em 1em 0em 1em; } .wrapper hr { border-top: none; border-bottom: 1px solid var(--hairline); margin-bottom: 1em; } img.fit { max-width: 99%; max-height: 99%; } hr { border-top: none; border-bottom: 1px solid var(--accent); margin-bottom: 1em; } footer { text-align: center; margin-top: 1em; } span.avoidwrap { display: inline-block; } .backlink { font-size: 18px; color: var(--text-primary); text-decoration: none; } .infobox { border-color: var(--hairline); border-image: none; border-radius: 3px; border-style: solid; border-width: 1px 1px 1px 5px; margin: 20px 0; padding: 20px; } .infobox h2 { margin-bottom: 5px; margin-top: 0; } .infobox p:last-child { margin-bottom: 0 } .infobox-hint { border-left-color: var(--sev-info); } .infobox-hint h2 { color: var(--sev-info); } .infobox-error { border-left-color: var(--sev-error); } .infobox-error h2 { color: var(--sev-error); } .infobox-warning { border-left-color: var(--sev-warning); } .infobox-warning h2 { color: var(--sev-warning); } /* label styles copied from Bootstrap */ .label { border-radius: 0.25em; color: #fff; display: inline; font-size: 75%; font-weight: 700; line-height: 1; padding: 0.2em 0.6em 0.3em; text-align: center; vertical-align: baseline; white-space: nowrap; } .label-info { background-color: var(--sev-info); } .label-warning { background-color: var(--sev-warning); } .label-error { background-color: var(--sev-error); } .label-neutral { background-color: #777; } .overviewlisting a { color: var(--text-primary); text-decoration: none; } .overviewlisting li { padding: 2px 4px 2px; } code { background-color: var(--code-bg); border-radius: 4px; color: var(--code-fg); font-size: 90%; padding: 2px 4px; } .well{ min-height:20px; padding:19px; margin-bottom:20px; background-color:var(--surface-sunken); border:1px solid var(--hairline); border-radius:4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05); box-shadow:inset 0 1px 1px rgba(0,0,0,.05) } .well pre { overflow-x: auto; } .progress { background-color: var(--surface-sunken); border-radius: 4px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset; height: 20px; margin-bottom: 20px; overflow: hidden; } .progress-bar { background-color: #337ab7; box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15) inset; color: #fff; float: left; font-size: 12px; height: 100%; line-height: 20px; text-align: center; transition: width 0.6s ease 0s; width: 0; } .progress-bar-blue { background-color: var(--sev-info); } .progress-bar-green { background-color: var(--sev-clean); } .progress-bar-yellow { background-color: var(--sev-warning); } .progress-bar-red { background-color: var(--sev-error); } .sr-only { border: 0 none; clip: rect(0px, 0px, 0px, 0px); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } .permalink { font-size: 75%; color: var(--text-muted); line-height: 100%; font-weight: normal; text-decoration: none; } /* --------------------------------------------------------------------------- * Section health & trend display * ------------------------------------------------------------------------ */ .healthbar { display: flex; width: 100%; height: 30px; gap: 2px; border-radius: 4px; overflow: hidden; background-color: var(--surface-sunken); } .healthbar-segment { height: 100%; min-width: 2px; font-size: 11px; line-height: 30px; text-align: center; color: #ffffff; white-space: nowrap; overflow: hidden; } .healthbar-segment-clean { background-color: var(--sev-clean); } .healthbar-segment-warning { background-color: var(--sev-warning); } /* Rejected is the only red in the bar, so it needs nothing to set it apart. */ .healthbar-segment-rejected { background-color: var(--sev-error); } .healthbar-legend { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 10px 0 0; padding: 0; list-style: none; font-size: 13px; color: var(--text-secondary); } .healthbar-legend li { display: flex; align-items: center; gap: 6px; } .healthbar-legend .swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; } .healthbar-legend .count { color: var(--text-primary); font-weight: 600; } /* One section at a glance: the number, where it is going, and its health. */ .stat-tiles { display: flex; flex-wrap: wrap; /* tiles are as tall as they need to be - one with nothing to report yet should not be stretched into a big empty box by its neighbours */ align-items: flex-start; gap: 16px; margin: 0 0 1.5em; padding: 0; list-style: none; } .stat-tile { flex: 0 1 420px; min-width: 260px; position: relative; padding: 16px 18px; border: 1px solid var(--hairline); border-radius: 6px; background-color: var(--surface); } .stat-tile-label { font-size: 22px; margin: 0 0 6px; } .stat-tile-label a { color: var(--text-primary); font-weight: 600; text-decoration: none; } .stat-tile-label a::after { content: ""; position: absolute; inset: 0; border-radius: 6px; } .stat-tile:hover { border-color: var(--text-muted); background-color: var(--surface-sunken); } .stat-tile:focus-within { outline: 2px solid var(--link); outline-offset: 2px; } .stat-tile-figure { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; } .stat-tile-count { margin: 0 0 12px; font-size: 14px; color: var(--text-secondary); } .stat-tile-value { font-size: 17px; font-weight: 600; color: var(--text-primary); } .stat-tile .healthbar { height: 14px; } /* Suites that are not processed any more: still reachable, but not competing for attention with the ones that are. */ .suite-archived { display: flex; flex-wrap: wrap; gap: 6px 10px; margin: 0 0 1.5em; padding: 0; list-style: none; } .suite-archived a { display: inline-block; padding: 3px 10px; border: 1px solid var(--hairline); border-radius: 999px; color: var(--text-secondary); font-size: 14px; text-decoration: none; } .suite-archived a:hover { border-color: var(--text-muted); color: var(--text-primary); } .sparkline { display: block; margin-left: auto; overflow: visible; } .sparkline polyline { fill: none; stroke: var(--text-muted); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } .sparkline circle { fill: var(--chart-total); stroke: var(--surface); stroke-width: 2; } /* --------------------------------------------------------------------------- * Trend charts * ------------------------------------------------------------------------ */ .chart-panel { margin: 0 0 1.5em; } .chart-panel h3 { margin: 0 0 2px; font-size: 15px; font-weight: 600; } .chart-panel-hint { margin: 0 0 8px; font-size: 13px; color: var(--text-muted); } .chart-fallback { padding: 12px 14px; border: 1px solid var(--hairline); border-radius: 4px; color: var(--text-secondary); font-size: 14px; } .u-legend { font-size: 13px; color: var(--text-secondary); } .u-legend .u-marker { border-radius: 2px; } .u-legend .u-value { color: var(--text-primary); font-variant-numeric: tabular-nums; } .u-select { background: rgba(51, 122, 183, 0.14); } appstream-generator-1.0.0/data/templates/default/static/css/theme.css000066400000000000000000000000771524443220100257170ustar00rootroot00000000000000/* * Placeholder for custom overrides of other templates. */ appstream-generator-1.0.0/data/templates/default/static/img/000077500000000000000000000000001524443220100240635ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/static/img/asgen.png000066400000000000000000000365031524443220100256750ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYs tEXtSoftwarewww.inkscape.org<?ٔ >}ϟھww{*pc\ǹ9q2'x,ظyM~RW,ԕR~97zf"< sӝx 1uQ~'C~?oIYDIĺ u[o /8{RA-\>e{loӘO^k{J{1_!u Qyیk }O!y:O]Y}oڨ8{T==KnU"D ӭFtn?>==ܕl{{7zQb(gMԼqt@kP;L>$wmOUvh!ڱhѝhZzD!}=d%R.M4)ԘϵLS)(wy艴,j>cmzs9n=,N=~OfS(Q#iACRdD.A)`'oF}9~)c!k^P1#Qϴ Ϩ+RiY u=Nw$Z#+CR*ٵy A3\,Y^usm4<-@I]#ԜFK IS$Q37̀pr2x+"߆; /Q+~^OE3gH{ΜB[N/):Bt2/N>:pӈ1/Ԑw-ҏةSd N7F%zR,} 0Zz)&!8IOZsk60_xH{=)>qM5"jCL1rlk[WHJ.mV%6}5cGcV`MO&!CM}YWN8!ft3Hq年.fs1;=#TJM1W R4)F,>;oIJ8]W$`7H'N<,gK?:9,+RO-,~h+q>2O\ gΜyxر"nCǦp{BjՄ<:ΰ;ͯ 575ǎ} Ɂig5@Sn1S&Φ}-9tLNN.<2== @1BD/ etuOOMzP7E}7=!=:i\looނ?G F˻'gL.~hZ>sHh!$hg{_>zte;yA3=υf#{ԤBUS裩=hZrh'A^Y=S!e; m 0HMn}uBڹRx/}-sr$_%Lxdj5Tz:<5e~6[4MhZzdӃ<*AtD4{Vb١%BjH㓓MM!|zфǎ7Z \|s#?EH %!}]!(brV%Yvd ;#!}2Kgq<81gONU_g1co4BccK*E-b1D--U67vՅzzdp9xהLAʉY4"vm!U4#vs-ߵET^esPzuZ+ѣG_1q(NMAJ“QRn.*!aMy/&'P8pd7D4t"iRH*/e7 ?`7 wݨbpz[%9@t'bfY IJB[֘br0\9!۷lTvpg!H{=OPFxZ1J$sbVvB, Bn!17CL.^ ^r^Q=7}W9vhى<:K)Rz!Rsgw.⧳UJJ/. uĄALKCUPoTc>x8N 67II.G"2IIIPݻQJ~>!f,mB%hjB{29F}xLPHp_ZĮSݪ+"K|t'38rf c < D`RSQ\f&jH(?K}@$$BB'y(%%PȐ\^46.N|f ( iW%$4)qq`Rq!܄ y110|>ʂldlL!DĬ߅}{@w7\H5)1b2+G^0g^PDKBJ. &#(6k|y<=^ 8g`q nAWLXA3&SA̡w"(HVdymsNLoj"NEqYYhqbƾSK_>I[IGLA#T@„&*x'>]%jR XOTWD2h^+^.oxak\?(1[uތŮKS( Ȯ@Ðѫ ng?Ev+2 1/m\4800D[H%2ގF ' Y9K-($EB !V 6 ;5dsU>b5͍9U3o{D 1aO&:hcR2%`k7޸k|CY9]uȹ2ƵzuDq792)Ku س #ךĘGSWSA'ڈz(N̡gpr 5إB)g:e]Sw'9W䃘eݳV2`ƃ&wV64H!Qڳ$\=#-޿'矧 3gCo)#"^+l n lpoGki ρ^E)$A zj{DC@=3/7H@I[ɹ>x {SBJƂg"sfK|K^ssȒ=}S[kU{(yt5!-ҒC-Lb[ŮCxխl%]rZs#5* ?䷘mMK[ >5 qZɠu>\4R},)sȒٓkFC:E]R'zMr,&ƽJSj؝l]!e w_{kjOgE^kL?ZhFHMw\Ư q3p(ҩfIKy̙Og$ӧNļ&< #%/rnM 3 2N%!*ݰu&-ɋGzT2$ElB((Ufr5TÚ.|2˴kIrmCܶNA2=}Ộu+~B꼵!/r1a"9HDSăl\n 9LO7Xr' d8{cjK?E~GSI%A!iHIWBY<i&?\=EU 6_Ϭ Ud\'I##xAc IL$_n7x\1LM=Qūnh%%hQRM5"]]':5J+ȣ[Rc=$-ԛ(!^6` A{\ bpŔQSƊI$C}{Qd Ԃa{u 0b\D&(#N; 9{]!{Mhm3Ay* ,^֜b>˸l9`*؏otM ؘK,GkXLNk܈rrl>iWT3dUѱ W R+a7n+Kz^Y״"c)ʩ5-Id\ d!@K4SvH~pz@dwt*Tb'cj2}JR j]6I$r~GSt'蹖vx78'eKi}=}kC{.1_nMWr1>D<Vf#Yo 8hSv޹e:{!{Xh IsZM1 /!XWw3Q1xw5eZ~ I"7 d3VޑX kk)^~\9GGW1Վ8bULy>z=z}gNB>vg' O@9E TSN, bB]֣yD*!`WVVefɹ6ԎDohyNpb}#>BNx($['"k;VNL RMcbYBGlXwx#!nvKLH5)xS.VZY'($$ B(WJ00 f,x_I21E"@osD F^F[L<Ԙ!F30JL +T)_b'h,֒2eݳR!1$2} n"8kHl f|̰o*LOOo TSWd`fS=}}?xsBB 9:륷ICN2 * TǽE6 G=7}*9>"yI D9 S~5xInq14ttYg(VI$ H b6Bb\Zśn6C*&DGQx): 9 \VuJU$ݮ..sݗ}_s?>[ʰY? ygƀ DL?IgD|T4tut1ۑ`i^k(;(*!)MA%:v=*JK(@ĆdRz`5Nk&16 ܔVQJ(Ax\8{n,]5!s /`,xbјxSs)uCgei#HĬimEd76B*q=0(H^]o9!R* &5Ѡ~'I3dI~5vA/`ZsqA3[StAjSՖ}0<,Yޞ#:XH1ڏ55[܂IdPwG EN)*!ɤԐWZC=MȽ^#J ^W#lܽllٸ`}_ X@Oʠ]%nƂQ%mQxzAs=Q=0j.fjAhh@ǘJ5N*kɑ\ڙ#sb`.|/G^ه!5 B9 vjJ)ë=뵎(HuQ~cp^:UoNQOvtJ5EN$&8lsjl RHC|TV_OHk.xV63 ~M)&v]rR.y!4~b!^4BRT Ѱ?K&隸Uʅqϗ~kí3ULŜR<t9ƍʉ$uL9{{hJ3 fC(v8iACxҗ9r'BKLuqm-!%X[RAڕm,kTmAN ;sjrNFQQoaP䘔Yur-(׈vQ I\9NT\C$Ismi*d?˄xnx(@[w= ySŒ)teO1<+>ub bp!JQ%7H%f(YM*lF *b]،آ^R!Yr!󈵒pvK(ؑHdjNZ$3r˭W>Љz{dz{{q/d>z#ׅʈBQ]HZa9,f~U*9-I~mmKf8Y=ґ'}]l"hϧ,ݫqH+Jj +mcA |׸6ؾ] SM.a$b˷v לszc)=YH0CN;m!ԳʩʫDWA/ jV׀Kn7CQ}YpK[d׷!feM%K*&g2fwGx`9qdbbu9*Wzα9l7{-pGn3)-eܑcefVɢBl$SF7]JɅԒ %XXMNnKw-b#=={{z@H&f70r!If=;u!dIUnUկ2KJb 2U@7$,2(B \D zFLu*׆Z'޻#?i/_?fΒq]J/vGw/!)bo6rԄ!uV[ڜ9bqpDrq4x0LLL@b%5$]}_ EI# iS)&+LɺR$p-!RPɌGmm܊mmBL.db"w9C;JQ U FW5W'JAj]![eE9(.R mըَ%nF`n6x[@@*f$ol%5c. B^&]Ebv!z2x!S!V.WVHEMNZ$?Dgt6db*MIEEI])}mpn DR'J%f|\DM!Z.Ud#25t1(pW:1|C-468s+_b&&#QRc4!B)ꯄ:S$ cKL(W3yBH?!3O)ё݈H(3Uז N 2rWT6Crmm%w211M P:QR6CO/ #mBBD[a3]}>,T{W@M eR))n5DCl( z 5#VVqBB$)&p3o=i[e%rrfnƢ40Rى2[PRS3ЂҚ[PfKu>:L% IȎB5݈۾BEPEU'!Ϯ>kUkcH>Xo,:v~Q"[IIC droBŵȅoT{B$h縳hHP9-.vrfe=\XYC$*tohB+J+t-l*[rE5+SlOA8ҐRHa;mW-U4Իw[xWd(fȰCٽ oLDz"Bnex${Ŷ[D,bDj U";ed]`cGAAy}GjY)/pm,O*jWUF"[WzIP3Nj lOEI =Hcݪ0{CGw-%Zق\2+cT6b~/+WmC"R!beVl(@x *Z2*2,b%9"䔕şrssT(QՍ%A6(-_ :Au'G}mAzqTbbmi(% q%o+4!ǘJ9~!8sm4Ჺ ?Ewy.1DMUS!( 1|E i>%(=DBNSI[m+r-"4Ф^b"{YiCpXjVYXJ"b%5R(Șa>1g0cJ{KET#e s^? /<'@$DoǴII.vk$|6])]A6d>҆EEzTώ^ Sccb[) HS k{qPIΙ9 gf՜ VYcRu,6UҨ!D-AMʨfG jjEqb J٢6rH6~8!6ThXBkA1rHa!.eS"6*i Ou͇/C)7T QhQ Yp-b;9nM-IT\*2!"Jဋ8d3B}ȉXsA$ 9,fHN,RDR`Ԫ#ZT~ת66Y?Ӑԋ) Ł>h3&PU+VuɆjA\ c7G'!;}n[\ gnl1OچČ3ALWH2h aU۬*o>؆Yl(cr)vB9}&f CEIv@C2#f,gˍqߴmBZ 9]Kul(A.ngC;B0>y!-ܬj/WN)|x D4MI/g/K6 ~fQ^D}NDфLmݬVu^fHʉEP"\\X7Е,Q݈LSg lK^KVrò 7vr[N ^}S]R.5ȵ iYWyG6nF̨xČMf=}KZN/ƈ&'r,3E*\l=6-咥mR)1K.G dbLJBL~2ҳh7x\" :;< yЊ׽E8B+ďf g$]t[ɢE߻HwzMXZB*#Ӊ}Z Rws]N"\9YFBd2 ^FxӼe& =)`qNzMw,QlkNԐRmN r[1:H1/?KY2ՓS3oEB`_07őL/gcb[ɶtx~}N1%.kFID7 Fs-3r霋ԗnXx˦DTH%_f^0/ٶhzjth2Sd.fVL]Iv[+GΈF4)ID$K;rr^z'h+!&b` %'E4"QŶDҀd3O#g\/ɛҭx2{[frnD+= kjFrU,:M% !q,!!S/Xe̵y'nhJx'ă>< hJyL(GZoF  /V53>+6k(u< 4pVvX 5(| FK&^ux %zL7OtNh n-9Ly5q ٿ6m0= JSr߶e B a[~`0-OK_HqBN$dR 5bJgn~S:OK0(Ad[A!@[_*-cJcJ-~)$_-ə%eTaR[өs~uJP(%գLš5}9nd>PDp9LGm"29lƂq'G6˱q-b#S,U6m`1!ᎹPWw_`>pL{+]@4r_WIp)$Id\'lk||t\6+P(~r/%842/[ $:/?eU`R!k $ӽ"/PH*}噲J'[)͍)tfuXYL-w;(O+IxJVý$10y676ؠ|uO\c$*Ŏ8#]l ^6KGC2"w?p(W',M)vBi 0W謍=x#K6m/}&)YCI[il[-\*ʹheYwku7-]rL ~5? tC66-s8*%hDL&GpGtTS$zR: ,ّnm lHe7I(ZLyEhg-B5Xo1x.SM/! hOGe&ZDf9|&?%Lr <&uOE@SW$N%*{|Й,,:b2y=W2iAI7X&T |0-S#]>~ (VTAeb闠wD(P@bÀᑒrۈl 8]`I(HN]B˹4gRR64a`RhXIZY43yG[k OyI=o^9%P pBZ20O|?H zA lńtdo%::DL7 & ~!ODZ"-sU)_VG29}5Be,2ixP1"a)Q*N=a*X^ ȼA %m?rIENDB`appstream-generator-1.0.0/data/templates/default/static/img/favicon.png000066400000000000000000000101311524443220100262120ustar00rootroot00000000000000PNG  IHDR00WbKGD pHYs tIME 9}IDATh͚yUu?;!/sBhC(# ,"˶J:DYjKavYD*ZDF1U)$( C< wtRe[wsg߽Ne_>L7d+Z_f8}޵T럪,w<-~ JVu gbY(TJcT iAgr /sJW5.V8@UU^I*~zsW|RFDDUwc#Dշ; iJբE$Isc 0P(P&ʀؑx`$Nﶰ.aY't3c7v}N6mϘ/ AZGd5Ʃ2^208&u,9 "G. ,ctwujٚպ"-S Y^}w-[i&">O^g׾}l"̈"RBbDu?y\|RD14[RD>Pmuhb"2+i^AN}Fyznno:@,U^s4FU0p",@}rrOP |VM1^t݆&Sc{l*gYf /pjRpQ$= ēccIwͲyȱ 0 wՂz>g2M LgR9)l--J-'ƴ?ۊj6J6DqYdnڵZZztM4e`p,䛝eCCnhDѽs5ᑑ CCygx N? 'Hwfv "5Zz3ñW1atm;}kr ƘyS&uF'&@Ģ 7ݴxiG=c>gSbVtRuz˃O:_ c~X=~=wwxڙ9H-xu:/NLZ<^1c?=,XI\ߊ%QHL#cce۶EY}b霹7[_d~ "ABP* TIrJtm1m߾$˲llΝ$IKW,EDNJ]+'-/1ƄkX8y UU]h5SoEFc(oj:W(`DQĊ)- K{{,1oW7Q[Y#Tc9 T92k2ɡ=z(řE9p$MyѲeÐ(QyҰs|k [m\mspnCH3$!91*=o~ |.4:3@G)`8>Z!:Dex1a;En:c&O/k:޶۳cazqf;v(6= S@U}MaA`lVWi8I84x*0dڅNsh&&pcULwCHTe yޞy DV*cNLPK2~=\Fj- XUrg-W~+.U^ t51QT޳{uۆ^#A+ejC'<٧Pݺ# JbǀtZe:v71l/Mso]k3rfj;_D>%Q8Ř|ƪo^ U~)q?{{ǏikoitQWLoWc*˺4ǡj%<_?rM5.W3q\OPM 5ƛCni(Zks)1m` t+z] $i\P-LRR=zzǬ+D9͈sl^rqz˛߽_ v^N9%s&d& msR [EjLDl,ǭֽΘwa ըHF݁ϢgqFt[n`Ĭ!|}YV f766[`m;1ڜgSmwQ$D#Y(/L}9 ᛌKK}֢hS([fNDwS<Ү#MV?29*t,6yLxGƥ4}f58MOw}w}4!{1gzǜU ͲQSApQL3pDXɌnM6-!3n2pΎ:O6X%Hj ߵ CYuQ3Z7kߡbzŘ25shc-\ksߟdAr> Y]6fݪZuIvGƍltx`sSu6Yc>{mg˥&,HE20@ y\B$(`rH|`ƑK;fK'6^JXZe"Ԍ ɝмypY~pvfT[h+%鯲$zjolΚ~ПSvaX|[fc+]9L)ISmx&?Zw7ݰnol"\yC9Hƙ.M ;4l﵎ iNGyRFe# Ή5LGg . c9+/W/nm+FJ 䑩)Ui_p?ciduUӕulש)SNN>Klt#ⷹLqVSlRۙIENDB`appstream-generator-1.0.0/data/templates/default/static/img/no-image.png000066400000000000000000000017521524443220100262720ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs P%tIME6jǠOOϠOMJ(rx)E,-ڔ+=IxI! ^2)!Ps `9s"@"wY:JO40|(DKBFǦ^3I=UmBl|F*E^3s/o?FW: ߤ׈pE.f ]_p~U!k7U/c(#'&f5 ɱo"ޖ0 P3A20wE 0pBlS+"P G`9pM/٬60.P |EInY pV3!7ׇͰm³;tMC|@X7%9]`Җ׏'l$Qn`2M+ǣU 4sLTV(cnCi%+" Jma? |S>$WI2KOXk8(}" "$E EȩPǿ OYB-3W '@Jc1NğYCjUi,hjO]I$^A~C<Aq4ha_#G|< QOU)#r {X:a-|m+!qU8i;[-x-xl-U)b)hfY͢_)"!dƒ}IENDB`appstream-generator-1.0.0/data/templates/default/static/js/000077500000000000000000000000001524443220100237235ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/static/js/highlight/000077500000000000000000000000001524443220100256725ustar00rootroot00000000000000appstream-generator-1.0.0/data/templates/default/static/js/highlight/LICENSE000066400000000000000000000027521524443220100267050ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2006, Ivan Sagalaev. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. appstream-generator-1.0.0/data/templates/default/static/js/highlight/README.source.md000066400000000000000000000016541524443220100304560ustar00rootroot00000000000000# highlight.js (vendored) This directory contains a trimmed, verbatim copy of [highlight.js](https://github.com/highlightjs/highlight.js), used to colour the metadata dump on the per-package pages. | | | |---|---| | Upstream | https://github.com/highlightjs/highlight.js | | Version | 11.12.0 | | Git tag | `11.12.0` (commit `f7f7d3803bd898e37c017ffb881317f0cde04a70`) | | License | BSD-3-Clause - see `LICENSE` | ## Updating 1. Pick a release from https://github.com/highlightjs/highlight.js/releases. 2. Fetch both npm packages at that version and replace, from them: * `@highlightjs/cdn-assets`: `es/core.min.js` → `core.min.js`, `es/core.js` → `core.js`, `LICENSE` → `LICENSE` * `highlight.js`: `es/languages/xml.js` → `xml.js`, `es/languages/yaml.js` → `yaml.js` 3. Update the version, tag and commit in the table above. 4. Check that `static/css/highlight.css` still covers the classes the new version emits. appstream-generator-1.0.0/data/templates/default/static/js/highlight/core.js000066400000000000000000002251731524443220100271720ustar00rootroot00000000000000/*! Highlight.js v11.12.0 (git: f7f7d3803b) (c) 2006-2026 Josh Goebel and other contributors License: BSD-3-Clause */ /* eslint-disable no-multi-assign */ function deepFreeze(obj) { if (obj instanceof Map) { obj.clear = obj.delete = obj.set = function () { throw new Error('map is read-only'); }; } else if (obj instanceof Set) { obj.add = obj.clear = obj.delete = function () { throw new Error('set is read-only'); }; } // Freeze self Object.freeze(obj); Object.getOwnPropertyNames(obj).forEach((name) => { const prop = obj[name]; const type = typeof prop; // Freeze prop if it is an object or function and also not already frozen if ((type === 'object' || type === 'function') && !Object.isFrozen(prop)) { deepFreeze(prop); } }); return obj; } /** @typedef {import('highlight.js').CallbackResponse} CallbackResponse */ /** @typedef {import('highlight.js').CompiledMode} CompiledMode */ /** @implements CallbackResponse */ class Response { /** * @param {CompiledMode} mode */ constructor(mode) { // eslint-disable-next-line no-undefined if (mode.data === undefined) mode.data = {}; this.data = mode.data; this.isMatchIgnored = false; } ignoreMatch() { this.isMatchIgnored = true; } } /** * @param {string} value * @returns {string} */ function escapeHTML(value) { return value .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } /** * performs a shallow merge of multiple objects into one * * @template T * @param {T} original * @param {Record[]} objects * @returns {T} a single new object */ function inherit$1(original, ...objects) { /** @type Record */ const result = Object.create(null); for (const key in original) { result[key] = original[key]; } objects.forEach(function(obj) { for (const key in obj) { result[key] = obj[key]; } }); return /** @type {T} */ (result); } /** * @typedef {object} Renderer * @property {(text: string) => void} addText * @property {(node: Node) => void} openNode * @property {(node: Node) => void} closeNode * @property {() => string} value */ /** @typedef {{scope?: string, language?: string, sublanguage?: boolean}} Node */ /** @typedef {{walk: (r: Renderer) => void}} Tree */ /** */ const SPAN_CLOSE = ''; /** * Determines if a node needs to be wrapped in * * @param {Node} node */ const emitsWrappingTags = (node) => { // rarely we can have a sublanguage where language is undefined // TODO: track down why return !!node.scope; }; /** * * @param {string} name * @param {{prefix:string}} options */ const scopeToCSSClass = (name, { prefix }) => { // sub-language if (name.startsWith("language:")) { return name.replace("language:", "language-"); } // tiered scope: comment.line if (name.includes(".")) { const pieces = name.split("."); return [ `${prefix}${pieces.shift()}`, ...(pieces.map((x, i) => `${x}${"_".repeat(i + 1)}`)) ].join(" "); } // simple scope return `${prefix}${name}`; }; /** @type {Renderer} */ class HTMLRenderer { /** * Creates a new HTMLRenderer * * @param {Tree} parseTree - the parse tree (must support `walk` API) * @param {{classPrefix: string}} options */ constructor(parseTree, options) { this.buffer = ""; this.classPrefix = options.classPrefix; parseTree.walk(this); } /** * Adds texts to the output stream * * @param {string} text */ addText(text) { this.buffer += escapeHTML(text); } /** * Adds a node open to the output stream (if needed) * * @param {Node} node */ openNode(node) { if (!emitsWrappingTags(node)) return; const className = scopeToCSSClass(node.scope, { prefix: this.classPrefix }); this.span(className); } /** * Adds a node close to the output stream (if needed) * * @param {Node} node */ closeNode(node) { if (!emitsWrappingTags(node)) return; this.buffer += SPAN_CLOSE; } /** * returns the accumulated buffer */ value() { return this.buffer; } // helpers /** * Builds a span element * * @param {string} className */ span(className) { this.buffer += ``; } } /** @typedef {{scope?: string, language?: string, children: Node[]} | string} Node */ /** @typedef {{scope?: string, language?: string, children: Node[]} } DataNode */ /** @typedef {import('highlight.js').Emitter} Emitter */ /** */ /** @returns {DataNode} */ const newNode = (opts = {}) => { /** @type DataNode */ const result = { children: [] }; Object.assign(result, opts); return result; }; class TokenTree { constructor() { /** @type DataNode */ this.rootNode = newNode(); this.stack = [this.rootNode]; } get top() { return this.stack[this.stack.length - 1]; } get root() { return this.rootNode; } /** @param {Node} node */ add(node) { this.top.children.push(node); } /** @param {string} scope */ openNode(scope) { /** @type Node */ const node = newNode({ scope }); this.add(node); this.stack.push(node); } closeNode() { if (this.stack.length > 1) { return this.stack.pop(); } // eslint-disable-next-line no-undefined return undefined; } closeAllNodes() { while (this.closeNode()); } toJSON() { return JSON.stringify(this.rootNode, null, 4); } /** * @typedef { import("./html_renderer").Renderer } Renderer * @param {Renderer} builder */ walk(builder) { // this does not return this.constructor._walk(builder, this.rootNode); // this works // return TokenTree._walk(builder, this.rootNode); } /** * @param {Renderer} builder * @param {Node} node */ static _walk(builder, node) { if (typeof node === "string") { builder.addText(node); } else if (node.children) { builder.openNode(node); node.children.forEach((child) => this._walk(builder, child)); builder.closeNode(node); } return builder; } /** * @param {Node} node */ static _collapse(node) { if (typeof node === "string") return; if (!node.children) return; if (node.children.every(el => typeof el === "string")) { // node.text = node.children.join(""); // delete node.children; node.children = [node.children.join("")]; } else { node.children.forEach((child) => { TokenTree._collapse(child); }); } } } /** Currently this is all private API, but this is the minimal API necessary that an Emitter must implement to fully support the parser. Minimal interface: - addText(text) - __addSublanguage(emitter, subLanguageName) - startScope(scope) - endScope() - finalize() - toHTML() */ /** * @implements {Emitter} */ class TokenTreeEmitter extends TokenTree { /** * @param {*} options */ constructor(options) { super(); this.options = options; } /** * @param {string} text */ addText(text) { if (text === "") { return; } this.add(text); } /** @param {string} scope */ startScope(scope) { this.openNode(scope); } endScope() { this.closeNode(); } /** * @param {Emitter & {root: DataNode}} emitter * @param {string} name */ __addSublanguage(emitter, name) { /** @type DataNode */ const node = emitter.root; if (name) node.scope = `language:${name}`; this.add(node); } toHTML() { const renderer = new HTMLRenderer(this, this.options); return renderer.value(); } finalize() { this.closeAllNodes(); return true; } } /** * @param {string} value * @returns {RegExp} * */ /** * @param {RegExp | string } re * @returns {string} */ function source(re) { if (!re) return null; if (typeof re === "string") return re; return re.source; } /** * @param {RegExp | string } re * @returns {string} */ function lookahead(re) { return concat('(?=', re, ')'); } /** * @param {RegExp | string } re * @returns {string} */ function anyNumberOfTimes(re) { return concat('(?:', re, ')*'); } /** * @param {RegExp | string } re * @returns {string} */ function optional(re) { return concat('(?:', re, ')?'); } /** * @param {...(RegExp | string) } args * @returns {string} */ function concat(...args) { const joined = args.map((x) => source(x)).join(""); return joined; } /** * @param { Array } args * @returns {object} */ function stripOptionsFromArgs(args) { const opts = args[args.length - 1]; if (typeof opts === 'object' && opts.constructor === Object) { args.splice(args.length - 1, 1); return opts; } else { return {}; } } /** @typedef { {capture?: boolean} } RegexEitherOptions */ /** * Any of the passed expresssions may match * * Creates a huge this | this | that | that match * @param {(RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]} args * @returns {string} */ function either(...args) { /** @type { object & {capture?: boolean} } */ const opts = stripOptionsFromArgs(args); const joined = '(' + (opts.capture ? "" : "?:") + args.map((x) => source(x)).join("|") + ")"; return joined; } /** * @param {RegExp | string} re * @returns {number} */ function countMatchGroups(re) { return (new RegExp(re.toString() + '|')).exec('').length - 1; } /** * Does lexeme start with a regular expression match at the beginning * @param {RegExp} re * @param {string} lexeme */ function startsWith(re, lexeme) { const match = re && re.exec(lexeme); return match && match.index === 0; } // BACKREF_RE matches an open parenthesis or backreference. To avoid an // incorrect parse, it also matches the constructs where the meaning of // parentheses, escapes, or capture counting changes. const BACKREF_RE = new RegExp(either( /\[(?:[^\\\]]|\\.)*\]/, // a character class, inside which ( and \ lose their meaning /\(\?<(?![=!])[^>]+>/, // a named capture group `(?` (not a lookbehind `(?<=` / `(? { numCaptures += 1; const offset = numCaptures; let re = source(regex); let out = ''; while (re.length > 0) { const match = BACKREF_RE.exec(re); if (!match) { out += re; break; } out += re.substring(0, match.index); re = re.substring(match.index + match[0].length); if (match[0][0] === '\\' && match[1]) { // Adjust the backreference. out += '\\' + String(Number(match[1]) + offset); } else { out += match[0]; if (match[0] === '(' || /^\(\?[<']/.test(match[0])) { numCaptures++; } } } return out; }).map(re => `(${re})`).join(joinWith); } /** @typedef {import('highlight.js').Mode} Mode */ /** @typedef {import('highlight.js').ModeCallback} ModeCallback */ // Common regexps const MATCH_NOTHING_RE = /\b\B/; const IDENT_RE = '[a-zA-Z]\\w*'; const UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*'; const NUMBER_RE = '\\b\\d+(\\.\\d+)?'; const C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float const BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... const RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; /** * @param { Partial & {binary?: string | RegExp} } opts */ const SHEBANG = (opts = {}) => { const beginShebang = /^#![ ]*\//; if (opts.binary) { opts.begin = concat( beginShebang, /.*\b/, opts.binary, /\b.*/); } return inherit$1({ scope: 'meta', begin: beginShebang, end: /$/, relevance: 0, /** @type {ModeCallback} */ "on:begin": (m, resp) => { if (m.index !== 0) resp.ignoreMatch(); } }, opts); }; // Common modes const BACKSLASH_ESCAPE = { begin: '\\\\[\\s\\S]', relevance: 0 }; const APOS_STRING_MODE = { scope: 'string', begin: '\'', end: '\'', illegal: '\\n', contains: [BACKSLASH_ESCAPE] }; const QUOTE_STRING_MODE = { scope: 'string', begin: '"', end: '"', illegal: '\\n', contains: [BACKSLASH_ESCAPE] }; const PHRASAL_WORDS_MODE = { begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ }; /** * Creates a comment mode * * @param {string | RegExp} begin * @param {string | RegExp} end * @param {Mode | {}} [modeOptions] * @returns {Partial} */ const COMMENT = function(begin, end, modeOptions = {}) { const mode = inherit$1( { scope: 'comment', begin, end, contains: [] }, modeOptions ); mode.contains.push({ scope: 'doctag', // hack to avoid the space from being included. the space is necessary to // match here to prevent the plain text rule below from gobbling up doctags begin: '[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)', end: /(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/, excludeBegin: true, relevance: 0 }); const ENGLISH_WORD = either( // list of common 1 and 2 letter words in English "I", "a", "is", "so", "us", "to", "at", "if", "in", "it", "on", // note: this is not an exhaustive list of contractions, just popular ones /[A-Za-z]+['](d|ve|re|ll|t|s|n)/, // contractions - can't we'd they're let's, etc /[A-Za-z]+[-][a-z]+/, // `no-way`, etc. /[A-Za-z][a-z]{2,}/ // allow capitalized words at beginning of sentences ); // looking like plain text, more likely to be a comment mode.contains.push( { // TODO: how to include ", (, ) without breaking grammars that use these for // comment delimiters? // begin: /[ ]+([()"]?([A-Za-z'-]{3,}|is|a|I|so|us|[tT][oO]|at|if|in|it|on)[.]?[()":]?([.][ ]|[ ]|\))){3}/ // --- // this tries to find sequences of 3 english words in a row (without any // "programming" type syntax) this gives us a strong signal that we've // TRULY found a comment - vs perhaps scanning with the wrong language. // It's possible to find something that LOOKS like the start of the // comment - but then if there is no readable text - good chance it is a // false match and not a comment. // // for a visual example please see: // https://github.com/highlightjs/highlight.js/issues/2827 begin: concat( /[ ]+/, // necessary to prevent us gobbling up doctags like /* @author Bob Mcgill */ '(', ENGLISH_WORD, /[.]?[:]?([.][ ]|[ ])/, '){3}') // look for 3 words in a row } ); return mode; }; const C_LINE_COMMENT_MODE = COMMENT('//', '$'); const C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/'); const HASH_COMMENT_MODE = COMMENT('#', '$'); const NUMBER_MODE = { scope: 'number', begin: NUMBER_RE, relevance: 0 }; const C_NUMBER_MODE = { scope: 'number', begin: C_NUMBER_RE, relevance: 0 }; const BINARY_NUMBER_MODE = { scope: 'number', begin: BINARY_NUMBER_RE, relevance: 0 }; const REGEXP_MODE = { scope: "regexp", begin: /\/(?=[^/\n]*\/)/, end: /\/[gimuy]*/, contains: [ BACKSLASH_ESCAPE, { begin: /\[/, end: /\]/, relevance: 0, contains: [BACKSLASH_ESCAPE] } ] }; const TITLE_MODE = { scope: 'title', begin: IDENT_RE, relevance: 0 }; const UNDERSCORE_TITLE_MODE = { scope: 'title', begin: UNDERSCORE_IDENT_RE, relevance: 0 }; const METHOD_GUARD = { // excludes method names from keyword processing begin: '\\.\\s*' + UNDERSCORE_IDENT_RE, relevance: 0 }; /** * Adds end same as begin mechanics to a mode * * Your mode must include at least a single () match group as that first match * group is what is used for comparison * @param {Partial} mode */ const END_SAME_AS_BEGIN = function(mode) { return Object.assign(mode, { /** @type {ModeCallback} */ 'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; }, /** @type {ModeCallback} */ 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); } }); }; var MODES = /*#__PURE__*/Object.freeze({ __proto__: null, APOS_STRING_MODE: APOS_STRING_MODE, BACKSLASH_ESCAPE: BACKSLASH_ESCAPE, BINARY_NUMBER_MODE: BINARY_NUMBER_MODE, BINARY_NUMBER_RE: BINARY_NUMBER_RE, COMMENT: COMMENT, C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE, C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE, C_NUMBER_MODE: C_NUMBER_MODE, C_NUMBER_RE: C_NUMBER_RE, END_SAME_AS_BEGIN: END_SAME_AS_BEGIN, HASH_COMMENT_MODE: HASH_COMMENT_MODE, IDENT_RE: IDENT_RE, MATCH_NOTHING_RE: MATCH_NOTHING_RE, METHOD_GUARD: METHOD_GUARD, NUMBER_MODE: NUMBER_MODE, NUMBER_RE: NUMBER_RE, PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE, QUOTE_STRING_MODE: QUOTE_STRING_MODE, REGEXP_MODE: REGEXP_MODE, RE_STARTERS_RE: RE_STARTERS_RE, SHEBANG: SHEBANG, TITLE_MODE: TITLE_MODE, UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE, UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE }); /** @typedef {import('highlight.js').CallbackResponse} CallbackResponse @typedef {import('highlight.js').CompilerExt} CompilerExt */ // Grammar extensions / plugins // See: https://github.com/highlightjs/highlight.js/issues/2833 // Grammar extensions allow "syntactic sugar" to be added to the grammar modes // without requiring any underlying changes to the compiler internals. // `compileMatch` being the perfect small example of now allowing a grammar // author to write `match` when they desire to match a single expression rather // than being forced to use `begin`. The extension then just moves `match` into // `begin` when it runs. Ie, no features have been added, but we've just made // the experience of writing (and reading grammars) a little bit nicer. // ------ // TODO: We need negative look-behind support to do this properly /** * Skip a match if it has a preceding dot * * This is used for `beginKeywords` to prevent matching expressions such as * `bob.keyword.do()`. The mode compiler automatically wires this up as a * special _internal_ 'on:begin' callback for modes with `beginKeywords` * @param {RegExpMatchArray} match * @param {CallbackResponse} response */ function skipIfHasPrecedingDot(match, response) { const before = match.input[match.index - 1]; if (before === ".") { response.ignoreMatch(); } } /** * * @type {CompilerExt} */ function scopeClassName(mode, _parent) { // eslint-disable-next-line no-undefined if (mode.className !== undefined) { mode.scope = mode.className; delete mode.className; } } /** * `beginKeywords` syntactic sugar * @type {CompilerExt} */ function beginKeywords(mode, parent) { if (!parent) return; if (!mode.beginKeywords) return; // for languages with keywords that include non-word characters checking for // a word boundary is not sufficient, so instead we check for a word boundary // or whitespace - this does no harm in any case since our keyword engine // doesn't allow spaces in keywords anyways and we still check for the boundary // first mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)'; mode.__beforeBegin = skipIfHasPrecedingDot; mode.keywords = mode.keywords || mode.beginKeywords; delete mode.beginKeywords; // prevents double relevance, the keywords themselves provide // relevance, the mode doesn't need to double it // eslint-disable-next-line no-undefined if (mode.relevance === undefined) mode.relevance = 0; } /** * Allow `illegal` to contain an array of illegal values * @type {CompilerExt} */ function compileIllegal(mode, _parent) { if (!Array.isArray(mode.illegal)) return; mode.illegal = either(...mode.illegal); } /** * `match` to match a single expression for readability * @type {CompilerExt} */ function compileMatch(mode, _parent) { if (!mode.match) return; if (mode.begin || mode.end) throw new Error("begin & end are not supported with match"); mode.begin = mode.match; delete mode.match; } /** * provides the default 1 relevance to all modes * @type {CompilerExt} */ function compileRelevance(mode, _parent) { // eslint-disable-next-line no-undefined if (mode.relevance === undefined) mode.relevance = 1; } // allow beforeMatch to act as a "qualifier" for the match // the full match begin must be [beforeMatch][begin] const beforeMatchExt = (mode, parent) => { if (!mode.beforeMatch) return; // starts conflicts with endsParent which we need to make sure the child // rule is not matched multiple times if (mode.starts) throw new Error("beforeMatch cannot be used with starts"); const originalMode = Object.assign({}, mode); Object.keys(mode).forEach((key) => { delete mode[key]; }); mode.keywords = originalMode.keywords; mode.begin = concat(originalMode.beforeMatch, lookahead(originalMode.begin)); mode.starts = { relevance: 0, contains: [ Object.assign(originalMode, { endsParent: true }) ] }; mode.relevance = 0; delete originalMode.beforeMatch; }; // keywords that should have no default relevance value const COMMON_KEYWORDS = [ 'of', 'and', 'for', 'in', 'not', 'or', 'if', 'then', 'parent', // common variable name 'list', // common variable name 'value' // common variable name ]; const DEFAULT_KEYWORD_SCOPE = "keyword"; /** * Given raw keywords from a language definition, compile them. * * @param {string | Record | Array} rawKeywords * @param {boolean} caseInsensitive */ function compileKeywords(rawKeywords, caseInsensitive, scopeName = DEFAULT_KEYWORD_SCOPE) { /** @type {import("highlight.js/private").KeywordDict} */ const compiledKeywords = Object.create(null); // input can be a string of keywords, an array of keywords, or a object with // named keys representing scopeName (which can then point to a string or array) if (typeof rawKeywords === 'string') { compileList(scopeName, rawKeywords.split(" ")); } else if (Array.isArray(rawKeywords)) { compileList(scopeName, rawKeywords); } else { Object.keys(rawKeywords).forEach(function(scopeName) { // collapse all our objects back into the parent object Object.assign( compiledKeywords, compileKeywords(rawKeywords[scopeName], caseInsensitive, scopeName) ); }); } return compiledKeywords; // --- /** * Compiles an individual list of keywords * * Ex: "for if when while|5" * * @param {string} scopeName * @param {Array} keywordList */ function compileList(scopeName, keywordList) { if (caseInsensitive) { keywordList = keywordList.map(x => x.toLowerCase()); } keywordList.forEach(function(keyword) { const pair = keyword.split('|'); compiledKeywords[pair[0]] = [scopeName, scoreForKeyword(pair[0], pair[1])]; }); } } /** * Returns the proper score for a given keyword * * Also takes into account comment keywords, which will be scored 0 UNLESS * another score has been manually assigned. * @param {string} keyword * @param {string} [providedScore] */ function scoreForKeyword(keyword, providedScore) { // manual scores always win over common keywords // so you can force a score of 1 if you really insist if (providedScore) { return Number(providedScore); } return commonKeyword(keyword) ? 0 : 1; } /** * Determines if a given keyword is common or not * * @param {string} keyword */ function commonKeyword(keyword) { return COMMON_KEYWORDS.includes(keyword.toLowerCase()); } /* For the reasoning behind this please see: https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419 */ /** * @type {Record} */ const seenDeprecations = {}; /** * @param {string} message */ const error = (message) => { console.error(message); }; /** * @param {string} message * @param {any} args */ const warn = (message, ...args) => { console.log(`WARN: ${message}`, ...args); }; /** * @param {string} version * @param {string} message */ const deprecated = (version, message) => { if (seenDeprecations[`${version}/${message}`]) return; console.log(`Deprecated as of ${version}. ${message}`); seenDeprecations[`${version}/${message}`] = true; }; /* eslint-disable no-throw-literal */ /** @typedef {import('highlight.js').CompiledMode} CompiledMode */ const MultiClassError = new Error(); /** * Renumbers labeled scope names to account for additional inner match * groups that otherwise would break everything. * * Lets say we 3 match scopes: * * { 1 => ..., 2 => ..., 3 => ... } * * So what we need is a clean match like this: * * (a)(b)(c) => [ "a", "b", "c" ] * * But this falls apart with inner match groups: * * (a)(((b)))(c) => ["a", "b", "b", "b", "c" ] * * Our scopes are now "out of alignment" and we're repeating `b` 3 times. * What needs to happen is the numbers are remapped: * * { 1 => ..., 2 => ..., 5 => ... } * * We also need to know that the ONLY groups that should be output * are 1, 2, and 5. This function handles this behavior. * * @param {CompiledMode} mode * @param {Array} regexes * @param {{key: "beginScope"|"endScope"}} opts */ function remapScopeNames(mode, regexes, { key }) { let offset = 0; const scopeNames = mode[key]; /** @type Record */ const emit = {}; /** @type Record */ const positions = {}; for (let i = 1; i <= regexes.length; i++) { positions[i + offset] = scopeNames[i]; emit[i + offset] = true; offset += countMatchGroups(regexes[i - 1]); } // we use _emit to keep track of which match groups are "top-level" to avoid double // output from inside match groups mode[key] = positions; mode[key]._emit = emit; mode[key]._multi = true; } /** * @param {CompiledMode} mode */ function beginMultiClass(mode) { if (!Array.isArray(mode.begin)) return; if (mode.skip || mode.excludeBegin || mode.returnBegin) { error("skip, excludeBegin, returnBegin not compatible with beginScope: {}"); throw MultiClassError; } if (typeof mode.beginScope !== "object" || mode.beginScope === null) { error("beginScope must be object"); throw MultiClassError; } remapScopeNames(mode, mode.begin, { key: "beginScope" }); mode.begin = _rewriteBackreferences(mode.begin, { joinWith: "" }); } /** * @param {CompiledMode} mode */ function endMultiClass(mode) { if (!Array.isArray(mode.end)) return; if (mode.skip || mode.excludeEnd || mode.returnEnd) { error("skip, excludeEnd, returnEnd not compatible with endScope: {}"); throw MultiClassError; } if (typeof mode.endScope !== "object" || mode.endScope === null) { error("endScope must be object"); throw MultiClassError; } remapScopeNames(mode, mode.end, { key: "endScope" }); mode.end = _rewriteBackreferences(mode.end, { joinWith: "" }); } /** * this exists only to allow `scope: {}` to be used beside `match:` * Otherwise `beginScope` would necessary and that would look weird { match: [ /def/, /\w+/ ] scope: { 1: "keyword" , 2: "title" } } * @param {CompiledMode} mode */ function scopeSugar(mode) { if (mode.scope && typeof mode.scope === "object" && mode.scope !== null) { mode.beginScope = mode.scope; delete mode.scope; } } /** * @param {CompiledMode} mode */ function MultiClass(mode) { scopeSugar(mode); if (typeof mode.beginScope === "string") { mode.beginScope = { _wrap: mode.beginScope }; } if (typeof mode.endScope === "string") { mode.endScope = { _wrap: mode.endScope }; } beginMultiClass(mode); endMultiClass(mode); } /** @typedef {import('highlight.js').Mode} Mode @typedef {import('highlight.js').CompiledMode} CompiledMode @typedef {import('highlight.js').Language} Language @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin @typedef {import('highlight.js').CompiledLanguage} CompiledLanguage */ // compilation /** * Compiles a language definition result * * Given the raw result of a language definition (Language), compiles this so * that it is ready for highlighting code. * @param {Language} language * @returns {CompiledLanguage} */ function compileLanguage(language) { /** * Builds a regex with the case sensitivity of the current language * * @param {RegExp | string} value * @param {boolean} [global] */ function langRe(value, global) { return new RegExp( source(value), 'm' + (language.case_insensitive ? 'i' : '') + (language.unicodeRegex ? 'u' : '') + (global ? 'g' : '') ); } /** Stores multiple regular expressions and allows you to quickly search for them all in a string simultaneously - returning the first match. It does this by creating a huge (a|b|c) regex - each individual item wrapped with () and joined by `|` - using match groups to track position. When a match is found checking which position in the array has content allows us to figure out which of the original regexes / match groups triggered the match. The match object itself (the result of `Regex.exec`) is returned but also enhanced by merging in any meta-data that was registered with the regex. This is how we keep track of which mode matched, and what type of rule (`illegal`, `begin`, end, etc). */ class MultiRegex { constructor() { this.matchIndexes = {}; // @ts-ignore this.regexes = []; this.matchAt = 1; this.position = 0; } // @ts-ignore addRule(re, opts) { opts.position = this.position++; // @ts-ignore this.matchIndexes[this.matchAt] = opts; this.regexes.push([opts, re]); this.matchAt += countMatchGroups(re) + 1; } compile() { if (this.regexes.length === 0) { // avoids the need to check length every time exec is called // @ts-ignore this.exec = () => null; } const terminators = this.regexes.map(el => el[1]); this.matcherRe = langRe(_rewriteBackreferences(terminators, { joinWith: '|' }), true); this.lastIndex = 0; } /** @param {string} s */ exec(s) { this.matcherRe.lastIndex = this.lastIndex; const match = this.matcherRe.exec(s); if (!match) { return null; } // eslint-disable-next-line no-undefined const i = match.findIndex((el, i) => i > 0 && el !== undefined); // @ts-ignore const matchData = this.matchIndexes[i]; // trim off any earlier non-relevant match groups (ie, the other regex // match groups that make up the multi-matcher) match.splice(0, i); return Object.assign(match, matchData); } } /* Created to solve the key deficiently with MultiRegex - there is no way to test for multiple matches at a single location. Why would we need to do that? In the future a more dynamic engine will allow certain matches to be ignored. An example: if we matched say the 3rd regex in a large group but decided to ignore it - we'd need to started testing again at the 4th regex... but MultiRegex itself gives us no real way to do that. So what this class creates MultiRegexs on the fly for whatever search position they are needed. NOTE: These additional MultiRegex objects are created dynamically. For most grammars most of the time we will never actually need anything more than the first MultiRegex - so this shouldn't have too much overhead. Say this is our search group, and we match regex3, but wish to ignore it. regex1 | regex2 | regex3 | regex4 | regex5 ' ie, startAt = 0 What we need is a new MultiRegex that only includes the remaining possibilities: regex4 | regex5 ' ie, startAt = 3 This class wraps all that complexity up in a simple API... `startAt` decides where in the array of expressions to start doing the matching. It auto-increments, so if a match is found at position 2, then startAt will be set to 3. If the end is reached startAt will return to 0. MOST of the time the parser will be setting startAt manually to 0. */ class ResumableMultiRegex { constructor() { // @ts-ignore this.rules = []; // @ts-ignore this.multiRegexes = []; this.count = 0; this.lastIndex = 0; this.regexIndex = 0; } // @ts-ignore getMatcher(index) { if (this.multiRegexes[index]) return this.multiRegexes[index]; const matcher = new MultiRegex(); this.rules.slice(index).forEach(([re, opts]) => matcher.addRule(re, opts)); matcher.compile(); this.multiRegexes[index] = matcher; return matcher; } resumingScanAtSamePosition() { return this.regexIndex !== 0; } considerAll() { this.regexIndex = 0; } // @ts-ignore addRule(re, opts) { this.rules.push([re, opts]); if (opts.type === "begin") this.count++; } /** @param {string} s */ exec(s) { const m = this.getMatcher(this.regexIndex); m.lastIndex = this.lastIndex; let result = m.exec(s); // The following is because we have no easy way to say "resume scanning at the // existing position but also skip the current rule ONLY". What happens is // all prior rules are also skipped which can result in matching the wrong // thing. Example of matching "booger": // our matcher is [string, "booger", number] // // ....booger.... // if "booger" is ignored then we'd really need a regex to scan from the // SAME position for only: [string, number] but ignoring "booger" (if it // was the first match), a simple resume would scan ahead who knows how // far looking only for "number", ignoring potential string matches (or // future "booger" matches that might be valid.) // So what we do: We execute two matchers, one resuming at the same // position, but the second full matcher starting at the position after: // /--- resume first regex match here (for [number]) // |/---- full match here for [string, "booger", number] // vv // ....booger.... // Which ever results in a match first is then used. So this 3-4 step // process essentially allows us to say "match at this position, excluding // a prior rule that was ignored". // // 1. Match "booger" first, ignore. Also proves that [string] does non match. // 2. Resume matching for [number] // 3. Match at index + 1 for [string, "booger", number] // 4. If #2 and #3 result in matches, which came first? if (this.resumingScanAtSamePosition()) { if (result && result.index === this.lastIndex) ; else { // use the second matcher result const m2 = this.getMatcher(0); m2.lastIndex = this.lastIndex + 1; result = m2.exec(s); } } if (result) { this.regexIndex += result.position + 1; if (this.regexIndex === this.count) { // wrap-around to considering all matches again this.considerAll(); } } return result; } } /** * Given a mode, builds a huge ResumableMultiRegex that can be used to walk * the content and find matches. * * @param {CompiledMode} mode * @returns {ResumableMultiRegex} */ function buildModeRegex(mode) { const mm = new ResumableMultiRegex(); mode.contains.forEach(term => mm.addRule(term.begin, { rule: term, type: "begin" })); if (mode.terminatorEnd) { mm.addRule(mode.terminatorEnd, { type: "end" }); } if (mode.illegal) { mm.addRule(mode.illegal, { type: "illegal" }); } return mm; } /** skip vs abort vs ignore * * @skip - The mode is still entered and exited normally (and contains rules apply), * but all content is held and added to the parent buffer rather than being * output when the mode ends. Mostly used with `sublanguage` to build up * a single large buffer than can be parsed by sublanguage. * * - The mode begin ands ends normally. * - Content matched is added to the parent mode buffer. * - The parser cursor is moved forward normally. * * @abort - A hack placeholder until we have ignore. Aborts the mode (as if it * never matched) but DOES NOT continue to match subsequent `contains` * modes. Abort is bad/suboptimal because it can result in modes * farther down not getting applied because an earlier rule eats the * content but then aborts. * * - The mode does not begin. * - Content matched by `begin` is added to the mode buffer. * - The parser cursor is moved forward accordingly. * * @ignore - Ignores the mode (as if it never matched) and continues to match any * subsequent `contains` modes. Ignore isn't technically possible with * the current parser implementation. * * - The mode does not begin. * - Content matched by `begin` is ignored. * - The parser cursor is not moved forward. */ /** * Compiles an individual mode * * This can raise an error if the mode contains certain detectable known logic * issues. * @param {Mode} mode * @param {CompiledMode | null} [parent] * @returns {CompiledMode | never} */ function compileMode(mode, parent) { const cmode = /** @type CompiledMode */ (mode); if (mode.isCompiled) return cmode; [ scopeClassName, // do this early so compiler extensions generally don't have to worry about // the distinction between match/begin compileMatch, MultiClass, beforeMatchExt ].forEach(ext => ext(mode, parent)); language.compilerExtensions.forEach(ext => ext(mode, parent)); // __beforeBegin is considered private API, internal use only mode.__beforeBegin = null; [ beginKeywords, // do this later so compiler extensions that come earlier have access to the // raw array if they wanted to perhaps manipulate it, etc. compileIllegal, // default to 1 relevance if not specified compileRelevance ].forEach(ext => ext(mode, parent)); mode.isCompiled = true; let keywordPattern = null; if (typeof mode.keywords === "object" && mode.keywords.$pattern) { // we need a copy because keywords might be compiled multiple times // so we can't go deleting $pattern from the original on the first // pass mode.keywords = Object.assign({}, mode.keywords); keywordPattern = mode.keywords.$pattern; delete mode.keywords.$pattern; } keywordPattern = keywordPattern || /\w+/; if (mode.keywords) { mode.keywords = compileKeywords(mode.keywords, language.case_insensitive); } cmode.keywordPatternRe = langRe(keywordPattern, true); if (parent) { if (!mode.begin) mode.begin = /\B|\b/; cmode.beginRe = langRe(cmode.begin); if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/; if (mode.end) cmode.endRe = langRe(cmode.end); cmode.terminatorEnd = source(cmode.end) || ''; if (mode.endsWithParent && parent.terminatorEnd) { cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd; } } if (mode.illegal) cmode.illegalRe = langRe(/** @type {RegExp | string} */ (mode.illegal)); if (!mode.contains) mode.contains = []; mode.contains = [].concat(...mode.contains.map(function(c) { return expandOrCloneMode(c === 'self' ? mode : c); })); mode.contains.forEach(function(c) { compileMode(/** @type Mode */ (c), cmode); }); if (mode.starts) { compileMode(mode.starts, parent); } cmode.matcher = buildModeRegex(cmode); return cmode; } if (!language.compilerExtensions) language.compilerExtensions = []; // self is not valid at the top-level if (language.contains && language.contains.includes('self')) { throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation."); } // we need a null object, which inherit will guarantee language.classNameAliases = inherit$1(language.classNameAliases || {}); return compileMode(/** @type Mode */ (language)); } /** * Determines if a mode has a dependency on it's parent or not * * If a mode does have a parent dependency then often we need to clone it if * it's used in multiple places so that each copy points to the correct parent, * where-as modes without a parent can often safely be re-used at the bottom of * a mode chain. * * @param {Mode | null} mode * @returns {boolean} - is there a dependency on the parent? * */ function dependencyOnParent(mode) { if (!mode) return false; return mode.endsWithParent || dependencyOnParent(mode.starts); } /** * Expands a mode or clones it if necessary * * This is necessary for modes with parental dependenceis (see notes on * `dependencyOnParent`) and for nodes that have `variants` - which must then be * exploded into their own individual modes at compile time. * * @param {Mode} mode * @returns {Mode | Mode[]} * */ function expandOrCloneMode(mode) { if (mode.variants && !mode.cachedVariants) { mode.cachedVariants = mode.variants.map(function(variant) { return inherit$1(mode, { variants: null }, variant); }); } // EXPAND // if we have variants then essentially "replace" the mode with the variants // this happens in compileMode, where this function is called from if (mode.cachedVariants) { return mode.cachedVariants; } // CLONE // if we have dependencies on parents then we need a unique // instance of ourselves, so we can be reused with many // different parents without issue if (dependencyOnParent(mode)) { return inherit$1(mode, { starts: mode.starts ? inherit$1(mode.starts) : null }); } if (Object.isFrozen(mode)) { return inherit$1(mode); } // no special dependency issues, just return ourselves return mode; } var version = "11.12.0"; class HTMLInjectionError extends Error { constructor(reason, html) { super(reason); this.name = "HTMLInjectionError"; this.html = html; } } /* Syntax highlighting with language autodetection. https://highlightjs.org/ */ /** @typedef {import('highlight.js').Mode} Mode @typedef {import('highlight.js').CompiledMode} CompiledMode @typedef {import('highlight.js').CompiledScope} CompiledScope @typedef {import('highlight.js').Language} Language @typedef {import('highlight.js').HLJSApi} HLJSApi @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin @typedef {import('highlight.js').PluginEvent} PluginEvent @typedef {import('highlight.js').HLJSOptions} HLJSOptions @typedef {import('highlight.js').LanguageFn} LanguageFn @typedef {import('highlight.js').HighlightedHTMLElement} HighlightedHTMLElement @typedef {import('highlight.js').BeforeHighlightContext} BeforeHighlightContext @typedef {import('highlight.js/private').MatchType} MatchType @typedef {import('highlight.js/private').KeywordData} KeywordData @typedef {import('highlight.js/private').EnhancedMatch} EnhancedMatch @typedef {import('highlight.js/private').AnnotatedError} AnnotatedError @typedef {import('highlight.js').AutoHighlightResult} AutoHighlightResult @typedef {import('highlight.js').HighlightOptions} HighlightOptions @typedef {import('highlight.js').HighlightResult} HighlightResult */ const escape = escapeHTML; const inherit = inherit$1; const NO_MATCH = Symbol("nomatch"); const MAX_KEYWORD_HITS = 7; /** * @param {any} hljs - object that is extended (legacy) * @returns {HLJSApi} */ const HLJS = function(hljs) { // Global internal variables used within the highlight.js library. /** @type {Record} */ const languages = Object.create(null); /** @type {Record} */ const aliases = Object.create(null); /** @type {HLJSPlugin[]} */ const plugins = []; // safe/production mode - swallows more errors, tries to keep running // even if a single syntax or parse hits a fatal error let SAFE_MODE = true; const LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?"; /** @type {Language} */ const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] }; // Global options used when within external APIs. This is modified when // calling the `hljs.configure` function. /** @type HLJSOptions */ let options = { ignoreUnescapedHTML: false, throwUnescapedHTML: false, noHighlightRe: /^(no-?highlight)$/i, languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i, classPrefix: 'hljs-', cssSelector: 'pre code', languages: null, // beta configuration options, subject to change, welcome to discuss // https://github.com/highlightjs/highlight.js/issues/1086 __emitter: TokenTreeEmitter }; /* Utility functions */ /** * Tests a language name to see if highlighting should be skipped * @param {string} languageName */ function shouldNotHighlight(languageName) { return options.noHighlightRe.test(languageName); } /** * @param {HighlightedHTMLElement} block - the HTML element to determine language for */ function blockLanguage(block) { let classes = block.className + ' '; classes += block.parentNode ? block.parentNode.className : ''; // language-* takes precedence over non-prefixed class names. const match = options.languageDetectRe.exec(classes); if (match) { const language = getLanguage(match[1]); if (!language) { warn(LANGUAGE_NOT_FOUND.replace("{}", match[1])); warn("Falling back to no-highlight mode for this block.", block); } return language ? match[1] : 'no-highlight'; } return classes .split(/\s+/) .find((_class) => shouldNotHighlight(_class) || getLanguage(_class)); } /** * Core highlighting function. * * OLD API * highlight(lang, code, ignoreIllegals, continuation) * * NEW API * highlight(code, {lang, ignoreIllegals}) * * @param {string} codeOrLanguageName - the language to use for highlighting * @param {string | HighlightOptions} optionsOrCode - the code to highlight * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail * * @returns {HighlightResult} Result - an object that represents the result * @property {string} language - the language name * @property {number} relevance - the relevance score * @property {string} value - the highlighted HTML code * @property {string} code - the original raw code * @property {CompiledMode} top - top of the current mode stack * @property {boolean} illegal - indicates whether any illegal matches were found */ function highlight(codeOrLanguageName, optionsOrCode, ignoreIllegals) { let code = ""; let languageName = ""; if (typeof optionsOrCode === "object") { code = codeOrLanguageName; ignoreIllegals = optionsOrCode.ignoreIllegals; languageName = optionsOrCode.language; } else { // old API deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated."); deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"); languageName = codeOrLanguageName; code = optionsOrCode; } // https://github.com/highlightjs/highlight.js/issues/3149 // eslint-disable-next-line no-undefined if (ignoreIllegals === undefined) { ignoreIllegals = true; } /** @type {BeforeHighlightContext} */ const context = { code, language: languageName }; // the plugin can change the desired language or the code to be highlighted // just be changing the object it was passed fire("before:highlight", context); // a before plugin can usurp the result completely by providing it's own // in which case we don't even need to call highlight const result = context.result ? context.result : _highlight(context.language, context.code, ignoreIllegals); result.code = context.code; // the plugin can change anything in result to suite it fire("after:highlight", result); return result; } /** * private highlight that's used internally and does not fire callbacks * * @param {string} languageName - the language to use for highlighting * @param {string} codeToHighlight - the code to highlight * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail * @param {CompiledMode?} [continuation] - current continuation mode, if any * @returns {HighlightResult} - result of the highlight operation */ function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) { const keywordHits = Object.create(null); /** * Return keyword data if a match is a keyword * @param {CompiledMode} mode - current mode * @param {string} matchText - the textual match * @returns {KeywordData | false} */ function keywordData(mode, matchText) { return mode.keywords[matchText]; } function processKeywords() { if (!top.keywords) { emitter.addText(modeBuffer); return; } let lastIndex = 0; top.keywordPatternRe.lastIndex = 0; let match = top.keywordPatternRe.exec(modeBuffer); let buf = ""; while (match) { buf += modeBuffer.substring(lastIndex, match.index); const word = language.case_insensitive ? match[0].toLowerCase() : match[0]; const data = keywordData(top, word); if (data) { const [kind, keywordRelevance] = data; emitter.addText(buf); buf = ""; keywordHits[word] = (keywordHits[word] || 0) + 1; if (keywordHits[word] <= MAX_KEYWORD_HITS) relevance += keywordRelevance; if (kind.startsWith("_")) { // _ implied for relevance only, do not highlight // by applying a class name buf += match[0]; } else { const cssClass = language.classNameAliases[kind] || kind; emitKeyword(match[0], cssClass); } } else { buf += match[0]; } lastIndex = top.keywordPatternRe.lastIndex; match = top.keywordPatternRe.exec(modeBuffer); } buf += modeBuffer.substring(lastIndex); emitter.addText(buf); } function processSubLanguage() { if (modeBuffer === "") return; /** @type HighlightResult */ let result = null; if (typeof top.subLanguage === 'string') { if (!languages[top.subLanguage]) { emitter.addText(modeBuffer); return; } result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]); continuations[top.subLanguage] = /** @type {CompiledMode} */ (result._top); } else { result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null); } // Counting embedded language score towards the host language may be disabled // with zeroing the containing mode relevance. Use case in point is Markdown that // allows XML everywhere and makes every XML snippet to have a much larger Markdown // score. if (top.relevance > 0) { relevance += result.relevance; } emitter.__addSublanguage(result._emitter, result.language); } function processBuffer() { if (top.subLanguage != null) { processSubLanguage(); } else { processKeywords(); } modeBuffer = ''; } /** * @param {string} text * @param {string} scope */ function emitKeyword(keyword, scope) { if (keyword === "") return; emitter.startScope(scope); emitter.addText(keyword); emitter.endScope(); } /** * @param {CompiledScope} scope * @param {RegExpMatchArray} match */ function emitMultiClass(scope, match) { let i = 1; const max = match.length - 1; while (i <= max) { if (!scope._emit[i]) { i++; continue; } const klass = language.classNameAliases[scope[i]] || scope[i]; const text = match[i]; if (klass) { emitKeyword(text, klass); } else { modeBuffer = text; processKeywords(); modeBuffer = ""; } i++; } } /** * @param {CompiledMode} mode - new mode to start * @param {RegExpMatchArray} match */ function startNewMode(mode, match) { if (mode.scope && typeof mode.scope === "string") { emitter.openNode(language.classNameAliases[mode.scope] || mode.scope); } if (mode.beginScope) { // beginScope just wraps the begin match itself in a scope if (mode.beginScope._wrap) { emitKeyword(modeBuffer, language.classNameAliases[mode.beginScope._wrap] || mode.beginScope._wrap); modeBuffer = ""; } else if (mode.beginScope._multi) { // at this point modeBuffer should just be the match emitMultiClass(mode.beginScope, match); modeBuffer = ""; } } top = Object.create(mode, { parent: { value: top } }); return top; } /** * @param {CompiledMode } mode - the mode to potentially end * @param {RegExpMatchArray} match - the latest match * @param {string} matchPlusRemainder - match plus remainder of content * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode */ function endOfMode(mode, match, matchPlusRemainder) { let matched = startsWith(mode.endRe, matchPlusRemainder); if (matched) { if (mode["on:end"]) { const resp = new Response(mode); mode["on:end"](match, resp); if (resp.isMatchIgnored) matched = false; } if (matched) { while (mode.endsParent && mode.parent) { mode = mode.parent; } return mode; } } // even if on:end fires an `ignore` it's still possible // that we might trigger the end node because of a parent mode if (mode.endsWithParent) { return endOfMode(mode.parent, match, matchPlusRemainder); } } /** * Handle matching but then ignoring a sequence of text * * @param {string} lexeme - string containing full match text */ function doIgnore(lexeme) { if (top.matcher.regexIndex === 0) { // no more regexes to potentially match here, so we move the cursor forward one // space modeBuffer += lexeme[0]; return 1; } else { // no need to move the cursor, we still have additional regexes to try and // match at this very spot resumeScanAtSamePosition = true; return 0; } } /** * Handle the start of a new potential mode match * * @param {EnhancedMatch} match - the current match * @returns {number} how far to advance the parse cursor */ function doBeginMatch(match) { const lexeme = match[0]; const newMode = match.rule; const resp = new Response(newMode); // first internal before callbacks, then the public ones const beforeCallbacks = [newMode.__beforeBegin, newMode["on:begin"]]; for (const cb of beforeCallbacks) { if (!cb) continue; cb(match, resp); if (resp.isMatchIgnored) return doIgnore(lexeme); } if (newMode.skip) { modeBuffer += lexeme; } else { if (newMode.excludeBegin) { modeBuffer += lexeme; } processBuffer(); if (!newMode.returnBegin && !newMode.excludeBegin) { modeBuffer = lexeme; } } startNewMode(newMode, match); return newMode.returnBegin ? 0 : lexeme.length; } /** * Handle the potential end of mode * * @param {RegExpMatchArray} match - the current match */ function doEndMatch(match) { const lexeme = match[0]; const matchPlusRemainder = codeToHighlight.substring(match.index); const endMode = endOfMode(top, match, matchPlusRemainder); if (!endMode) { return NO_MATCH; } const origin = top; if (top.endScope && top.endScope._wrap) { processBuffer(); emitKeyword(lexeme, top.endScope._wrap); } else if (top.endScope && top.endScope._multi) { processBuffer(); emitMultiClass(top.endScope, match); } else if (origin.skip) { modeBuffer += lexeme; } else { if (!(origin.returnEnd || origin.excludeEnd)) { modeBuffer += lexeme; } processBuffer(); if (origin.excludeEnd) { modeBuffer = lexeme; } } do { if (top.scope) { emitter.closeNode(); } if (!top.skip && !top.subLanguage) { relevance += top.relevance; } top = top.parent; } while (top !== endMode.parent); if (endMode.starts) { startNewMode(endMode.starts, match); } return origin.returnEnd ? 0 : lexeme.length; } function processContinuations() { const list = []; for (let current = top; current !== language; current = current.parent) { if (current.scope) { list.unshift(current.scope); } } list.forEach(item => emitter.openNode(item)); } /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */ let lastMatch = {}; /** * Process an individual match * * @param {string} textBeforeMatch - text preceding the match (since the last match) * @param {EnhancedMatch} [match] - the match itself */ function processLexeme(textBeforeMatch, match) { const lexeme = match && match[0]; // add non-matched text to the current mode buffer modeBuffer += textBeforeMatch; if (lexeme == null) { processBuffer(); return 0; } // we've found a 0 width match and we're stuck, so we need to advance // this happens when we have badly behaved rules that have optional matchers to the degree that // sometimes they can end up matching nothing at all // Ref: https://github.com/highlightjs/highlight.js/issues/2140 if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") { // spit the "skipped" character that our regex choked on back into the output sequence modeBuffer += codeToHighlight.slice(match.index, match.index + 1); if (!SAFE_MODE) { /** @type {AnnotatedError} */ const err = new Error(`0 width match regex (${languageName})`); err.languageName = languageName; err.badRule = lastMatch.rule; throw err; } return 1; } lastMatch = match; if (match.type === "begin") { return doBeginMatch(match); } else if (match.type === "illegal" && !ignoreIllegals) { // illegal match, we do not continue processing /** @type {AnnotatedError} */ const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.scope || '') + '"'); err.mode = top; throw err; } else if (match.type === "end") { const processed = doEndMatch(match); if (processed !== NO_MATCH) { return processed; } } // edge case for when illegal matches $ (end of line/text) which is technically // a 0 width match but not a begin/end match so it's not caught by the // first handler (when `ignoreIllegals` is true) if (match.type === "illegal" && lexeme === "") { if (match.index === codeToHighlight.length) ; else { // matched literal `\n` (with `$`) so we must manually add the newline // itself to the modeBuffer so it is not lost when we advance the cursor modeBuffer += "\n"; } return 1; } // infinite loops are BAD, this is a last ditch catch all. if we have a // decent number of iterations yet our index (cursor position in our // parsing) still 3x behind our index then something is very wrong // so we bail if (iterations > 100000 && iterations > match.index * 3) { const err = new Error('potential infinite loop, way more iterations than matches'); throw err; } /* Why might be find ourselves here? An potential end match that was triggered but could not be completed. IE, `doEndMatch` returned NO_MATCH. (this could be because a callback requests the match be ignored, etc) This causes no real harm other than stopping a few times too many. */ modeBuffer += lexeme; return lexeme.length; } const language = getLanguage(languageName); if (!language) { error(LANGUAGE_NOT_FOUND.replace("{}", languageName)); throw new Error('Unknown language: "' + languageName + '"'); } const md = compileLanguage(language); let result = ''; /** @type {CompiledMode} */ let top = continuation || md; /** @type Record */ const continuations = {}; // keep continuations for sub-languages const emitter = new options.__emitter(options); processContinuations(); let modeBuffer = ''; let relevance = 0; let index = 0; let iterations = 0; let resumeScanAtSamePosition = false; try { if (!language.__emitTokens) { top.matcher.considerAll(); for (;;) { iterations++; if (resumeScanAtSamePosition) { // only regexes not matched previously will now be // considered for a potential match resumeScanAtSamePosition = false; } else { top.matcher.considerAll(); } top.matcher.lastIndex = index; const match = top.matcher.exec(codeToHighlight); // console.log("match", match[0], match.rule && match.rule.begin) if (!match) break; const beforeMatch = codeToHighlight.substring(index, match.index); const processedCount = processLexeme(beforeMatch, match); index = match.index + processedCount; } processLexeme(codeToHighlight.substring(index)); } else { language.__emitTokens(codeToHighlight, emitter); } emitter.finalize(); result = emitter.toHTML(); return { language: languageName, value: result, relevance, illegal: false, _emitter: emitter, _top: top }; } catch (err) { if (err.message && err.message.includes('Illegal')) { return { language: languageName, value: escape(codeToHighlight), illegal: true, relevance: 0, _illegalBy: { message: err.message, index, context: codeToHighlight.slice(index - 100, index + 100), mode: err.mode, resultSoFar: result }, _emitter: emitter }; } else if (SAFE_MODE) { return { language: languageName, value: escape(codeToHighlight), illegal: false, relevance: 0, errorRaised: err, _emitter: emitter, _top: top }; } else { throw err; } } } /** * returns a valid highlight result, without actually doing any actual work, * auto highlight starts with this and it's possible for small snippets that * auto-detection may not find a better match * @param {string} code * @returns {HighlightResult} */ function justTextHighlightResult(code) { const result = { value: escape(code), illegal: false, relevance: 0, _top: PLAINTEXT_LANGUAGE, _emitter: new options.__emitter(options) }; result._emitter.addText(code); return result; } /** Highlighting with language detection. Accepts a string with the code to highlight. Returns an object with the following properties: - language (detected language) - relevance (int) - value (an HTML string with highlighting markup) - secondBest (object with the same structure for second-best heuristically detected language, may be absent) @param {string} code @param {Array} [languageSubset] @returns {AutoHighlightResult} */ function highlightAuto(code, languageSubset) { languageSubset = languageSubset || options.languages || Object.keys(languages); const plaintext = justTextHighlightResult(code); const results = languageSubset.filter(getLanguage).filter(autoDetection).map(name => _highlight(name, code, false) ); results.unshift(plaintext); // plaintext is always an option const sorted = results.sort((a, b) => { // sort base on relevance if (a.relevance !== b.relevance) return b.relevance - a.relevance; // always award the tie to the base language // ie if C++ and Arduino are tied, it's more likely to be C++ if (a.language && b.language) { if (getLanguage(a.language).supersetOf === b.language) { return 1; } else if (getLanguage(b.language).supersetOf === a.language) { return -1; } } // otherwise say they are equal, which has the effect of sorting on // relevance while preserving the original ordering - which is how ties // have historically been settled, ie the language that comes first always // wins in the case of a tie return 0; }); const [best, secondBest] = sorted; /** @type {AutoHighlightResult} */ const result = best; result.secondBest = secondBest; return result; } /** * Builds new class name for block given the language name * * @param {HTMLElement} element * @param {string} [currentLang] * @param {string} [resultLang] */ function updateClassName(element, currentLang, resultLang) { const language = (currentLang && aliases[currentLang]) || resultLang; element.classList.add("hljs"); element.classList.add(`language-${language}`); } /** * Applies highlighting to a DOM node containing code. * * @param {HighlightedHTMLElement} element - the HTML element to highlight */ function highlightElement(element) { /** @type HTMLElement */ let node = null; const language = blockLanguage(element); if (shouldNotHighlight(language)) return; fire("before:highlightElement", { el: element, language }); if (element.dataset.highlighted) { console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.", element); return; } // we should be all text, no child nodes (unescaped HTML) - this is possibly // an HTML injection attack - it's likely too late if this is already in // production (the code has likely already done its damage by the time // we're seeing it)... but we yell loudly about this so that hopefully it's // more likely to be caught in development before making it to production if (element.children.length > 0) { if (!options.ignoreUnescapedHTML) { console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."); console.warn("https://github.com/highlightjs/highlight.js/wiki/security"); console.warn("The element with unescaped HTML:"); console.warn(element); } if (options.throwUnescapedHTML) { const err = new HTMLInjectionError( "One of your code blocks includes unescaped HTML.", element.innerHTML ); throw err; } } node = element; const text = node.textContent; const result = language ? highlight(text, { language, ignoreIllegals: true }) : highlightAuto(text); element.innerHTML = result.value; element.dataset.highlighted = "yes"; updateClassName(element, language, result.language); element.result = { language: result.language, // TODO: remove with version 11.0 re: result.relevance, relevance: result.relevance }; if (result.secondBest) { element.secondBest = { language: result.secondBest.language, relevance: result.secondBest.relevance }; } fire("after:highlightElement", { el: element, result, text }); } /** * Updates highlight.js global options with the passed options * * @param {Partial} userOptions */ function configure(userOptions) { options = inherit(options, userOptions); } // TODO: remove v12, deprecated const initHighlighting = () => { highlightAll(); deprecated("10.6.0", "initHighlighting() deprecated. Use highlightAll() now."); }; // TODO: remove v12, deprecated function initHighlightingOnLoad() { highlightAll(); deprecated("10.6.0", "initHighlightingOnLoad() deprecated. Use highlightAll() now."); } let wantsHighlight = false; /** * auto-highlights all pre>code elements on the page */ function highlightAll() { function boot() { // if a highlight was requested before DOM was loaded, do now highlightAll(); } // if we are called too early in the loading process if (document.readyState === "loading") { // make sure the event listener is only added once if (!wantsHighlight) { window.addEventListener('DOMContentLoaded', boot, false); } wantsHighlight = true; return; } const blocks = document.querySelectorAll(options.cssSelector); blocks.forEach(highlightElement); } /** * Register a language grammar module * * @param {string} languageName * @param {LanguageFn} languageDefinition */ function registerLanguage(languageName, languageDefinition) { let lang = null; try { lang = languageDefinition(hljs); } catch (error$1) { error("Language definition for '{}' could not be registered.".replace("{}", languageName)); // hard or soft error if (!SAFE_MODE) { throw error$1; } else { error(error$1); } // languages that have serious errors are replaced with essentially a // "plaintext" stand-in so that the code blocks will still get normal // css classes applied to them - and one bad language won't break the // entire highlighter lang = PLAINTEXT_LANGUAGE; } // give it a temporary name if it doesn't have one in the meta-data if (!lang.name) lang.name = languageName; languages[languageName] = lang; lang.rawDefinition = languageDefinition.bind(null, hljs); if (lang.aliases) { registerAliases(lang.aliases, { languageName }); } } /** * Remove a language grammar module * * @param {string} languageName */ function unregisterLanguage(languageName) { delete languages[languageName]; for (const alias of Object.keys(aliases)) { if (aliases[alias] === languageName) { delete aliases[alias]; } } } /** * @returns {string[]} List of language internal names */ function listLanguages() { return Object.keys(languages); } /** * @param {string} name - name of the language to retrieve * @returns {Language | undefined} */ function getLanguage(name) { name = (name || '').toLowerCase(); return languages[name] || languages[aliases[name]]; } /** * * @param {string|string[]} aliasList - single alias or list of aliases * @param {{languageName: string}} opts */ function registerAliases(aliasList, { languageName }) { if (typeof aliasList === 'string') { aliasList = [aliasList]; } aliasList.forEach(alias => { aliases[alias.toLowerCase()] = languageName; }); } /** * Determines if a given language has auto-detection enabled * @param {string} name - name of the language */ function autoDetection(name) { const lang = getLanguage(name); return lang && !lang.disableAutodetect; } /** * Upgrades the old highlightBlock plugins to the new * highlightElement API * @param {HLJSPlugin} plugin */ function upgradePluginAPI(plugin) { // TODO: remove with v12 if (plugin["before:highlightBlock"] && !plugin["before:highlightElement"]) { plugin["before:highlightElement"] = (data) => { plugin["before:highlightBlock"]( Object.assign({ block: data.el }, data) ); }; } if (plugin["after:highlightBlock"] && !plugin["after:highlightElement"]) { plugin["after:highlightElement"] = (data) => { plugin["after:highlightBlock"]( Object.assign({ block: data.el }, data) ); }; } } /** * @param {HLJSPlugin} plugin */ function addPlugin(plugin) { upgradePluginAPI(plugin); plugins.push(plugin); } /** * @param {HLJSPlugin} plugin */ function removePlugin(plugin) { const index = plugins.indexOf(plugin); if (index !== -1) { plugins.splice(index, 1); } } /** * * @param {PluginEvent} event * @param {any} args */ function fire(event, args) { const cb = event; plugins.forEach(function(plugin) { if (plugin[cb]) { plugin[cb](args); } }); } /** * DEPRECATED * @param {HighlightedHTMLElement} el */ function deprecateHighlightBlock(el) { deprecated("10.7.0", "highlightBlock will be removed entirely in v12.0"); deprecated("10.7.0", "Please use highlightElement now."); return highlightElement(el); } /* Interface definition */ Object.assign(hljs, { highlight, highlightAuto, highlightAll, highlightElement, // TODO: Remove with v12 API highlightBlock: deprecateHighlightBlock, configure, initHighlighting, initHighlightingOnLoad, registerLanguage, unregisterLanguage, listLanguages, getLanguage, registerAliases, autoDetection, inherit, addPlugin, removePlugin }); hljs.debugMode = function() { SAFE_MODE = false; }; hljs.safeMode = function() { SAFE_MODE = true; }; hljs.versionString = version; hljs.regex = { concat: concat, lookahead: lookahead, either: either, optional: optional, anyNumberOfTimes: anyNumberOfTimes }; for (const key in MODES) { // @ts-ignore if (typeof MODES[key] === "object") { // @ts-ignore deepFreeze(MODES[key]); } } // merge all the modes/regexes into our main object Object.assign(hljs, MODES); return hljs; }; // Other names for the variable may break build script const highlight = HLJS({}); // returns a new instance of the highlighter to be used for extensions // check https://github.com/wooorm/lowlight/issues/47 highlight.newInstance = () => HLJS({}); export { highlight as default }; appstream-generator-1.0.0/data/templates/default/static/js/highlight/core.min.js000066400000000000000000000500251524443220100277440ustar00rootroot00000000000000/*! Highlight.js v11.12.0 (git: f7f7d3803b) (c) 2006-2026 Josh Goebel and other contributors License: BSD-3-Clause */ function e(t){return t instanceof Map?t.clear=t.delete=t.set=()=>{ throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ throw Error("set is read-only") }),Object.freeze(t),Object.getOwnPropertyNames(t).forEach(n=>{ const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) }),t}class t{constructor(e){ void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") }function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] ;return t.forEach(e=>{for(const t in e)n[t]=e[t]}),n}const s=e=>!!e.scope ;class r{constructor(e,t){ this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ if(e.startsWith("language:"))return e.replace("language:","language-") ;if(e.includes(".")){const n=e.split(".") ;return[`${t}${n.shift()}`,...n.map((e,t)=>`${e}${"_".repeat(t+1)}`)].join(" ")} return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}closeNode(e){ s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ this.buffer+=``}}const o=(e={})=>{const t={children:[]} ;return Object.assign(t,e),t};class a{constructor(){ this.rootNode=o(),this.stack=[this.rootNode]}get top(){ return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ this.top.children.push(e)}openNode(e){const t=o({scope:e}) ;this.add(t),this.stack.push(t)}closeNode(){ if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), t.children.forEach(t=>this._walk(e,t)),e.closeNode(t)),e}static _collapse(e){ "string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{ a._collapse(e)}))}}class c extends a{constructor(e){super(),this.options=e} addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ this.closeNode()}__addSublanguage(e,t){const n=e.root ;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ return new r(this,this.options).value()}finalize(){ return this.closeAllNodes(),!0}}function l(e){ return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} function h(...e){return e.map(e=>l(e)).join("")}function f(...e){const t=(e=>{ const t=e[e.length-1] ;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} })(e);return"("+(t.capture?"":"?:")+e.map(e=>l(e)).join("|")+")"}function p(e){ return RegExp(e.toString()+"|").exec("").length-1} const b=RegExp(f(/\[(?:[^\\\]]|\\.)*\]/,/\(\?<(?![=!])[^>]+>/,/\(\?'[^']+'/,/\(\??/,/\\([1-9][0-9]*)/,/\\./)) ;function m(e,{joinWith:t}){let n=0;return e.map(e=>{n+=1;const t=n ;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} s+=i.substring(0,e.index), i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], ("("===e[0]||/^\(\?[<']/.test(e[0]))&&n++)}return s}).map(e=>`(${e})`).join(t)} const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",_="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={ begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'", illegal:"\\n",contains:[O]},N={scope:"string",begin:'"',end:'"',illegal:"\\n", contains:[O]},S=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, contains:[]},n);s.contains.push({scope:"doctag", begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) ;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) ;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s },v=S("//","$"),M=S("/\\*","\\*/"),R=S("#","$");var A=Object.freeze({ __proto__:null,APOS_STRING_MODE:k,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:S, C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:v,C_NUMBER_MODE:{scope:"number", begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{ "on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, NUMBER_MODE:{scope:"number",begin:_,relevance:0},NUMBER_RE:_, PHRASAL_WORDS_MODE:{ begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ },QUOTE_STRING_MODE:N,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", SHEBANG:(e={})=>{const t=/^#![ ]*\// ;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function j(e,t){ "."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", e.__beforeBegin=j,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, void 0===e.relevance&&(e.relevance=0))}function L(e,t){ Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ if(e.match){ if(e.begin||e.end)throw Error("begin & end are not supported with match") ;e.begin=e.match,delete e.match}}function P(e,t){ void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return ;if(e.starts)throw Error("beforeMatch cannot be used with starts") ;const n=Object.assign({},e);Object.keys(e).forEach(t=>{delete e[t] }),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ relevance:0,contains:[Object.assign(n,{endsParent:!0})] },e.relevance=0,delete n.beforeMatch },H=["of","and","for","in","not","or","if","then","parent","list","value"] ;function C(e,t,n="keyword"){const i=Object.create(null) ;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach(n=>{ Object.assign(i,C(e[n],t,n))}),i;function s(e,n){ t&&(n=n.map(e=>e.toLowerCase())),n.forEach(t=>{const n=t.split("|") ;i[n[0]]=[e,$(n[0],n[1])]})}}function $(e,t){ return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{ console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{ U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0) },G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={} ;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1]) ;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{ e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ _wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope }),(e=>{if(Array.isArray(e.begin)){ if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), G ;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"), G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ if(Array.isArray(e.end)){ if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"), G ;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"), G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){ function t(t,n){ return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) }class n{constructor(){ this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} addRule(e,t){ t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) ;const e=this.regexes.map(e=>e[1]);this.matcherRe=t(m(e,{joinWith:"|" }),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex ;const t=this.matcherRe.exec(e);if(!t)return null ;const n=t.findIndex((e,t)=>t>0&&void 0!==e),i=this.matchIndexes[n] ;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ this.rules=[],this.multiRegexes=[], this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n ;return this.rules.slice(e).forEach(([e,n])=>t.addRule(e,n)), t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex ;let n=t.exec(e) ;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} return n&&(this.regexIndex+=n.position+1, this.regexIndex===this.count&&this.considerAll()),n}} if(e.compilerExtensions||(e.compilerExtensions=[]), e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") ;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r ;if(r.isCompiled)return a ;[I,B,F,D].forEach(e=>e(r,o)),e.compilerExtensions.forEach(e=>e(r,o)), r.__beforeBegin=null,[T,L,P].forEach(e=>e(r,o)),r.isCompiled=!0;let c=null ;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), c=r.keywords.$pattern, delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)), a.keywordPatternRe=t(c,!0), o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), r.end&&(a.endRe=t(a.end)), a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)), r.illegal&&(a.illegalRe=t(r.illegal)), r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map(e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(t=>i(e,{ variants:null},t))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{ starts:e.starts?i(e.starts):null }):Object.isFrozen(e)?i(e):e))("self"===e?r:e))),r.contains.forEach(e=>{n(e,a) }),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s ;return e.contains.forEach(e=>t.addRule(e.begin,{rule:e,type:"begin" })),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" }),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){ return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{ constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{ const i=Object.create(null),s=Object.create(null),r=[];let o=!0 ;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ disableAutodetect:!0,name:"Plain text",contains:[]};let p={ ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", cssSelector:"pre code",languages:null,__emitter:c};function b(e){ return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" ;"object"==typeof t?(i=e, n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."), X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};S("before:highlight",r) ;const o=r.result?r.result:E(r.language,r.code,n) ;return o.code=r.code,S("after:highlight",o),o}function E(e,n,s,r){ const c=Object.create(null);function l(e,t){return e.keywords[t]}function g(){ if(!v.keywords)return void R.addText(A);let e=0;v.keywordPatternRe.lastIndex=0 ;let t=v.keywordPatternRe.exec(A),n="";for(;t;){n+=A.substring(e,t.index) ;const i=k.case_insensitive?t[0].toLowerCase():t[0],s=l(v,i);if(s){const[e,r]=s ;if(R.addText(n), n="",c[i]=(c[i]||0)+1,c[i]<=7&&(j+=r),e.startsWith("_"))n+=t[0];else{ const n=k.classNameAliases[e]||e;d(t[0],n)}}else n+=t[0] ;e=v.keywordPatternRe.lastIndex,t=v.keywordPatternRe.exec(A)} n+=A.substring(e),R.addText(n)}function u(){null!=v.subLanguage?(()=>{ if(""===A)return;let e=null;if("string"==typeof v.subLanguage){ if(!i[v.subLanguage])return void R.addText(A) ;e=E(v.subLanguage,A,!0,M[v.subLanguage]),M[v.subLanguage]=e._top }else e=x(A,v.subLanguage.length?v.subLanguage:null) ;v.relevance>0&&(j+=e.relevance),R.__addSublanguage(e._emitter,e.language) })():g(),A=""}function d(e,t){ ""!==e&&(R.startScope(t),R.addText(e),R.endScope())}function h(e,t){let n=1 ;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} const i=k.classNameAliases[e[n]]||e[n],s=t[n];i?d(s,i):(A=s,g(),A=""),n++}} function f(e,t){ return e.scope&&"string"==typeof e.scope&&R.openNode(k.classNameAliases[e.scope]||e.scope), e.beginScope&&(e.beginScope._wrap?(d(A,k.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), A=""):e.beginScope._multi&&(h(e.beginScope,t),A="")),v=Object.create(e,{parent:{ value:v}}),v}function b(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) ;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) ;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ for(;e.endsParent&&e.parent;)e=e.parent;return e}} if(e.endsWithParent)return b(e.parent,n,i)}function m(e){ return 0===v.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}function _(e){ const t=e[0],i=n.substring(e.index),s=b(v,e,i);if(!s)return Q;const r=v ;v.endScope&&v.endScope._wrap?(u(), d(t,v.endScope._wrap)):v.endScope&&v.endScope._multi?(u(), h(v.endScope,e)):r.skip?A+=t:(r.returnEnd||r.excludeEnd||(A+=t), u(),r.excludeEnd&&(A=t));do{ v.scope&&R.closeNode(),v.skip||v.subLanguage||(j+=v.relevance),v=v.parent }while(v!==s.parent);return s.starts&&f(s.starts,e),r.returnEnd?0:t.length} let y={};function w(i,r){const a=r&&r[0];if(A+=i,null==a)return u(),0 ;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===a){ if(A+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`) ;throw t.languageName=e,t.badRule=y.rule,t}return 1} if(y=r,"begin"===r.type)return(e=>{ const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]] ;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return m(n) ;return i.skip?A+=n:(i.excludeBegin&&(A+=n), u(),i.returnBegin||i.excludeBegin||(A=n)),f(i,e),i.returnBegin?0:n.length})(r) ;if("illegal"===r.type&&!s){ const e=Error('Illegal lexeme "'+a+'" for mode "'+(v.scope||"")+'"') ;throw e.mode=v,e}if("end"===r.type){const e=_(r);if(e!==Q)return e} if("illegal"===r.type&&""===a)return r.index===n.length||(A+="\n"),1 ;if(T>1e5&&T>3*r.index)throw Error("potential infinite loop, way more iterations than matches") ;return A+=a,a.length}const k=O(e) ;if(!k)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"') ;const N=Z(k);let S="",v=r||N;const M={},R=new p.__emitter(p);(()=>{const e=[] ;for(let t=v;t!==k;t=t.parent)t.scope&&e.unshift(t.scope) ;e.forEach(e=>R.openNode(e))})();let A="",j=0,I=0,T=0,L=!1;try{ if(k.__emitTokens)k.__emitTokens(n,R);else{for(v.matcher.considerAll();;){ T++,L?L=!1:v.matcher.considerAll(),v.matcher.lastIndex=I ;const e=v.matcher.exec(n);if(!e)break;const t=w(n.substring(I,e.index),e) ;I=e.index+t}w(n.substring(I))}return R.finalize(),S=R.toHTML(),{language:e, value:S,relevance:j,illegal:!1,_emitter:R,_top:v}}catch(t){ if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n), illegal:!0,relevance:0,_illegalBy:{message:t.message,index:I, context:n.slice(I-100,I+100),mode:t.mode,resultSoFar:S},_emitter:R};if(o)return{ language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:R,_top:v} ;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} ;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(N).map(t=>E(t,e,!1)) ;s.unshift(n);const r=s.sort((e,t)=>{ if(e.relevance!==t.relevance)return t.relevance-e.relevance ;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 ;if(O(t.language).supersetOf===e.language)return-1}return 0}),[o,a]=r,c=o ;return c.secondBest=a,c}function _(e){let t=null;const n=(e=>{ let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" ;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) ;return t||(W(a.replace("{}",n[1])), W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} return t.split(/\s+/).find(e=>b(e)||O(e))})(e);if(b(n))return ;if(S("before:highlightElement",{el:e,language:n }),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) ;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), console.warn("The element with unescaped HTML:"), console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML) ;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i) ;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n ;e.classList.add("hljs"),e.classList.add("language-"+i) })(e,n,r.language),e.result={language:r.language,re:r.relevance, relevance:r.relevance},r.secondBest&&(e.secondBest={ language:r.secondBest.language,relevance:r.secondBest.relevance }),S("after:highlightElement",{el:e,result:r,text:i})}let y=!1;function w(){ if("loading"===document.readyState)return y||window.addEventListener("DOMContentLoaded",()=>{ w()},!1),void(y=!0);document.querySelectorAll(p.cssSelector).forEach(_)} function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} function k(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach(e=>{ s[e.toLowerCase()]=t})}function N(e){const t=O(e);return t&&!t.disableAutodetect }function S(e,t){const n=e;r.forEach(e=>{e[n]&&e[n](t)})}Object.assign(n,{ highlight:m,highlightAuto:x,highlightAll:w,highlightElement:_, highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"), X("10.7.0","Please use highlightElement now."),_(e)),configure:e=>{p=Y(p,e)}, initHighlighting:()=>{ w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, initHighlightingOnLoad:()=>{ w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") },registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ if(z("Language definition for '{}' could not be registered.".replace("{}",e)), !o)throw t;z(t),s=l} s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&k(s.aliases,{ languageName:e})},unregisterLanguage:e=>{delete i[e] ;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:k, autoDetection:N,inherit:Y,addPlugin:e=>{(e=>{ e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ e["before:highlightBlock"](Object.assign({block:t.el},t)) }),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)}, removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{ o=!1},n.safeMode=()=>{o=!0},n.versionString="11.12.0",n.regex={concat:h, lookahead:g,either:f,optional:d,anyNumberOfTimes:u} ;for(const t in A)"object"==typeof A[t]&&e(A[t]);return Object.assign(n,A),n },te=ee({});te.newInstance=()=>ee({});export{te as default};appstream-generator-1.0.0/data/templates/default/static/js/highlight/xml.js000066400000000000000000000153531524443220100270370ustar00rootroot00000000000000/* Language: HTML, XML Website: https://www.w3.org/XML/ Category: common, web Audit: 2020 */ /** @type LanguageFn */ function xml(hljs) { const regex = hljs.regex; // XML names can have the following additional letters: https://www.w3.org/TR/xml/#NT-NameChar // OTHER_NAME_CHARS = /[:\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]/; // Element names start with NAME_START_CHAR followed by optional other Unicode letters, ASCII digits, hyphens, underscores, and periods // const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/);; // const XML_IDENT_RE = /[A-Z_a-z:\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]+/; // const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/); // however, to cater for performance and more Unicode support rely simply on the Unicode letter class const TAG_NAME_RE = regex.concat(/[\p{L}_]/u, regex.optional(/[\p{L}0-9_.-]*:/u), /[\p{L}0-9_.-]*/u); const XML_IDENT_RE = /[\p{L}0-9._:-]+/u; const XML_ENTITIES = { className: 'symbol', begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/ }; const XML_META_KEYWORDS = { begin: /\s/, contains: [ { className: 'keyword', begin: /#?[a-z_][a-z1-9_-]+/, illegal: /\n/ } ] }; const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, { begin: /\(/, end: /\)/ }); const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { className: 'string' }); const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' }); const TAG_INTERNALS = { endsWithParent: true, illegal: /`]+/ } ] } ] } ] }; return { name: 'HTML, XML', aliases: [ 'html', 'xhtml', 'rss', 'atom', 'xjb', 'xsd', 'xsl', 'plist', 'wsf', 'svg' ], case_insensitive: true, unicodeRegex: true, contains: [ { className: 'meta', begin: //, relevance: 10, contains: [ XML_META_KEYWORDS, QUOTE_META_STRING_MODE, APOS_META_STRING_MODE, XML_META_PAR_KEYWORDS, { begin: /\[/, end: /\]/, contains: [ { className: 'meta', begin: //, contains: [ XML_META_KEYWORDS, XML_META_PAR_KEYWORDS, QUOTE_META_STRING_MODE, APOS_META_STRING_MODE ] } ] } ] }, hljs.COMMENT( //, { relevance: 10 } ), { begin: //, relevance: 10 }, XML_ENTITIES, // xml processing instructions { className: 'meta', end: /\?>/, variants: [ { begin: /<\?xml/, relevance: 10, contains: [ QUOTE_META_STRING_MODE ] }, { begin: /<\?[a-z][a-z0-9]+/, } ] }, { className: 'tag', /* The lookahead pattern (?=...) ensures that 'begin' only matches ')/, end: />/, keywords: { name: 'style' }, contains: [ TAG_INTERNALS ], starts: { end: /<\/style>/, returnEnd: true, subLanguage: 'css' } }, { className: 'tag', // See the comment in the