wxt-dev / wxt

⚡ Next-gen Web Extension Framework
https://wxt.dev
MIT License
4.14k stars 168 forks source link

Extract entrypoint options improvements #506

Closed aklinker1 closed 2 months ago

aklinker1 commented 7 months ago

This is an issue for addressing several issues with how entrypoints are imported to get their options.

Edit: As of v0.19.0-alpha1, all the above issues work with:

export default defineConfig({
  experimental: {
    entrypointImporter: "vite-node",
  },
});

Will add to this list if other issues come up.

aklinker1 commented 5 months ago

Alright, I have a proof of concept using Vite's runtime API to load entrypoints: https://github.com/wxt-dev/wxt/pull/585

So this should cover all the issues people have reported.

HOWEVER... it appears the vite runtime can't import CSS modules, which is kinda important. I've asked a question about it in the vite discord, so we'll se if I get a useful response. https://discord.com/channels/804011606160703521/1236142702785990677/1236142702785990677

aklinker1 commented 5 months ago

Alright, it was an issue on my side. Just needed to start the vite server lol. So it's working now.

I need to clean up my changes before opening a real PR. Probably won't get to it this weekend... we'll see. But I got everything working :)

aklinker1 commented 5 months ago

Last problem to address... Module side effects.

Say you're using storage item in your content script:

// utils/storage.ts
export const isXyzEnabled = storage.defineItem(...);
// entrypoints/content.ts
import { isXyzEnabled } from '~/utils/storage';

export default defineContentScript({
  // ...
  async main() {
    console.log(await isXyzEnabled.getValue());
  }
});

In this example, storage.defineItem actually includes side effects, like checking if the storage item needs migrated. Should a side effect like this run? I don't think so, and I can't think of reason a side effect like this should ever run. That is, unless it's related to an imported variable used in the options.

So I have a decision to make. When importing entrypoints during the build to get their config, do I...

  1. delete the main function and remove unused imports to prevent side-effects from running? or
  2. Leave everything as is and let all side-effects run

I think... I'm gonna go with option 1, and remove the main function. For performance reasons (importing less code as project grows) as well as avoiding weird edge cases around the node environment these files are loaded into (similar to what I'm currently doing with jiti).

So I have to decide the best way to do that. Removing the main function should be simple... Use magicast or regex. To remove unused imports after that... I'm not sure how to do that. Regex again? Or use magicast to get the imports, and find/replace if they're used. Probably that. I'll just use magicast for everything if possible.

aklinker1 commented 5 months ago

Playing around a bit, removing unused imports isn't a simple task lol. Instead, I'm just going to remove main function via magicast, then let vite tree-shake out the modules? Not sure how the runtime API handles this... Might need to come back in the future and remove imports properly.

aklinker1 commented 5 months ago

Released v0.18.0 with an experimental flag to enable this feature. If you use PNPM, for some reason it requires setting shamefully-hoist=true... And there are a couple of other problems when ran outside this monorepo... So this isn't quite ready for use yet.

rxliuli commented 3 months ago

I don't understand why there are no obvious side effects after importing, and it doesn't work. For example, extracting the configuration file to config.json and then using it doesn't work.

config.json

{
  "list": [
    { "name": "John", "age": 30 },
    { "name": "Jane", "age": 25 },
    { "name": "Jim", "age": 40 }
  ]
}

background.ts

import { defineBackground } from '~/sandbox';
import config from './config.json';

const list = config.list;

export default defineBackground({
  main() {
    console.log(list);
  },
});
aklinker1 commented 3 months ago

@rxliuli WXT doesn't look at any files to try and decide if there are side-effects or not, it just removes all imports from the top of your entrypoints.

So instead of loading your background as is, it loads it with the following text content:

import { defineBackground } from 'wxt';

const list = config.list;

export default defineBackground({
  main() {
    console.log(list);
  },
});

In this case, auto-imports are disabled, so of course config is not defined, and config.list throws an error.

aklinker1 commented 3 months ago

Also, not sure how I didn't think of this until now, but I could use vite-node to load the entrypoints 🤦

https://github.com/vitest-dev/vitest/tree/main/packages/vite-node#programmatic-usage

aklinker1 commented 3 months ago

Alright, I added an experimental option to let you choose between jiti, vite-runtime, and vite-node (new). Released in v0.18.9.

experimental: {
-  viteRuntime: true,
+  entrypointImporter: "vite-node"
}

Gonna go through all the original issues to see if this option fixes them!

aklinker1 commented 3 months ago

Alright, integrated entrypointImporter: "vite-node" into GitHub Better Line Counts for real world testing, and it crashed... I had to add any modules that import webextension-polyfill to the ssr.noExternal vite config, and then it worked like magic:

// wxt.config.ts
  vite: () => ({
    plugins: [Icons({ compiler: "vue3" }), Vue()],
+   ssr: {
+     noExternal: ["@webext-core/messaging", "@webext-core/proxy-service"],
+   },
  }),

You also have to do this for unit tests... I think it's time I implement something to detect these packages and add them to the ssr.noExternal option.

That, or make everything ssr.noExternal: [/.*/]? Edit: Nope, that causes lots of logs, but it did technically work: https://github.com/wxt-dev/wxt/issues/703#issuecomment-2181663412

aklinker1 commented 2 months ago

I haven't received any feedback, but vite-node is working for me, just need to add packages to ssr.noExternal. I'm going to make the the default, and release it along with other breaking changes in v0.19.0.

I've also confirmed that all the issues mentioned in the original description have been fixed.

NXY666 commented 1 month ago

Is the current configuration set to use "vite-node" by default? When I upgrade from "0.18.4" to any later version, I always encounter an error.

WXT 0.19.4                                                                                                    10:53:03
× Command failed after 4.019 s                                                                                10:53:07

 ERROR  Cannot find module '&/modules/messages'                                                               10:53:07
Require stack:
- E:\Projects\bower-bird\src\modules\chrome_api.ts

  Require stack:
  - src\modules\chrome_api.ts
  at Module._resolveFilename (node:internal/modules/cjs/loader:1145:15)
  at Function.resolve (node:internal/modules/helpers:190:19)
  at _resolve (node_modules\jiti\dist\jiti.js:1:241814)
  at jiti (node_modules\jiti\dist\jiti.js:1:244531)
  at E:/Projects/bower-bird/src/modules/chrome_api.ts:1:278
  at evalModule (node_modules\jiti\dist\jiti.js:1:247313)
  at Object.jiti (node_modules\jiti\dist\jiti.js:1:245241)
  at resolveConfig (/E:/Projects/bower-bird/node_modules/c12/dist/shared/c12.cab0c9da.mjs:345:26)
  at loadConfig (/E:/Projects/bower-bird/node_modules/c12/dist/shared/c12.cab0c9da.mjs:147:29)
  at /E:/Projects/bower-bird/node_modules/wxt/dist/core/utils/building/resolve-config.mjs:342:32

I still see jiti in the stack—is this correct?

Here is my configuration file:

import {defineConfig} from "wxt";
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import {ElementPlusResolver} from "unplugin-vue-components/resolvers";
import htmlMinifier from 'vite-plugin-html-minifier';

// noinspection JSUnusedGlobalSymbols
export default defineConfig({
    srcDir: 'src',
    publicDir: 'public',
    entrypointsDir: 'entries',
    browser: 'chrome',
    manifest: {
        name: "Gardener Bird",
        minimum_chrome_version: "120",
        version: "0.0.0.0",
        version_name: "0.0.0",
        description: "The description is well-written.",
        icons: {
            "128": "./icons/icon.png"
        },
        author: "NXYBW",
        host_permissions: [
            "<all_urls>"
        ],
        permissions: [
            "contextMenus",
            "storage",
            "tabs",
            "offscreen",
            "scripting",
            "userScripts",
            "activeTab",
            "unlimitedStorage",
            "declarativeNetRequest",
            "declarativeNetRequestFeedback",
        ]
    },
    alias: {
        "&": "src",
        "@": "src/entries",
        "@OPT": "src/entries/options",
        "@POP": "src/entries/popup",
        "@OFF": "src/entries/offscreen",
    },
    vite: () => ({
        plugins: [
            vue(),
            AutoImport({
                resolvers: [ElementPlusResolver()]
            }),
            Components({
                resolvers: [ElementPlusResolver()]
            }),
            htmlMinifier()
        ],
        build: {
            target: ['chrome120', 'edge120'],
        },
    })
});

Since I haven't worked on it for a few months, I'm not sure if the configuration needs to be modified or if this might be a new issue?

I also tried adding

ssr: {
    noExternal: ['@webext-core/messaging', '@webext-core/proxy-service'],
},

but it doesn't seem to help :(

1natsu172 commented 1 month ago

@NXY666 Although not yet documented in the directory-structure doc, modules directory have recently become a meaningful concept. (https://wxt.dev/guide/go-further/reusable-modules.html)

I think there is a namespace conflict with the traditional src/modules you have. Try customizing modulesDir in your wxt.config.ts.

{
    srcDir: 'src',
    publicDir: 'public',
    entrypointsDir: 'entries',
    modulesDir: 'wxt-modules', // dir name is example, as you like
...
}
NXY666 commented 1 month ago

@NXY666 Although not yet documented in the directory-structure doc, modules directory have recently become a meaningful concept. (https://wxt.dev/guide/go-further/reusable-modules.html)

I think there is a namespace conflict with the traditional src/modules you have. Try customizing modulesDir in your wxt.config.ts.

Thank you very much!

You're right, I saw this new feature in the documentation this morning, but I didn't realize it was caused by a folder naming conflict.