├── .github └── workflows │ ├── build.yml │ ├── codeql.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .yarn └── releases │ └── yarn-4.9.1.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── build-scripts ├── add-os-restrictions.js ├── build-compiler.js ├── build.sh ├── checkout-newer-version-tag.js ├── clean.sh ├── ensure-file-exists.js ├── graal.js ├── jasmine.sh ├── publish.js ├── reflection-config.json ├── run-command.js ├── test.sh └── version-packages.js ├── deployments.md ├── package.json ├── packages ├── google-closure-compiler-java │ ├── LICENSE │ ├── index.js │ ├── package.json │ ├── readme.md │ └── test.js ├── google-closure-compiler-linux-arm64 │ ├── LICENSE │ ├── build-image.js │ ├── index.js │ ├── package.json │ ├── readme.md │ └── test.js ├── google-closure-compiler-linux │ ├── LICENSE │ ├── build-image.js │ ├── index.js │ ├── package.json │ ├── readme.md │ └── test.js ├── google-closure-compiler-macos │ ├── LICENSE │ ├── build-image.js │ ├── index.js │ ├── package.json │ ├── readme.md │ └── test.js ├── google-closure-compiler-windows │ ├── LICENSE │ ├── build-image.js │ ├── index.js │ ├── package.json │ ├── readme.md │ └── test.js └── google-closure-compiler │ ├── LICENSE │ ├── README.md │ ├── cli.js │ ├── docs │ ├── grunt.md │ └── gulp.md │ ├── index.js │ ├── lib │ ├── grunt │ │ ├── index.js │ │ └── vinyl-stream.js │ ├── gulp │ │ ├── concat-to-json.js │ │ ├── index.js │ │ └── json-to-vinyl.js │ ├── node │ │ └── index.js │ └── utils.js │ ├── package.json │ └── test │ ├── cli.js │ ├── fixtures │ ├── extern.js │ ├── one.js │ └── two.js │ ├── grunt.js │ ├── gulp.js │ ├── node.js │ └── support │ ├── jasmine-launcher.js │ └── jasmine.sh ├── test └── compiler.js └── yarn.lock /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | workflow_dispatch: {} 7 | workflow_call: 8 | inputs: 9 | release-tag: 10 | required: true 11 | type: string 12 | secrets: 13 | NPM_PUBLISH_AUTH_TOKEN: 14 | required: true 15 | pull_request: 16 | branches: [ master ] 17 | 18 | jobs: 19 | # Build the compiler jar file from the submodule 20 | build-compiler: 21 | name: Build Compiler 22 | runs-on: ubuntu-latest 23 | env: 24 | FORCE_COLOR: '1' 25 | steps: 26 | - name: Setup Java 27 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 28 | with: 29 | distribution: adopt-hotspot 30 | java-version: 21 31 | java-package: jdk 32 | architecture: x64 33 | - name: Setup Bazelisk 34 | uses: bazel-contrib/setup-bazel@e8776f58fb6a6e9055cbaf1b38c52ccc5247e9c4 # 0.14.0 35 | with: 36 | # Avoid downloading Bazel every time. 37 | bazelisk-cache: true 38 | # Store build cache per workflow. 39 | disk-cache: ${{ github.workflow }} 40 | # Share repository cache between workflows. 41 | repository-cache: true 42 | - name: Checkout repo 43 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 44 | with: 45 | submodules: recursive 46 | ref: ${{ inputs.release-tag || '' }} 47 | - name: Fetch submodule tags 48 | working-directory: compiler 49 | run: git fetch --tags https://github.com/google/closure-compiler.git 50 | - name: Use Node.js ${{ env.NODE_VERSION }} 51 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 52 | with: 53 | node-version: ${{ env.NODE_VERSION }} 54 | cache: yarn 55 | - name: Install packages 56 | run: YARN_ENABLE_COLORS=true yarn install 57 | - name: Build jar 58 | # unset ANDROID_HOME to prevent bazel from trying to use the Android SDK 59 | run: unset ANDROID_HOME && ./build-scripts/build-compiler.js 60 | - name: Tests 61 | run: YARN_ENABLE_COLORS=true yarn test:root 62 | - name: Upload contrib folder 63 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 64 | with: 65 | name: Contrib folder 66 | path: compiler/contrib 67 | - name: Upload externs folder 68 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 69 | with: 70 | name: Externs folder 71 | path: compiler/externs 72 | - name: Upload compiler jar 73 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 74 | with: 75 | name: Compiler.jar 76 | path: packages/google-closure-compiler-java/compiler.jar 77 | 78 | # Build the native image on Linux 79 | build-linux: 80 | name: Build Linux Native Image 81 | needs: build-compiler 82 | runs-on: ubuntu-24.04 83 | env: 84 | NODE_VERSION: '20.x' 85 | FORCE_COLOR: '1' 86 | MUSL_HOME: ${{ github.workspace }}/musl-toolchain 87 | steps: 88 | - name: Checkout repo 89 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 90 | with: 91 | ref: ${{ inputs.release-tag || '' }} 92 | - name: Checkout musl 93 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 94 | with: 95 | repository: kraj/musl 96 | ref: c47ad25ea3b484e10326f933e927c0bc8cded3da # patched 1.2.5 version 97 | path: musl 98 | clean: false 99 | - name: Checkout zlib 100 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 101 | with: 102 | repository: madler/zlib 103 | ref: 04f42ceca40f73e2978b50e93806c2a18c1281fc # v1.2.13 104 | path: zlib 105 | clean: false 106 | - name: Build musl and zlib 107 | run: | 108 | # See https://www.graalvm.org/latest/reference-manual/native-image/guides/build-static-executables/ 109 | # We have to build MUSL from source to ensure recent CVEs are patched 110 | 111 | # Build musl from source 112 | pushd musl 113 | ./configure --prefix=$MUSL_HOME --static 114 | sudo make && make install 115 | popd 116 | 117 | # Install a symlink for use by native-image 118 | ln -s $MUSL_HOME/bin/musl-gcc $MUSL_HOME/bin/x86_64-linux-musl-gcc 119 | 120 | # Extend the system path and confirm that musl is available by printing its version 121 | export PATH="$MUSL_HOME/bin:$PATH" 122 | echo "$path" >> $GITHUB_PATH 123 | x86_64-linux-musl-gcc --version 124 | 125 | # Build zlib with musl from source and install into the MUSL_HOME directory 126 | pushd zlib 127 | CC=musl-gcc ./configure --prefix=$MUSL_HOME --static 128 | make && make install 129 | popd 130 | - name: Use Node.js ${{ env.NODE_VERSION }} 131 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 132 | with: 133 | node-version: ${{ env.NODE_VERSION }} 134 | cache: yarn 135 | - name: Install GraalVM 136 | uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # 1.3.3 137 | with: 138 | java-version: 24 139 | distribution: 'graalvm-community' 140 | github-token: ${{ secrets.GITHUB_TOKEN }} 141 | native-image-job-reports: 'true' 142 | - name: Download compiler jar 143 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 144 | with: 145 | name: Compiler.jar 146 | path: packages/google-closure-compiler-java/ 147 | - name: Download contrib folder 148 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 149 | with: 150 | name: Contrib folder 151 | path: packages/google-closure-compiler/contrib 152 | - name: Download externs folder 153 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 154 | with: 155 | name: Externs folder 156 | path: packages/google-closure-compiler/externs 157 | - name: Install packages 158 | run: YARN_ENABLE_COLORS=true yarn install 159 | - name: Build image 160 | working-directory: packages/google-closure-compiler-linux 161 | run: | 162 | export PATH="$MUSL_HOME/bin:$PATH" 163 | cp ../google-closure-compiler-java/compiler.jar compiler.jar 164 | yarn run build 165 | - name: Tests 166 | run: FORCE_COLOR=1 yarn workspaces foreach -A --no-private run test 167 | - name: Upload artifacts 168 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 169 | with: 170 | name: Linux image 171 | path: packages/google-closure-compiler-linux/compiler 172 | 173 | # Build the native image on Linux 174 | # The runner image determines GLIBC compatibility and should not be changed without 175 | # understanding the impact. See https://github.com/google/closure-compiler-npm/issues/280 176 | build-linux-arm64: 177 | name: Build Linux Native Image for arm processors 178 | needs: build-compiler 179 | runs-on: ubuntu-24.04-arm 180 | env: 181 | NODE_VERSION: '20.x' 182 | FORCE_COLOR: '1' 183 | steps: 184 | - name: Checkout repo 185 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 186 | with: 187 | ref: ${{ inputs.release-tag || '' }} 188 | - name: Use Node.js ${{ env.NODE_VERSION }} 189 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 190 | with: 191 | node-version: ${{ env.NODE_VERSION }} 192 | cache: yarn 193 | - name: Setup GraalVM 194 | uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # 1.3.3 195 | with: 196 | java-version: 24 197 | distribution: 'graalvm-community' 198 | github-token: ${{ secrets.GITHUB_TOKEN }} 199 | native-image-job-reports: 'true' 200 | - name: Download compiler jar 201 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 202 | with: 203 | name: Compiler.jar 204 | path: packages/google-closure-compiler-java/ 205 | - name: Download contrib folder 206 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 207 | with: 208 | name: Contrib folder 209 | path: packages/google-closure-compiler/contrib 210 | - name: Download externs folder 211 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 212 | with: 213 | name: Externs folder 214 | path: packages/google-closure-compiler/externs 215 | - name: Install packages 216 | run: YARN_ENABLE_COLORS=true yarn install 217 | - name: Build image 218 | working-directory: packages/google-closure-compiler-linux-arm64 219 | run: | 220 | cp ../google-closure-compiler-java/compiler.jar compiler.jar 221 | yarn run build 222 | - name: Tests 223 | run: FORCE_COLOR=1 yarn workspaces foreach -A --no-private run test 224 | - name: Upload artifacts 225 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 226 | with: 227 | name: Linux arm64 image 228 | path: packages/google-closure-compiler-linux-arm64/compiler 229 | 230 | # Build the native image on MacOS 231 | build-macos: 232 | name: Build MacOS Native Image 233 | needs: build-compiler 234 | runs-on: macos-latest 235 | env: 236 | NODE_VERSION: '22.x' 237 | FORCE_COLOR: '1' 238 | steps: 239 | - name: Checkout repo 240 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 241 | with: 242 | ref: ${{ inputs.release-tag || '' }} 243 | - name: Use Node.js ${{ env.NODE_VERSION }} 244 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 245 | with: 246 | node-version: ${{ env.NODE_VERSION }} 247 | cache: yarn 248 | - name: Setup GraalVM 249 | uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # 1.3.3 250 | with: 251 | java-version: 24 252 | distribution: 'graalvm-community' 253 | github-token: ${{ secrets.GITHUB_TOKEN }} 254 | native-image-job-reports: 'true' 255 | - name: Download compiler jar 256 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 257 | with: 258 | name: Compiler.jar 259 | path: packages/google-closure-compiler-java/ 260 | - name: Download contrib folder 261 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 262 | with: 263 | name: Contrib folder 264 | path: packages/google-closure-compiler/contrib 265 | - name: Download externs folder 266 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 267 | with: 268 | name: Externs folder 269 | path: packages/google-closure-compiler/externs 270 | - name: Install packages 271 | run: YARN_ENABLE_COLORS=true yarn install 272 | - name: Build image 273 | working-directory: packages/google-closure-compiler-macos 274 | run: | 275 | cp ../google-closure-compiler-java/compiler.jar compiler.jar 276 | yarn run build 277 | - name: Tests 278 | run: FORCE_COLOR=1 yarn workspaces foreach -A --no-private run test 279 | - name: Upload artifacts 280 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 281 | with: 282 | name: MacOS image 283 | path: packages/google-closure-compiler-macos/compiler 284 | 285 | # Build the native image on Windows 286 | build-windows: 287 | name: Build Windows Native Image 288 | needs: build-compiler 289 | runs-on: windows-latest 290 | env: 291 | NODE_VERSION: '22.x' 292 | FORCE_COLOR: '1' 293 | steps: 294 | - name: Checkout repo 295 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 296 | with: 297 | ref: ${{ inputs.release-tag || '' }} 298 | - name: Use Node.js ${{ env.NODE_VERSION }} 299 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 300 | with: 301 | node-version: ${{ env.NODE_VERSION }} 302 | cache: yarn 303 | - name: Setup GraalVM 304 | uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # 1.3.3 305 | with: 306 | java-version: 24 307 | distribution: 'graalvm-community' 308 | github-token: ${{ secrets.GITHUB_TOKEN }} 309 | native-image-job-reports: 'true' 310 | - name: Download compiler jar 311 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 312 | with: 313 | name: Compiler.jar 314 | path: packages/google-closure-compiler-java/ 315 | - name: Download contrib folder 316 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 317 | with: 318 | name: Contrib folder 319 | path: packages/google-closure-compiler/contrib 320 | - name: Download externs folder 321 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 322 | with: 323 | name: Externs folder 324 | path: packages/google-closure-compiler/externs 325 | - name: Install packages 326 | run: yarn install 327 | - name: Build image 328 | working-directory: packages/google-closure-compiler-windows 329 | run: | 330 | cp ../google-closure-compiler-java/compiler.jar compiler.jar 331 | yarn run build 332 | - name: Tests 333 | shell: cmd 334 | run: yarn workspaces foreach -A --no-private run test 335 | - name: Upload artifacts 336 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 337 | with: 338 | name: Windows image 339 | path: packages/google-closure-compiler-windows/compiler.exe 340 | 341 | # Publish the packages if needed 342 | publish-packages: 343 | name: Publish Packages 344 | runs-on: ubuntu-latest 345 | if: ${{ github.event_name == 'schedule' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' }} 346 | env: 347 | NODE_VERSION: '22.x' 348 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_AUTH_TOKEN }} 349 | FORCE_COLOR: '1' 350 | needs: 351 | - build-linux 352 | - build-linux-arm64 353 | - build-macos 354 | - build-windows 355 | steps: 356 | - name: Checkout repo 357 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 358 | with: 359 | ref: ${{ inputs.release-tag || '' }} 360 | - name: Use Node.js ${{ env.NODE_VERSION }} 361 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 362 | with: 363 | node-version: ${{ env.NODE_VERSION }} 364 | registry-url: https://registry.npmjs.org/ 365 | cache: yarn 366 | - name: Download compiler jar 367 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 368 | with: 369 | name: Compiler.jar 370 | path: packages/google-closure-compiler-java/ 371 | - name: Download Linux image 372 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 373 | with: 374 | name: Linux image 375 | path: packages/google-closure-compiler-linux/ 376 | - name: Download Linux arm64 image 377 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 378 | with: 379 | name: Linux arm64 image 380 | path: packages/google-closure-compiler-linux-arm64/ 381 | - name: Download MacOS image 382 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 383 | with: 384 | name: MacOS image 385 | path: packages/google-closure-compiler-macos/ 386 | - name: Download Windows image 387 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 388 | with: 389 | name: Windows image 390 | path: packages/google-closure-compiler-windows/ 391 | - name: Download contrib folder 392 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 393 | with: 394 | name: Contrib folder 395 | path: packages/google-closure-compiler/contrib 396 | - name: Download externs folder 397 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 4.3.0 398 | with: 399 | name: Externs folder 400 | path: packages/google-closure-compiler/externs 401 | - name: Mark binaries executable 402 | run: | 403 | chmod 755 packages/google-closure-compiler-linux/compiler 404 | chmod 755 packages/google-closure-compiler-linux-arm64/compiler 405 | chmod 755 packages/google-closure-compiler-macos/compiler 406 | chmod 755 packages/google-closure-compiler-windows/compiler.exe 407 | - name: Install packages 408 | run: YARN_ENABLE_COLORS=true yarn install 409 | - name: Prepare for publish 410 | run: ./build-scripts/add-os-restrictions.js 411 | - name: Publish packages to npm 412 | run: yarn publish-packages 413 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | name: "CodeQL" 8 | 9 | on: 10 | push: 11 | branches: [ "master" ] 12 | pull_request: 13 | # The branches below must be a subset of the branches above 14 | branches: [ "master" ] 15 | schedule: 16 | - cron: '36 10 * * 3' 17 | 18 | jobs: 19 | analyze: 20 | name: Analyze 21 | runs-on: ubuntu-latest 22 | permissions: 23 | actions: read 24 | contents: read 25 | security-events: write 26 | 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | language: [ 'javascript' ] 31 | 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 35 | 36 | # Initializes the CodeQL tools for scanning. 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # 3.28.18 39 | with: 40 | languages: ${{ matrix.language }} 41 | # If you wish to specify custom queries, you can do so here or in a config file. 42 | # By default, queries listed here will override any specified in a config file. 43 | # Prefix the list here with "+" to use these queries and those in the config file. 44 | 45 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 46 | # queries: security-extended,security-and-quality 47 | 48 | 49 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 50 | # If this step fails, then you should remove it and run the build manually (see below) 51 | - name: Autobuild 52 | uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # 3.28.18 53 | 54 | # ℹ️ Command-line programs to run using the OS shell. 55 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 56 | 57 | # If the Autobuild fails above, remove it and uncomment the following three lines. 58 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 59 | 60 | # - run: | 61 | # echo "Run, Build Application using script" 62 | # ./location_of_script_within_repo/buildscript.sh 63 | 64 | - name: Perform CodeQL Analysis 65 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # 3.28.18 66 | with: 67 | category: "/language:${{matrix.language}}" 68 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Compiler release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | COMPILER_VERSION_NUMBER: 7 | description: 'Compiler version to base release from' 8 | required: false 9 | type: string 10 | schedule: 11 | # Daily at 12pm UTC 12 | - cron: '0 12 * * *' 13 | 14 | jobs: 15 | create-release: 16 | name: Create release 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | outputs: 21 | COMPILER_VERSION_NUMBER: ${{ env.COMPILER_VERSION_NUMBER }} 22 | env: 23 | FORCE_COLOR: '1' 24 | NODE_VERSION: '22.x' 25 | steps: 26 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 27 | with: 28 | submodules: recursive 29 | - name: Setup Node.js 30 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # 4.4.0 31 | with: 32 | node-version: ${{ env.NODE_VERSION }} 33 | cache: yarn 34 | - name: Install packages 35 | run: YARN_ENABLE_COLORS=true yarn install 36 | - name: Fetch compiler submodule tags 37 | working-directory: compiler 38 | run: git fetch --tags https://github.com/google/closure-compiler.git 39 | - name: Find compiler submodule newer release tag 40 | if: ${{ github.event.inputs.COMPILER_VERSION_NUMBER == '' }} 41 | run: | 42 | echo "COMPILER_VERSION_NUMBER=$(./build-scripts/checkout-newer-version-tag.js)" >> "$GITHUB_ENV" 43 | echo "COMPILER_VERSION_NUMBER='$COMPILER_VERSION_NUMBER'" >> "$GITHUB_OUTPUT" 44 | - name: Set compiler version to provided value 45 | if: ${{ github.event.inputs.COMPILER_VERSION_NUMBER != '' }} 46 | run: | 47 | echo "COMPILER_VERSION_NUMBER=${{ github.event.inputs.COMPILER_VERSION_NUMBER }}" >> "$GITHUB_ENV" 48 | - name: Set compiler submodule to provided release branch 49 | if: ${{ env.COMPILER_VERSION_NUMBER != '' }} 50 | working-directory: compiler 51 | run: git checkout v${{ env.COMPILER_VERSION_NUMBER }} 52 | - name: Create release commit and tag 53 | if: ${{ env.COMPILER_VERSION_NUMBER != '' }} 54 | run: | 55 | git config --global user.email "github-bot@github.com" 56 | git config --global user.name "Github Bot" 57 | git add compiler 58 | ./build-scripts/version-packages.js --new-version ${{ env.COMPILER_VERSION_NUMBER }}.0.0 59 | git push origin master 60 | git push origin v${{ env.COMPILER_VERSION_NUMBER }}.0.0 61 | - name: Create GitHub Release 62 | if: ${{ env.COMPILER_VERSION_NUMBER != '' }} 63 | run: | 64 | curl \ 65 | -X POST \ 66 | -H 'Accept: application/vnd.github+json' \ 67 | -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' \ 68 | https://api.github.com/repos/google/closure-compiler-npm/releases \ 69 | -d '{"tag_name":"v${{ env.COMPILER_VERSION_NUMBER }}.0.0","name":"${{ env.COMPILER_VERSION_NUMBER }}.0.0","body":"Closure-compiler ${{ env.COMPILER_VERSION_NUMBER }} release","draft":false,"prerelease":false,"generate_release_notes":true}' 70 | 71 | build: 72 | needs: create-release 73 | if: ${{ needs.create-release.outputs.COMPILER_VERSION_NUMBER != '' }} 74 | uses: ./.github/workflows/build.yml 75 | with: 76 | release-tag: v${{ needs.create-release.outputs.COMPILER_VERSION_NUMBER }}.0.0 77 | secrets: 78 | NPM_PUBLISH_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_AUTH_TOKEN }} 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | yarn-error.log 4 | /lerna-debug.log 5 | /musl-toolchain 6 | /packages/google-closure-compiler/contrib/ 7 | /packages/google-closure-compiler/externs/ 8 | /packages/google-closure-compiler-java/compiler.jar 9 | /packages/google-closure-compiler-linux/compiler.jar 10 | /packages/google-closure-compiler-linux/compiler 11 | /packages/google-closure-compiler-linux-arm64/compiler.jar 12 | /packages/google-closure-compiler-linux-arm64/compiler 13 | /packages/google-closure-compiler-macos/compiler.jar 14 | /packages/google-closure-compiler-macos/compiler 15 | /packages/google-closure-compiler-windows/compiler.* 16 | /publish-log.txt 17 | /temp 18 | /zlib 19 | .npmrc 20 | .pnp.* 21 | .yarn/* 22 | !.yarn/patches 23 | !.yarn/plugins 24 | !.yarn/releases 25 | !.yarn/sdks 26 | !.yarn/versions 27 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "compiler"] 2 | path = compiler 3 | url = https://github.com/google/closure-compiler.git 4 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | yarnPath: .yarn/releases/yarn-4.9.1.cjs 3 | npmRegistryServer: "https://registry.npmjs.org" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler 2 | 3 | Check, compile, optimize and compress JavaScript with Closure-Compiler 4 | 5 | This repository tracks issues related to the publication to npmjs.org and associated plugins. 6 | Any bugs not related to the plugins themselves should be reported to the 7 | [main repository](https://github.com/google/closure-compiler/). 8 | 9 | ## Packages 10 | 11 | The compiler is distributed for multiple platforms. Each platform is its own npm package. 12 | 13 | ### Main Package 14 | 15 | The google-closure-compiler package contains Grunt and Gulp plugins and a CLI: 16 | [google-closure-compiler](https://github.com/google/closure-compiler-npm/tree/master/packages/google-closure-compiler) 17 | 18 | ### Other packages 19 | 20 | Bare bones distributions intended for developers who want to author against a specific platform. 21 | 22 | - Java build: [google-closure-compiler-java](https://github.com/google/closure-compiler-npm/tree/master/packages/google-closure-compiler-java) 23 | - Native Linux build: [google-closure-compiler-linux](https://github.com/google/closure-compiler-npm/tree/master/packages/google-closure-compiler-linux) 24 | - Native Linux (arm64) build: [google-closure-compiler-linux-arm64](https://github.com/google/closure-compiler-npm/tree/master/packages/google-closure-compiler-linux-arm64) 25 | - Native MacOS build: [google-closure-compiler-macos](https://github.com/google/closure-compiler-npm/tree/master/packages/google-closure-compiler-macos) 26 | - Native Windows build: [google-closure-compiler-windows](https://github.com/google/closure-compiler-npm/tree/master/packages/google-closure-compiler-windows) 27 | 28 | ## License 29 | Copyright 2015 The Closure Compiler Authors 30 | 31 | Licensed under the Apache License, Version 2.0 (the "License"); 32 | you may not use this file except in compliance with the License. 33 | You may obtain a copy of the License at 34 | 35 | http://www.apache.org/licenses/LICENSE-2.0 36 | 37 | Unless required by applicable law or agreed to in writing, software 38 | distributed under the License is distributed on an "AS IS" BASIS, 39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 | See the License for the specific language governing permissions and 41 | limitations under the License. 42 | 43 | ## Version History 44 | Closure Compiler release notes can be found on the 45 | [main repository wiki](https://github.com/google/closure-compiler/wiki/Binary-Downloads). 46 | -------------------------------------------------------------------------------- /build-scripts/add-os-restrictions.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Before publication, add OS restrictions to the graal packages. 21 | * They can't be present before publication as it errors out the installs. 22 | * Also set correct architectures. In order to execute `yarn install` in the main package, the current architecture 23 | */ 24 | 25 | import fs from 'node:fs'; 26 | import path from 'node:path'; 27 | import {fileURLToPath, URL} from 'node:url'; 28 | 29 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 30 | 31 | // Maps of the os marketing name to the platform name used in package.json os restriction fields 32 | const osRestrictions = new Map([ 33 | ['macos', {os: ['darwin'], cpu: ['arm64']}], 34 | ['linux', {os: ['linux'], cpu: ['x32', 'x64']}], 35 | ['linux-arm64', {os: ['linux'], cpu: ['arm64']}], 36 | ['windows', {os: ['win32'], cpu: ['x32', 'x64']}] 37 | ]); 38 | 39 | // Read the package.json files, add the OS restriction, then write it back. 40 | osRestrictions.forEach((osAndCpu, packageKey) => { 41 | const packagePath = path.resolve(__dirname, '..', 'packages', `google-closure-compiler-${packageKey}`, 'package.json'); 42 | const packageContents = JSON.parse(fs.readFileSync(packagePath, 'utf8')); 43 | packageContents.os = osAndCpu.os; 44 | packageContents.cpu = osAndCpu.cpu; 45 | fs.writeFileSync(packagePath, JSON.stringify(packageContents, null, 2) + '\n', 'utf8'); 46 | }); 47 | -------------------------------------------------------------------------------- /build-scripts/build-compiler.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * @fileoverview Build the compiler java jar from source. 20 | * 21 | * Invoked as part of the package build process: 22 | * 23 | * yarn run build 24 | * 25 | * For pull requests and pushes to master, the compiler submodule is expected to be pointed at the tagged commit 26 | * that matches the package major version. 27 | */ 28 | import fs from 'node:fs'; 29 | import path from 'node:path'; 30 | import {fileURLToPath, URL} from 'node:url'; 31 | import ncp from 'ncp'; 32 | import semver from 'semver'; 33 | import runCommand from './run-command.js'; 34 | 35 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 36 | const packageInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8')); 37 | 38 | /** 39 | * The compiler version that will be built. 40 | * 41 | * For release builds, this is of the form: "vYYYYMMDD" 42 | * 43 | * @type {string} 44 | */ 45 | const compilerVersion = `v${semver.major(packageInfo.version)}`; 46 | 47 | const compilerTargetName = 'compiler_uberjar_deploy.jar'; 48 | const compilerJavaBinaryPath = `./compiler/bazel-bin/${compilerTargetName}`; 49 | 50 | async function main() { 51 | console.log(process.platform, process.arch, compilerVersion); 52 | 53 | const { exitCode } = await runCommand( 54 | 'bazelisk', 55 | [ 56 | 'build', 57 | '--color=yes', 58 | `//:${compilerTargetName}`, 59 | `--define=COMPILER_VERSION=${compilerVersion}`, 60 | ], 61 | { cwd: './compiler' } 62 | ); 63 | if (exitCode !== 0) { 64 | throw new Error(exitCode); 65 | } 66 | 67 | return Promise.all([ 68 | copy( 69 | compilerJavaBinaryPath, 70 | './packages/google-closure-compiler-java/compiler.jar' 71 | ), 72 | copy( 73 | compilerJavaBinaryPath, 74 | './packages/google-closure-compiler-linux/compiler.jar' 75 | ), 76 | copy( 77 | compilerJavaBinaryPath, 78 | './packages/google-closure-compiler-linux/compiler-arm64.jar' 79 | ), 80 | copy( 81 | compilerJavaBinaryPath, 82 | './packages/google-closure-compiler-macos/compiler.jar' 83 | ), 84 | copy( 85 | compilerJavaBinaryPath, 86 | './packages/google-closure-compiler-windows/compiler.jar' 87 | ), 88 | copy('./compiler/contrib', './packages/google-closure-compiler/contrib'), 89 | ]); 90 | } 91 | 92 | /** 93 | * @param {string} src path to source file or folder 94 | * @param {string} dest path to destination file or folder 95 | * @return {!Promise} 96 | */ 97 | function copy(src, dest) { 98 | return new Promise((resolve, reject) => { 99 | ncp(src, dest, (err) => { 100 | err ? reject(err) : resolve(); 101 | }); 102 | }); 103 | } 104 | 105 | main(); 106 | -------------------------------------------------------------------------------- /build-scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Run the build commands and fail the script if any of them failed 3 | ./build-scripts/build-compiler.js "$@" && yarn workspaces run build "$@" 4 | -------------------------------------------------------------------------------- /build-scripts/checkout-newer-version-tag.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2025 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Determine if there is a newer compiler release tag than the package version. 21 | * Select and output the oldest version that is greater than the package major version. 22 | */ 23 | 24 | import {spawn} from 'node:child_process'; 25 | import fs from 'node:fs'; 26 | import path from 'node:path'; 27 | import {fileURLToPath, URL} from 'node:url'; 28 | import semver from 'semver'; 29 | 30 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 31 | 32 | const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8')); 33 | const currentVersion = semver(packageJson.version); 34 | 35 | const gitProcess = spawn( 36 | 'git', 37 | [ 38 | 'tag', 39 | '--list', 40 | 'v*', 41 | ], 42 | { 43 | cwd: path.resolve(__dirname, '../compiler'), 44 | stdio: 'pipe', 45 | // env: { 46 | // PAGER: 'cat', 47 | // }, 48 | }, 49 | ); 50 | 51 | const tags = []; 52 | 53 | gitProcess.stdout.on('data', (data) => { 54 | tags.push(data.toString()); 55 | }); 56 | 57 | const errData = []; 58 | gitProcess.stderr.on('data', (data) => { 59 | errData.push(data.toString()); 60 | }); 61 | 62 | gitProcess.on('error', (err) => { 63 | err.exitCode = 1; 64 | }); 65 | 66 | gitProcess.on('close', (exitCode) => { 67 | if (exitCode !== 0) { 68 | process.stderr.write(errData.join('')); 69 | process.exitCode = exitCode; 70 | } else { 71 | const versionTags = tags.join('').trim().split(/\n/); 72 | let oldestNewVersion; 73 | for (const versionTag of versionTags) { 74 | // Only process version tags of an expected format: v######## 75 | if (!/^v\d{8}$/.test(versionTag)) { 76 | continue; 77 | } 78 | const newerVersion = semver(`${versionTag.slice(1)}.0.0`); 79 | if ( 80 | newerVersion.major > currentVersion.major && 81 | (!oldestNewVersion || oldestNewVersion.major > newerVersion.major) 82 | ) { 83 | oldestNewVersion = newerVersion; 84 | } 85 | } 86 | if (oldestNewVersion) { 87 | process.stdout.write(oldestNewVersion.major.toString()); 88 | } 89 | } 90 | }); 91 | -------------------------------------------------------------------------------- /build-scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm -rf ./temp 3 | rm -rf ./packages/google-closure-compiler/contrib 4 | rm ./packages/google-closure-compiler-java/compiler.jar 5 | rm ./packages/google-closure-compiler-linux/compiler.jar 6 | rm ./packages/google-closure-compiler-linux/compiler 7 | rm ./packages/google-closure-compiler-linux-arm64/compiler.jar 8 | rm ./packages/google-closure-compiler-linux-arm64/compiler 9 | rm ./packages/google-closure-compiler-macos/compiler.jar 10 | rm ./packages/google-closure-compiler-macos/compiler 11 | rm ./packages/google-closure-compiler-windows/compiler.jar 12 | rm ./packages/google-closure-compiler-windows/compiler.exe 13 | cd ./compiler && mvn clean 14 | -------------------------------------------------------------------------------- /build-scripts/ensure-file-exists.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2020 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import fs from 'node:fs'; 19 | import chalk from 'chalk'; 20 | 21 | if (!fs.existsSync(process.argv[2])) { 22 | process.stderr.write(chalk.red(`${process.argv[2]} file is missing\n`)); 23 | process.exitCode = 1; 24 | } 25 | -------------------------------------------------------------------------------- /build-scripts/graal.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Build the graal native compiler image for the current OS. 21 | * Intended to be run with a working directory of the intended package. 22 | */ 23 | 24 | import path from 'node:path'; 25 | import {fileURLToPath, URL} from 'node:url'; 26 | import runCommand from './run-command.js'; 27 | 28 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 29 | 30 | // This script should catch and handle all rejected promises. 31 | // If it ever fails to do so, report that and exit immediately. 32 | process.on('unhandledRejection', error => { 33 | console.error(error); 34 | process.exit(1); 35 | }); 36 | 37 | const flagsByPlatformAndArch = new Map([ 38 | // Statically link libraries when supported. Allows usage on systems 39 | // which are missing or have incompatible versions of GLIBC. 40 | // Only linux x86 architectures can fully statically link 41 | // See https://www.graalvm.org/latest/reference-manual/native-image/guides/build-static-executables/ 42 | ['linux-x86', ['--static', '--libc=musl']], 43 | ['linux-x64', ['--static', '--libc=musl']], 44 | ['linux-arm64', ['--static-nolibc']], 45 | ]); 46 | 47 | const NATIVE_IMAGE_BUILD_ARGS = ['-H:+UnlockExperimentalVMOptions'].concat( 48 | flagsByPlatformAndArch.get(`${process.platform}-${process.arch}`) || [], 49 | [ 50 | '-H:IncludeResourceBundles=org.kohsuke.args4j.Messages', 51 | '-H:IncludeResourceBundles=org.kohsuke.args4j.spi.Messages', 52 | '-H:IncludeResourceBundles=com.google.javascript.jscomp.parsing.ParserConfig', 53 | '-H:+AllowIncompleteClasspath', 54 | `-H:ReflectionConfigurationFiles=${path.resolve(__dirname, 'reflection-config.json')}`, 55 | '-H:IncludeResources=externs\.zip', 56 | '-H:IncludeResources=.*\.typedast', 57 | '-H:IncludeResources=com/google/javascript/.*\.js', 58 | '-H:IncludeResources=com/google/javascript/.*\.txt', 59 | '-H:IncludeResources=lib/.*\.js', 60 | '-H:IncludeResources=META-INF/.*\.txt', 61 | '-H:+ReportExceptionStackTraces', 62 | // '-H:+GenerateEmbeddedResourcesFile', 63 | '-J--sun-misc-unsafe-memory-access=allow', // See https://github.com/google/closure-compiler/issues/4229 64 | '--initialize-at-build-time', 65 | '-march=compatibility', 66 | '--color=always', 67 | '-jar', 68 | path.resolve(process.cwd(), 'compiler.jar'), 69 | ], 70 | ); 71 | 72 | const spawnOpts = { 73 | ...(process.platform === 'win32' ? { shell: true } : {}), 74 | }; 75 | 76 | runCommand(`native-image${process.platform === 'win32' ? '.cmd' : ''}`, NATIVE_IMAGE_BUILD_ARGS, spawnOpts) 77 | .catch(e => { 78 | console.error(e); 79 | process.exit(1); 80 | }); 81 | -------------------------------------------------------------------------------- /build-scripts/jasmine.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Run the test commands and fail the script if any of them failed 3 | EXIT_STATUS=0 4 | ./packages/google-closure-compiler/test/support/jasmine-launcher.js "$@" || EXIT_STATUS=$? 5 | exit $EXIT_STATUS 6 | -------------------------------------------------------------------------------- /build-scripts/publish.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2022 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * @fileoverview 20 | * 21 | * Publish each package in this project in order. 22 | * Packages can only be published after all their dependencies have been successfully published. 23 | */ 24 | 25 | import fs from 'node:fs/promises'; 26 | import path from 'node:path'; 27 | import graphlib from 'graphlib'; 28 | import {fileURLToPath, URL} from 'node:url'; 29 | import runCommand from './run-command.js'; 30 | 31 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 32 | const packagesDirPath = path.resolve(__dirname, '../packages'); 33 | 34 | async function isPackageVersionPublished(packageName, version) { 35 | return fetch(`https://registry.npmjs.org/${encodeURI(packageName)}/${version}`) 36 | .then((res) => res.ok); 37 | } 38 | 39 | async function isValidPackagePath(packageDir) { 40 | const packageJsonPath = `${packageDir}/package.json`; 41 | try { 42 | // check to see if the file already exists - if so do nothing 43 | await fs.stat(packageJsonPath); 44 | return true; 45 | } catch { 46 | return false; 47 | } 48 | } 49 | 50 | async function getPackageInfo(packageDir) { 51 | return { 52 | path: packageDir, 53 | pkg: JSON.parse(await fs.readFile(`${packageDir}/package.json`, 'utf8')) 54 | }; 55 | } 56 | 57 | async function publishPackagesIfNeeded(packageInfo) { 58 | const pkgJson = packageInfo.pkg; 59 | const isAlreadyPublished = await isPackageVersionPublished(pkgJson.name, pkgJson.version); 60 | if (isAlreadyPublished) { 61 | console.log('Already published', pkgJson.name, pkgJson.version); 62 | return; 63 | } 64 | console.log('Publishing', pkgJson.name, pkgJson.version); 65 | const publishArgs = ['-w', pkgJson.name, 'publish']; 66 | await runCommand('npm', publishArgs); 67 | } 68 | 69 | (async () => { 70 | const packagesDirEntries = await fs.readdir(packagesDirPath); 71 | // build a graph of the interdependencies of projects and only publish 72 | const graph = new graphlib.Graph({directed: true, compound: false}); 73 | for (const packageDirName of packagesDirEntries) { 74 | const packagePath = `${packagesDirPath}/${packageDirName}`; 75 | if (await isValidPackagePath(packagePath)) { 76 | const packageInfo = await getPackageInfo(packagePath); 77 | graph.setNode(packageInfo.pkg.name, packageInfo); 78 | } 79 | } 80 | 81 | // Create edges from each package to any non-development dependency 82 | graph.nodes().forEach((packageName) => { 83 | const packageInfo = graph.node(packageName); 84 | const allDeps = Object.keys(packageInfo.pkg.dependencies || {}) 85 | .concat(Object.keys(packageInfo.pkg.optionalDependencies || {})) 86 | .concat(Object.keys(packageInfo.pkg.peerDependencies || {})); 87 | allDeps.forEach((depName) => { 88 | if (graph.hasNode(depName)) { 89 | graph.setEdge(packageName, depName); 90 | } 91 | }); 92 | }); 93 | 94 | // Publish the packages in order 95 | const publishedPackages = new Set(); 96 | while (publishedPackages.size !== graph.nodeCount()) { 97 | const startingSize = publishedPackages.size; 98 | // Find any package where all dependencies have already been published 99 | const packagesToPublish = graph.nodes().filter((packageName) => { 100 | const packageDeps = graph.outEdges(packageName).map((edge) => edge.w); 101 | return !publishedPackages.has(packageName) && packageDeps.every((depName) => publishedPackages.has(depName)); 102 | }); 103 | for (const packageName of packagesToPublish) { 104 | const packageInfo = graph.node(packageName); 105 | await publishPackagesIfNeeded(packageInfo); 106 | publishedPackages.add(packageName); 107 | } 108 | if (startingSize === publishedPackages.size) { 109 | throw new Error('Unable to publish packages: cyclical dependencies encountered.'); 110 | } 111 | } 112 | })().catch((e) => { 113 | throw e; 114 | }); 115 | -------------------------------------------------------------------------------- /build-scripts/reflection-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags", 4 | "allDeclaredFields" : true 5 | }, 6 | { 7 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags$BooleanOptionHandler", 8 | "allDeclaredConstructors" : true, 9 | "allDeclaredMethods": true 10 | }, 11 | { 12 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags$WarningGuardErrorOptionHandler", 13 | "allDeclaredConstructors" : true, 14 | "allDeclaredMethods": true 15 | }, 16 | { 17 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags$WarningGuardWarningOptionHandler", 18 | "allDeclaredConstructors" : true, 19 | "allDeclaredMethods": true 20 | }, 21 | { 22 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags$WarningGuardOffOptionHandler", 23 | "allDeclaredConstructors" : true, 24 | "allDeclaredMethods": true 25 | }, 26 | { 27 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags$JsOptionHandler", 28 | "allDeclaredConstructors" : true, 29 | "allDeclaredMethods": true 30 | }, 31 | { 32 | "name" : "com.google.javascript.jscomp.CommandLineRunner$Flags$JsZipOptionHandler", 33 | "allDeclaredConstructors" : true, 34 | "allDeclaredMethods": true 35 | }, 36 | { 37 | "name" : "com.google.javascript.jscomp.AbstractCommandLineRunner$JsonFileSpec", 38 | "allDeclaredConstructors" : true, 39 | "allDeclaredMethods": true, 40 | "allDeclaredFields": true, 41 | "fields" : [ 42 | { "name" : "src", "allowWrite" : true }, 43 | { "name" : "path", "allowWrite" : true }, 44 | { "name" : "sourceMap", "allowWrite" : true }, 45 | { "name" : "webpackId", "allowWrite" : true } 46 | ] 47 | }, 48 | { 49 | "name" : "org.kohsuke.args4j.CmdLineParser", 50 | "allDeclaredConstructors" : true, 51 | "allDeclaredMethods": true, 52 | "allDeclaredFields": true 53 | }, 54 | { 55 | "name" : "org.kohsuke.args4j.Option", 56 | "allDeclaredConstructors" : true, 57 | "allDeclaredMethods": true, 58 | "allDeclaredFields": true 59 | }, 60 | { 61 | "name" : "org.kohsuke.args4j.OptionDef", 62 | "allDeclaredConstructors" : true, 63 | "allDeclaredMethods": true, 64 | "allDeclaredFields": true 65 | }, 66 | { 67 | "name" : "org.kohsuke.args4j.spi.FieldSetter", 68 | "allDeclaredConstructors" : true, 69 | "allDeclaredMethods": true, 70 | "allDeclaredFields": true 71 | }, 72 | { 73 | "name" : "org.kohsuke.args4j.spi.Setter", 74 | "allDeclaredConstructors" : true, 75 | "allDeclaredMethods": true, 76 | "allDeclaredFields": true 77 | }, 78 | { 79 | "name": "org.kohsuke.args4j.spi.BooleanOptionHandler", 80 | "allDeclaredConstructors": true, 81 | "allDeclaredMethods": true, 82 | "allDeclaredFields": true 83 | }, 84 | { 85 | "name": "org.kohsuke.args4j.spi.ByteOptionHandler", 86 | "allDeclaredConstructors": true, 87 | "allDeclaredMethods": true, 88 | "allDeclaredFields": true 89 | }, 90 | { 91 | "name": "org.kohsuke.args4j.spi.CharOptionHandler", 92 | "allDeclaredConstructors": true, 93 | "allDeclaredMethods": true, 94 | "allDeclaredFields": true 95 | }, 96 | { 97 | "name": "org.kohsuke.args4j.spi.DoubleOptionHandler", 98 | "allDeclaredConstructors": true, 99 | "allDeclaredMethods": true, 100 | "allDeclaredFields": true 101 | }, 102 | { 103 | "name": "org.kohsuke.args4j.spi.EnumOptionHandler", 104 | "allDeclaredConstructors": true, 105 | "allDeclaredMethods": true, 106 | "allDeclaredFields": true 107 | }, 108 | { 109 | "name": "org.kohsuke.args4j.spi.FileOptionHandler", 110 | "allDeclaredConstructors": true, 111 | "allDeclaredMethods": true, 112 | "allDeclaredFields": true 113 | }, 114 | { 115 | "name": "org.kohsuke.args4j.spi.FloatOptionHandler", 116 | "allDeclaredConstructors": true, 117 | "allDeclaredMethods": true, 118 | "allDeclaredFields": true 119 | }, 120 | { 121 | "name": "org.kohsuke.args4j.spi.InetAddressOptionHandler", 122 | "allDeclaredConstructors": true, 123 | "allDeclaredMethods": true, 124 | "allDeclaredFields": true 125 | }, 126 | { 127 | "name": "org.kohsuke.args4j.spi.IntOptionHandler", 128 | "allDeclaredConstructors": true, 129 | "allDeclaredMethods": true, 130 | "allDeclaredFields": true 131 | }, 132 | { 133 | "name": "org.kohsuke.args4j.spi.LongOptionHandler", 134 | "allDeclaredConstructors": true, 135 | "allDeclaredMethods": true, 136 | "allDeclaredFields": true 137 | }, 138 | { 139 | "name": "org.kohsuke.args4j.spi.MapOptionHandler", 140 | "allDeclaredConstructors": true, 141 | "allDeclaredMethods": true, 142 | "allDeclaredFields": true 143 | }, 144 | { 145 | "name": "org.kohsuke.args4j.spi.OneArgumentOptionHandler", 146 | "allDeclaredConstructors": true, 147 | "allDeclaredMethods": true, 148 | "allDeclaredFields": true 149 | }, 150 | { 151 | "name": "org.kohsuke.args4j.spi.OptionHandler", 152 | "allDeclaredConstructors": true, 153 | "allDeclaredMethods": true, 154 | "allDeclaredFields": true 155 | }, 156 | { 157 | "name": "org.kohsuke.args4j.spi.PatternOptionHandler", 158 | "allDeclaredConstructors": true, 159 | "allDeclaredMethods": true, 160 | "allDeclaredFields": true 161 | }, 162 | { 163 | "name": "org.kohsuke.args4j.spi.PathOptionHandler", 164 | "allDeclaredConstructors": true, 165 | "allDeclaredMethods": true, 166 | "allDeclaredFields": true 167 | }, 168 | { 169 | "name": "org.kohsuke.args4j.spi.ShortOptionHandler", 170 | "allDeclaredConstructors": true, 171 | "allDeclaredMethods": true, 172 | "allDeclaredFields": true 173 | }, 174 | { 175 | "name": "org.kohsuke.args4j.spi.StringOptionHandler", 176 | "allDeclaredConstructors": true, 177 | "allDeclaredMethods": true, 178 | "allDeclaredFields": true 179 | }, 180 | { 181 | "name": "org.kohsuke.args4j.spi.URIOptionHandler", 182 | "allDeclaredConstructors": true, 183 | "allDeclaredMethods": true, 184 | "allDeclaredFields": true 185 | }, 186 | { 187 | "name": "org.kohsuke.args4j.spi.URLOptionHandler", 188 | "allDeclaredConstructors": true, 189 | "allDeclaredMethods": true, 190 | "allDeclaredFields": true 191 | }, 192 | { 193 | "name" : "com.google.javascript.jscomp.ConformanceConfig", 194 | "allDeclaredMethods" : true, 195 | "allDeclaredFields" : true, 196 | "allPublicMethods" : true, 197 | "allPublicFields" : true 198 | }, 199 | { 200 | "name" : "com.google.javascript.jscomp.ConformanceConfig$Builder", 201 | "allDeclaredMethods" : true, 202 | "allDeclaredFields" : true, 203 | "allPublicMethods" : true, 204 | "allPublicFields" : true 205 | }, 206 | { 207 | "name": "com.google.javascript.jscomp.ConformanceConfig$LibraryLevelNonAllowlistedConformanceViolationsBehavior", 208 | "allPublicMethods" : true, 209 | "allPublicFields" : true 210 | }, 211 | { 212 | "name" : "com.google.javascript.jscomp.ConformanceRules", 213 | "allDeclaredMethods" : true, 214 | "allDeclaredFields" : true, 215 | "allPublicMethods" : true, 216 | "allPublicFields" : true 217 | }, 218 | { 219 | "name" : "com.google.javascript.jscomp.ConformanceRules$BanCreateDom", 220 | "methods" : [ { "name" : "" } ], 221 | "allDeclaredMethods" : true, 222 | "allDeclaredFields" : true, 223 | "allPublicMethods" : true, 224 | "allPublicFields" : true 225 | }, 226 | { 227 | "name" : "com.google.javascript.jscomp.ConformanceRules$BanCreateElement", 228 | "methods" : [ { "name" : "" } ], 229 | "allDeclaredMethods" : true, 230 | "allDeclaredFields" : true, 231 | "allPublicMethods" : true, 232 | "allPublicFields" : true 233 | }, 234 | { 235 | "name" : "com.google.javascript.jscomp.ConformanceRules$BanGlobalVars", 236 | "methods" : [ { "name" : "" } ], 237 | "allDeclaredMethods" : true, 238 | "allDeclaredFields" : true, 239 | "allPublicMethods" : true, 240 | "allPublicFields" : true 241 | }, 242 | { 243 | "name" : "com.google.javascript.jscomp.ConformanceRules$BanThrowOfNonErrorTypes", 244 | "methods" : [ { "name" : "" } ], 245 | "allDeclaredMethods" : true, 246 | "allDeclaredFields" : true, 247 | "allPublicMethods" : true, 248 | "allPublicFields" : true 249 | }, 250 | { 251 | "name" : "com.google.javascript.jscomp.ConformanceRules$BanUnknownThis", 252 | "methods" : [ { "name" : "" } ], 253 | "allDeclaredMethods" : true, 254 | "allDeclaredFields" : true, 255 | "allPublicMethods" : true, 256 | "allPublicFields" : true 257 | }, 258 | { 259 | "name" : "com.google.javascript.jscomp.Requirement", 260 | "allDeclaredMethods" : true, 261 | "allDeclaredFields" : true, 262 | "allPublicMethods" : true, 263 | "allPublicFields" : true 264 | }, 265 | { 266 | "name" : "com.google.javascript.jscomp.Requirement$Builder", 267 | "allDeclaredMethods" : true, 268 | "allDeclaredFields" : true, 269 | "allPublicMethods" : true, 270 | "allPublicFields" : true 271 | }, 272 | { 273 | "name" : "com.google.javascript.jscomp.Requirement$WhitelistEntry", 274 | "allDeclaredMethods" : true, 275 | "allDeclaredFields" : true, 276 | "allPublicMethods" : true, 277 | "allPublicFields" : true 278 | }, 279 | { 280 | "name" : "com.google.javascript.jscomp.Requirement$Type", 281 | "allDeclaredMethods" : true, 282 | "allDeclaredFields" : true, 283 | "allPublicMethods" : true, 284 | "allPublicFields" : true 285 | }, 286 | { 287 | "name" : "com.google.javascript.jscomp.Requirement$TypeMatchingStrategy", 288 | "allDeclaredMethods" : true, 289 | "allDeclaredFields" : true, 290 | "allPublicMethods" : true, 291 | "allPublicFields" : true 292 | }, 293 | { 294 | "name" : "com.google.javascript.jscomp.Requirement$Severity", 295 | "allDeclaredMethods" : true, 296 | "allDeclaredFields" : true, 297 | "allPublicMethods" : true, 298 | "allPublicFields" : true 299 | } 300 | ] 301 | -------------------------------------------------------------------------------- /build-scripts/run-command.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2019 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | import {spawn} from 'node:child_process'; 18 | 19 | /** 20 | * Execute a shell command as a promise which resolves to an Array of the form 21 | * [standardOut: string, standardError: string, exitCode: number] 22 | * 23 | * @param {string} cmd 24 | * @param {(Array|Object)=} args if undefined, the cmd argument is assumed to contain 25 | * arguments and will be split on whitespace 26 | * @param {Object=} spawnOpts 27 | * @return {!Promise} 28 | */ 29 | export default function(cmd, args, spawnOpts) { 30 | if (!spawnOpts && args && !Array.isArray(args)) { 31 | spawnOpts = args; 32 | args = undefined; 33 | } 34 | if (!args || !Array.isArray(args)) { 35 | // TODO(ChadKillingsworth): Not really safe in general, since this could split in the middle of quoted strings. 36 | // This is good enough for the purposes of this script. 37 | const commandParts = cmd.split(/\s+/); 38 | cmd = commandParts[0]; 39 | args = commandParts.slice(1); 40 | } 41 | // child process should inherit stdin/out/err from this process unless spawnOpts says otherwise 42 | spawnOpts = { 43 | stdio: 'inherit', 44 | ...spawnOpts, 45 | }; 46 | 47 | let externalProcess; 48 | const promise = new Promise((resolve, reject) => { 49 | let stdout = ''; 50 | let stderr = ''; 51 | 52 | externalProcess = spawn(cmd, args, spawnOpts); 53 | externalProcess.on('error', (err) => { 54 | if (!err) { 55 | err = new Error(stderr || 'external process error'); 56 | } else if (!(err instanceof Error)) { 57 | err = new Error(err); 58 | } 59 | err.stdout = stdout; 60 | err.stderr = stderr; 61 | err.exitCode = 1; 62 | reject(err); 63 | }); 64 | externalProcess.on('close', (exitCode) => { 65 | if (exitCode != 0) { 66 | const err = new Error(`non-zero exit code ${exitCode}`); 67 | err.stdout = stdout; 68 | err.stderr = stderr; 69 | err.exitCode = exitCode; 70 | reject(err); 71 | } 72 | resolve({stdout, stderr, exitCode}); 73 | }); 74 | if (externalProcess.stdout) { 75 | externalProcess.stdout.on('data', (data) => { 76 | stdout += data.toString(); 77 | }); 78 | } 79 | if (externalProcess.stderr) { 80 | externalProcess.stderr.on('data', (data) => { 81 | stderr += data.toString(); 82 | }); 83 | } 84 | }); 85 | promise.childProcess = externalProcess; 86 | return promise; 87 | } 88 | -------------------------------------------------------------------------------- /build-scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Run the test commands and fail the script if any of them failed 3 | EXIT_STATUS=0 4 | ./packages/google-closure-compiler/test/support/jasmine-launcher.js "$@" || EXIT_STATUS=$? 5 | exit $EXIT_STATUS 6 | -------------------------------------------------------------------------------- /build-scripts/version-packages.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2022 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * @fileoverview 20 | * 21 | * Create a new version for each package. 22 | * Update any dependencies on other packages in this project. 23 | */ 24 | 25 | import childProcess from 'node:child_process'; 26 | import fs from 'node:fs'; 27 | import path from 'node:path'; 28 | import {fileURLToPath, URL} from 'node:url'; 29 | import parseArgs from 'minimist'; 30 | import semver from 'semver'; 31 | 32 | const flags = parseArgs(process.argv.slice(2)); 33 | if (!flags['new-version']) { 34 | process.stderr.write(`No new version specified\n`); 35 | process.exit(); 36 | } 37 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 38 | const rootPackageJsonPath = path.resolve(__dirname, '../package.json'); 39 | const rootPackageJson = JSON.parse(fs.readFileSync(rootPackageJsonPath, 'utf-8')); 40 | const currentVersion = semver(rootPackageJson.version); 41 | const newVersion = semver(flags['new-version']); 42 | 43 | if (!semver.gt(newVersion, currentVersion)) { 44 | process.stderr.write(`New version must be greater than current version\n`); 45 | process.exit(); 46 | } 47 | 48 | rootPackageJson.version = newVersion.toString(); 49 | fs.writeFileSync(rootPackageJsonPath, `${JSON.stringify(rootPackageJson, null, 2)}\n`, 'utf8'); 50 | childProcess.execSync(`git add "${rootPackageJsonPath}"`, {stdio: 'inherit'}); 51 | 52 | const dependencyTypes = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']; 53 | 54 | const packagesDirPath = path.resolve(__dirname, '../packages'); 55 | const packages = fs.readdirSync(packagesDirPath); 56 | 57 | for (const packageName of packages) { 58 | const packageJsonPath = `${packagesDirPath}/${packageName}/package.json`; 59 | // Only directories that have package.json files are packages in this project. 60 | // For instance, the google-closure-compiler-js directory only has a readme for historical purposes and should 61 | // be excluded. 62 | try { 63 | fs.statSync(packageJsonPath); // check if file exists 64 | } catch { 65 | continue; 66 | } 67 | const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath)); 68 | pkgJson.version = newVersion.toString(); 69 | 70 | for (const dependencyType of dependencyTypes) { 71 | for (const dependencyName of Object.keys(pkgJson[dependencyType] || {})) { 72 | if (packages.includes(dependencyName)) { 73 | pkgJson[dependencyType][dependencyName] = `^${newVersion.toString()}`; 74 | } 75 | } 76 | } 77 | fs.writeFileSync(packageJsonPath, `${JSON.stringify(pkgJson, null, 2)}\n`, 'utf8'); 78 | childProcess.execSync(`git add "${packageJsonPath}"`, {stdio: 'inherit'}); 79 | } 80 | 81 | childProcess.execSync(`yarn install --no-immutable`, {stdio: 'inherit'}); 82 | childProcess.execSync(`git add "${path.resolve(__dirname, '../yarn.lock')}"`, {stdio: 'inherit'}); 83 | childProcess.execSync(`git commit -m "v${newVersion.toString()}"`, {stdio: 'inherit'}); 84 | childProcess.execSync(`git tag -a v${newVersion.toString()} -m "v${newVersion.toString()}"`, {stdio: 'inherit'}); 85 | -------------------------------------------------------------------------------- /deployments.md: -------------------------------------------------------------------------------- 1 | # Deploying Closure Compiler to NPM 2 | 3 | *You now need yarn installed: https://yarnpkg.com/en/docs/install* 4 | 5 | Workflows now automatically check for and create new releases daily based from tags 6 | on the main compiler repo. 7 | 8 | ## Manually deploying new releases of the main compiler 9 | 10 | 1. Run the [Compiler release workflow](https://github.com/google/closure-compiler-npm/actions/workflows/release.yml) 11 | * For the `COMPILER_VERSION_NUMBER` input, use the actual version number here without the `v`. 12 | * If you do not provide a version number, the workflow will use the oldest version tag that is newer than the current package major version. 13 | 2. Verify the workflow runs successfully. It will push the release commit and tag. 14 | 3. Verify the new version published to npm. 15 | 16 | ## Deploying changes to the package CLIs or plugins 17 | 18 | Features and fixes to the packages in this repo need not wait for a main compiler release. 19 | They can be published at any time if they are backwards compatible with the last major version. 20 | Breaking changes should be deployed at the same time as a major compiler release. 21 | 22 | 1. Run `yarn install`. 23 | 2. Run `yarn version (patch|minor)`. 24 | Patch level versions should be used for fixing issues. 25 | Minor level versions should be used for new features that are backwards compatible. 26 | 3. Push commit and tags 27 | 28 | ## Verifying Publication Was Successful 29 | 30 | After pushing a new tagged commit, [Github Actions](https://github.com/google/closure-compiler-npm/actions) 31 | will automatically start building this commit. Expand the `Deploying application` section at the bottom of the log. 32 | Each package should show npm logs for packaging and publication. In addition, 33 | the npm registry page for each package should list the newly published version. 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "closure-compiler-npm", 3 | "version": "20250603.0.0", 4 | "private": true, 5 | "type": "module", 6 | "workspaces": { 7 | "packages": [ 8 | "packages/google-closure-compiler-java", 9 | "packages/google-closure-compiler-linux", 10 | "packages/google-closure-compiler-linux-arm64", 11 | "packages/google-closure-compiler-macos", 12 | "packages/google-closure-compiler-windows", 13 | "packages/google-closure-compiler" 14 | ] 15 | }, 16 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/google/closure-compiler-npm.git" 20 | }, 21 | "contributors": [ 22 | { 23 | "name": "Chad Killingsworth", 24 | "email": "chadkillingsworth@gmail.com" 25 | } 26 | ], 27 | "license": "Apache-2.0", 28 | "bugs": { 29 | "url": "https://github.com/google/closure-compiler/issues" 30 | }, 31 | "homepage": "https://developers.google.com/closure/compiler/", 32 | "devDependencies": { 33 | "glob": "11.x", 34 | "graphlib": "2.x", 35 | "jasmine": "^5.6.0", 36 | "jasmine-console-reporter": "^3.1.0", 37 | "minimist": "1.x", 38 | "ncp": "2.x", 39 | "semver": "5.x" 40 | }, 41 | "resolutions": { 42 | "chokidar/glob-parent": "^5.1.2", 43 | "expand-brackets/debug": "^3.0.0", 44 | "findup-sync/micromatch": "^4.0.8", 45 | "glob-stream/glob-parent": "^5.1.2", 46 | "glob-watcher": "^6.0.0", 47 | "matchdep/micromatch": "^4.0.8", 48 | "snapdragon/debug": "^3.0.0", 49 | "yargs": "^13.3.0" 50 | }, 51 | "scripts": { 52 | "build": "./build-scripts/build.sh", 53 | "test": "./build-scripts/test.sh", 54 | "test:root": "./build-scripts/jasmine.sh --reporter=jasmine-console-reporter test/*.js", 55 | "clean": "./build-scripts/clean.sh", 56 | "publish-packages": "./build-scripts/publish.js" 57 | }, 58 | "packageManager": "yarn@4.9.1" 59 | } 60 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-java/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/google-closure-compiler-java/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import path from 'node:path'; 18 | import { fileURLToPath, URL } from 'node:url'; 19 | 20 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 21 | export default path.resolve(__dirname, 'compiler.jar'); 22 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-java/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-closure-compiler-java", 3 | "version": "20250603.0.0", 4 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler using Java", 5 | "type": "module", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/google/closure-compiler-npm.git#master" 10 | }, 11 | "homepage": "https://developers.google.com/closure/compiler/", 12 | "author": "Chad Killingsworth ", 13 | "license": "Apache-2.0", 14 | "keywords": [ 15 | "javascript", 16 | "compiler", 17 | "optimizer", 18 | "minifier", 19 | "closure", 20 | "java" 21 | ], 22 | "files": [ 23 | "compiler.jar", 24 | "index.js", 25 | "package.json", 26 | "readme.md", 27 | "LICENSE" 28 | ], 29 | "preferUnplugged": true, 30 | "scripts": { 31 | "build": "echo \"google-closure-compiler-java build\"", 32 | "test": "node ./test.js", 33 | "prepublishOnly": "node ../../build-scripts/ensure-file-exists.js compiler.jar" 34 | }, 35 | "devDependencies": { 36 | "chalk": "5.x" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-java/readme.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler-java 2 | 3 | Java platform distribution of Closure Compiler. 4 | 5 | For cli scripts, build tool plugins and more see the [main distribution](https://www.npmjs.com/package/google-closure-compiler). 6 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-java/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import fs from 'node:fs'; 19 | import javaPath from './index.js'; 20 | import chalk from 'chalk'; 21 | 22 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 23 | 24 | process.stdout.write('google-closure-compiler-java\n'); 25 | if (fs.existsSync(javaPath)) { 26 | process.stdout.write(` ${chalk.greenBright('✓')} ${dimWhite('compiler jar exists')}\n`); 27 | } else { 28 | process.stdout.write(` ${chalk.red('compiler jar does not exist')}\n`); 29 | process.exitCode = 1; 30 | } 31 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux-arm64/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux-arm64/build-image.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2025 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Check to see if the graal native image for this platform should be built 21 | */ 22 | 23 | import fs from 'node:fs'; 24 | import path from 'node:path'; 25 | import {fileURLToPath, URL} from 'node:url'; 26 | import chalk from 'chalk'; 27 | import runCommand from '../../build-scripts/run-command.js'; 28 | 29 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 30 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 31 | 32 | if (fs.existsSync(path.resolve(__dirname, 'compiler'))) { 33 | process.stdout.write(dimWhite(` google-closure-compiler-linux-arm64 binary already exists\n`)); 34 | } else if (process.platform !== 'linux' || process.arch !== 'arm64') { 35 | process.stdout.write(dimWhite(` google-closure-compiler-linux-arm64 build wrong platform\n`)); 36 | } else { 37 | process.stdout.write(dimWhite(` google-closure-compiler-linux-arm64 building image\n`)); 38 | runCommand('node', ['../../build-scripts/graal.js']) 39 | .then(({exitCode}) => { 40 | process.exitCode = exitCode || 0; 41 | }) 42 | .catch((e) => { 43 | process.exitCode = e.exitCode || 1; 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux-arm64/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import path from 'node:path'; 18 | import { fileURLToPath, URL } from 'node:url'; 19 | 20 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 21 | export default path.resolve(__dirname, 'compiler'); 22 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux-arm64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-closure-compiler-linux-arm64", 3 | "version": "20250603.0.0", 4 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler using Java", 5 | "type": "module", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/google/closure-compiler-npm.git#master" 10 | }, 11 | "homepage": "https://developers.google.com/closure/compiler/", 12 | "author": "Chad Killingsworth ", 13 | "license": "Apache-2.0", 14 | "keywords": [ 15 | "javascript", 16 | "compiler", 17 | "optimizer", 18 | "minifier", 19 | "closure", 20 | "java" 21 | ], 22 | "files": [ 23 | "compiler", 24 | "index.js", 25 | "package.json", 26 | "readme.md", 27 | "LICENSE" 28 | ], 29 | "preferUnplugged": true, 30 | "scripts": { 31 | "build": "node ./build-image.js", 32 | "test": "node ./test.js", 33 | "prepublishOnly": "node ../../build-scripts/ensure-file-exists.js compiler" 34 | }, 35 | "devDependencies": { 36 | "chalk": "5.x" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux-arm64/readme.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler-linux-arm64 2 | 3 | Linux arm64 native platform distribution of Closure Compiler. 4 | 5 | For cli scripts, build tool plugins and more see the [main distribution](https://www.npmjs.com/package/google-closure-compiler). 6 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux-arm64/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2025 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import {spawn} from 'node:child_process'; 19 | import fs from 'node:fs'; 20 | import chalk from 'chalk'; 21 | import nativeImagePath from './index.js'; 22 | 23 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 24 | 25 | process.stdout.write('google-closure-compiler-linux-arm64\n'); 26 | if (process.platform !== 'linux' || process.arch !== 'arm64') { 27 | process.stdout.write(dimWhite(` skipping tests - incorrect platform\n`)); 28 | } else if (fs.existsSync(nativeImagePath)) { 29 | process.stdout.write(` ${chalk.greenBright('✓')} ${dimWhite('compiler binary exists')}\n`); 30 | new Promise( 31 | (resolve, reject) => { 32 | const compilerTest = spawn( 33 | nativeImagePath, 34 | ['--version'], 35 | {stdio: 'inherit'}); 36 | compilerTest.on('error', (err) => { 37 | reject(err); 38 | }); 39 | compilerTest.on('close', (exitCode) => { 40 | if (exitCode != 0) { 41 | return reject('non zero exit code'); 42 | } 43 | process.stdout.write( 44 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler version successfully reported')}\n`); 45 | resolve(); 46 | }); 47 | }) 48 | .then(() => new Promise((resolve, reject) => { 49 | const compilerTest = spawn( 50 | nativeImagePath, 51 | ['--help'], 52 | {stdio: 'inherit'}); 53 | compilerTest.on('error', (err) => { 54 | reject(err); 55 | }); 56 | compilerTest.on('close', (exitCode) => { 57 | if (exitCode != 0) { 58 | return reject('non zero exit code'); 59 | } 60 | process.stdout.write( 61 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler help successfully reported')}\n`); 62 | resolve(); 63 | }); 64 | })) 65 | .catch((err) => { 66 | process.stderr.write((err || '').toString() + '\n'); 67 | process.stdout.write(` ${chalk.red('compiler execution tests failed')}\n`); 68 | process.exitCode = 1; 69 | }); 70 | } else { 71 | process.stdout.write(` ${chalk.red('compiler binary does not exist')}\n`); 72 | process.exitCode = 1; 73 | } 74 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux/build-image.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Check to see if the graal native image for this platform should be built 21 | */ 22 | 23 | import fs from 'node:fs'; 24 | import path from 'node:path'; 25 | import {fileURLToPath, URL} from 'node:url'; 26 | import chalk from 'chalk'; 27 | import runCommand from '../../build-scripts/run-command.js'; 28 | 29 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 30 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 31 | 32 | if (fs.existsSync(path.resolve(__dirname, 'compiler'))) { 33 | process.stdout.write(dimWhite(` google-closure-compiler-linux binary already exists\n`)); 34 | } else if (process.platform !== 'linux' || !['x32', 'x64'].includes(process.arch)) { 35 | process.stdout.write(dimWhite(` google-closure-compiler-linux build wrong platform\n`)); 36 | } else { 37 | process.stdout.write(dimWhite(` google-closure-compiler-linux building image\n`)); 38 | runCommand('node', ['../../build-scripts/graal.js']) 39 | .then(({exitCode}) => { 40 | process.exitCode = exitCode || 0; 41 | }) 42 | .catch((e) => { 43 | process.exitCode = e.exitCode || 1; 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import path from 'node:path'; 18 | import { fileURLToPath, URL } from 'node:url'; 19 | 20 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 21 | export default path.resolve(__dirname, 'compiler'); 22 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-closure-compiler-linux", 3 | "version": "20250603.0.0", 4 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler using Java", 5 | "type": "module", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/google/closure-compiler-npm.git#master" 10 | }, 11 | "homepage": "https://developers.google.com/closure/compiler/", 12 | "author": "Chad Killingsworth ", 13 | "license": "Apache-2.0", 14 | "keywords": [ 15 | "javascript", 16 | "compiler", 17 | "optimizer", 18 | "minifier", 19 | "closure", 20 | "java" 21 | ], 22 | "files": [ 23 | "compiler", 24 | "index.js", 25 | "package.json", 26 | "readme.md", 27 | "LICENSE" 28 | ], 29 | "preferUnplugged": true, 30 | "scripts": { 31 | "build": "node ./build-image.js", 32 | "test": "node ./test.js", 33 | "prepublishOnly": "node ../../build-scripts/ensure-file-exists.js compiler" 34 | }, 35 | "devDependencies": { 36 | "chalk": "5.x" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux/readme.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler-linux 2 | 3 | Linux native platform distribution of Closure Compiler. 4 | 5 | For cli scripts, build tool plugins and more see the [main distribution](https://www.npmjs.com/package/google-closure-compiler). 6 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-linux/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import {spawn} from 'node:child_process'; 19 | import fs from 'node:fs'; 20 | import chalk from 'chalk'; 21 | import nativeImagePath from './index.js'; 22 | 23 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 24 | 25 | process.stdout.write('google-closure-compiler-linux\n'); 26 | if (process.platform !== 'linux' || !['x64','x32'].includes(process.arch)) { 27 | process.stdout.write(dimWhite(` skipping tests - incorrect platform\n`)); 28 | } else if (fs.existsSync(nativeImagePath)) { 29 | process.stdout.write(` ${chalk.greenBright('✓')} ${dimWhite('compiler binary exists')}\n`); 30 | new Promise( 31 | (resolve, reject) => { 32 | const compilerTest = spawn( 33 | nativeImagePath, 34 | ['--version'], 35 | {stdio: 'inherit'}); 36 | compilerTest.on('error', (err) => { 37 | reject(err); 38 | }); 39 | compilerTest.on('close', (exitCode) => { 40 | if (exitCode != 0) { 41 | return reject('non zero exit code'); 42 | } 43 | process.stdout.write( 44 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler version successfully reported')}\n`); 45 | resolve(); 46 | }); 47 | }) 48 | .then(() => new Promise((resolve, reject) => { 49 | const compilerTest = spawn( 50 | nativeImagePath, 51 | ['--help'], 52 | {stdio: 'inherit'}); 53 | compilerTest.on('error', (err) => { 54 | reject(err); 55 | }); 56 | compilerTest.on('close', (exitCode) => { 57 | if (exitCode != 0) { 58 | return reject('non zero exit code'); 59 | } 60 | process.stdout.write( 61 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler help successfully reported')}\n`); 62 | resolve(); 63 | }); 64 | })) 65 | .catch((err) => { 66 | process.stderr.write((err || '').toString() + '\n'); 67 | process.stdout.write(` ${chalk.red('compiler execution tests failed')}\n`); 68 | process.exitCode = 1; 69 | }); 70 | } else { 71 | process.stdout.write(` ${chalk.red('compiler binary does not exist')}\n`); 72 | process.exitCode = 1; 73 | } 74 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-macos/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/google-closure-compiler-macos/build-image.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Check to see if the graal native image for this platform should be built 21 | */ 22 | 23 | import fs from 'node:fs'; 24 | import path from 'node:path'; 25 | import {fileURLToPath, URL} from 'node:url'; 26 | import chalk from 'chalk'; 27 | import runCommand from '../../build-scripts/run-command.js'; 28 | 29 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 30 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 31 | 32 | if (fs.existsSync(path.resolve(__dirname, 'compiler'))) { 33 | process.stdout.write(dimWhite(` google-closure-compiler-macos binary already exists\n`)); 34 | } else if (process.platform !== 'darwin') { 35 | process.stdout.write(dimWhite(` google-closure-compiler-macos build wrong platform\n`)); 36 | } else { 37 | process.stdout.write(dimWhite(` google-closure-compiler-macos building image\n`)); 38 | runCommand('node', ['../../build-scripts/graal.js']) 39 | .then(({exitCode}) => { 40 | process.exitCode = exitCode || 0; 41 | }) 42 | .catch((e) => { 43 | process.exitCode = e.exitCode || 1; 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-macos/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import path from 'node:path'; 18 | import { fileURLToPath, URL } from 'node:url'; 19 | 20 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 21 | export default path.resolve(__dirname, 'compiler'); 22 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-macos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-closure-compiler-macos", 3 | "version": "20250603.0.0", 4 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler using Java", 5 | "type": "module", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/google/closure-compiler-npm.git#master" 10 | }, 11 | "homepage": "https://developers.google.com/closure/compiler/", 12 | "author": "Chad Killingsworth ", 13 | "license": "Apache-2.0", 14 | "keywords": [ 15 | "javascript", 16 | "compiler", 17 | "optimizer", 18 | "minifier", 19 | "closure", 20 | "java" 21 | ], 22 | "files": [ 23 | "compiler", 24 | "index.js", 25 | "package.json", 26 | "readme.md", 27 | "LICENSE" 28 | ], 29 | "preferUnplugged": true, 30 | "scripts": { 31 | "build": "node ./build-image.js", 32 | "test": "node ./test.js", 33 | "prepublishOnly": "node ../../build-scripts/ensure-file-exists.js compiler" 34 | }, 35 | "devDependencies": { 36 | "chalk": "5.x" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-macos/readme.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler-macos 2 | 3 | Mac OS native platform distribution of Closure Compiler. 4 | 5 | For cli scripts, build tool plugins and more see the [main distribution](https://www.npmjs.com/package/google-closure-compiler). 6 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-macos/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import {spawn} from 'node:child_process'; 19 | import fs from 'node:fs'; 20 | import chalk from 'chalk'; 21 | import nativeImagePath from './index.js'; 22 | 23 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 24 | 25 | process.stdout.write('google-closure-compiler-macos\n'); 26 | if (process.platform !== 'darwin') { 27 | process.stdout.write(dimWhite(` skipping tests - incorrect platform\n`)); 28 | } else if (fs.existsSync(nativeImagePath)) { 29 | process.stdout.write(` ${chalk.greenBright('✓')} ${dimWhite('compiler binary exists')}\n`); 30 | new Promise( 31 | (resolve, reject) => { 32 | const compilerTest = spawn( 33 | nativeImagePath, 34 | ['--version'], 35 | {stdio: 'inherit'}); 36 | compilerTest.on('error', (err) => { 37 | reject(err); 38 | }); 39 | compilerTest.on('close', (exitCode) => { 40 | if (exitCode != 0) { 41 | return reject('non zero exit code'); 42 | } 43 | process.stdout.write( 44 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler version successfully reported')}\n`); 45 | resolve(); 46 | }); 47 | }) 48 | .then(() => new Promise((resolve, reject) => { 49 | const compilerTest = spawn( 50 | nativeImagePath, 51 | ['--help'], 52 | {stdio: 'inherit'}); 53 | compilerTest.on('error', (err) => { 54 | reject(err); 55 | }); 56 | compilerTest.on('close', (exitCode) => { 57 | if (exitCode != 0) { 58 | return reject('non zero exit code'); 59 | } 60 | process.stdout.write( 61 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler help successfully reported')}\n`); 62 | resolve(); 63 | }); 64 | })) 65 | .catch((err) => { 66 | process.stderr.write((err || '').toString() + '\n'); 67 | process.stdout.write(` ${chalk.red('compiler execution tests failed')}\n`); 68 | process.exitCode = 1; 69 | }); 70 | } else { 71 | process.stdout.write(` ${chalk.red('compiler binary does not exist')}\n`); 72 | process.exitCode = 1; 73 | } 74 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-windows/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/google-closure-compiler-windows/build-image.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2019 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Check to see if the graal native image for this platform should be built 21 | */ 22 | 23 | import fs from 'node:fs'; 24 | import path from 'node:path'; 25 | import {fileURLToPath, URL} from 'node:url'; 26 | import chalk from 'chalk'; 27 | import runCommand from '../../build-scripts/run-command.js'; 28 | 29 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 30 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 31 | 32 | // The windows sdk set env command messes with colors, so reset the console back to default 33 | process.stdout.write(chalk.reset('')); 34 | 35 | if (fs.existsSync(path.resolve(__dirname, 'compiler'))) { 36 | process.stdout.write(dimWhite(` google-closure-compiler-windows binary already exists\n`)); 37 | process.exit(0); 38 | } else if (process.platform !== 'win32') { 39 | process.stdout.write(dimWhite(` google-closure-compiler-windows build wrong platform\n`)); 40 | process.exit(0); 41 | } else { 42 | process.stdout.write(dimWhite(` google-closure-compiler-windows building image\n`)); 43 | runCommand('node', ['../../build-scripts/graal.js']) 44 | .then(({exitCode}) => { 45 | process.exitCode = exitCode || 0; 46 | }) 47 | .catch((e) => { 48 | process.exitCode = e.exitCode || 1; 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-windows/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import path from 'node:path'; 18 | import { fileURLToPath, URL } from 'node:url'; 19 | 20 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 21 | export default path.resolve(__dirname, 'compiler.exe'); 22 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-windows/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-closure-compiler-windows", 3 | "version": "20250603.0.0", 4 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler using Java", 5 | "type": "module", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/google/closure-compiler-npm.git#master" 10 | }, 11 | "homepage": "https://developers.google.com/closure/compiler/", 12 | "author": "Chad Killingsworth ", 13 | "license": "Apache-2.0", 14 | "keywords": [ 15 | "javascript", 16 | "compiler", 17 | "optimizer", 18 | "minifier", 19 | "closure" 20 | ], 21 | "files": [ 22 | "compiler.exe", 23 | "index.js", 24 | "package.json", 25 | "readme.md", 26 | "LICENSE" 27 | ], 28 | "preferUnplugged": true, 29 | "scripts": { 30 | "build": "node build-image.js", 31 | "test": "node ./test.js", 32 | "prepublishOnly": "node ../../build-scripts/ensure-file-exists.js compiler.exe" 33 | }, 34 | "devDependencies": { 35 | "chalk": "5.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-windows/readme.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler-windows 2 | 3 | Windows native platform distribution of Closure Compiler. 4 | 5 | For cli scripts, build tool plugins and more see the [main distribution](https://www.npmjs.com/package/google-closure-compiler). 6 | -------------------------------------------------------------------------------- /packages/google-closure-compiler-windows/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2019 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import {spawn} from 'node:child_process'; 19 | import fs from 'node:fs'; 20 | import chalk from 'chalk'; 21 | import nativeImagePath from './index.js'; 22 | 23 | const dimWhite = (text) => chalk.dim(chalk.white(text)); 24 | 25 | process.stdout.write('google-closure-compiler-windows\n'); 26 | if (process.platform !== 'win32') { 27 | process.stdout.write(dimWhite(` skipping tests - incorrect platform\n`)); 28 | } else if (fs.existsSync(nativeImagePath)) { 29 | process.stdout.write(` ${chalk.greenBright('✓')} ${dimWhite('compiler binary exists')}\n`); 30 | new Promise( 31 | (resolve, reject) => { 32 | const compilerTest = spawn( 33 | nativeImagePath, 34 | ['--version'], 35 | {stdio: 'inherit'}); 36 | compilerTest.on('error', (err) => { 37 | reject(err); 38 | }); 39 | compilerTest.on('close', (exitCode) => { 40 | if (exitCode != 0) { 41 | return reject('non zero exit code'); 42 | } 43 | process.stdout.write( 44 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler version successfully reported')}\n`); 45 | resolve(); 46 | }); 47 | }) 48 | .then(() => new Promise((resolve, reject) => { 49 | const compilerTest = spawn( 50 | nativeImagePath, 51 | ['--help'], 52 | {stdio: 'inherit'}); 53 | compilerTest.on('error', (err) => { 54 | reject(err); 55 | }); 56 | compilerTest.on('close', (exitCode) => { 57 | if (exitCode != 0) { 58 | return reject('non zero exit code'); 59 | } 60 | process.stdout.write( 61 | ` ${chalk.greenBright('✓')} ${dimWhite('compiler help successfully reported')}\n`); 62 | resolve(); 63 | }); 64 | })) 65 | .catch((err) => { 66 | process.stderr.write((err || '').toString() + '\n'); 67 | process.stdout.write(` ${chalk.red('compiler execution tests failed')}\n`); 68 | process.exitCode = 1; 69 | }); 70 | } else { 71 | process.stdout.write(` ${chalk.red('compiler binary does not exist')}\n`); 72 | process.exitCode = 1; 73 | } 74 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/google-closure-compiler/README.md: -------------------------------------------------------------------------------- 1 | # google-closure-compiler 2 | [![npm version](https://badge.fury.io/js/google-closure-compiler.svg)](https://badge.fury.io/js/google-closure-compiler) 3 | 4 | Check, compile, optimize and compress Javascript with Closure-Compiler 5 | 6 | This repository tracks issues related to the publication to npmjs.org and associated plugins. 7 | Any bugs not related to the plugins themselves should be reported to the 8 | [main repository](https://github.com/google/closure-compiler/). 9 | 10 | **Note that the installed binary is not actually JavaScript, but native binary or Java jar file depending on the local system.** 11 | 12 | ## Getting Started 13 | If you are new to [Closure-Compiler](https://developers.google.com/closure/compiler/), make 14 | sure to read and understand the 15 | [compilation levels](https://developers.google.com/closure/compiler/docs/compilation_levels) as 16 | the compiler works very differently depending on the compilation level selected. 17 | 18 | For help or questions with the compiler, the best resource is 19 | [Stack Overflow](http://stackoverflow.com/questions/tagged/google-closure-compiler). Posts there 20 | are monitored by multiple Closure Compiler team members. 21 | 22 | You may also post in the 23 | [Closure Compiler Discuss Google Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss). 24 | 25 | *Please don't cross post to both Stack Overflow and Closure Compiler Discuss.* 26 | 27 | The compiler is distributed as a Java jar or as Mac OS, Linux and Windows native binaries. 28 | 29 | ### Native Binary Version 30 | On Linux, Mac OS and Windows, optional dependencies will install a native binary of the compiler. 31 | Native binaries offer faster compile times without requiring Java to be installed and available. 32 | Compilations with a very large number of source files may be slightly slower than the java version. 33 | 34 | ### Java Version 35 | Requires java to be installed and in the path. Using the java version typically results in faster compilation times. 36 | 37 | ## Usage 38 | The simplest way to invoke the compiler (e.g. if you're just trying it out) is with [`npx`](https://www.npmjs.com/package/npx): 39 | 40 | npx google-closure-compiler --js=my_program.js --js_output_file=out.js 41 | 42 | The npx version will attempt to detect the best platform to use. You can also specify the platform 43 | with the special `--platform` flag. 44 | 45 | ### Installation 46 | 47 | ``` 48 | npm install --save google-closure-compiler 49 | ``` 50 | 51 | ### Configuration 52 | 53 | See the [full list of compiler flags](https://github.com/google/closure-compiler/wiki/Flags-and-Options). 54 | 55 | The build tool plugins take options objects. The option parameters map directly to the 56 | compiler flags without the leading '--' characters. You may also use camelCase option names. 57 | 58 | Values are either strings or booleans. Options which have multiple values can be arrays. 59 | 60 | ```js 61 | { 62 | js: ['/file-one.js', '/file-two.js'], 63 | compilation_level: 'ADVANCED', 64 | js_output_file: 'out.js', 65 | debug: true 66 | } 67 | ``` 68 | 69 | For the java version, some shells (particularly windows) try to do expansion on globs rather 70 | than passing the string on to the compiler. To prevent this it is necessary to quote 71 | certain arguments: 72 | 73 | ```js 74 | { 75 | js: '"my/quoted/glob/**.js"', 76 | compilation_level: 'ADVANCED', 77 | js_output_file: 'out.js', 78 | debug: true 79 | } 80 | ``` 81 | 82 | ## Build Tool Plugins 83 | The compiler package also includes build tool plugins for [Grunt](http://gruntjs.com/) and [Gulp](http://gulpjs.com/). There is also an [official webpack plugin](https://www.npmjs.com/package/closure-webpack-plugin). 84 | 85 | * [Grunt Plugin](https://github.com/google/closure-compiler-npm/blob/master/packages/google-closure-compiler/docs/grunt.md) 86 | * [Gulp Plugin](https://github.com/google/closure-compiler-npm/blob/master/packages/google-closure-compiler/docs/gulp.md) 87 | 88 | ### Community Maintained Plugins 89 | Additionally, community members have created plugins leveraging this library. 90 | * [Rollup Plugin](https://github.com/ampproject/rollup-plugin-closure-compiler) 91 | 92 | ## Advanced Java Version Usage 93 | 94 | ### Changing the Path to the Java SDK 95 | 96 | Override the path before first use. 97 | 98 | ``` 99 | import Compiler from 'google-closure-compiler'; 100 | 101 | const compiler = new Compiler({args}); 102 | compiler.javaPath = '/node_modules/MODULE_NAME/jre/jre1.8.0_131.jre/Contents/Home/bin/java'; 103 | ``` 104 | 105 | ## Native Node Usage (for Plugin Authors) 106 | A low-level node class is included to facilitate spawning the compiler jar as a process from Node. 107 | In addition, it exposes a static property with the path to the compiler jar file. 108 | 109 | ### Java Version 110 | 111 | ```js 112 | import ClosureCompiler, {COMPILER_PATH, CONTRIB_PATH} from 'google-closure-compiler'; 113 | 114 | console.log(COMPILER_PATH); // absolute path to the compiler jar 115 | console.log(CONTRIB_PATH); // absolute path to the contrib folder which contain externs 116 | 117 | const closureCompiler = new ClosureCompiler({ 118 | js: 'file-one.js', 119 | compilation_level: 'ADVANCED' 120 | }); 121 | 122 | const compilerProcess = closureCompiler.run((exitCode, stdOut, stdErr) => { 123 | //compilation complete 124 | }); 125 | ``` 126 | 127 | ## License 128 | Copyright 2015 The Closure Compiler Authors 129 | 130 | Licensed under the Apache License, Version 2.0 (the "License"); 131 | you may not use this file except in compliance with the License. 132 | You may obtain a copy of the License at 133 | 134 | http://www.apache.org/licenses/LICENSE-2.0 135 | 136 | Unless required by applicable law or agreed to in writing, software 137 | distributed under the License is distributed on an "AS IS" BASIS, 138 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139 | See the License for the specific language governing permissions and 140 | limitations under the License. 141 | 142 | ## Version History 143 | Closure Compiler release notes can be found on the 144 | [main repository wiki](https://github.com/google/closure-compiler/wiki/Releases). 145 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2018 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | import Compiler from './lib/node/index.js'; 18 | import {getNativeImagePath, getFirstSupportedPlatform} from './lib/utils.js'; 19 | import parseArgs from 'minimist'; 20 | 21 | const compilerFlags = parseArgs(process.argv.slice(2)); 22 | 23 | // The platform flag is only used by this cli script - it is not natively supported by any compiler version. 24 | // If it exists, use the value, but then delete it so that it's not actually passed to the compiler. 25 | let platform; 26 | if (compilerFlags.hasOwnProperty('platform')) { 27 | platform = getFirstSupportedPlatform(compilerFlags.platform.split(',')); 28 | delete compilerFlags.platform; 29 | } else { 30 | platform = getFirstSupportedPlatform(['native', 'java']); 31 | } 32 | 33 | // The compiler treats default arguments as if they were --js args. 34 | // Minimist parses default arguments and puts them under the '_' key. 35 | // Move the '_' key to the 'js' key. 36 | if (compilerFlags.hasOwnProperty('_') && compilerFlags['_'].length > 0) { 37 | let existingJsFlags = []; 38 | if (compilerFlags.js) { 39 | if (Array.isArray(compilerFlags.js)) { 40 | existingJsFlags = compilerFlags.js; 41 | } else { 42 | existingJsFlags = [compilerFlags.js]; 43 | } 44 | } 45 | compilerFlags.js = existingJsFlags.concat(compilerFlags['_']); 46 | delete compilerFlags['_']; 47 | } else { 48 | delete compilerFlags['_']; 49 | } 50 | 51 | // Boolean arguments can in some cases be parsed as strings. 52 | // Since its highly unlikely that an argument actually needs to be the strings 'true' or 'false', 53 | // convert them to true booleans. 54 | Object.keys(compilerFlags).forEach((flag) => { 55 | if (compilerFlags[flag] === 'true') { 56 | compilerFlags[flag] = true; 57 | } else if (compilerFlags[flag] === 'false') { 58 | compilerFlags[flag] = false; 59 | } 60 | }); 61 | 62 | let args = process.argv.slice(2); 63 | for (let i = 0; i < args.length; i++) { 64 | if (/^--platform/.test(args[i])) { 65 | let delCount = 1; 66 | if (args[i].indexOf('=') < 0 && args.length > i + 1) { 67 | delCount++; 68 | } 69 | args.splice(i, delCount); 70 | break; 71 | } 72 | } 73 | 74 | const compiler = new Compiler(args); 75 | 76 | compiler.spawnOptions = {stdio: 'inherit'}; 77 | if (platform === 'native') { 78 | compiler.JAR_PATH = null; 79 | compiler.javaPath = getNativeImagePath(); 80 | } 81 | 82 | compiler.run((exitCode) => { 83 | process.exitCode = exitCode; 84 | }); 85 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/docs/grunt.md: -------------------------------------------------------------------------------- 1 | # Using the Grunt Task 2 | 3 | Task targets, files and options may be specified according to the grunt 4 | [Configuring tasks](http://gruntjs.com/configuring-tasks) guide. 5 | 6 | Include the plugin in your Gruntfile.js: 7 | 8 | ```js 9 | import {CONTRIB_PATH, grunt as gruntPlugin} from 'google-closure-compiler'; 10 | 11 | gruntPlugin(grunt, { 12 | platform: ['native', 'java'], 13 | max_parallel_compilations: require('os').cpus().length 14 | }); 15 | // The load-grunt-tasks plugin won't automatically load closure-compiler 16 | ``` 17 | 18 | The `platform` option specifies whether to use the `java` or `native` versions of the compiler. 19 | The option can be either a string or an array where the first supported platform will be used. 20 | 21 | The `max_parallel_compilations` option caps number of parallel compilations to specified number. If it's 22 | `false` or not set all files compiled in parallel. 23 | 24 | ## Basic Configuration Example: 25 | 26 | ```js 27 | import {grunt as gruntPlugin} from 'google-closure-compiler'; 28 | 29 | gruntPlugin(grunt, { 30 | platform: ['native', 'java'], 31 | max_parallel_compilations: require('os').cpus().length 32 | }); 33 | 34 | // Project configuration. 35 | grunt.initConfig({ 36 | 'closure-compiler': { 37 | my_target: { 38 | files: { 39 | 'dest/output.min.js': ['src/js/**/*.js'] 40 | }, 41 | options: { 42 | js: '/node_modules/google-closure-library/**.js', 43 | externs: `${CONTRIB_PATH}/externs/jquery-1.9.js`, 44 | compilation_level: 'SIMPLE', 45 | language_in: 'ECMASCRIPT5_STRICT', 46 | create_source_map: 'dest/output.min.js.map', 47 | output_wrapper: '(function(){\n%output%\n}).call(this)\n//# sourceMappingURL=output.min.js.map' 48 | } 49 | } 50 | } 51 | }); 52 | ``` 53 | 54 | ## Specifying Extra Java Arguments 55 | Some users may wish to pass the java vm extra arguments - such as to specify the amount of memory the compiler should 56 | be allocated. Both the grunt and gulp plugins support this. 57 | 58 | ```js 59 | import {grunt as gruntPlugin} from 'google-closure-compiler'; 60 | 61 | gruntPlugin(grunt, { 62 | platform: 'java', 63 | extraArguments: ['-Xms2048m'] 64 | }); 65 | ``` 66 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/docs/gulp.md: -------------------------------------------------------------------------------- 1 | # Using the Gulp Plugin 2 | 3 | The gulp plugin supports piping multiple files through the compiler. 4 | 5 | Options are a direct match to the compiler flags without the leading "--". 6 | 7 | ## Basic Configuration Example: 8 | 9 | ```js 10 | import {gulp as closureCompiler} from 'google-closure-compiler'; 11 | 12 | gulp.task('js-compile', function () { 13 | return gulp.src('./src/js/**/*.js', {base: './'}) 14 | .pipe(closureCompiler({ 15 | compilation_level: 'SIMPLE', 16 | warning_level: 'VERBOSE', 17 | language_in: 'ECMASCRIPT6_STRICT', 18 | language_out: 'ECMASCRIPT5_STRICT', 19 | output_wrapper: '(function(){\n%output%\n}).call(this)', 20 | js_output_file: 'output.min.js' 21 | }, { 22 | platform: ['native', 'java', 'javascript'] 23 | })) 24 | .pipe(gulp.dest('./dist/js')); 25 | }); 26 | ``` 27 | 28 | The `platform` option specifies whether to use the `java` or `native` versions of the compiler. 29 | The option can be either a string or an array where the first supported platform will be used: 30 | 31 | ## Use without gulp.src (Java Version Only) 32 | Gulp files are all read into memory, transformed into a JSON stream, and piped through the 33 | compiler. With large source sets this may require a large amount of memory. 34 | 35 | Closure-compiler can natively expand file globs which will greatly alleviate this issue. 36 | 37 | ```js 38 | import {gulp as closureCompiler, CONTRIB_PATH} from 'google-closure-compiler'; 39 | 40 | gulp.task('js-compile', function () { 41 | return closureCompiler({ 42 | js: './src/js/**.js', 43 | externs: `${CONTRIB_PATH}/externs/jquery-1.9.js`, 44 | compilation_level: 'SIMPLE', 45 | warning_level: 'VERBOSE', 46 | language_in: 'ECMASCRIPT6_STRICT', 47 | language_out: 'ECMASCRIPT5_STRICT', 48 | output_wrapper: '(function(){\n%output%\n}).call(this)', 49 | js_output_file: 'output.min.js' 50 | }) 51 | .src() // needed to force the plugin to run without gulp.src 52 | .pipe(gulp.dest('./dist/js')); 53 | }); 54 | ``` 55 | 56 | ## gulp.src base option 57 | Gulp attempts to set the base of a glob from the point of the first wildcard. This isn't always 58 | what is desired. Users can specify the { base: 'path' } option to `gulp.src` calls to override 59 | this behavior. 60 | 61 | ## Gulp Sourcemaps 62 | The gulp plugin supports gulp sourcemaps. 63 | 64 | ```js 65 | import {gulp as closureCompiler} from 'google-closure-compiler'; 66 | import sourcemaps from 'gulp-sourcemaps'; 67 | 68 | gulp.task('js-compile', function () { 69 | return gulp.src('./src/js/**/*.js', {base: './'}) 70 | .pipe(sourcemaps.init()) 71 | .pipe(closureCompiler({ 72 | compilation_level: 'SIMPLE', 73 | warning_level: 'VERBOSE', 74 | language_in: 'ECMASCRIPT6_STRICT', 75 | language_out: 'ECMASCRIPT5_STRICT', 76 | output_wrapper: '(function(){\n%output%\n}).call(this)', 77 | js_output_file: 'output.min.js' 78 | })) 79 | .pipe(sourcemaps.write('/')) // gulp-sourcemaps automatically adds the sourcemap url comment 80 | .pipe(gulp.dest('./dist/js')); 81 | }); 82 | ``` 83 | 84 | ## Specifying Extra Java Arguments 85 | Some users may wish to pass the java vm extra arguments - such as to specify the amount of memory the compiler should 86 | be allocated. Both the grunt and gulp plugins support this. 87 | 88 | ```js 89 | import {gulp as closureCompiler} from 'google-closure-compiler'; 90 | 91 | gulp.task('js-compile', function () { 92 | return gulp.src('./src/js/**/*.js', {base: './'}) 93 | .pipe(closureCompiler({ 94 | compilation_level: 'SIMPLE', 95 | warning_level: 'VERBOSE', 96 | language_in: 'ECMASCRIPT6_STRICT', 97 | language_out: 'ECMASCRIPT5_STRICT', 98 | output_wrapper: '(function(){\n%output%\n}).call(this)', 99 | js_output_file: 'output.min.js' 100 | }, { 101 | extraArguments: ['-Xms2048m'] 102 | })) 103 | .pipe(gulp.dest('./dist/js')); 104 | }); 105 | 106 | ``` 107 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Nodejs plugins and build tools for Google Closure Compiler 19 | * 20 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 21 | */ 22 | 23 | import path from 'node:path'; 24 | import {fileURLToPath, URL} from 'node:url'; 25 | export {default as JAR_PATH} from 'google-closure-compiler-java'; 26 | 27 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 28 | export const CONTRIB_PATH = path.resolve(__dirname, './contrib'); 29 | export const EXTERNS_PATH = path.resolve(__dirname, './externs'); 30 | 31 | export {default as grunt} from './lib/grunt/index.js'; 32 | export {default as gulp} from './lib/gulp/index.js'; 33 | export { 34 | default as compiler, 35 | default, 36 | javaPath, 37 | } from './lib/node/index.js'; 38 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/grunt/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Grunt task for closure-compiler. 19 | * The task is simply a grunt wrapper for the gulp plugin. The gulp plugin 20 | * is used to stream multiple input files in via stdin. This alleviates 21 | * problems with the windows command shell which has restrictions on the 22 | * length of a command. 23 | * 24 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 25 | */ 26 | import {Transform} from 'node:stream'; 27 | import chalk from 'chalk'; 28 | import gulpCompiler from '../gulp/index.js'; 29 | import {getFirstSupportedPlatform} from '../utils.js'; 30 | import VinylStream from './vinyl-stream.js'; 31 | 32 | export default (grunt, pluginOptions) => { 33 | const gulpCompilerOptions = {}; 34 | let extraArguments; 35 | let platforms; 36 | let maxParallelCompilations = false; 37 | if (pluginOptions) { 38 | if (pluginOptions.platform) { 39 | platforms = Array.isArray(pluginOptions.platform) ? pluginOptions.platform : [pluginOptions.platform]; 40 | } 41 | if (pluginOptions.extraArguments) { 42 | extraArguments = pluginOptions.extraArguments; 43 | } 44 | if (pluginOptions.compile_in_batches && pluginOptions.max_parallel_compilations === undefined) { 45 | pluginOptions.max_parallel_compilations = pluginOptions.compile_in_batches; 46 | grunt.log.warn('DEPRECATED: compile_in_batches is deprecated. Use max_parallel_compilations.'); 47 | } 48 | if (typeof pluginOptions.max_parallel_compilations === 'number' && pluginOptions.max_parallel_compilations > 0) { 49 | maxParallelCompilations = pluginOptions.max_parallel_compilations; 50 | } 51 | } 52 | 53 | if (!platforms) { 54 | platforms = ['native', 'java']; 55 | } 56 | const platform = getFirstSupportedPlatform(platforms); 57 | const compilationPromiseGenerator = 58 | (files, options, pluginOpts) => () => compilationPromise(files, options, pluginOpts); 59 | 60 | /** 61 | * @param {Array|null} files 62 | * @param {Object>|Array} options 63 | * @param {?} pluginOpts 64 | * @return {Promise} 65 | */ 66 | const compilationPromise = (files, options, pluginOpts) => { 67 | let hadError = false; 68 | const logFile = (cb) => { 69 | // If an error was encoutered, it will have already been logged 70 | if (!hadError) { 71 | if (options.js_output_file) { 72 | grunt.log.ok(chalk.cyan(options.js_output_file) + ' created'); 73 | } else { 74 | grunt.log.ok('Compilation succeeded'); 75 | } 76 | } 77 | cb(); 78 | }; 79 | 80 | const loggingStream = new Transform({ 81 | objectMode: true, 82 | transform: function() {}, 83 | flush: logFile 84 | }); 85 | 86 | return new Promise((resolve, reject) => { 87 | let stream; 88 | let gulpOpts = { 89 | ...gulpCompilerOptions, 90 | streamMode: 'IN', 91 | logger: grunt.log, 92 | pluginName: 'grunt-google-closure-compiler', 93 | requireStreamInput: false 94 | }; 95 | const compilerOpts = { 96 | ...gulpOpts, 97 | ...pluginOpts, 98 | ...(extraArguments ? { extraArguments } : {}), 99 | }; 100 | if (files) { 101 | // Source files were provided by grunt. Read these 102 | // in to a stream of vinyl files and pipe them through 103 | // the compiler task 104 | stream = new VinylStream(files, {base: process.cwd()}) 105 | .pipe(gulpCompiler(options, compilerOpts)); 106 | } else { 107 | // No source files were provided. Assume the options specify 108 | // --js flags and invoke the compiler without any grunt inputs. 109 | // Manually end the stream to force compilation to begin. 110 | stream = gulpCompiler(options, compilerOpts); 111 | stream.end(); 112 | } 113 | 114 | stream.on('error', function(err) { 115 | hadError = true; 116 | reject(err); 117 | }); 118 | stream.on('end', function(err) { 119 | resolve(); 120 | }); 121 | 122 | stream.pipe(loggingStream); 123 | stream.resume(); //logging stream doesn't output files, so we have to manually resume; 124 | }); 125 | }; 126 | 127 | /** 128 | * Grabs `ps` as array of promise-returning functions, separates it in `maxParallelCount` 129 | * count of sequential processing consumers and runs these consumers in parallel to process 130 | * all promises. 131 | * 132 | * @param {!Array>} ps functions returning promises 133 | * @param {!number} maxParallelCount Maximum promises running in parallel 134 | * @return {!Promise|undefined} 135 | */ 136 | const processPromisesParallel = (ps, maxParallelCount) => { 137 | // While ps is not empty grab one function, run promise from it, then repeat. Else resolve to true. 138 | const goInSequence = async () => { 139 | if (!ps.length) { 140 | return true; 141 | } 142 | await ps.shift()(); 143 | return goInSequence(); 144 | }; 145 | 146 | let bulk = []; 147 | // run `maxParallelCount` or lesser (if array of promises lesser) count of goInSequence 148 | for (let i = 0; i < Math.min(maxParallelCount, ps.length); i++) { 149 | bulk.push(goInSequence()); 150 | } 151 | return Promise.all(bulk); 152 | }; 153 | 154 | function closureCompilerGruntTask() { 155 | const asyncDone = this.async(); 156 | const compileTasks = []; 157 | 158 | const getCompilerOptions = () => { 159 | const opts = this.options({ 160 | args: undefined 161 | }); 162 | 163 | const args = opts.args; 164 | 165 | delete opts.args; 166 | 167 | return { 168 | args, 169 | compilerOpts: opts 170 | } 171 | } 172 | 173 | // Invoke the compiler once for each set of source files 174 | for (const f of this.files) { 175 | const options = getCompilerOptions(); 176 | 177 | const src = f.src.filter(filepath => { 178 | if (!grunt.file.exists(filepath)) { 179 | grunt.log.warn(`Source file ${chalk.cyan(filepath)} not found`); 180 | return false; 181 | } 182 | return true; 183 | }); 184 | 185 | // Require source files 186 | if (src.length === 0) { 187 | grunt.log.warn(`Destination ${chalk.cyan(f.dest)} not written because src files were empty`); 188 | continue; 189 | } else { 190 | options.compilerOpts.js_output_file = f.dest; 191 | } 192 | 193 | compileTasks.push(compilationPromiseGenerator(src, options.args || options.compilerOpts, {platform})); 194 | } 195 | 196 | // If the task was invoked without any files provided by grunt, assume that 197 | // --js flags are present and we want to run the compiler anyway. 198 | if (this.files.length === 0) { 199 | const options = getCompilerOptions(); 200 | compileTasks.push(compilationPromiseGenerator(null, options.args || options.compilerOpts, {platform})); 201 | } 202 | 203 | // Multiple invocations of the compiler can occur for a single task target. Wait until 204 | // they are all completed before calling the "done" method. 205 | 206 | return (maxParallelCompilations ? processPromisesParallel(compileTasks, maxParallelCompilations) : Promise.all(compileTasks.map((t) => t()))) 207 | .then(() => asyncDone()) 208 | .catch((err) => { 209 | grunt.log.warn(err.message); 210 | grunt.fail.warn('Compilation error'); 211 | asyncDone(); 212 | }); 213 | } 214 | 215 | grunt.registerMultiTask('closure-compiler', 216 | 'Minify files with Google Closure Compiler', 217 | closureCompilerGruntTask); 218 | 219 | return closureCompilerGruntTask; 220 | }; 221 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/grunt/vinyl-stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Class to convert an array of file paths to 19 | * as stream of Vinyl files. 20 | * 21 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 22 | */ 23 | import fs from 'node:fs/promises'; 24 | import path from 'node:path'; 25 | import {Readable} from 'node:stream'; 26 | import File from 'vinyl'; 27 | 28 | export default class VinylStream extends Readable { 29 | constructor(files, opts) { 30 | super({objectMode: true}); 31 | this._base = path.resolve(opts.base || process.cwd()); 32 | this._files = files.slice(); 33 | this.resume(); 34 | } 35 | 36 | async _read() { 37 | if (this._files.length === 0) { 38 | this.push(null); 39 | return; 40 | } 41 | const filepath = this._files.shift(); 42 | const fullpath = path.resolve(this._base, filepath); 43 | try { 44 | const data = await fs.readFile(fullpath); 45 | this.push(new File({ 46 | base: this._base, 47 | path: fullpath, 48 | contents: data 49 | })); 50 | } catch (err) { 51 | this.emit('error', err); 52 | } 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/gulp/concat-to-json.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Convert an array of vinyl files to 19 | * a single JSON encoded string to pass to closure-compiler 20 | * 21 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 22 | */ 23 | 24 | import path from 'node:path'; 25 | 26 | /** 27 | * @param {string} src 28 | * @param {string=} path 29 | * @param {string=} sourceMap 30 | * @return {{ 31 | * src: string, 32 | * path: string|undefined, 33 | * sourceMap: string|undefined, 34 | * }} 35 | */ 36 | const json_file = (src, path, sourceMap) => { 37 | return { 38 | src: src, 39 | ...(path !== undefined ? {path} : undefined), 40 | ...(sourceMap !== undefined ? {sourceMap} : undefined), 41 | }; 42 | }; 43 | 44 | /** 45 | * @param {!Array} files 50 | * @return {string} 51 | */ 52 | export default (files) => { 53 | /** 54 | * @type {!Array} 59 | */ 60 | const jsonFiles = []; 61 | for (let i = 0; i < files.length; i++) { 62 | jsonFiles.push( 63 | json_file(files[i].contents.toString(), files[i].relative || path.relative(process.cwd(), files[i].path), 64 | files[i].sourceMap ? JSON.stringify(files[i].sourceMap) : undefined)); 65 | } 66 | 67 | return jsonFiles; 68 | }; 69 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/gulp/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Gulp task for closure-compiler. Multiplexes input 19 | * files into a json encoded stream which can be piped into closure-compiler. 20 | * Each json file object includes the contents, path and optionally sourcemap 21 | * for every input file. 22 | * 23 | * Closure-compiler will return the same style string via standard-out which 24 | * is then converted back to vinyl files. 25 | * 26 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 27 | */ 28 | 29 | import stream from 'node:stream'; 30 | import chalk from 'chalk'; 31 | import File from 'vinyl'; 32 | import applySourceMap from 'vinyl-sourcemaps-apply'; 33 | 34 | import filesToJson from './concat-to-json.js'; 35 | import jsonToVinyl from './json-to-vinyl.js'; 36 | import Compiler from '../node/index.js'; 37 | import {getNativeImagePath, getFirstSupportedPlatform} from '../utils.js'; 38 | 39 | const PLUGIN_NAME = 'gulp-google-closure-compiler'; 40 | 41 | const getLogger = async () => { 42 | try { 43 | const { default: fancyLog } = await import('fancy-log'); 44 | return fancyLog; 45 | } catch {} 46 | 47 | try { 48 | const { default: gulpUtil } = await import('gulp-util'); 49 | return gulpUtil.log; 50 | } catch {} 51 | 52 | return console; 53 | }; 54 | 55 | /** 56 | * Rethrow an error with a custom message. 57 | * @see https://stackoverflow.com/a/42755876/1211524 58 | */ 59 | class PluginError extends Error { 60 | constructor(plugin, message) { 61 | if (message instanceof Error) { 62 | super(`Error in ${plugin}`, {cause: message}); 63 | } else { 64 | super(`${plugin}: ${message}`); 65 | } 66 | } 67 | } 68 | 69 | class CompilationStream extends stream.Transform { 70 | constructor(compilationOptions, pluginOptions = {}) { 71 | super({objectMode: true}); 72 | this.compilationOptions_ = compilationOptions; 73 | this.streamMode_ = pluginOptions.streamMode || 'BOTH'; 74 | this.logger_ = pluginOptions.logger; 75 | this.PLUGIN_NAME_ = pluginOptions.pluginName || PLUGIN_NAME; 76 | this.extraCommandArgs_ = pluginOptions.extraCommandArguments || []; 77 | 78 | this.fileList_ = []; 79 | this._streamInputRequired = pluginOptions.requireStreamInput !== false; 80 | 81 | let platforms = (pluginOptions && pluginOptions.platform) || ['native', 'java']; 82 | if (!Array.isArray(platforms)) { 83 | platforms = [platforms]; 84 | } 85 | this.platform = getFirstSupportedPlatform(platforms); 86 | } 87 | 88 | src() { 89 | this._streamInputRequired = false; 90 | process.nextTick(() => { 91 | const stdInStream = new stream.Readable({ read: function() { 92 | return new File(); 93 | }}); 94 | stdInStream.pipe(this); 95 | stdInStream.push(null); 96 | }); 97 | return this; 98 | } 99 | 100 | _transform(file, enc, cb) { 101 | // ignore empty files 102 | if (!file || file.isNull()) { 103 | cb(); 104 | return; 105 | } 106 | 107 | if (file.isStream()) { 108 | cb(new PluginError(this.PLUGIN_NAME_, 'Streaming not supported')); 109 | return; 110 | } 111 | 112 | this.fileList_.push(file); 113 | cb(); 114 | } 115 | 116 | async _flush(cb) { 117 | let jsonFiles; 118 | if (this.fileList_.length > 0) { 119 | // Input files are present. Convert them to a JSON encoded string 120 | jsonFiles = filesToJson(this.fileList_); 121 | } else { 122 | // If files in the stream were required, no compilation needed here. 123 | if (this._streamInputRequired) { 124 | this.push(null); 125 | cb(); 126 | return; 127 | } 128 | 129 | // The compiler will always expect something on standard-in. So pass it an empty 130 | // list if no files were piped into this plugin. 131 | jsonFiles = []; 132 | } 133 | const compiler = new Compiler(this.compilationOptions_, this.extraCommandArgs_); 134 | if (this.platform === 'native') { 135 | compiler.JAR_PATH = null; 136 | compiler.javaPath = getNativeImagePath(); 137 | } 138 | let stdOutData = ''; 139 | let stdErrData = ''; 140 | 141 | // Add the gulp-specific argument so the compiler will understand the JSON encoded input 142 | // for gulp, the stream mode will be 'BOTH', but when invoked from grunt, we only use 143 | // a stream mode of 'IN' 144 | compiler.commandArguments.push('--json_streams', this.streamMode_); 145 | const compilerProcess = compiler.run(); 146 | 147 | compilerProcess.stdout.on('data', (data) => { 148 | stdOutData += data; 149 | }); 150 | compilerProcess.stderr.on('data', (data) => { 151 | stdErrData += data; 152 | }); 153 | // Error events occur when there was a problem spawning the compiler process 154 | compilerProcess.on('error', async (err) => { 155 | this.emit('error', new PluginError(this.PLUGIN_NAME_, 156 | `Process spawn error. Is java in the path?\n${err.message}`)); 157 | cb(); 158 | }); 159 | compilerProcess.stdin.on('error', (err) => { 160 | stdErrData += `Error writing to stdin of the compiler. ${err.message}`; 161 | }); 162 | 163 | const compileExecComplete = Promise.all([ 164 | new Promise((resolve) => compilerProcess.on('close', resolve)), 165 | new Promise((resolve) => compilerProcess.stdout.on('end', resolve)), 166 | new Promise((resolve) => compilerProcess.stderr.on('end', resolve)) 167 | ]); 168 | 169 | const stdInStream = new stream.Readable({ read: function() {}}); 170 | stdInStream.pipe(compilerProcess.stdin); 171 | await new Promise((resolve) => { 172 | process.nextTick(() => { 173 | stdInStream.push(JSON.stringify(jsonFiles)); 174 | stdInStream.push(null); 175 | resolve(); 176 | }); 177 | }); 178 | 179 | try { 180 | const [code] = await compileExecComplete; 181 | 182 | // If present, standard output will be a string of JSON encoded files. 183 | // Convert these back to vinyl 184 | let outputFiles = []; 185 | if (stdOutData.trim().length > 0) { 186 | if (code !== 0) { 187 | this.emit('error', new PluginError(this.PLUGIN_NAME_, `Compiler error.\n${stdOutData}\n${stdErrData}`)); 188 | cb(); 189 | return; 190 | } 191 | 192 | // stdOutData = stdOutData.substr(stdOutData.indexOf('{')); 193 | try { 194 | outputFiles = JSON.parse(stdOutData); 195 | } catch (e) { 196 | const composedError = new Error('Error parsing json encoded files', {cause: e}); 197 | this.emit('error', new PluginError(this.PLUGIN_NAME_, composedError)); 198 | cb(); 199 | return; 200 | } 201 | } 202 | 203 | if (!this.logger_) { 204 | this.logger_ = await getLogger(); 205 | } 206 | this._compilationComplete(code, outputFiles, stdErrData); 207 | } catch (err) { 208 | this.emit('error', new PluginError(this.PLUGIN_NAME_, err)); 209 | } 210 | cb(); 211 | } 212 | 213 | /** 214 | * @param {number} exitCode 215 | * @param {Array} compiledJs 220 | * @param {string} errors 221 | * @private 222 | */ 223 | _compilationComplete(exitCode, compiledJs, errors) { 224 | // standard error will contain compilation warnings, log those 225 | if (errors && errors.trim().length > 0) { 226 | const logger = this.logger_.warn ? this.logger_.warn : this.logger_; 227 | logger(`${chalk.yellow(this.PLUGIN_NAME_)}: ${errors}`); 228 | } 229 | 230 | // non-zero exit means a compilation error 231 | if (exitCode !== 0) { 232 | this.emit('error', new PluginError(this.PLUGIN_NAME_, 'Compilation errors occurred')); 233 | } 234 | 235 | // If present, standard output will be a string of JSON encoded files. 236 | // Convert these back to vinyl 237 | const outputFiles = jsonToVinyl(compiledJs); 238 | 239 | for (let i = 0; i < outputFiles.length; i++) { 240 | if (outputFiles[i].sourceMap) { 241 | applySourceMap(outputFiles[i], outputFiles[i].sourceMap); 242 | } 243 | this.push(outputFiles[i]); 244 | } 245 | } 246 | } 247 | 248 | /** 249 | * @param {Object} initOptions 250 | * @return {function(Object|Array):Object} 251 | */ 252 | export default (compilationOptions, pluginOptions) => new CompilationStream(compilationOptions, pluginOptions); 253 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/gulp/json-to-vinyl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Convert a string of JSON encoded files 19 | * back to an array of vinyl files 20 | * 21 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 22 | */ 23 | import File from 'vinyl'; 24 | 25 | /** 26 | * @param {!Array} fileList array of file objects 31 | * @return {!Array} 32 | */ 33 | export default (fileList) => { 34 | let outputFiles = []; 35 | for (let i = 0; i < fileList.length; i++) { 36 | const file = new File({ 37 | path: fileList[i].path, 38 | contents: Buffer.from(fileList[i].src) 39 | }); 40 | if (fileList[i].source_map || fileList[i].sourceMap) { 41 | file.sourceMap = JSON.parse(fileList[i].source_map || fileList[i].sourceMap); 42 | } 43 | outputFiles.push(file); 44 | } 45 | 46 | return outputFiles; 47 | }; 48 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/node/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Low level class for calling the closure-compiler jar 19 | * from nodejs 20 | * 21 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 22 | */ 23 | import {spawn} from 'node:child_process'; 24 | import compilerPath from 'google-closure-compiler-java'; 25 | 26 | /** 27 | * @type {string} 28 | */ 29 | export const javaPath = 'java'; 30 | 31 | export default class Compiler { 32 | /** 33 | * @param {Object|Array} args 34 | * @param {Array=} extraCommandArgs 35 | */ 36 | constructor(args, extraCommandArgs) { 37 | this.commandArguments = []; 38 | this.extraCommandArgs = extraCommandArgs; 39 | this.JAR_PATH = compilerPath; 40 | this.javaPath = javaPath; 41 | 42 | if (Array.isArray(args)) { 43 | this.commandArguments.push(...args); 44 | } else { 45 | for (let key in args) { 46 | if (Array.isArray(args[key])) { 47 | for (let i = 0; i < args[key].length; i++) { 48 | this.commandArguments.push( 49 | this.formatArgument(key, args[key][i])); 50 | } 51 | } else { 52 | this.commandArguments.push( 53 | this.formatArgument(key, args[key])); 54 | } 55 | } 56 | } 57 | 58 | /** @type {function(...*)|null} */ 59 | this.logger = null; 60 | 61 | /** @type {Object} */ 62 | this.spawnOptions = undefined; 63 | } 64 | 65 | /** @param {function(number, string, string)=} callback */ 66 | run(callback) { 67 | if (this.JAR_PATH) { 68 | this.commandArguments.unshift('-jar', this.JAR_PATH); 69 | if (this.extraCommandArgs) { 70 | this.commandArguments.unshift(...this.extraCommandArgs); 71 | } 72 | } 73 | 74 | if (this.logger) { 75 | this.logger(this.getFullCommand() + '\n'); 76 | } 77 | let compileProcess = spawn(this.javaPath, this.commandArguments, this.spawnOptions); 78 | 79 | let stdOutData = ''; 80 | let stdErrData = ''; 81 | if (callback) { 82 | if (compileProcess.stdout) { 83 | compileProcess.stdout.setEncoding('utf8'); 84 | compileProcess.stdout.on('data', (data) => { 85 | stdOutData += data; 86 | }); 87 | compileProcess.stdout.on('error', (err) => { 88 | stdErrData += err.toString(); 89 | }); 90 | } 91 | 92 | if (compileProcess.stderr) { 93 | compileProcess.stderr.setEncoding('utf8'); 94 | compileProcess.stderr.on('data', (data) => { 95 | stdErrData += data; 96 | }); 97 | } 98 | 99 | compileProcess.on('close', (code) => { 100 | if (code !== 0) { 101 | stdErrData = this.prependFullCommand(stdErrData); 102 | } 103 | 104 | callback(code, stdOutData, stdErrData); 105 | }); 106 | 107 | compileProcess.on('error', (err) => { 108 | callback(1, stdOutData, 109 | this.prependFullCommand('Process spawn error. Is java in the path?\n' + err.message)); 110 | }); 111 | } 112 | 113 | return compileProcess; 114 | } 115 | 116 | 117 | /** 118 | * @return {string} 119 | */ 120 | getFullCommand() { 121 | return `${this.javaPath} ${this.commandArguments.join(' ')}`; 122 | } 123 | 124 | /** 125 | * @param {string} msg 126 | * @return {string} 127 | */ 128 | prependFullCommand(msg) { 129 | return `${this.getFullCommand()}\n\n${msg}\n\n`; 130 | } 131 | 132 | /** 133 | * @param {string} key 134 | * @param {(string|boolean)=} val 135 | * @return {string} 136 | */ 137 | formatArgument(key, val) { 138 | let normalizedKey = key.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`); 139 | normalizedKey = normalizedKey.replace(/^--/, ''); 140 | 141 | if (val === undefined || val === null) { 142 | return `--${normalizedKey}`; 143 | } 144 | 145 | return `--${normalizedKey}=${val}`; 146 | } 147 | }; 148 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/lib/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import fs from 'node:fs'; 17 | import {createRequire} from 'node:module'; 18 | const require = createRequire(import.meta.url); 19 | 20 | /** @type {!Map} */ 21 | const platformLookup = new Map([ 22 | ['darwin', 'macos'], 23 | ['win32', 'windows'], 24 | ['linux', 'linux'], 25 | ]); 26 | 27 | /** @return {string|undefined} */ 28 | export const getNativeImagePath = () => { 29 | let platform = platformLookup.get(process.platform); 30 | let binarySuffix = ''; 31 | if (!platform) { 32 | return; 33 | } else if (platform === 'linux' && process.arch === 'arm64') { 34 | platform += '-arm64'; 35 | } else if (platform === 'windows') { 36 | binarySuffix = '.exe'; 37 | } 38 | const compilerPath = `google-closure-compiler-${platform}/compiler${binarySuffix}`; 39 | try { 40 | return require.resolve(compilerPath); 41 | } catch {} 42 | }; 43 | 44 | /** 45 | * @param {!Array} platforms 46 | * @return {string} 47 | */ 48 | export const getFirstSupportedPlatform = (platforms) => { 49 | const platform = platforms.find((platform, index) => { 50 | switch (platform.toLowerCase()) { 51 | case 'java': 52 | if (index === platforms.length - 1) { 53 | return true; 54 | } 55 | return process.env.JAVA_HOME; 56 | 57 | case 'native': 58 | if (getNativeImagePath()) { 59 | return true; 60 | } 61 | } 62 | }); 63 | if (!platform) { 64 | throw new Error('No supported platform for closure-compiler found.'); 65 | } 66 | return platform; 67 | }; 68 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-closure-compiler", 3 | "version": "20250603.0.0", 4 | "description": "Check, compile, optimize and compress Javascript with Closure-Compiler", 5 | "type": "module", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/google/closure-compiler-npm.git#master" 9 | }, 10 | "keywords": [ 11 | "javascript", 12 | "compiler", 13 | "optimizer", 14 | "minifier", 15 | "closure", 16 | "gulpplugin", 17 | "gruntplugin" 18 | ], 19 | "bin": "cli.js", 20 | "main": "index.js", 21 | "contributors": [ 22 | { 23 | "name": "Chad Killingsworth", 24 | "email": "chadkillingsworth@gmail.com" 25 | } 26 | ], 27 | "files": [ 28 | "lib/", 29 | "cli.js", 30 | "index.js", 31 | "package.json", 32 | "contrib/", 33 | "externs/", 34 | "README.md", 35 | "LICENSE" 36 | ], 37 | "license": "Apache-2.0", 38 | "bugs": { 39 | "url": "https://github.com/google/closure-compiler/issues" 40 | }, 41 | "homepage": "https://developers.google.com/closure/compiler/", 42 | "dependencies": { 43 | "chalk": "5.x", 44 | "google-closure-compiler-java": "^20250603.0.0", 45 | "minimist": "1.x", 46 | "vinyl": "3.x", 47 | "vinyl-sourcemaps-apply": "^0.2.0" 48 | }, 49 | "optionalDependencies": { 50 | "google-closure-compiler-linux": "^20250603.0.0", 51 | "google-closure-compiler-linux-arm64": "^20250603.0.0", 52 | "google-closure-compiler-macos": "^20250603.0.0", 53 | "google-closure-compiler-windows": "^20250603.0.0" 54 | }, 55 | "devDependencies": { 56 | "gulp": "4.x", 57 | "gulp-sourcemaps": "2.x", 58 | "jasmine": "^5.6.0", 59 | "jasmine-console-reporter": "^3.1.0", 60 | "ncp": "^2.0.0", 61 | "semver": "^5.7.2" 62 | }, 63 | "scripts": { 64 | "build": "echo \"google-closure-compiler build\"", 65 | "test": "./test/support/jasmine.sh --reporter=jasmine-console-reporter 'test/*.js'" 66 | }, 67 | "engines": { 68 | "node": ">=18" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/cli.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for google-closure-compiler cli 19 | * 20 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 21 | */ 22 | 23 | import {createRequire} from 'node:module'; 24 | import runCommand from '../../../build-scripts/run-command.js'; 25 | 26 | const require = createRequire(import.meta.url); 27 | 28 | const javaOnly = process.argv.find((arg) => arg == '--java-only'); 29 | const platforms = ['java']; 30 | if (!javaOnly) { 31 | platforms.push('native'); 32 | } 33 | 34 | process.on('unhandledRejection', (e) => { throw e; }); 35 | 36 | describe('command line interface', () => { 37 | let cliPath = require.resolve('../cli.js'); 38 | if (process.platform === 'win32') { 39 | cliPath = `node ${cliPath}`; 40 | } 41 | 42 | if (!javaOnly) { 43 | it('chooses an acceptable platform automatically', async () => { 44 | const retVal = await runCommand(`${cliPath} --js test/fixtures/one.js`, {stdio: 'pipe'}); 45 | expect(retVal).not.toBeInstanceOf(Error); 46 | const {stdout, exitCode} = retVal; 47 | expect(exitCode).toBe(0); 48 | expect(stdout.length).toBeGreaterThan(0); 49 | }); 50 | } 51 | 52 | platforms.forEach((platform) => { 53 | describe(`${platform} version`, () => { 54 | let originalTimeout; 55 | beforeEach(() => { 56 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 57 | if (platform === 'java') { 58 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 59 | } 60 | }); 61 | afterEach(() => { 62 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 63 | }); 64 | 65 | it('--help flag', async () => { 66 | const retVal = 67 | await runCommand(`${cliPath} --platform=${platform} --help`, {stdio: 'pipe'}); 68 | expect(retVal).not.toBeInstanceOf(Error); 69 | const {stdout, exitCode} = retVal; 70 | expect(exitCode).toBe(0); 71 | expect(stdout.length).toBeGreaterThan(0); 72 | }); 73 | 74 | it('invalid flag', async () => { 75 | try { 76 | await runCommand(`${cliPath} --platform=${platform} --foo=bar --js=test/fixtures/one.js`, {stdio: 'pipe'}); 77 | fail('should have thrown'); 78 | } catch (err) { 79 | expect(err).toBeInstanceOf(Error); 80 | const {exitCode} = err; 81 | expect(exitCode).toBeDefined(); 82 | expect(exitCode).not.toBe(0); 83 | } 84 | }); 85 | 86 | it('compile successfully', async () => { 87 | const retVal = 88 | await runCommand(`${cliPath} --platform=${platform} --js=test/fixtures/one.js --use_types_for_optimization`, {stdio: 'pipe'}); 89 | if (retVal instanceof Error) { 90 | console.error(retVal); 91 | } 92 | expect(retVal).not.toBeInstanceOf(Error); 93 | const {stdout, exitCode} = retVal; 94 | expect(exitCode).toBe(0); 95 | expect(stdout.length).toBeGreaterThan(0); 96 | expect(stdout.indexOf('console.log')).toBeGreaterThan(-1); 97 | }); 98 | 99 | it('accept piped input', async () => { 100 | const cmd = runCommand(`${cliPath} --platform=${platform}`, {stdio: 'pipe'}); 101 | cmd.childProcess.stdin.setEncoding('utf8'); 102 | cmd.childProcess.stdin.end('alert("hello world")'); 103 | 104 | const retVal = await cmd; 105 | expect(retVal).not.toBeInstanceOf(Error); 106 | const {exitCode, stdout} = retVal; 107 | expect(exitCode).toBe(0); 108 | expect(stdout.length).toBeGreaterThan(0); 109 | expect(stdout.indexOf('alert("hello world")')).toBeGreaterThan(-1); 110 | }); 111 | 112 | it('read input js files', async () => { 113 | const retVal = await runCommand(`${cliPath} --platform=${platform} --js=test/fixtures/one.js`, {stdio: 'pipe'}); 114 | expect(retVal).not.toBeInstanceOf(Error); 115 | const {stdout, exitCode} = retVal; 116 | expect(exitCode).toBe(0); 117 | expect(stdout.length).toBeGreaterThan(0); 118 | expect(stdout.indexOf('console.log')).toBeGreaterThan(-1); 119 | }); 120 | 121 | it('read extern files', async () => { 122 | const cmd = runCommand( 123 | `${cliPath} --platform=${platform} --warning_level=VERBOSE --externs=test/fixtures/extern.js`, 124 | {stdio: 'pipe'}, 125 | ); 126 | cmd.childProcess.stdin.setEncoding('utf8'); 127 | cmd.childProcess.stdin.end('externalMethod("foo")'); 128 | 129 | const retVal = await cmd; 130 | expect(retVal).not.toBeInstanceOf(Error); 131 | const {stdout, exitCode} = retVal; 132 | expect(exitCode).toBe(0); 133 | expect(stdout.length).toBeGreaterThan(0); 134 | expect(stdout.indexOf('externalMethod')).toBeGreaterThan(-1); 135 | }); 136 | }); 137 | }); 138 | }); 139 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/fixtures/extern.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview 19 | * @externs 20 | */ 21 | 22 | /** @param {string} input */ 23 | function externalMethod(input) {} 24 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/fixtures/one.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | function log(a) { 17 | console.log(a) 18 | } 19 | log('one.js'); 20 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/fixtures/two.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | class WindowInfo { 17 | constructor() { 18 | this.props = []; 19 | 20 | } 21 | propList() { 22 | for (var prop in window) { 23 | this.props.push(prop); 24 | } 25 | } 26 | 27 | list() { 28 | log(this.props.join(', ')); 29 | } 30 | } 31 | 32 | (new WindowInfo()).list(); 33 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/grunt.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for grunt-google-closure-compiler plugin 19 | * 20 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 21 | */ 22 | import fs from 'node:fs'; 23 | import path from 'node:path'; 24 | import {fileURLToPath, URL} from 'node:url'; 25 | import ClosureCompiler from '../lib/node/index.js'; 26 | import gruntPlugin from '../lib/grunt/index.js'; 27 | 28 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 29 | 30 | process.on('unhandledRejection', (e) => { throw e; }); 31 | 32 | const javaOnly = process.argv.find((arg) => arg == '--java-only'); 33 | const platforms = ['java']; 34 | if (!javaOnly) { 35 | platforms.push('native'); 36 | } 37 | 38 | /** 39 | * Grunt plugins are very hard to test. In this case we're passing a mock grunt object 40 | * that defines the properties we need in order to test the actual task. 41 | */ 42 | const mockGrunt = { 43 | log: { 44 | ok: function () {}, 45 | warn: function(x) { 46 | console.warn(x); 47 | }, 48 | }, 49 | file: { 50 | exists: function(path) { 51 | try { 52 | return fs.statSync(path).isFile(); 53 | } catch (e) { 54 | return false; 55 | } 56 | }, 57 | write: function(filepath, contents, opts) { 58 | const pathSegments = filepath.split(path.sep); 59 | for (let i = 0; i < pathSegments.length - 1; i++) { 60 | const intermediatePath = pathSegments.slice(0, i + 1).join(path.sep); 61 | try { 62 | fs.mkdirSync(intermediatePath); 63 | } catch (e) {} 64 | } 65 | return fs.writeFileSync(filepath, contents, opts); 66 | } 67 | }, 68 | fail: { 69 | warn: function(...args) { 70 | console.error(args); 71 | } 72 | }, 73 | registerMultiTask: function() {} 74 | }; 75 | 76 | function gruntTaskOptions(options) { 77 | options = options || {}; 78 | return (defaults) => { 79 | const baseOpts = structuredClone(defaults || {}); 80 | return { 81 | ...baseOpts, 82 | ...options, 83 | }; 84 | }; 85 | } 86 | 87 | function getGruntTaskObject(fileObj, options, asyncDone) { 88 | return { 89 | async: function() { 90 | return function() { 91 | asyncDone(); 92 | } 93 | }, 94 | options: gruntTaskOptions(options), 95 | files: fileObj 96 | }; 97 | } 98 | 99 | describe('grunt-google-closure-compiler', () => { 100 | let originalCompilerRunMethod; 101 | let platformUtilized; 102 | 103 | beforeEach(() => { 104 | platformUtilized = undefined; 105 | originalCompilerRunMethod = ClosureCompiler.prototype.run; 106 | spyOn(ClosureCompiler.prototype, 'run') 107 | .and.callFake(function(...args) { 108 | const retVal = originalCompilerRunMethod.apply(this, args); 109 | platformUtilized = /^java/.test(this.getFullCommand()) ? 'java' : 'native'; 110 | return retVal; 111 | }); 112 | }); 113 | 114 | platforms.forEach((platform) => { 115 | describe(`${platform} version`, () => { 116 | let closureCompiler; 117 | let originalTimeout; 118 | beforeEach(() => { 119 | closureCompiler = gruntPlugin(mockGrunt, {platform}); 120 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 121 | if (platform === 'java') { 122 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 123 | } 124 | }); 125 | 126 | afterEach(() => { 127 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 128 | }); 129 | 130 | const ensureCorrectPlatformUtilized = () => { 131 | if (platformUtilized) { 132 | expect(platform).toBe(platformUtilized); 133 | } 134 | }; 135 | 136 | it('should emit an error for invalid options', (done) => { 137 | let didFail = false; 138 | let gruntWarning; 139 | 140 | debugger; 141 | let taskObj; 142 | const completed = new Promise((resolve) => { 143 | taskObj = getGruntTaskObject([{ 144 | dest: 'unused.js', 145 | src: [__dirname + '/fixtures/one.js'] 146 | }], { 147 | compilation_level: 'FOO' 148 | }, () => { 149 | resolve(); 150 | }); 151 | }); 152 | 153 | const logWarning = new Promise((resolve) => { 154 | mockGrunt.log.warn = (msg) => { 155 | gruntWarning = msg; 156 | console.warn(gruntWarning); 157 | resolve(); 158 | }; 159 | }); 160 | 161 | const failWarning = new Promise((resolve) => { 162 | mockGrunt.fail.warn = (err, code) => { 163 | expect(err).toMatch(/^Compilation error/); 164 | console.warn(err); 165 | didFail = true; 166 | resolve(); 167 | }; 168 | }); 169 | 170 | Promise.all([completed, logWarning, failWarning]).then(() => { 171 | expect(didFail).toBe(true); 172 | expect(gruntWarning).toBeDefined(); 173 | ensureCorrectPlatformUtilized(); 174 | done(); 175 | }).catch((err) => { 176 | fail('should not fail'); 177 | }); 178 | 179 | closureCompiler.call(taskObj); 180 | }); 181 | 182 | it('should warn if files cannot be found', (done) => { 183 | function taskDone() { 184 | expect(gruntWarnings.length).toBe(2); 185 | expect(gruntWarnings[0]).toMatch(/not found$/); 186 | expect(gruntWarnings[1]).toMatch(/not written because src files were empty$/); 187 | ensureCorrectPlatformUtilized(); 188 | done(); 189 | } 190 | 191 | let gruntWarnings = []; 192 | mockGrunt.log.warn = (msg) => { 193 | gruntWarnings.push(msg); 194 | }; 195 | 196 | mockGrunt.fail.warn = (err, code) => { 197 | taskDone(); 198 | }; 199 | 200 | let taskObj = getGruntTaskObject([{ 201 | dest: 'unused.js', 202 | src: ['dne.js'] 203 | }], { 204 | compilation_level: 'SIMPLE' 205 | }, () => { 206 | taskDone(); 207 | }); 208 | 209 | closureCompiler.call(taskObj); 210 | }); 211 | 212 | it('should run once for each destination', (done) => { 213 | const fileOneDest = 'test/out/one.js'; 214 | const fileTwoDest = 'test/out/two.js'; 215 | 216 | function taskDone() { 217 | const fileOne = fs.statSync(fileOneDest); 218 | expect(fileOne.isFile()).toBe(true); 219 | fs.unlinkSync(fileOneDest); 220 | 221 | const fileTwo = fs.statSync(fileTwoDest); 222 | expect(fileTwo.isFile()).toBe(true); 223 | fs.unlinkSync(fileTwoDest); 224 | 225 | fs.rmdirSync('test/out'); 226 | ensureCorrectPlatformUtilized(); 227 | done(); 228 | } 229 | 230 | mockGrunt.fail.warn = (err, code) => { 231 | console.error(err); 232 | fail('should not caused an error'); 233 | taskDone(); 234 | }; 235 | 236 | const taskObj = getGruntTaskObject([ 237 | {src: ['test/fixtures/one.js'], dest: fileOneDest}, 238 | {src: ['test/fixtures/one.js', 'test/fixtures/two.js'], dest: fileTwoDest} 239 | ], { 240 | compilation_level: 'SIMPLE' 241 | }, () => { 242 | taskDone(); 243 | }); 244 | 245 | closureCompiler.call(taskObj); 246 | }); 247 | 248 | it('should run when grunt provides no files', (done) => { 249 | const fileOneDest = 'test/out/one.js'; 250 | 251 | function taskDone() { 252 | const fileOne = fs.statSync(fileOneDest); 253 | expect(fileOne.isFile()).toBe(true); 254 | fs.unlinkSync(fileOneDest); 255 | 256 | fs.rmdirSync('test/out'); 257 | done(); 258 | } 259 | 260 | mockGrunt.fail.warn = (err, code) => { 261 | fail('should not caused an error'); 262 | taskDone(); 263 | }; 264 | 265 | const taskObj = getGruntTaskObject([], { 266 | compilation_level: 'SIMPLE', 267 | js: 'test/fixtures/one.js', 268 | js_output_file: fileOneDest 269 | }, () => { 270 | taskDone(); 271 | }); 272 | 273 | closureCompiler.call(taskObj); 274 | ensureCorrectPlatformUtilized(); 275 | }); 276 | 277 | it('should support an arguments array', (done) => { 278 | const fileOneDest = 'test/out/one.js'; 279 | 280 | function taskDone() { 281 | const fileOne = fs.statSync(fileOneDest); 282 | expect(fileOne.isFile()).toBe(true); 283 | fs.unlinkSync(fileOneDest); 284 | 285 | fs.rmdirSync('test/out'); 286 | ensureCorrectPlatformUtilized(); 287 | done(); 288 | } 289 | 290 | mockGrunt.fail.warn = (err, code) => { 291 | fail('should not caused an error'); 292 | taskDone(); 293 | }; 294 | 295 | const taskObj = getGruntTaskObject([], { 296 | args: [ 297 | '--compilation_level=SIMPLE', 298 | '--js=test/fixtures/one.js', 299 | '--js_output_file=' + fileOneDest 300 | ] 301 | }, () => { 302 | taskDone(); 303 | }); 304 | 305 | closureCompiler.call(taskObj); 306 | }); 307 | }); 308 | }); 309 | }); 310 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/gulp.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for gulp-google-closure-compiler plugin 19 | * 20 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 21 | */ 22 | import {Transform} from 'node:stream'; 23 | import {fileURLToPath, URL} from 'node:url'; 24 | import gulp from 'gulp'; 25 | import sourcemaps from 'gulp-sourcemaps'; 26 | import File from 'vinyl'; 27 | import { 28 | default as ClosureCompiler, 29 | gulp as closureCompiler, 30 | } from '../index.js'; 31 | 32 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 33 | const javaOnly = process.argv.find((arg) => arg == '--java-only'); 34 | const platforms = ['java']; 35 | if (!javaOnly) { 36 | platforms.push('native'); 37 | } 38 | 39 | process.on('unhandledRejection', (e) => { throw e; }); 40 | 41 | class ExpectFile extends Transform { 42 | constructor(fileMatcher) { 43 | super({objectMode: true}); 44 | this.fileCount = 0; 45 | this.fileMatcher = fileMatcher; 46 | } 47 | _transform(file, _, cb) { 48 | this.fileCount++; 49 | this.fileMatcher.call(this, file); 50 | this.push(file); 51 | cb(); 52 | } 53 | } 54 | 55 | describe('gulp-google-closure-compiler', function() { 56 | let originalCompilerRunMethod; 57 | let platformUtilized; 58 | 59 | beforeEach(() => { 60 | platformUtilized = undefined; 61 | originalCompilerRunMethod = ClosureCompiler.prototype.run; 62 | spyOn(ClosureCompiler.prototype, 'run') 63 | .and.callFake(function(...args) { 64 | const retVal = originalCompilerRunMethod.apply(this, args); 65 | platformUtilized = /^java/.test(this.getFullCommand()) ? 'java' : 'native'; 66 | return retVal; 67 | }); 68 | }); 69 | 70 | platforms.forEach((platform) => { 71 | const ensureCorrectPlatformUtilized = () => { 72 | if (platformUtilized) { 73 | expect(platform).toBe(platformUtilized); 74 | } 75 | }; 76 | 77 | describe(`${platform} version`, function() { 78 | const fakeFile1 = new File({ 79 | path: '/foo.js', 80 | contents: Buffer.from('console.log("foo");') 81 | }); 82 | const fakeFile2 = new File({ 83 | path: '/bar.js', 84 | contents: Buffer.from('console.log("bar");') 85 | }); 86 | 87 | const fixturesCompiled = 'function log(a){console.log(a)}log("one.js");' + 88 | 'var WindowInfo=function(){this.props=[]};WindowInfo.prototype.propList=function(){' + 89 | 'for(var a in window)this.props.push(a)};WindowInfo.prototype.list=function(){' + 90 | 'log(this.props.join(", "))};(new WindowInfo).list();\n'; 91 | 92 | let originalTimeout; 93 | beforeEach(() => { 94 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 95 | if (platform === 'java') { 96 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 97 | } 98 | }); 99 | afterEach(() => { 100 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 101 | }); 102 | 103 | it('should emit an error for invalid flag', async () => { 104 | let resolvePromise; 105 | const complete = new Promise((resolve) => { 106 | resolvePromise = resolve; 107 | }); 108 | const stream = closureCompiler({ 109 | foo: 'BAR' 110 | }, { 111 | platform 112 | }); 113 | 114 | let error; 115 | stream.on('error', (err) => { 116 | error = err; 117 | resolvePromise(); 118 | }); 119 | stream.write(fakeFile1); 120 | stream.end(); 121 | await complete; 122 | expect(error.message).toMatch(/^(gulp-google-closure-compiler: )?Compilation error/); 123 | ensureCorrectPlatformUtilized(); 124 | }); 125 | 126 | it('should emit an error for a flag with an invalid value', async () => { 127 | let resolvePromise; 128 | const complete = new Promise((resolve) => { 129 | resolvePromise = resolve; 130 | }); 131 | const stream = closureCompiler({ 132 | compilation_level: 'FOO' 133 | }, { 134 | platform 135 | }); 136 | 137 | let error; 138 | stream.on('error', (err) => { 139 | error = err; 140 | resolvePromise(); 141 | }); 142 | stream.write(fakeFile1); 143 | stream.end(); 144 | await complete; 145 | expect(error.message).toMatch(/^(gulp-google-closure-compiler: )?Compilation error/); 146 | ensureCorrectPlatformUtilized(); 147 | }); 148 | 149 | it('should compile a single file', async () => { 150 | let resolvePromise; 151 | const complete = new Promise((resolve) => { 152 | resolvePromise = resolve; 153 | }); 154 | const stream = closureCompiler({ 155 | compilation_level: 'SIMPLE', 156 | warning_level: 'VERBOSE' 157 | }, { 158 | platform 159 | }); 160 | 161 | const expectFile = new ExpectFile( 162 | (file) => expect(file.contents.toString().trim()).toBe(fakeFile1.contents.toString()) 163 | ); 164 | stream.pipe(expectFile).on('finish', resolvePromise); 165 | 166 | stream.write(fakeFile1); 167 | stream.end(); 168 | await complete; 169 | expect(expectFile.fileCount).toBe(1); 170 | ensureCorrectPlatformUtilized(); 171 | }); 172 | 173 | it('should name the output file when no js_output_file option is provided', async () => { 174 | let resolvePromise; 175 | const complete = new Promise((resolve) => { 176 | resolvePromise = resolve; 177 | }); 178 | const stream = closureCompiler({ 179 | compilation_level: 'SIMPLE', 180 | warning_level: 'VERBOSE' 181 | }, { 182 | platform 183 | }); 184 | const expectFile = new ExpectFile( 185 | (file) => expect(file.path).toBe('compiled.js') 186 | ); 187 | stream.pipe(expectFile).on('finish', resolvePromise); 188 | 189 | stream.write(fakeFile1); 190 | stream.end(); 191 | await complete; 192 | expect(expectFile.fileCount).toBe(1); 193 | ensureCorrectPlatformUtilized(); 194 | }); 195 | 196 | it('should name the output file from the js_output_file option', async () => { 197 | let resolvePromise; 198 | const complete = new Promise((resolve) => { 199 | resolvePromise = resolve; 200 | }); 201 | const stream = closureCompiler({ 202 | compilation_level: 'SIMPLE', 203 | warning_level: 'VERBOSE', 204 | js_output_file: 'out.js' 205 | }, { 206 | platform 207 | }); 208 | const expectFile = new ExpectFile( 209 | (file) => expect(file.path).toBe('out.js') 210 | ); 211 | stream.pipe(expectFile).on('finish', resolvePromise); 212 | 213 | stream.write(fakeFile1); 214 | stream.end(); 215 | await complete; 216 | expect(expectFile.fileCount).toBe(1); 217 | ensureCorrectPlatformUtilized(); 218 | }); 219 | 220 | it('should compile multiple input files into a single output', async () => { 221 | let resolvePromise; 222 | const complete = new Promise((resolve) => { 223 | resolvePromise = resolve; 224 | }); 225 | const stream = closureCompiler({ 226 | compilation_level: 'SIMPLE', 227 | warning_level: 'VERBOSE' 228 | }, { 229 | platform 230 | }); 231 | 232 | const expectFile = new ExpectFile( 233 | (file) => expect(file.contents.toString().trim()) 234 | .toBe(fakeFile1.contents.toString() + fakeFile2.contents.toString()) 235 | ); 236 | stream.pipe(expectFile).on('finish', resolvePromise); 237 | 238 | stream.write(fakeFile1); 239 | stream.write(fakeFile2); 240 | stream.end(); 241 | await complete; 242 | expect(expectFile.fileCount).toBe(1); 243 | ensureCorrectPlatformUtilized(); 244 | }); 245 | 246 | it('should compile multiple inputs into multiple outputs with chunk options', async () => { 247 | let resolvePromise; 248 | const complete = new Promise((resolve) => { 249 | resolvePromise = resolve; 250 | }); 251 | const stream = closureCompiler({ 252 | compilation_level: 'SIMPLE', 253 | warning_level: 'VERBOSE', 254 | chunk: [ 255 | 'one:1', 256 | 'two:1' 257 | ], 258 | createSourceMap: true 259 | }, { 260 | platform 261 | }); 262 | 263 | const expectFile = new ExpectFile(function (file){ 264 | if (this.fileCount === 1) { 265 | expect(file.contents.toString().trim()).toBe(fakeFile1.contents.toString()); 266 | expect(file.path).toBe('one.js'); 267 | } else if (this.fileCount === 2) { 268 | expect(file.contents.toString().trim()).toBe(fakeFile2.contents.toString()); 269 | expect(file.path).toBe('two.js'); 270 | } 271 | }); 272 | 273 | stream.pipe(expectFile).on('finish', resolvePromise); 274 | 275 | stream.write(fakeFile1); 276 | stream.write(fakeFile2); 277 | stream.end(); 278 | await complete; 279 | expect(expectFile.fileCount).toBe(2); 280 | ensureCorrectPlatformUtilized(); 281 | }); 282 | 283 | it('should generate a sourcemap for a single output file', async () => { 284 | const expectFile = new ExpectFile((file) => { 285 | expect(file.sourceMap.sources.length).toBe(2); 286 | expect(file.sourceMap.file).toBe('compiled.js'); 287 | }); 288 | await new Promise((resolve) => 289 | gulp.src('test/fixtures/**/*.js', {base: './'}) 290 | .pipe(sourcemaps.init()) 291 | .pipe(closureCompiler({ 292 | compilation_level: 'SIMPLE', 293 | warning_level: 'VERBOSE' 294 | }, { 295 | platform 296 | })) 297 | .pipe(expectFile) 298 | .on('finish', resolve) 299 | ); 300 | expect(expectFile.fileCount).toBe(1); 301 | ensureCorrectPlatformUtilized(); 302 | }); 303 | 304 | it('should generate a sourcemap for each output file with chunks', async () => { 305 | const expectFile = new ExpectFile(function (file){ 306 | if (this.fileCount === 1) { 307 | expect(file.sourceMap.sources.length).toBe(1); 308 | expect(file.sourceMap.file).toBe('./one.js'); 309 | } else if (this.fileCount === 2) { 310 | expect(file.sourceMap.sources.length).toBe(1); 311 | expect(file.sourceMap.file).toBe('./two.js'); 312 | } 313 | }); 314 | await new Promise((resolve) => 315 | gulp.src([`${__dirname}fixtures/one.js`, `${__dirname}fixtures/two.js`]) 316 | .pipe(sourcemaps.init()) 317 | .pipe(closureCompiler({ 318 | compilation_level: 'SIMPLE', 319 | warning_level: 'VERBOSE', 320 | chunk: [ 321 | 'one:1', 322 | 'two:1:one' 323 | ], 324 | createSourceMap: true 325 | }, { 326 | debugLog: true, 327 | platform 328 | })) 329 | .pipe(expectFile) 330 | .on('finish', resolve) 331 | ); 332 | expect(expectFile.fileCount).toBe(2); 333 | ensureCorrectPlatformUtilized(); 334 | }); 335 | 336 | it('should support passing input globs directly to the compiler', async () => { 337 | const expectFile = new ExpectFile((file) => { 338 | expect(file.contents.toString()).toBe(fixturesCompiled); 339 | }); 340 | await new Promise((resolve) => 341 | closureCompiler({ 342 | js: `${__dirname}fixtures/**.js`, 343 | compilation_level: 'SIMPLE', 344 | warning_level: 'VERBOSE', 345 | language_out: 'ECMASCRIPT5' 346 | }, { 347 | platform 348 | }).src().pipe(expectFile).on('finish', resolve) 349 | ); 350 | expect(expectFile.fileCount).toBe(1); 351 | ensureCorrectPlatformUtilized(); 352 | }); 353 | 354 | it('should include js options before gulp.src files', async () => { 355 | const expectFile = new ExpectFile((file) => { 356 | expect(file.contents.toString()).toBe(fixturesCompiled); 357 | }); 358 | await new Promise((resolve) => 359 | gulp.src(`${__dirname}fixtures/two.js`) 360 | .pipe(closureCompiler({ 361 | js: `${__dirname}fixtures/one.js`, 362 | compilation_level: 'SIMPLE', 363 | warning_level: 'VERBOSE', 364 | language_out: 'ECMASCRIPT5' 365 | }, { 366 | platform 367 | })) 368 | .pipe(expectFile) 369 | .on('finish', resolve) 370 | ); 371 | expect(expectFile.fileCount).toBe(1); 372 | ensureCorrectPlatformUtilized(); 373 | }); 374 | 375 | it('should support calling the compiler with an arguments array', async () => { 376 | const expectFile = new ExpectFile((file) => { 377 | expect(file.contents.toString()).toBe(fixturesCompiled); 378 | }); 379 | await new Promise((resolve) => 380 | closureCompiler([ 381 | `--js="${__dirname}fixtures/**.js"`, 382 | '--compilation_level=SIMPLE', 383 | '--warning_level=VERBOSE', 384 | '--language_out=ECMASCRIPT5' 385 | ], { 386 | platform 387 | }).src().pipe(expectFile).on('finish', resolve) 388 | ); 389 | expect(expectFile.fileCount).toBe(1); 390 | ensureCorrectPlatformUtilized(); 391 | }); 392 | 393 | it('should compile without gulp.src files when .src() is called', async () => { 394 | const expectFile = new ExpectFile((file) => { 395 | expect(file.contents.toString()).toBe(fixturesCompiled); 396 | }); 397 | await new Promise((resolve) => 398 | closureCompiler({ 399 | compilation_level: 'SIMPLE', 400 | warning_level: 'VERBOSE', 401 | language_out: 'ECMASCRIPT5', 402 | js: `${__dirname}fixtures/**.js`, 403 | }, { 404 | platform 405 | }).src().pipe(expectFile).on('finish', resolve) 406 | ); 407 | expect(expectFile.fileCount).toBe(1); 408 | ensureCorrectPlatformUtilized(); 409 | }); 410 | 411 | it('should generate no output without gulp.src files', async () => { 412 | const expectFile = new ExpectFile((file) => { 413 | fail('should not produce output files'); 414 | }); 415 | await new Promise((resolve) => 416 | gulp.src('test/does-not-exist.js', {allowEmpty: true}) 417 | .pipe(closureCompiler({ 418 | compilation_level: 'SIMPLE', 419 | warning_level: 'VERBOSE' 420 | }, { 421 | platform 422 | })).pipe(expectFile).on('finish', resolve) 423 | ); 424 | expect(expectFile.fileCount).toBe(0); 425 | ensureCorrectPlatformUtilized(); 426 | }); 427 | 428 | it('should properly compose sourcemaps when multiple transformations are chained', async () => { 429 | const expectFile = new ExpectFile((file) => { 430 | expect(file.sourceMap.sources).toContain('test/fixtures/one.js'); 431 | expect(file.sourceMap.sources).toContain('test/fixtures/two.js'); 432 | }); 433 | await new Promise((resolve) => 434 | gulp.src(['test/fixtures/one.js', 'test/fixtures/two.js'], {base: './'}) 435 | .pipe(sourcemaps.init()) 436 | .pipe(closureCompiler({ 437 | compilation_level: 'WHITESPACE_ONLY', 438 | warning_level: 'VERBOSE', 439 | formatting: 'PRETTY_PRINT', 440 | sourceMapIncludeContent: true 441 | }, { 442 | platform 443 | })) 444 | .pipe(closureCompiler({ 445 | compilation_level: 'SIMPLE', 446 | warning_level: 'QUIET', 447 | formatting: 'PRETTY_PRINT', 448 | js_output_file: 'final.js', 449 | sourceMapIncludeContent: true 450 | }, { 451 | platform 452 | })) 453 | .pipe(expectFile) 454 | .on('error', (err) => { 455 | console.error(err); 456 | }) 457 | .on('finish', resolve) 458 | ); 459 | expect(expectFile.fileCount).toBe(1); 460 | ensureCorrectPlatformUtilized(); 461 | }); 462 | 463 | it('in streaming mode should emit an error', async () => { 464 | // Gulp throws a global uncatchable stream error 465 | // Handle the error so that the test suite does not fail 466 | const globalExceptionListener = (err) => {}; 467 | process.on('uncaughtException', globalExceptionListener); 468 | let errorEncountered = false; 469 | await new Promise((resolve) => { 470 | gulp.src(`${__dirname}fixtures/**/*.js`, {buffer: false}) 471 | .pipe(closureCompiler({ 472 | compilation_level: 'SIMPLE', 473 | warning_level: 'VERBOSE' 474 | }, { 475 | platform 476 | })) 477 | .on('error', (err) => { 478 | errorEncountered = true; 479 | expect(err.message).toMatch(/^(gulp-google-closure-compiler: )?Streaming not supported/); 480 | resolve(); 481 | }); 482 | }); 483 | expect(errorEncountered).toBe(true); 484 | await new Promise((resolve) => { 485 | setTimeout(() => { 486 | process.off('uncaughtException', globalExceptionListener); 487 | resolve(); 488 | }); 489 | }); 490 | }); 491 | }); 492 | }); 493 | }); 494 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/node.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for google-closure-compiler node bindings 19 | * 20 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 21 | */ 22 | 23 | import {default as Compiler, JAR_PATH, CONTRIB_PATH, EXTERNS_PATH} from '../index.js'; 24 | 25 | process.on('unhandledRejection', e => { throw e; }); 26 | 27 | describe('closure-compiler node bindings', () => { 28 | it('should export a property for the jar path', () => { 29 | expect(JAR_PATH).toMatch(/[\/\\]compiler\.jar$/); 30 | }); 31 | 32 | it('should export a property for the contrib folder', () => { 33 | expect(CONTRIB_PATH).toMatch(/[\/\\]contrib$/); 34 | }); 35 | 36 | it('should export a property for the externs folder', () => { 37 | expect(EXTERNS_PATH).toMatch(/[\/\\]externs$/); 38 | }); 39 | 40 | describe('java version', () => { 41 | let originalTimeout; 42 | beforeEach(() => { 43 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 44 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 45 | }); 46 | afterEach(() => { 47 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 48 | }); 49 | 50 | it('should error when java is not in the path', async () => { 51 | const compiler = new Compiler({version: true}); 52 | compiler.javaPath = 'DOES_NOT_EXIST'; 53 | let hasRun = false; 54 | await new Promise((resolve) => { 55 | compiler.run((exitCode, stdout, stderr) => { 56 | if (hasRun) { 57 | return; 58 | } 59 | hasRun = true; 60 | expect(exitCode).not.toBe(0); 61 | expect(stderr.indexOf('Is java in the path?')).toBeGreaterThanOrEqual(0); 62 | resolve(); 63 | }); 64 | }); 65 | expect(hasRun).toBe(true); 66 | }); 67 | 68 | it('should normalize an options object to an arguments array', () => { 69 | const compiler = new Compiler({ 70 | one: true, 71 | two: 'two', 72 | three: ['one', 'two', 'three'] 73 | }); 74 | 75 | const expectedArray = ['--one=true', '--two=two', 76 | '--three=one', '--three=two', '--three=three']; 77 | expect(compiler.commandArguments.length).toBe(expectedArray.length); 78 | compiler.commandArguments.forEach((item, index) => { 79 | expect(expectedArray[index]).toBe(item); 80 | }); 81 | }); 82 | 83 | it('should prepend the -jar argument and compiler path when configured by array', async () => { 84 | const expectedArray = ['-jar', JAR_PATH, '--one=true', '--two=two', 85 | '--three=one', '--three=two', '--three=three']; 86 | 87 | const compiler = new Compiler(expectedArray.slice(2)); 88 | await new Promise((resolve) => compiler.run(resolve)); 89 | 90 | expect(compiler.commandArguments.length).toBe(expectedArray.length); 91 | compiler.commandArguments.forEach((item, index) => { 92 | expect(expectedArray[index]).toBe(item); 93 | }); 94 | }); 95 | 96 | describe('extra command arguments', () => { 97 | it('should include initial command arguments when configured by an options object', async () => { 98 | const expectedArray = ['-Xms2048m', '-jar', JAR_PATH, '--one=true', '--two=two', 99 | '--three=one', '--three=two', '--three=three']; 100 | 101 | const compiler = new Compiler(expectedArray.slice(3), expectedArray.slice(0, 1)); 102 | await new Promise((resolve) => compiler.run(resolve)); 103 | 104 | expect(compiler.commandArguments.length).toBe(expectedArray.length); 105 | compiler.commandArguments.forEach(function (item, index) { 106 | expect(expectedArray[index]).toBe(item); 107 | }); 108 | }); 109 | 110 | it('should include initial command arguments when configured by array', async () => { 111 | const expectedArray = ['-Xms2048m', '-jar', JAR_PATH, '--one=true', '--two=two', 112 | '--three=one', '--three=two', '--three=three']; 113 | 114 | const compiler = new Compiler(expectedArray.slice(3), expectedArray.slice(0, 1)); 115 | await new Promise((resolve) => compiler.run(resolve)); 116 | expect(compiler.commandArguments.length).toBe(expectedArray.length); 117 | compiler.commandArguments.forEach(function (item, index) { 118 | expect(expectedArray[index]).toBe(item); 119 | }); 120 | }); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/support/jasmine-launcher.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright 2025 The Closure Compiler Authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * @fileoverview 19 | * 20 | * Execute jasmine with the arguments in the correct order. 21 | * Extra arguments passed to test specs must be preceded by an '--' argument. 22 | * We want to keep the arguments in the same order, but arguments for the jasmine runner itself must 23 | * come first followed by a '--' argument and then finally by any extra arguments. 24 | */ 25 | 26 | import {spawn} from 'node:child_process'; 27 | import parseArgs from 'minimist'; 28 | 29 | const supportedJasmineFlags = new Set([ 30 | 'parallel', 31 | 'no-color', 32 | 'color', 33 | 'filter', 34 | 'helper', 35 | 'require', 36 | 'fail-fast', 37 | 'config', 38 | 'reporter', 39 | 'verbose', 40 | ]); 41 | 42 | const cliFlags = parseArgs(process.argv.slice(2)); 43 | const jasmineFlags = []; 44 | const extraFlags = []; 45 | for (const [name, value] of Object.entries(cliFlags)) { 46 | const normalizedValues = Array.isArray(value) ? value : [value]; 47 | if (name === '_') { 48 | jasmineFlags.push(...value); 49 | } else if (supportedJasmineFlags.has(name)) { 50 | for (const normalizedValue of normalizedValues) { 51 | jasmineFlags.push(`--${name}${typeof normalizedValue === 'boolean' ? '' : `=${normalizedValue}`}`); 52 | } 53 | } else { 54 | for (const normalizedValue of normalizedValues) { 55 | extraFlags.push(`--${name}${typeof normalizedValue === 'boolean' ? '' : `=${normalizedValue}`}`); 56 | } 57 | } 58 | } 59 | 60 | const flagName = (flag) => { 61 | if (flag.startsWith('--')) { 62 | const valStart = flag.indexOf('=', 2); 63 | return flag.slice(0, valStart > 0 ? valStart : flag.length); 64 | } 65 | return flag; 66 | } 67 | const flagSorter = (a, b) => { 68 | const aFlagName = flagName(a); 69 | const bFlagName = flagName(b); 70 | const aIndex = process.argv.findIndex((arg) => arg === aFlagName || arg.startsWith(`${aFlagName}=`)); 71 | const bIndex = process.argv.findIndex((arg) => arg === bFlagName || arg.startsWith(`${bFlagName}=`)); 72 | return aIndex - bIndex; 73 | }; 74 | jasmineFlags.sort(flagSorter); 75 | extraFlags.sort(flagSorter); 76 | if (extraFlags.length > 0) { 77 | jasmineFlags.push('--', ...extraFlags); 78 | } 79 | 80 | const jasmineProcess = spawn( 81 | 'jasmine', 82 | jasmineFlags, 83 | { 84 | stdio: 'inherit', 85 | ...(process.platform === 'win32' ? { shell: true } : {}), 86 | }, 87 | ); 88 | 89 | jasmineProcess.on('close', (exitCode) => { 90 | process.exitCode = exitCode; 91 | }); 92 | -------------------------------------------------------------------------------- /packages/google-closure-compiler/test/support/jasmine.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Run the test commands and fail the script if any of them failed 3 | EXIT_STATUS=0 4 | ./test/support/jasmine-launcher.js "$@" || EXIT_STATUS=$? 5 | exit $EXIT_STATUS 6 | -------------------------------------------------------------------------------- /test/compiler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for compiler.jar versions 19 | * 20 | * @author Chad Killingsworth (chadkillingsworth@gmail.com) 21 | */ 22 | 23 | import {spawnSync as spawn} from 'node:child_process'; 24 | import fs from 'node:fs'; 25 | import path from 'node:path'; 26 | import {fileURLToPath, URL} from 'node:url'; 27 | import {compiler as Compiler} from 'google-closure-compiler'; 28 | import Semver from 'semver'; 29 | 30 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 31 | const packageInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8')); 32 | 33 | const compilerVersionMatch = /^Version: v(\d+)$/m; 34 | 35 | process.on('unhandledRejection', (e) => { throw e; }); 36 | 37 | describe('compiler.jar', function () { 38 | it('should not be a snapshot build', async () => { 39 | const compiler = new Compiler({version: true}); 40 | const {stdout} = await new Promise((resolve) => 41 | compiler.run((exitCode, stdout, stderr) => 42 | resolve({ 43 | exitCode, 44 | stdout, 45 | stderr, 46 | }) 47 | ) 48 | ); 49 | let versionInfo = (stdout || '').match(compilerVersionMatch); 50 | expect(versionInfo).not.toBeNullish(); 51 | versionInfo = versionInfo || []; 52 | expect(versionInfo.length).toBe(2); 53 | expect(versionInfo[1].indexOf('SNAPSHOT')).toBeLessThan(0); 54 | }); 55 | 56 | it('version should be equal to the package major version', async () => { 57 | const compiler = new Compiler({version: true}); 58 | const packageVer = new Semver(packageInfo.version); 59 | const {stdout} = await new Promise((resolve) => 60 | compiler.run((exitCode, stdout, stderr) => 61 | resolve({ 62 | exitCode, 63 | stdout, 64 | stderr, 65 | }) 66 | ) 67 | ); 68 | let versionInfo = (stdout || '').match(compilerVersionMatch); 69 | expect(versionInfo).not.toBeNullish(); 70 | versionInfo = versionInfo || []; 71 | expect(versionInfo.length).toBe(2); 72 | 73 | let compilerVersion; 74 | try { 75 | console.log(versionInfo[1] + '.0.0'); 76 | compilerVersion = new Semver(versionInfo[1] + '.0.0'); 77 | } catch (e) { 78 | fail('should be a semver parseable'); 79 | } 80 | expect(compilerVersion.major).toBe(packageVer.major); 81 | }); 82 | }); 83 | 84 | describe('compiler submodule', () => { 85 | it('should be synced to the tagged commit', () => { 86 | const gitCmd = spawn('git', ['tag', '--points-at', 'HEAD'], { 87 | cwd: './compiler' 88 | }); 89 | expect(gitCmd.status).toBe(0); 90 | console.log(gitCmd.stdout.toString()); 91 | const currentTag = gitCmd.stdout.toString().replace(/\s/g, ''); 92 | const packageVer = new Semver(packageInfo.version); 93 | const mvnVersion = 'v' + packageVer.major; 94 | let normalizedTag = currentTag; 95 | if (normalizedTag) { 96 | normalizedTag = currentTag.replace(/^([-a-z]+-)?(v\d{8})(.*)$/, '$2'); 97 | } 98 | expect(normalizedTag).toBe(mvnVersion) 99 | }); 100 | }); 101 | --------------------------------------------------------------------------------