Skip to content

For contributors

This project is a monorepo with the following structure:

  • apps - applications.
    • docs - documentation website.
  • demos - demos.
    • all - meta-package for all demos.
    • inline - inline demos for various pages outside of specific framework context.
    • * - demos for each supported framework.
  • packages - loader itself, integrations and internal utilities.
    • integration-utils - utility functions and constants for integrations. They are exposed to the user via vite-awesome-svg-loader/integration-utils package. This makes it possible to write custom integrations.
    • loader - loader’s source code.
    • types - types for usage throughout the project.
    • common-utils - various utilities for usage throughout the project. They are exposed to the user via vite-awesome-svg-loader/common-utils package for convenience.
    • internal-utils - utilities for internal usage only.
    • *-integration - specific framework integration.
    • vite-astro-entry-generator - a plugin that generates an Astro component that loads a script on the client side.
    • vite-awesome-svg-loader - final loader bundle (in other words, meta-package).
    • vite-file-tree-builder - a plugin that builds a project’s file tree.
  • tests - tests.
  • scripts - build scripts.
  • modules.d.ts - types for untyped external modules.

Turborepo is used to manage monorepo.

Demos are self-contained packages.

All demos should contain their own scripts and assets. The code and the assets are often duplicated across the demos.

When a demo is built, its file tree is generated and displayed in the docs website.

Demos should be small and should not have a sprawling file tree.

Demos are built in Vite library mode and consumed by the apps/docs package.

This pattern avoids the need to run multiple web servers for live reload and simplifies build process.

  1. generate-sources - generates project’s source code required for building. Runs automatically before any other build command.
  2. build - builds whole project.
  3. build:packages - builds all packages. Use this while developing to speed up the build.
  4. build:apps - builds all apps.
  5. build:demos - builds all demos.
  6. dev - builds all packages and runs all dev commands. This command is resource-heavy!
  7. dev:docs:standalone - runs dev command for docs packages without running all demos in watch mode. This is a lighter alternative to dev. The docs are still updated whenever demo is rebuilt.
  8. test - runs the tests.
  9. lint - lints the code with ESLint.
  10. format - formats the code with Prettier.

The primary language for this project is TypeScript. TypeScript should be used while the developing loader itself, integrations and demos.

However, build scripts are written in JS to simplify development and speed up the build. Typing is not crucial for these scripts. Thus, TypeScript is not necessary.

Vite is the primary bundler for this project.

Other bundlers may be used if Vite ecosystem doesn’t have a solution, or if specific package requires specific tooling. For example, docs app uses Astro and its tooling (it’s Vite internally, but this may change in future).

Custom build scripts and/or plugins may be used in the build process. Ideally, custom additions to the build process should be implemented as bundler’s plugins or build lifecycle hooks. But plain JS scripts are fine if they’re suited better for the job.

Every project (package, demo or app) should be bundled into a dist directory inside the project’s root.

For example, packages/utils bundle should be in packages/utils/dist directory.

Dependencies that are used by more than one project should be installed for the whole project (added to the root package.json).

Such dependencies include Vite, TypeScript, Vue, React, Solid, etc.

Local dependencies should be installed in package or app where they’re required.

This is how build command works:

  1. generate-sources command is run, and the source code is generated:
    1. vite-awesome-svg-loader source code is generated:
    2. Packages are added as dependencies.
    3. Re-exports from packages are added.
    4. Subpath exports are updated.
    5. all-demos package is generated. All demos are added as the dependencies to the apps/docs package.
  2. All packages are built.
  3. vite-awesome-svg-loader package is built.
  4. All demos are built.
  5. Apps are built.

ESLint is used for linting.

Prettier is used for formatting. All configuration options are set. Formatting that is not covered by Prettier is handled by ESLint.

This project tries to keep the source code clean and readable. Please try to do so as well. For example, don’t nest too many conditions, don’t use bit shifts unnecessarily, etc.

  1. Create an issue to notify the community that you’ll be working on your integration.
  2. Create an integration package using npx turbo generate workspace command or by copying an existing integration.
  3. Develop integration:
    1. Open package.json file inside your integration directory.
    2. Add "integration-utils": "*" dependency.
    3. Develop your integration.
  4. Run npm run generate-sources command to regenerate vite-awesome-svg-loader source code. Or just run any build or dev command.
  5. Develop demos for your integration:
    1. Copy any existing demo and pages in the /apps/docs/src/content/docs directory.
    2. Develop your demos.
    3. Update the docs:
      1. Add a glob import for the demos inside the /apps/docs/src/demos directory.
      2. Update the previously copied pages.
      3. Feel free to add your own pages if needed.
  6. Submit a PR with your integration.
  1. Create an issue to notify the community that you’ll be working on your feature.
  2. Develop your feature. Make sure to document it with JSDoc.
  3. Update the docs to showcase your feature.
  4. Test if demos work.
  5. Submit a PR.

Docs and demos may be run in parallel with live reload by using npm run dev command.

However, this spins up a lot of Vite watchers running simultaneously. If your machine can handle it, it’s fine.

Alternative workflow is recommended:

  1. Run npm run build:apps command to build all packages and demos.
  2. Run npm run dev:docs:standalone command to run documentation website.
  3. After changing the code, run npm run build:apps command again. This will rebuild only changed packages.
  4. If multiple packages are rebuilt, Astro server may hang. In this case, kill it and run npm run dev:docs:standalone command again.

This guide outlines testing principles. For concrete examples, see already existing tests.

The goal of the tests is to verify that the final library’s package is working correctly.

  1. Tests should be added as packages inside the /tests/ directory.

  2. There must be one test suite per package.

  3. Tests structure must mirror package’s structure. Multiple test files per one package’s file may be used. In this case, test files must be placed in a folder with the package’s file’s name.

  4. In future, demos in the documentation website may also be automatically visually tested.

  5. If multiple packages perform the same tests, the tests may be abstracted. The abstraction must be placed in the tests directory.

  6. While testing is performed on per-package basis, the vite-awesome-svg-loader package must be used instead of its dependencies.

    It is reasonable to think that if all dependencies are tested, transitively the compilation of these dependencies is also tested. In practice, a change in tsconfig.json or some Vite plugin may alter the final bundle’s behavior which renders tests invalid.

  7. Tests must be performed in a real browser for correctness’ sake.

  8. If possible, the widest range of modern browsers should be tested.

  9. Tests should focus on vite-awesome-svg-loader, not the frameworks.

  10. Tests for trivial code may be omitted. For example, there’s no need to test that 2+2 equals 5. Nevertheless, these tests are welcome if they don’t bring unreasonable maintenance burden.

Each test suite may use its own tooling. However, some recommendations have formed while researching the available software.

The tests must follow chosen tooling’s conventions.

Unit and integration testing. DOM manipulation. Components.

Section titled “Unit and integration testing. DOM manipulation. Components.”

Cypress is recommended for testing DOM manipulation and framework components.

Cypress is the only tool that can run tests in the same context as components and supports multiple browsers out of the box. This makes state modification easy and boilerplate-free.

Cypress supports synchronous assertions via Chai and asynchronous assertions (with wait time, retries and other features) via its own API (cy.get(...).should(...)).

This allows testing both synchronous DOM manipulation and asynchronous framework lifecycle.

If testing user’s interactions only is required, Playwright is recommended.

It supports all major browsers and accurately simulates user’s actions (events are fired as the result of user’s interactions, not as a JS script).