Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

能不能增加一个可以直接行内插入的选项 #2

Open
zhujianxintian opened this issue Jul 7, 2024 · 8 comments
Open

能不能增加一个可以直接行内插入的选项 #2

zhujianxintian opened this issue Jul 7, 2024 · 8 comments

Comments

@zhujianxintian
Copy link
Contributor

1、因为多个 js 会有多个 http 请求, 虽然现在 http 2 有多路复用,但是不可能所有的网站都是 http 2 以上
2、如果是使用导入的话可能会有脚本加载失败的可能性

所以想要一个直接将代码行内插入的功能

@hemengke1997
Copy link
Owner

这个建议很好,但是我这边加feature的话改动会比较大
在用户侧可以这样来实现你的想法:

  1. 使用 destination: 'file' 模式来生成js文件
  2. 自定义一个vite的transformIndexHtml钩子插件,通过fs.readFile来读取js文件,插入到html中。可参考 https://github.com/hemengke1997/vite-plugin-public-typescript/blob/master/src/node/plugins/inject-script.ts
  3. 使用你自定义的钩子替换 injectScripts

@zhujianxintian
Copy link
Contributor Author

@hemengke1997 我这边试着实现了一下,感觉改动幅度应该还好, 但是因为我不好直接更改项目

这是我直接在项目中写的代码,你可以先看一下, 看看是否能够将其合并进库中, 有不同或者冗余的地方可以改动一下

或者有更好的代码也是可以的

// inject-script.ts
import path from 'node:path';
import fsPromises from 'node:fs/promises';
import { type HtmlTagDescriptor, type PluginOption } from 'vite';
import { getManifest } from 'vite-plugin-public-typescript';

const VPPT_DATA_ATTR = 'data-vite-plugin-public-typescript';

export interface ScriptDescribeMetadata extends Omit<HtmlTagDescriptor, 'tag'> {
    inline?: boolean;
}

export type ScriptsCreator = (manifest: Record<string, string>) => ScriptDescribeMetadata[];

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const generateInjectScriptTags = async ({ inline, ...script }: ScriptDescribeMetadata) => {
    return {
        ...script,
        attrs: {
            crossorigin: true,
            ...script.attrs,
            [VPPT_DATA_ATTR]: true,
        },
        tag: 'script',
    };
};

const generateInlineScriptTags = async ({ inline, ...script }: ScriptDescribeMetadata) => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { src, ...attrs } = script.attrs as any;

    const projectRoot = process.cwd();

    const filePath = path.resolve(projectRoot, 'public', `.${src}`);

    const content = (await fsPromises.readFile(filePath)).toString('utf-8');

    return {
        ...script,
        attrs: {
            crossorigin: true,
            ...attrs,
            [VPPT_DATA_ATTR]: true,
        },
        tag: 'script',
        children: inline ? content : void 0,
    };
};

const generateScriptTagsAdapter = async (metadata: ScriptDescribeMetadata) => {
    const { inline } = metadata;
    if (inline) {
        return await generateInlineScriptTags(metadata);
    } else {
        return await generateInjectScriptTags(metadata);
    }
};

async function generateScriptTags(scriptsCreator: ScriptsCreator) {
    const _scripts = scriptsCreator(getManifest()) || [];
    return await Promise.all(_scripts.map(generateScriptTagsAdapter));
}

export function injectScripts(scriptsCreator: ScriptsCreator) {
    const plugin: PluginOption = {
        name: 'vite:public-typescript:inject-script',
        enforce: 'post',
        transformIndexHtml: {
            async handler(html) {
                return {
                    html,
                    tags: await generateScriptTags(scriptsCreator),
                };
            },
            order: 'post',
        },
    };

    // Return as `any` to avoid Plugin type mismatches when there are multiple Vite versions installed
    return plugin as any;
}

@hemengke1997
Copy link
Owner

const content = (await fsPromises.readFile(filePath)).toString('utf-8');

    return {
        ...script,
        attrs: {
            crossorigin: true,
            ...attrs,
            [VPPT_DATA_ATTR]: true,
        },
        tag: 'script',
        children: inline ? content : void 0,
    };

这样脚本走的是vite内部的transform流程,而不是vite-plugin-public-typescript内置的esbuild的transform,行为不一致

@hemengke1997
Copy link
Owner

按照你的思路,不需要此插件就可以实现你想要的功能了。你已经把关键的代码写出来了。

  1. fs读取指定目录下的ts文件
  2. 插入到script的children中

@zhujianxintian
Copy link
Contributor Author

是的,但是毕竟不想把这个代码直接写在项目中,如果库直接能支持那就更好,这样如果我有多个项目使用的话,就不用复制粘贴
还有我对 esbuild 不是太了解,不是很懂,只是参考你之前给出的链接写的

@zhujianxintian
Copy link
Contributor Author

按照你的思路,不需要此插件就可以实现你想要的功能了。你已经把关键的代码写出来了。

  1. fs读取指定目录下的ts文件
  2. 插入到script的children中

针对这个的话,我想的是我改动的只是插入方式,编译 ts 什么的我没有关注到,编译 ts 的话还是交给库来做

@hemengke1997
Copy link
Owner

我这边首先要做到 inline 和 非inline 的构建结果一致,所以要处理的一些边缘情况要多些,改动较大。
我会尽快实现此feature,因为正如你所说的,有些脚本放在html中是个更好的选择

@zhujianxintian
Copy link
Contributor Author

@hemengke1997 ok,非常感谢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants