skip to Main Content

I am trying to create a Chrome extension using SvelteKit and sveltekit-adapter-chrome-extension. I have an anilist_client.js file under /static where I am declaring an ANILIST_CLIENT object that stores an ID an a SECRET that will later be used to access the AniList API.

The file must be withing the /static directory so the user can replace the ID with their own without digging deep into the bundled code.

I tried to include the script in app.html and <svelte:head>, also followed this guide. All I got was "anilist_client is not defined".

// static/anilist_client.js

const anilist_client = {
    id: '<id>',
    secret: '<secret>'
};
// src/lib/components/Options.svelte

<script>
    console.log(anilist_client.secret);
</script>

<a href='https://anilist.co/api/v2/oauth/authorize?client_id={anilist_client.id}&response_type=token'>Login with AniList</a>
// src/app.html

<head>
    <meta charset="utf-8" />
    <link rel="icon" href="%sveltekit.assets%/favicon.png" />
    <meta name="viewport" content="width=device-width" />
    <script src="/anilist_client.js"></script>
    <!-- <script src="%sveltekit.assets%/anilist_client.js"></script> -->
    %sveltekit.head%
</head>
// svelte.config.js

import chromeExtensionAdapter from 'sveltekit-adapter-chrome-extension';
import { vitePreprocess } from '@sveltejs/kit/vite';
const config = {
    preprocess: vitePreprocess(),

    kit: {
        adapter: chromeExtensionAdapter({
            pages: 'build',
            assets: 'build',
            fallback: null,
            precompress: false,
            manifest: 'manifest.json',
        }),
        appDir: 'ext',
    }
};

export default config;

Is there a way to access this object from my components that will run both in development and production bundle? Feels like I am missing something very obvious here.

2

Answers


  1. Chosen as BEST ANSWER

    After a lot of searching, I finally find a way to make it work:

    First, I moved anilist_client.js from static/ to src/lib/anilist_client.ts, so I could easily import it from my components:

    // src/lib/components/Options.svelte
    
    <script>
        import anilist_client from '$lib/anilist_client';
        import '$lib/anime';
    
        console.log(anilist_client.secret);
    </script>
    
    <main>
        <a href='https://anilist.co/api/v2/oauth/authorize?client_id={anilist_client.id}&response_type=token'>Login with AniList</a>
    </main>
    

    The next step was to add anilist_client.ts to build.rollupOptions.input in vite.config.ts:

    build: {
        rollupOptions: {
            input: {
                anilist_client: fileURLToPath(new URL('./src/lib/anilist_client.ts', import.meta.url)),
            }
        }
    }
    

    Since Sveltekit seems to override the build.output.*FileNames Vite options, in order for it to work with Chrome's background script, I ended up writing a Vite plugin to remove the hash from the generated .js files, so I can refer to background.js in manifest.json. Thanks to raduab and wallw-teal.

    // remove-hash-from-entry.js
    
    export default function removeHashFromSvelteKitEntryFiles(config) {
        return {
            name: 'remove-hash-from-sveltekit-entry-files',
            apply: 'build',
            enforce: 'post',
    
            config(config) {
                config.build.rollupOptions.output.entryFileNames =
                    config.build.rollupOptions.output.entryFileNames.replace('[hash]', ``);
    
                return config;
            }
        }
    }
    
    // vite.config.ts
    
    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vite';
    import { fileURLToPath } from 'url';
    import removeHashFromSvelteKitEntryFiles from './remove-hash-from-entry';
    
    export default defineConfig({
        plugins: [sveltekit(), removeHashFromSvelteKitEntryFiles()],
        build: {
            rollupOptions: {
                input: {
                    anilist_client: fileURLToPath(new URL('./src/lib/anilist_client.ts', import.meta.url)),
                    background: fileURLToPath(new URL('./src/lib/background.ts', import.meta.url)),
                    anime: fileURLToPath(new URL('./src/lib/anime.ts', import.meta.url))
                },
                preserveEntrySignatures: 'strict',
            }
        }
    });
    

    There are probably better ways, but this was the first one to work after two days of research.


  2. You seem to be trying to define a global variable using const. If anything, I think you should be using var, and not const.

    But I digress because there are better alternatives. Right off the bat, you could just attach your aniList_client object to the global object, usually window in browsers. Try this:

    globalThis.aniList_client = {
        id: blah,
        secret: blahblah
    }
    

    Then access it the same way (globalThis.aniList_client).

    The other option would be to make your static JS a ESM module and load it as such in <head>:

    <head>
        ...
        <script src="/aniList_client.js" type="module"></script>
        ...
    </head>
    ...
    

    Then you would write said file as an ESM that exports your data:

    export default const aniList_client = {
        ...
    };
    

    The latter would probably require you to signal rollup or webpack that your module is external. I don’t have the specifics for this, though.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search