Skip to content

Setup Environment

Requirements

  • Install git
  • Install NodeJS 20 with NPM 10
  • All tools can be run directly from terminal (a.g., added to the Path environment variable)

Checkout Sources

  1. Clone the repository from GitHub
    git clone "https://github.com/manga-download/haruneko.git"
  2. Open a terminal and change into the cloned repository
    cd haruneko
  3. Download and install all package dependencies
    npm install

NOTE

Perform npm install regulary after pulling code from the remote repository as a contributor may have changed some package dependencies

Project Structure

The repository contains four workspaces (projects)

  • The web-application located in the /web folder
  • An NW.js client in the /app/nw folder used to run the web-application on desktop
  • An Electron client in the /app/electron folder used to run the web-application on desktop
  • All related documentation for deployment to a public website in the docs folder

Code Inspection

The project uses various tools to perform a static code analysis and detect compile time errors as well as code smells and formatting issues.

  • Run code inspection for all projects (e.g., before pushing changes to the remote)
    npm run check

Test

  • Run unit/component tests for all projects
    npm run test
  • Run end-to-end tests for all projects
    npm run test:e2e
  • Run website tests for one or more specific website names
    npm run test:websites -- MangaDex

Launch

Run the application based on the current source code locally.

NOTE

If you are on Apple Silicon (Arm64) and NW isn't starting, try:

zsh
xattr -cr ./node_modules/nw/nwjs/nwjs.app

See: ARM64 Error | “app” is damaged and can’t be opened.

Development Mode

Hot-Reloading is not working correctly, but the web-application will reload automatically whenever a change is made to the source. Changes made to the source code of the desktop clients while running however requires the corresponding desktop client to be closed and restarted.

  1. Host the web-application locally at localhost:3000
    npm run serve:dev --workspace web
  2. Either run the Electron or NW.js desktop client and connect it to the local hosted web-application
    npm run launch:dev --workspace app/electron

Production Mode

Changes to the source code are neither reflected in the web-application, nor in the desktop client. Both needs to be restarted in order to apply source code changes.

  1. Host the web-application locally at localhost:5000
    npm run serve:prod --workspace web
  2. Either run the Electron or NW.js desktop client and connect it to the local hosted web-application
    npm run launch:prod --workspace app/electron

Debug

While running the desktop application, press the F12 key to open the developer tools. Source code can be inspected and debugged from the Source tab. Code can directly be injected via the Console tab for prototyping.

Sample Injection Script
javascript
(async () => {
    const website = HakuNeko.PluginController.WebsitePlugins[0];
    console.log('Website:', website.Title);
    if(website.Entries.length === 0) {
        console.log('=>', 'Updating manga list (this may take some time ...)');
        await website.Update();
    } else {
        console.log('=>', 'Using manga list from local cache');
    }

    async function getPages(mangaIndex, chapterIndex) {

        const manga = website.Entries[mangaIndex]; // or with iterator: [...website][mangaIndex];
        console.log(' '.repeat(4), 'Manga:', manga.Title);
        if(manga.Entries.length === 0) {
            console.log(' '.repeat(4), '=>', 'Updating chapter list');
            await manga.Update();
        } else {
            console.log(' '.repeat(4), '=>', 'Use current chapter list');
        }
        
        const chapter = manga.Entries[chapterIndex]; // or with iterator: [...manga][chapterIndex];
        console.log(' '.repeat(8), 'Chapter:', chapter.Title);
        if(chapter.Entries.length === 0) {
            console.log(' '.repeat(8), '=>', 'Updating page list');
            await chapter.Update();
        } else {
            console.log(' '.repeat(8), '=>', 'Use current page list');
        }
        
        for(const page of chapter) {
            console.log(' '.repeat(12), 'Page:', page.SourceURL);
        }
    }

    await getPages(0, 0);
    await getPages(13, 7);