新 Vaadin 14 应用程序无法 运行、错误 "Failed to determine 'node' tool." 缺少 Node.js 和 npm 工具

New Vaadin 14 app fails to run, error "Failed to determine 'node' tool." Missing Node.js and npm tools

在选择 Plain Java Servlet 选项后,我使用 Get Started 页面创建了一个新的 Vaadin 14 应用程序。

网页成功下载了一个.zip文件,我解压后用IntelliJ Ultimate Edition 2019.2打开。我等了几分钟,Maven 正在做它的事情,下载并重新配置项目。最后我去了 IntelliJ 中的 Maven 面板,运行 Lifecyclecleaninstall

我在控制台上收到以下错误消息。

[ERROR] Failed to execute goal com.vaadin:vaadin-maven-plugin:14.0.0:prepare-frontend (default) on project acme: Execution default of goal com.vaadin:vaadin-maven-plugin:14.0.0:prepare-frontend failed: 
[ERROR] 
[ERROR] ======================================================================================================
[ERROR] Failed to determine 'node' tool.
[ERROR] Please install it either:
[ERROR]   - by following the https://nodejs.org/en/download/ guide to install it globally
[ERROR]   - or by running the frontend-maven-plugin goal to install it in this project:
[ERROR]   $ mvn com.github.eirslett:frontend-maven-plugin:1.7.6:install-node-and-npm -DnodeVersion="v10.16.0" 
[ERROR] ======================================================================================================

discussion on Vaadin Forum

我提交了 Ticket # 6262 Configure Maven to automatically install Node.js & npm,向 Vaadin 团队建议要求和省略 Node.js & npm 是一个问题。

更新:14.2 中的非问题

Vaadin 14.2 和 16 现在已更改为在 Maven 驱动的项目中自动包含必要的 npm 工具。无需手动安装 Node.js & npm.

引用this blog post:

Automatic Node.js install

Starting from versions 14.2 and 16, the Node.js install (which includes npm) happens automatically. It is installed to a .vaadin folder inside the home folder, and reused from there for all Vaadin projects. As before, Node is only used to build the frontend side of things; it does not run after you deploy for production!

进一步改进:pnpm代替npm.

Frontend dependency management that just works - pnpm Behind the scenes, npm has been used to manage frontend dependencies since 14.0. Now, we’ve added support for pnpm, which introduces the following benefits:

  1. Shorter build time, compared to npm on your local machine and CI system, because pnpm only downloads packages once and reuses them from a local cache.

  2. No need to delete package.json, lock file or the node_modules folder when updating the Vaadin version in your project.

In 14.2, npm is still used by default, but we encourage you to test pnpm and give us your feedback. Trying pnpm is easy: there is no migration, just enable it by using a configuration property or Maven plugin configuration. You can learn more about pnpm here. Vaadin 16 will use pnpm by default.

我已验证此功能运行良好。我现在已经从 Mac.

中手动删除了 Node.js/npm 安装

tl;博士

Vaadin 14 team expects you to have Node.js and npm 工具安装在您的计算机上。

作为替代方案,Vaadin 14 似乎 与 Node 一起工作。js/npm 由 frontend-maven-plugin tool you can specify in your Maven POM file. See XML 下面的 POM 片段。

如果您希望在计算机上全局安装 Mode/npm,请务必阅读 Tom Novosad 的

详情

从 Vaadin 14 开始,Vaadin team is switching:

…作为他们从 Polymer 2 to Polymer 3 过渡的一部分。

查看博客 post、Bower and npm in Vaadin 14+

希望,作为 Vaadin-on-Java 用户,我们不需要关心这些底层技术细节......但一方面:不幸的是,npm & Node.js 工具是必需的但默认情况下 捆绑 在您的 Vaadin 项目中。

你有两个解决方案:

  • 全局安装工具。
  • 在您的项目中安装。

我更喜欢后者。而且我更喜欢让 Maven 在我的项目中自动安装它们,减少我手动完成的内务工作。

警告: 我不知道我的 node/npm-per-project 解决方案的局限性或后果。我几乎不知道 node/npm 的目的或性质,也不知道 Vaadin 如何使用它们。因此,使用此解决方案需要您自担风险。我只能说它似乎对我有用。

frontend-maven-plugin 添加到您的项目

Maven 可以使用 frontend-maven-plugin 工具在您的 Vaadin 项目中通过 npm 下载和安装 Node.js。

在您的 Vaadin 项目中打开 Maven POM 文件。

在该 POM 的 <build> <defaultGoal>jetty:run</defaultGoal> <plugins> 元素内添加以下块。

    <plugin>
        <groupId>com.github.eirslett</groupId>
        <artifactId>frontend-maven-plugin</artifactId>
        <!-- Use the latest released version:
        https://repo1.maven.org/maven2/com/github/eirslett/frontend-maven-plugin/ -->
        <version>1.8.0</version>

        <executions>
            <execution>
                <!-- optional: you don't really need execution ids, but it looks nice in your build log. -->
                <id>install node and npm</id>
                <goals>
                    <goal>install-node-and-npm</goal>
                </goals>
                <!-- optional: default phase is "generate-resources" -->
                <phase>generate-resources</phase>
            </execution>
        </executions>
        <configuration>
            <nodeVersion>v10.16.3</nodeVersion>

            <!-- optional: with node version greater than 4.0.0 will use npm provided by node distribution -->
            <!--                    <npmVersion>2.15.9</npmVersion>-->

            <!-- optional: where to download node and npm from. Defaults to https://nodejs.org/dist/ -->
            <!--                    <downloadRoot>http://myproxy.example.org/nodejs/</downloadRoot>-->
        </configuration>
    </plugin>

当然,您可以调整该代码段以使用最新版本号。检查 the Node.js page 以获取最新版本号。

请注意,我们注释掉了 npm 项,因为该工具与最新版本的 Node.js 捆绑在一起。

剩余步骤:

  1. 在 IntelliJ 的 Maven 面板中,运行 Lifecycle 项名为 cleaninstall。稍等片刻,因为更多的项目正在下载和配置。 (请注意您的控制台历史记录中的项目 "Installing node version v10.16.3"。)
  2. 在同一面板的 Plugins > jetty 部分,运行 jetty:run 项目。在 Jetty 服务器启动到 运行 您的 Vaadin 应用程序时稍等片刻。

在控制台上,您应该会看到类似这样的内容(mysterious Quiet Time warning is perennial 所有版本的 Vaadin):

[INFO] Started Jetty Server
[INFO] Using Non-Native Java sun.nio.fs.PollingWatchService
[WARNING] Quiet Time is too low for non-native WatchService [sun.nio.fs.PollingWatchService]: 1000 < 5000 ms (defaulting to 5000 ms)
  1. 将您的网络浏览器指向:http://localhost:8080/ 以查看 "Click Me" 按钮出现,因为您的应用已成功 运行s。

此解决方案来自 Maven 插件的项目页面 frontend-maven-plugin。请注意,此处的示例 POM 片段不正确,无法将 <execution> 标记包装在复数 <executions> 标记中。我在那里提交了 ticket # 838

您可能想在 Vaadin 论坛中关注 this discussion

这里有一个完整的 POM 文件供您参考,可以与您的进行比较。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>work.basil.example</groupId>
    <artifactId>acme</artifactId>
    <name>acme</name>
    <version>2.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>

        <vaadin.version>14.0.5</vaadin.version>

        <drivers.downloader.phase>pre-integration-test</drivers.downloader.phase>
    </properties>

    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo1.maven.org/maven2/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <!-- Repository used by many Vaadin add-ons -->
        <repository>
            <id>Vaadin Directory</id>
            <url>https://maven.vaadin.com/vaadin-addons</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>central</id>
            <url>https://repo1.maven.org/maven2/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <type>pom</type>
                <scope>import</scope>
                <version>${vaadin.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>com.vaadin</groupId>
            <!-- Replace artifactId with vaadin-core to use only free components -->
            <artifactId>vaadin</artifactId>
            <exclusions>
                <!-- Webjars are only needed when running in Vaadin 13 compatibility mode -->
                <exclusion>
                    <groupId>com.vaadin.webjar</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.insites</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.polymer</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.polymerelements</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.vaadin</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.webcomponents</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Added to provide logging output as Vaadin uses -->
        <!-- the unbound SLF4J no-operation (NOP) logger implementation -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-testbench</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>jetty:run</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
            <!-- Jetty plugin for easy testing without a server -->
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.19.v20190610</version>
                <configuration>
                    <!-- If using IntelliJ IDEA with autocompilation, this
                    might cause lots of unnecessary compilations in the
                    background.-->
                    <scanIntervalSeconds>2</scanIntervalSeconds>
                    <!-- Use war output directory to get the webpack files -->
                    <webAppConfig>
                        <allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
                    </webAppConfig>
                </configuration>
            </plugin>

            <!--
                Take care of synchronizing java dependencies and imports in
                package.json and main.js files.
                It also creates webpack.config.js if not exists yet.
            -->
            <plugin>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-maven-plugin</artifactId>
                <version>${vaadin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>prepare-frontend</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>com.github.eirslett</groupId>
                <artifactId>frontend-maven-plugin</artifactId>
                <!-- Use the latest released version:
                https://repo1.maven.org/maven2/com/github/eirslett/frontend-maven-plugin/ -->
                <version>1.8.0</version>

                <executions>
                    <execution>
                        <!-- optional: you don't really need execution ids, but it looks nice in your build log. -->
                        <id>install node and npm</id>
                        <goals>
                            <goal>install-node-and-npm</goal>
                        </goals>
                        <!-- optional: default phase is "generate-resources" -->
                        <phase>generate-resources</phase>
                    </execution>
                </executions>
                <configuration>
                    <nodeVersion>v10.16.3</nodeVersion>

                    <!-- optional: with node version greater than 4.0.0 will use npm provided by node distribution -->
                    <!--                    <npmVersion>2.15.9</npmVersion>-->

                    <!-- optional: where to download node and npm from. Defaults to https://nodejs.org/dist/ -->
                    <!--                    <downloadRoot>http://myproxy.example.org/nodejs/</downloadRoot>-->
                </configuration>
            </plugin>

        </plugins>
    </build>

    <profiles>
        <profile>
            <!-- Production mode is activated using -Pproduction -->
            <id>production</id>
            <properties>
                <vaadin.productionMode>true</vaadin.productionMode>
            </properties>

            <dependencies>
                <dependency>
                    <groupId>com.vaadin</groupId>
                    <artifactId>flow-server-production-mode</artifactId>
                </dependency>
            </dependencies>

            <build>
                <plugins>
                    <plugin>
                        <groupId>com.vaadin</groupId>
                        <artifactId>vaadin-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>build-frontend</goal>
                                </goals>
                                <phase>compile</phase>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>

        <profile>
            <id>integration-tests</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-maven-plugin</artifactId>
                        <version>9.4.19.v20190610</version>
                        <configuration>
                            <scanIntervalSeconds>0</scanIntervalSeconds>
                            <stopPort>8081</stopPort>
                            <stopWait>5</stopWait>
                            <stopKey>${project.artifactId}</stopKey>
                        </configuration>
                        <executions>
                            <execution>
                                <id>start-jetty</id>
                                <phase>pre-integration-test</phase>
                                <goals>
                                    <goal>deploy-war</goal>
                                </goals>
                            </execution>
                            <execution>
                                <id>stop-jetty</id>
                                <phase>post-integration-test</phase>
                                <goals>
                                    <goal>stop</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- Runs the integration tests (*IT) after the server is started -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-failsafe-plugin</artifactId>
                        <version>3.0.0-M3</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>integration-test</goal>
                                    <goal>verify</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <trimStackTrace>false</trimStackTrace>
                            <enableAssertions>true</enableAssertions>
                            <systemPropertyVariables>
                                <!-- Pass location of downloaded webdrivers to the tests -->
                                <webdriver.chrome.driver>${webdriver.chrome.driver}</webdriver.chrome.driver>
                            </systemPropertyVariables>
                        </configuration>
                    </plugin>

                    <plugin>
                        <groupId>com.lazerycode.selenium</groupId>
                        <artifactId>driver-binary-downloader-maven-plugin</artifactId>
                        <version>1.0.17</version>
                        <configuration>
                            <onlyGetDriversForHostOperatingSystem>true
                            </onlyGetDriversForHostOperatingSystem>
                            <rootStandaloneServerDirectory>
                                ${project.basedir}/drivers/driver
                            </rootStandaloneServerDirectory>
                            <downloadedZipFileDirectory>
                                ${project.basedir}/drivers/driver_zips
                            </downloadedZipFileDirectory>
                            <customRepositoryMap>
                                ${project.basedir}/drivers.xml
                            </customRepositoryMap>
                        </configuration>
                        <executions>
                            <execution>
                                <!-- use phase "none" to skip download step -->
                                <phase>${drivers.downloader.phase}</phase>
                                <goals>
                                    <goal>selenium</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

这里几乎一样。 Win 10,JDK 11.0.2,全局安装节点 10.16.2

mvn 结果

[ERROR] Failed to determine 'npm.cmd' tool. 
[ERROR] Please install it either:
[ERROR] - by following the https://nodejs.org/en/download/ guide to install it globally
[ERROR] - or by running the frontend-maven-plugin goal to install it in this project:
[ERROR] $ mvn com.github.eirslett:frontend-maven-plugin:1.7.6:install-node-and-npm -DnodeVersion="v10.16.0"

所以...关于全局安装的第一个建议失败了。第二个有效。

问题出在方法上

FrontendUtils::getNpmExecutable(String baseDir)

来自流程-server.jar。此方法尝试定位

node/node_modules/npm/bin/npm-cli.js

在 $baseDir 中(如果是 prepare-frontend 目标,它是项目根文件夹)。当该路径不存在时,代码继续执行 "where/which npm.cmd" 以获得 'npm.cmd' 的绝对路径。在我的例子中,全局安装了 NodeJS,它 returns 正确的路径。

随后代码继续尝试执行 "path-to-npm.cmd\npm.cmd -v",以确保 npm.cmd 存在且可运行。

这是方法中的问题:

ProcessBuilder FrontEndUtils::createProcessBuilder(List<String> command)

在这种方法中,在某些情况下,程序代码将环境变量 'PATH' 设置为 npm.cmd 的路径(因为 ProcssBuilder.environment() returns 映射不包含 'PATH'变量)。

接下来尝试执行命令'path-to-npm\npm.cmd -v'时,进程的退出值为1,stderr非空,因为'chcp'命令在[=51=中的其他内容之前被调用](可能是 SETLOCAL 命令),但现在 'chcp' 不在 PATH 中。

以下代码将这些条件(退出代码 1,stderr 非空)评估为执行 npm.cmd 和

时的错误

无法确定 'npm.cmd' 工具。

消息已打印。

这发生在我的 Windows 10、Vaadin Flow 14.0.1、NodeJS 10.16.3 全局安装上。

当 运行 tomcat 服务器有一个应用程序时也遇到同样的问题,因为 $baseDir 变量包含 tomcat bin 目录的路径。

解决方法

作为解决方法,将 NodeJS 目录的符号 link 放入项目根目录(如果应用 运行 on Tomcat,也将 link of NodeJS 到 Tomcat 的 bin 目录)。

更准确地说,在插件 运行 的日志中可以找到真正的错误: 命令“[C:\Program Files\nodejs\npm.cmd, -v]”失败,退出代码为“1”

但是如果您 运行 从提示符中执行相同的命令,那么您会发现命令和退出代码没有任何问题。

正如有人提到的,问题是 Windows 尝试 运行 "chcp" 但它不在 PATH 上(由 maven 插件操作)。这发生在我的情况下,因为我在注册表中修改为 运行 cmd 始终使用 UTF 编码。在 Windows 中,这是通过执行 "chcp 65001" 以及(之前)任何批处理或命令文件(如 npm.cmd ... 我的简单解决方案是将 chcp.com 从 System32 目录复制到 nodejs 目录...

我在测试新的Vaadin 14时出现了这个问题。 我按照说明安装了节点,但我是用之前打开的 Idea 和另一个项目(除了这个)来做的。 只有当我重新启动 Idea 时问题才消失。

安装 node 后,我在编译 vaadin 应用程序时遇到了同样的问题。
需要重启电脑才能定位节点,运行成功

我找到的解决方案是添加

-Dvaadin.project.basedir="/home/me/project/root/directory"

到您的 JVM 启动参数。

答案来自这个帖子。

https://vaadin.com/forum/thread/18491365/vaadin-14-3-1-prepare-frontend-neede-at-every-eclipse-start