Maven项目中的RequireJS编译与外部JS依赖

2022-01-15 00:00:00 dependencies javascript requirejs maven

我有一个使用 Maven 构建的 Web 项目,我正在尝试找出使用 RequireJS 编译器编译 JavaScript 文件的最佳方法(这个问题也适用于任何编译器/压缩器).

I have a web project that's built with Maven and I'm trying to figure out the best way to compile the JavaScript files with the RequireJS compiler (this question could apply to any compiler/minifier as well).

我有一个可行的设置,但需要改进:

I have a setup that works, but it needs improvement:

我已经打包了第 3 方 JavaScript 库,它们作为依赖项被下载,然后与 WAR Overlay 插件一起添加.

I've packaged the 3rd party JavaScript libraries and they are being downloaded as dependencies and then added with the WAR Overlay plugin.

我有一个 Exec 插件任务,它在目标目录中运行 RequireJS 编译器.我目前在包目标运行后手动运行 exec:exec(因此 WAR 内容放置在目标目录中).

I have an Exec plugin task that runs the RequireJS compiler inside the target directory. I currently manually run exec:exec after the package target runs (and therefore the WAR contents are placed in the target directory).

我想要的是让 JS 编译成为主要 (Java) 编译的一部分.JS 编译器本身(需要 JS)在编译后发生的 WAR 覆盖阶段作为依赖项下载.因此,我需要下载和解压缩 Require JS 文件,并且需要在 Java 编译之前/期间/之后使用这些文件运行 JS 编译.

What I would like instead is to make JS compilation part of main (Java) compilation. The JS compiler itself (Require JS) is downloaded as a dependency during the WAR overlay phase which happens after compilation. So, I need the Require JS files to be downloaded and unpacked and I need to run the JS compilation using those files, before/during/after Java compilation.

我确信有几种方法可以实现这一目标.我正在寻找最优雅的解决方案.

I'm sure there could be several ways to achieve this. I'm looking for the most elegant solution.

更新:现有的 POM 片段

我已经压缩并添加到我们的存储库管理器中的 JavaScript 依赖项:

I have JavaScript dependencies which I have zipped and added to our repository manager:

    <dependency>
        <groupId>org.requirejs</groupId>
        <artifactId>requirejs</artifactId>
        <version>0.22.0</version>
        <classifier>repackaged</classifier>
        <type>zip</type>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.jqueryui</groupId>
        <artifactId>jquery-ui</artifactId>
        <version>1.8.7</version>
        <classifier>repackaged</classifier>
        <type>zip</type>
        <scope>runtime</scope>
    </dependency>

请注意,RequireJS 本身(编译其余库所必需的)也作为外部依赖项加载.所以第一件事是,我需要先下载并解压缩此依赖项,然后才能开始 RequireJS 编译.

Take note that RequireJS itself (which is required for the compilation of the rest of libraries) is also loaded as an external dependency. So the first thing is, I need this dependency to be downloaded and unzipped before I can commence with RequireJS compilation.

这些依赖项正在使用 WAR Overlay 插件添加到 WAR:

These dependencies are being added to the WAR using the WAR Overlay plugin:

        <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <overlays>
                    <overlay>
                        <groupId>org.requirejs</groupId>
                        <artifactId>requirejs</artifactId>
                        <classifier>repackaged</classifier>
                        <type>zip</type>
                        <targetPath>lib</targetPath>
                        <includes>
                            <include>requirejs/require.js</include>
                            <include>requirejs/require/*</include>
                            <include>requirejs/build/**</include>
                        </includes>
                    </overlay>
                    <overlay>
                        <groupId>com.jqueryui</groupId>
                        <artifactId>jquery-ui</artifactId>
                        <classifier>repackaged</classifier>
                        <type>zip</type>
                        <targetPath>lib</targetPath>
                    </overlay>
                </overlays>
            </configuration>
        </plugin>

即使我不需要 requirejs/build/** 最终进入 WAR,我还是将其作为覆盖的一部分包含在内以获取解压缩的 RequireJS 构建脚本,仅仅是因为我还没有找到更好的方法.

Even though I don't need requirejs/build/** to end up in the WAR, I'm including it as part of the overlay to get the unzipped RequireJS build scripts, simply because I haven't figured out a better way.

然后我有一个执行编译的 Exec 插件任务.但请注意,此任务尚未添加到正常的编译工作流程中:我必须在 WAR 打包完成后使用 mvn exec:exec 手动调用它:

Then I have a Exec plugin task that performs the compilation. But note that this task hasn't been added to the normal compilation work flow: I have to manually invoke it with mvn exec:exec after the WAR packaging is done:

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <executable>lib/requirejs/build/build.bat</executable>
                <workingDirectory>${project.build.directory}/${project.artifactId}</workingDirectory>
                <arguments>
                    <argument>name=bootstrap</argument>
                    <argument>out=combined.js</argument>
                    <argument>baseUrl=scripts</argument>
                    <argument>optimize=closure</argument>
                    <argument>excludeShallow=plugins/manifest</argument>
                </arguments>
            </configuration>
        </plugin>

所以,有些问题是:

  1. 如何为编译和 WAR 打包步骤提取单个压缩依赖项的不同部分?还是我必须创建两个 zip 文件,一个仅包含运行时内容,另一个用于编译脚本?
  2. 我希望在编译期间触发 exec:exec,如果不是,则在 WAR 打包之前触发.我该怎么做?
  1. How can I extract different portions of a single zipped dependency for the compilation and WAR packaging steps? Or do I have to create two zip files, one with just the runtime stuff and the other for compilations scripts?
  2. I would like to trigger exec:exec ideally during compilation, or if not, just prior to WAR packaging. How do I do that?

我实际上已经尝试了很多没有成功的东西,所以我希望我不会显得懒惰地发布大量代码并等待答案:) 只是我还没有完全理解 Maven 是如何实现的目标/阶段等工作还没有.

I have actually tried a bunch off stuff without success, so I hope I don't appear as lazily posting a huge chunk of code and waiting for answers :) It's just that I haven't quite wrapped my mind around how Maven targets/phases etc. work yet.

推荐答案

我想出了以下适合我的解决方案:

I've come up with the following solution that works for me:

我没有依赖 WAR 覆盖来解压 RequireJS 文件,而是使用 Dependency 插件显式解压它们:

Instead of depending on the WAR overlay for the unpacking of the RequireJS files, I explicitly unpack them using the Dependency plugin:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
      <execution>
        <phase>compile</phase>
        <goals>
          <goal>unpack</goal>
        </goals>
        <configuration>
          <artifactItems>
            <artifactItem>
              <groupId>org.requirejs</groupId>
              <artifactId>requirejs</artifactId>
              <version>0.22.0</version>
              <type>zip</type>
              <classifier>repackaged</classifier>
              <overWrite>true</overWrite>
            </artifactItem>
          </artifactItems>
        </configuration>
      </execution>
    </executions>
  </plugin>

阶段设置为编译".这允许我在编译期间拥有依赖"文件夹下的 RequireJS 包的全部内容.所以,接下来我要做的是:

The phase is set to "compile". This allows me to have the entire contents of the RequireJS package under the "dependency" folder, during compilation. So, the next thing I have is:

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>exec</goal>
            </goals>
            <configuration>
              <executable>
              ${project.build.directory}/dependency/requirejs/build/build.bat</executable>
              <workingDirectory>
              ${basedir}/src/main/webapp</workingDirectory>
              <arguments>
                <argument>name=bootstrap</argument>
                <argument>out=scripts/compiled.js</argument>
                <argument>baseUrl=scripts</argument>
                <argument>optimize=closure</argument>
                <argument>
                excludeShallow=plugins/manifest</argument>
              </arguments>
            </configuration>
          </execution>
        </executions>
      </plugin>

这会触发依赖"文件夹内的 RequireJS 编译,而无需触及我的源目录或 WAR 目录.然后我继续使用 WAR 覆盖插件来挑选想要进入 WAR 的 RequireJS 文件.

This triggers RequireJS compilation inside the "dependency" folder, without touching either my source directory or the WAR directory. I then continue using the WAR overlay plugin to cherry pick the RequireJS files that want to go into the WAR.

更新

自从编写此解决方案以来,我已切换到使用java"目标而不是exec",因为我在通过 Hudson 使用 RequireJS 编译器的 shell 脚本 + 批处理文件时遇到了问题.我基本上是在运行通过 Rhino 运行编译器的最终 Java 命令(由脚本生成).Node 解决方案会略有不同.对于 RequireJS 0.23.0,它是这样的:

Since writing this solution, I've switched to using the "java" goal instead of "exec" because I had problems with using the RequireJS compiler's shell script + batch file through Hudson. I'm basically running the final Java command (generated by the scripts) that run the compiler through Rhino. The Node solution would be slightly different. For RequireJS 0.23.0, it goes something like this:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.1</version>
    <executions>
        <execution>
            <id>compile-js</id>
            <phase>compile</phase>
            <goals>
                <goal>exec</goal>
            </goals>
            <configuration>
                <executable>java</executable>
                <workingDirectory>${basedir}/src/main/webapp</workingDirectory>
                <arguments>
                    <argument>-classpath</argument>
                    <!--argument>${project.build.directory}/dependency/requirejs/build/lib/rhino/js.jar${path.separator}${project.build.directory}/dependency/requirejs/build/lib/closure/compiler.jar</argument -->
                    <argument>${project.build.directory}/dependency/requirejs/build/lib/rhino/js.jar</argument>
                    <argument>org.mozilla.javascript.tools.shell.Main</argument>
                    <argument>${project.build.directory}/dependency/requirejs/bin/x.js</argument>
                    <argument>${project.build.directory}/dependency/requirejs/bin/</argument>
                    <argument>${project.build.directory}/dependency/requirejs/build/build.js</argument>
                    <argument>name=bootstrap</argument>
                    <argument>out=scripts/compiled.js</argument>
                    <argument>baseUrl=scripts</argument>
                    <argument>optimize=none</argument>
                </arguments>
            </configuration>
        </execution>
    </executions>
</plugin>

相关文章