typescript 如何用tsc编译本地包?

eagi6jfj  于 2022-12-30  发布在  TypeScript
关注(0)|答案(1)|浏览(201)
    • 项目结构**

我有一个monorepo(使用npm workspaces),它包含一个目录api(一个用类型脚本编写的express API)。api使用一个本地包@myapp/server-lib(类型脚本代码)。
目录结构为:

.
├── api/
└── libs/
    └── server-lib/
    • 问题**

当我使用tsc构建api时,构建输出包含@myapp/server-libserver-lib包)的require语句。然而,当部署API时,服务器不能解析@myapp/server-lib(因为它不打算从npm注册表安装)。
如何让tsc编译@myapp/server-lib,删除构建代码中@myapp/server-librequire语句,并将其替换为对导入代码的引用?
我希望实现的行为是next-transpile-modules为Next.js所做的。
我尝试使用typescript项目引用,但没有编译导入的@myapp/server-lib。我还阅读了为什么我在NextJS前端没有遇到这个问题(也位于同一个monorepo中,依赖于不同但非常相似的本地包),这就是我如何登陆next-transpile-modules的原因。
希望得到任何关于如何使用本地包构建 typescript 项目的帮助或提示。谢谢!!

更新(2022年12月28日)

我通过使用esbuildapi构建到一个out.js文件中来解决这个问题。
整个构建过程现在如下所示:

npx tsc --noEmit # checks types but does not output files
node build.js # uses esbuild to build the project

其中build.js脚本为:

const nativeNodeModulesPlugin = {
  name: 'native-node-modules',
  setup(build) {
    // If a ".node" file is imported within a module in the "file" namespace, resolve 
    // it to an absolute path and put it into the "node-file" virtual namespace.
    build.onResolve({ filter: /\.node$/, namespace: 'file' }, args => ({
      path: require.resolve(args.path, { paths: [args.resolveDir] }),
      namespace: 'node-file',
    }))

    // Files in the "node-file" virtual namespace call "require()" on the
    // path from esbuild of the ".node" file in the output directory.
    build.onLoad({ filter: /.*/, namespace: 'node-file' }, args => ({
      contents: `
        import path from ${JSON.stringify(args.path)}
        try { module.exports = require(path) }
        catch {}
      `,
    }))

    // If a ".node" file is imported within a module in the "node-file" namespace, put
    // it in the "file" namespace where esbuild's default loading behavior will handle
    // it. It is already an absolute path since we resolved it to one above.
    build.onResolve({ filter: /\.node$/, namespace: 'node-file' }, args => ({
      path: args.path,
      namespace: 'file',
    }))

    // Tell esbuild's default loading behavior to use the "file" loader for
    // these ".node" files.
    let opts = build.initialOptions
    opts.loader = opts.loader || {}
    opts.loader['.node'] = 'file'
  },
}

require("esbuild").build({
  entryPoints: ["./src/server.ts"], // the entrypoint of the server
  platform: "node",
  target: "node16.0",
  outfile: "./build/out.js", // the single file it will bundle everything into
  bundle: true,
  loader: {".ts": "ts"},
  plugins: [nativeNodeModulesPlugin], // addresses native node modules (like fs)
})
.then((res) => console.log(`⚡ Bundled!`))
.catch(() => process.exit(1));
sbtkgmzw

sbtkgmzw1#

已解决(2022年12月28日)

我通过使用esbuildapi构建到单个out.js文件中来解决这个问题。
整个构建过程现在如下所示:

npx tsc --noEmit # checks types but does not output files
node build.js # uses esbuild to build the project

其中build.js脚本为:

const nativeNodeModulesPlugin = {
  name: 'native-node-modules',
  setup(build) {
    // If a ".node" file is imported within a module in the "file" namespace, resolve 
    // it to an absolute path and put it into the "node-file" virtual namespace.
    build.onResolve({ filter: /\.node$/, namespace: 'file' }, args => ({
      path: require.resolve(args.path, { paths: [args.resolveDir] }),
      namespace: 'node-file',
    }))

    // Files in the "node-file" virtual namespace call "require()" on the
    // path from esbuild of the ".node" file in the output directory.
    build.onLoad({ filter: /.*/, namespace: 'node-file' }, args => ({
      contents: `
        import path from ${JSON.stringify(args.path)}
        try { module.exports = require(path) }
        catch {}
      `,
    }))

    // If a ".node" file is imported within a module in the "node-file" namespace, put
    // it in the "file" namespace where esbuild's default loading behavior will handle
    // it. It is already an absolute path since we resolved it to one above.
    build.onResolve({ filter: /\.node$/, namespace: 'node-file' }, args => ({
      path: args.path,
      namespace: 'file',
    }))

    // Tell esbuild's default loading behavior to use the "file" loader for
    // these ".node" files.
    let opts = build.initialOptions
    opts.loader = opts.loader || {}
    opts.loader['.node'] = 'file'
  },
}

require("esbuild").build({
  entryPoints: ["./src/server.ts"], // the entrypoint of the server
  platform: "node",
  target: "node16.0",
  outfile: "./build/out.js", // the single file it will bundle everything into
  bundle: true,
  loader: {".ts": "ts"},
  plugins: [nativeNodeModulesPlugin], // addresses native node modules (like fs)
})
.then((res) => console.log(`⚡ Bundled!`))
.catch(() => process.exit(1));

在我的服务器上,package.json中的start脚本只是node out.js,没有**dependenciesdevDependencies,因为所有脚本都捆绑到out.js中。

相关问题