在Docker构建中找不到TSC

22

当构建需要从TypeScript编译的映像时,我会遇到此错误:

sh: 1: tsc: 找不到命令

命令'/bin/sh -c npm run tsc'返回非零代码:127

以下是相关代码:

docker-compose.yaml

version: '3.1'

services:
  nodeserver:
    build:
      context: .
      target: prod
    ports:
      - "3000:3000"
    volumes:
      - ./src:/app/src
      - ./public:/app/public
      - ./templates:/app/templates

Dockerfile

FROM node:15.11.0 AS base
EXPOSE 3000
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./

RUN npm install --only=production && npm cache clean --force

##########################################################################################

FROM base AS dev

ENV NODE_ENV=development

RUN npm install --only=development

CMD npm run dev

##########################################################################################

FROM dev AS source

COPY dist dist
COPY templates templates
COPY public public

RUN npm run tsc

##########################################################################################

FROM base AS test

COPY --from=source /app/node_modules /app/node_modules
COPY --from=source /app/templates /app/templates
COPY --from=source /app/public /app/public
COPY --from=source /app/dist /app/dist

CMD npm run test

##########################################################################################

FROM test AS prod

CMD npm start

包文件.json

{
  "name": "nodeserver",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node ./dist/app.js",
    "deploy": "git add . && git commit -m Heroku && git push heroku main",
    "tsc": "tsc --outDir ./dist",
    "dev": "npm run ts-watch",
    "test": "npm run jest --runInBand",
    "ts-watch": "tsc-watch --project . --outDir ./dist --onSuccess \"nodemon ./dist/app.js\""
  },
  "jest": {
    "testEnvironment": "node"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/MiquelPiza/nodeserver.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/MiquelPiza/nodeserver/issues"
  },
  "homepage": "https://github.com/MiquelPiza/nodeserver#readme",
  "dependencies": {
    "@sendgrid/mail": "^7.4.2",
    "bcryptjs": "^2.4.3",
    "express": "^4.17.1",
    "handlebars": "^4.7.7",
    "jsonwebtoken": "^8.5.1",
    "lodash": "^4.17.20",
    "mongodb": "^3.6.4",
    "mongoose": "^5.11.19",
    "multer": "^1.4.2",
    "socket.io": "^4.0.0",
    "validator": "^13.5.2"
  },
  "devDependencies": {
    "@types/bcryptjs": "^2.4.2",
    "@types/express": "^4.17.11",
    "@types/jsonwebtoken": "^8.5.0",
    "@types/lodash": "^4.14.168",
    "@types/mongoose": "^5.10.3",
    "@types/multer": "^1.4.5",
    "@types/node": "^14.14.33",
    "@types/sendgrid": "^4.3.0",
    "@types/validator": "^13.1.3",
    "env-cmd": "^10.1.0",
    "jest": "^26.6.3",
    "nodemon": "^2.0.7",
    "supertest": "^6.1.3",
    "tsc-watch": "^4.2.9",
    "typescript": "^4.2.3"
  },
  "engines": {
    "node": "15.11.0"
  }
}

tsconfig.json

{
  "compilerOptions": {

    "target": "es5", 
    "module": "commonjs",
    "strict": true,
    "strictNullChecks": false,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
  },
  "include": ["src"]
}

这个 Dockerfile 是有效的:

FROM node:15.11.0 AS build

WORKDIR /app
COPY package.json .
RUN npm install
ADD . .
RUN npm run tsc


FROM node:15.11.0
WORKDIR /app

COPY package.json .
RUN npm install --production

ADD public ./public
ADD templates ./templates
COPY --from=build /app/dist dist

EXPOSE 3000
CMD npm start

我在参考这个Dockerfile,它来自于一门Docker课程:https://github.com/BretFisher/docker-mastery-for-nodejs/blob/master/typescript/Dockerfile 我不知道我做错了什么,源阶段应该具有开发依赖包,其中包括typescript,所以应该能够运行tsc。

非常感谢任何帮助。

编辑:

除了使用npm ci而不是npm install之外,我还需要将tsconfig.json复制到工作目录(同时复制src目录而不是由tsc创建的dist目录),才能使tsc正常工作。下面是修改后的Dockerfile中的源阶段:

FROM dev AS source

COPY src src
COPY templates templates
COPY public public
COPY tsconfig.json tsconfig.json

RUN npm run tsc
3个回答

28

这可能是一个NODE_ENV环境变量的问题。

ENV NODE_ENV=production

如果你这样设置,devDependencies 中的依赖项将不会被安装。


3
ENV NODE_ENV=production 行移动到 RUN npm ciRUN npm run build 之间后,解决了我的问题。 - Oren Chapo
1
谢谢回复,问题已解决! - SkyzohKey
如果你想要减小图片大小,可以在完成tsc编译步骤后运行RUN npm prune --production - dcsan

10

编辑2:

这个问题已经解决。如果你的package-lock.json文件已损坏,你可以使用工具fix-has-install-script修复它。

原始回答:

使用npm ci(或将package-lock.json添加到.dockerignore文件中,或在构建之前删除本地环境中的package-lock.json)。为什么会有这个问题?可以在这里找到答案。

编辑1:

我认为情况是这样的。免责声明:我不是nodejsnpm专家——事实上,我有点菜鸟。所有这些都是基于一些实验的猜测。

出了什么问题?

npm没有通过符号链接在node_modules/.bin中链接dev依赖项的二进制文件,因为package-lock.json文件已经处于损坏状态,其中(prod)依赖项处于lockfileVersion 2格式,而dev依赖项仍然处于lockfileVersion 1格式。

为什么会发生这种情况?

注意:在这里做了很多假设。

  1. 您的本地主机使用的是 npm 6 版本,而 Docker 容器使用的是 npm 7 版本。由于这个原因,现有的 package-lock.json 文件的 lockfileVersion 是 1,其中不包括依赖项二进制文件的 bin 部分。版本 2 包括保存 bin: 部分,npm 必须使用它来确定安装/链接哪些二进制文件。

  2. 当您运行生产环境的依赖项安装命令(例如 NODE_ENV=production npm install)时,使用 npm 7 版本时,npm 会将你的 package-lock.json 文件升级到 lockfileVersion 为 2,其中包括保存安装二进制文件的依赖项的 bin: 部分。重要的是,它只更新了生产环境的依赖项。现在 package-lock.json 文件已经损坏,因为它声称处于版本 2 格式,但所有开发环境中的依赖项仍然处于版本 1,或者至少没有正确应用 bin: 部分。

  3. 当您现在尝试安装开发环境中的依赖项时,npm 看到 package-lock.jsonlockfileVersion 是 2,因此它认为开发环境中的依赖项也已经升级了(但它们并没有被升级,或者至少没有正确地升级)。由于它们不存在 bin: 部分,所以它找不到二进制文件并且不会将其链接到 node_modules/.bin/ 目录。

您可以使用以下 Dockerfile 对其进行最小化复制:

FROM node:14 as npm6
WORKDIR /app
# Create a node project using npm 6 and install a dev dependency
# that contains a binary.
RUN npm init --yes && \
    npm install --save-dev typescript

FROM node:15 as npm7
COPY --from=npm6 /app/package*.json /app/
WORKDIR /app
# Install production dependencies, then all dependencies. This should
# link the binaries for typescript in (e.g. tsc) under node_modules/.bin.
RUN npm install -g npm@7.10.0 && \
    npm install --production && \
    npm install

# Causes error, tsc not found.
CMD ["npx", "-c", "tsc --version"]

我没有找到现有的错误票据,所以我在这里创建了一个。也许它会被修复。


3
我不明白为什么删除package-lock.json会起作用。npm ci不需要package-lock.json吗? - mikepa88
@mikepa88,看起来是npm的不同版本和package-lock.json格式的问题,我在我的答案中加入了更多细节。在我的有限经验中,package-lock.json经常是npm中出现错误和依赖问题的原因之一,而且我为了调试与节点依赖相关的任何问题,首先要做的事情之一就是摆脱它。 - kthompso

2

在容器上手动安装TypeScript

RUN npm install --only=production && npm cache clean --force && npm install -g typescript

2
这将安装一个全局的TypeScript实例,可能会解决问题,但并不是问题的核心。在开发/生产环境安装方面存在的问题似乎更有可能干净地解决这个问题。 - dcsan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接