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

首次启动后,变更service文件,没有触发热更新,应该是碰到了时序问题 #16

Open
yuntian001 opened this issue Apr 26, 2024 · 10 comments

Comments

@yuntian001
Copy link

yuntian001 commented Apr 26, 2024

const restart = debounce(async () => {
    if (fileChangeList.length === 0) return;
    async function aliasReplace() {
      if (hasPaths) {
        // 这里使用全量替换,tsc 增量编译会把老文件修改回去
        await replaceTscAliasPaths({
          configFile: tsconfigPath,
          outDir,
        });
        // 避免重复触发文件变化
        isCompileSuccess = false;
      }
      output(
        `${fileChangeList.length} ${colors.dim('Files has been changed.')}`,
        true
      );
      // 清空文件列表
      fileChangeList.length = 0;
    }

    await Promise.all([runChild && runChild.kill(), aliasReplace()]);
    runChild && runChild.forkChild();
  }, 1000);
  function onFileChange(p) {
    // 这里使用了标识来判断是否编译成功,理论上会有时序问题,但是本质上事件还是同步执行的,测试下来感觉没有问题
    if (!isCompileSuccess) return;
    debug(`${colors.dim('File ')}${p}${colors.dim(' has been changed.')}`);
    fileChangeList.push(p);
    // 单个文件的 hot reload 处理
    restart();
  }
onWatchCompileSuccess: () => {
      isCompileSuccess = true;
      restart();
    },

感觉这里的思路有问题 onWatchCompileSuccess 设置 isCompileSuccess = true; onFileChange设置 fileChangeList,这俩函数都触发 restart
restart 里边判断 fileChangeList.length >0 && isCompileSuccess 则执行。这样解决时序问题。而不是在onFileChange调用restart时加isCompileSuccess判断
2、 当删除文件或文件夹后没有触发restart
应该是因为删除时fileChangeList.length是0所以直接return出来了,正常情况删除应该和修改一块操作单纯的删除不触发,理论上也没问题,不知道官方是不是这样考虑的。

@yuntian001
Copy link
Author

我提交了pr 修复这个问题 #17
但是有个疑问,问什么不直接在onWatchCompileSuccess 后触发restart而是要加上fileWatch的判断呢。感觉onWatchCompileSuccess 直接触发restart 不加任何判断应该也没问题。

@czy88840616
Copy link
Member

czy88840616 commented Apr 26, 2024

一开始是没有 alias 逻辑的,这个时候很简单只需要 tsc --watch执行就行,工具都不需要跟踪文件变化,随着 alias path的加入,就越来越复杂了。

在包含 alias 文件重启的场景上,修改 ts,tsc 编译为 js,触发 tsc-alias 再次将 dist/js 文件修改,这个时候需要有个 flag 做判断,isCompileSuccess 在这里就是干这个用的,不然会和 chokidar.watch 死循环。

此外,原本是不需要 fileChangeList 的,因为现在重启比较频繁,后面是计划做单个文件修改的热重载的,不需要完整重启项目,所以我需要知道变化了哪个文件,判断是否需要完全的重启,不过这个还需要框架支持,现在 cli 工具里提前预支持了。

另外,删除没触发,可能是因为 chokidar 有 bug,windows 上不支持啥的,毕竟 tsc 也不支持删除的事件监听,我理解是一个原理(不然 tsc 早实现了)

@yuntian001
Copy link
Author

如果想拿到变更文件 是不是直接给 tsc 加上 --listEmittedFiles 读取输出即可,没有必要这么复杂。
alias 替换也只替换重新编译的文件即可。

@yuntian001
Copy link
Author

一开始是没有 alias 逻辑的,这个时候很简单只需要 tsc --watch执行就行,工具都不需要跟踪文件变化,随着 alias path的加入,就越来越复杂了。

在包含 alias 文件重启的场景上,修改 ts,tsc 编译为 js,触发 tsc-alias 再次将 dist/js 文件修改,这个时候需要有个 flag 做判断,isCompileSuccess 在这里就是干这个用的,不然会和 chokidar.watch 死循环。

此外,原本是不需要 fileChangeList 的,因为现在重启比较频繁,后面是计划做单个文件修改的热重载的,不需要完整重启项目,所以我需要知道变化了哪个文件,判断是否需要完全的重启,不过这个还需要框架支持,现在 cli 工具里提前预支持了。

另外,删除没触发,可能是因为 chokidar 有 bug,windows 上不支持啥的,毕竟 tsc 也不支持删除的事件监听,我理解是一个原理(不然 tsc 早实现了)

我的思路是, tsc 读取到--watch 就加上 --listEmittedFiles参数 去掉fileChange文件监听。
1.在onWatchCompileSuccess后读取tsc输出,根据输出将变更文件覆盖到fileChangeList
2.如果有fileChangeList有值则执行restart
3.restart内alias替换只执行fileChangeList的文件
您看下是否合适,如果合适我就修改下pr

@czy88840616
Copy link
Member

czy88840616 commented Apr 28, 2024

可以的,listEmittedFiles 这个我觉得有用,之前就是没找到这个参数,另外,alias的替换单文件的 api 不太好用,你也可以试试。

另外注意 windows 的兼容性。。。坑比较多

@zanminkian
Copy link

zanminkian commented Apr 30, 2024

一开始是没有 alias 逻辑的,这个时候很简单只需要 tsc --watch执行就行,工具都不需要跟踪文件变化,随着 alias path的加入,就越来越复杂了。

个人认为这个工具集成 tsc-alias 本身就是不合理的,这个工具最好就是一个 tsc 的 wrapper。将 tsc-alias 集成进来有点黑盒,对用户来说不那么好懂。

理想状态:

{
  "scripts": {
    "alias": "tsc-alias",
    "dev": "tscx --copyfiles --watch --script alias --exec bootstrap.js"
  },
  "devDependencies": {
    "tsc-alias": "^1.8.8",
    "@rnm/tscx": "^0.1.3"
  }
}

可以参考我的实现:https://github.com/rnmjs/tscx ,希望对你们有帮助

@czy88840616
Copy link
Member

tsc-alias一开始是没有加的,但是前端对alias 的依赖性实在太大了,考虑到兼容性还是加上了

@czy88840616
Copy link
Member

czy88840616 commented May 2, 2024

@yuntian001 试试这个版本 [email protected]

@yuntian001
Copy link
Author

@yuntian001 试试这个版本 [email protected]

1、这个版本解决了,首次不更新的问题
2、删除文件监听有问题

  function cleanOutDirAndRestart(p) {
    const distPath = path.join(cwd, outDir, p);
    if (!fs.existsSync(distPath)) return;
    const stat = fs.statSync(distPath);
    // is file
    if (stat.isFile()) {
      // remove file
      fs.unlinkSync(distPath);
    } else {
      // is directory
      deleteFolderRecursive(distPath);
    }
    runAfterTsc();
    restart();
  }

如果是删除文件夹或者资源文件这里是没问题的,但是如果是删除ts文件p传入的是源文件名 例如a.ts,distPath拿到的是dist/a.ts,应该删除的是a.d.tsa.js才对,建议做个兼容处理,将结尾的ts 分别替换为jsd.js再去删除对应dist文件,以兼容.ts.mts.cts文件的删除
3、希望run 函数 可以 返回restart函数 return {restart}; 方便项目自定义脚本调用
目前我用到的场景是和lernawatch一起使用,当自定义组件包更改后lernawatch会触发,我可以手动调用restart函数进行重启,目前已调试成功,对应开发第三方组件非常有用

@czy88840616
Copy link
Member

#20

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

No branches or pull requests

3 participants